路由器到底在做什么? 

SNAT:先路由后NAT

DNAT:先NAT后路由

第一个动作当然是进行CRC校验啦。路由器接受到数据帧(数据还是第二层状态时的称呼)第一步当然是先拆看前同步码以及帧尾的CRC校验部分, 只有校验对了才能进行后续的处理。这里,如果是交换机(普通的二层交换机)的话,紧接的是查看目的MAC和源MAC以及第三个字段——EtherType 字段。然后根据目的MAC和EtherType字段的值进行交换策略(具体的交互,不详细讨论,有兴趣的话告诉我!呵呵)。但如果是路由器的话,会继续拆 解数据帧,一般的情况EtherType都是0x0800(IP),也有可能是0x0806(ARP查询),又或者是0x8100(802.1q和 802.1p)。到了第三层的状态的时候应该是报文了(Packet,有人也叫它包,我也比较喜欢称这层的PDU叫包)。

重新描述一下数据包的格式:
(以下顺序是按数据包的字节顺序)
Version:长度4比特。标识目前采用的IP协议的版本号。一般的值为0100(IPv4),IPv6的值(0110)

Header Length:长度4比特。这个字段的作用是为了描述IP包头的长度,因为在IP包头中有变长的可选部分。IP包头最小长度为20字节,由于变长的可选部分最大长度可能会变成24字节。

Type of Service:长度8比特。这个子段可以拆分成两个部分:Precedence和TOS。TOS目前不太使用。而Precedence则用于QOS应用。

Total Length:长度16比特。IP包最大长度65535字节。

Identifier: 长度16比特。该字段和Flags和Fragment Offest字段联合使用,对大的上层数据包进行分段(fragment)操作。

Flags: 长度3比特。该字段第一位不使用。第二位是DF位,DF位设为1时表明路由器不能对该上层数据包分段。如果一个上层数据包无法在不分段的情况下进行转发, 则路由器会丢弃该上层数据包并返回一个错误信息。第三位是MF位,当路由器对一个上层数据包分段,则路由器会在除了最后一个分段的IP包的包头中将MF位 设为1。

Fragment Offset:长度13比特。该字段对包含分段的上层数据包的IP包赋予序号。由于IP包在网 络上传送的时候不一定能按顺序到达,这个字段保证了目标路由器在接受到IP包之后能够还原分段的上层数据包。到某个包含分段的上层数据包的IP包在传送是 丢失,则整个一系列包含分段的上层数据包的IP包都会被要求重传。

TTL:长度8比特。当IP包进行传送时,先会对该字段赋予某个特定的值。当IP包经过每一个沿途的路由器的时候,每个沿途的路由器会将IP包的TTL值减少1。如果TTL减少为0,则该IP包会被丢弃。这个字段可以防止由于故障而导致IP包在网络中不停被转发。

Protocol:长度8比特。标识了上层所使用的协议。

Header Checksum:长度16位,由于IP包头是变长的,所以提供一个头部校验来保证IP包头中信息的正确性。

Source and Destination IP Addresses:这两个地段都是32比特。标识了这个IP包的起源和目标地址。

Options:这是一个可变长的字段。该字段由起源设备根据需要改写。可选项目包含以下内容:
松散源路由(Loose source routing):给出一连串路由器接口的IP地址。IP包必须沿着这些IP地址传送,但是允许在相继的两个IP地址之间跳过多个路由器。
严格源路由(Strict source routing):给出一连串路由器接口的IP地址。IP包必须沿着这些IP地址传送,如果下一跳不在IP地址表中则表示发生错误。
路由记录(Record route):当IP包离开每个路由器的时候记录路由器的出站接口的IP地址。
时间戳(Timestamps):当IP包离开每个路由器的时候记录时间。

这是大概的一个IPv4数据包的描述。从 包的结构大概可以看出我们的NAT功能是不能在该层进行过多的操作。所以,肯定是通过上一层来进行后续的操作。这一层只能提供一个srcIP和desIP 和Protocol来为上层协议进行后续的处理。也就是说如果路由器做了NAT后,继续拆看第四层的信息(尤其是PAT,拆分第四层是必须的)。路由器需 要查看第四层的port这个字段的信息,以此来判断上层的服务程序,同时也是通过这个字段的信息来协同其它几个字段来唯一标识NAT talbe中的一条session。为什么需要第四层的port信息呢?

这里用几个例子来说明:

(1) A - [Ra] ============== [Rb] - B

A和B通讯,走IP。两端各有一个路由器。正常配置。

一个TC包从A发出来,
IP层的信息:srcIP=A,destIP=B,protocl=TCP。
TCP层的信息:srcPort=10000,destPort=80。

这个包穿过Ra和Rb(以及中间任何可能的路由设备),L2的封装不断变化,但IP和TCP层的信息均不改变。

B 的以太网卡看到这个帧,发现EtherType=0x0800,就上交给IP;IP部分接到这个包,看到IPprotocol=TCP,就上交给 TCP;TCP接到这个datagram,发现destPort=80,于是上就给本地web server process。


这个例子是普通情况下的数据包的收包拆包的过程,希望各位能理解这一步。

(2) A - [Ra]-[NAT] ============== [Rb] - B

A侧加了个NAT。注意,你没有权利要求B端也加NAT。换句话说,NAT对A、B、Rb都是透明的(实际上对Ra也是)。

按你的思路:

一个TC包从A发出来,
IP层的信息:srcIP=A,destIP=B,protocl=TCP。
TCP层的信息:srcPort=10000,destPort=80。

- 经过Ra,无改变;
- 经过NAT,变成:
IP层的信息:srcIP=A',destIP=B,protocl=NAT。
TCP层的信息:srcPort=10000,destPort=80。(注意A被NAT成了A')
-此后这个NAT过的包穿过Rb,到达B。B会怎么办?

B 的以太网卡看到这个帧,发现EtherType=0x800,就上交给IP;IP部分接到这个包,看到IPprotocol=NAT....嗯??!!这 个奇怪协议的datagram交给谁?注意我们的假定是B端无需理解NAT。B只能discard (标准IP行为)。

还是那句话,(B端)谁能负责理解你的NAT hints并且把这些hints又插入返回的包?


这里当初自己一直以为IP会在Protocol字段加点什么,其实这个字段无非就是那几个值,什么TCP什么UDP啦之类的。NAT的实现其实并没有和这个有多大的相关,只是会记录下这个session是通过什么相连的:TCP or UDP。



注意了,下面这个例子是Creator3D哥们的精彩部分

A1,A2 - [Ra]-[PAT] ============== [Rb] - B

A1和A2访问B的web服务。PAT外侧端口地址(Cisco所谓的Inside Global Address)记作Aout。初始状态下,NAT还没有建立任何translation。

一个TCP包(记作Pkt_A1吧)从A1发出来,
IP层的信息:srcIP=A1,destIP=B,protocl=TCP。
TCP层的信息:srcPort=10000,destPort=80。

差不多同时,另一个TCP包从A2发出来(记作Pkt_A2),
IP层的信息:srcIP=A2,destIP=B,protocl=TCP。
TCP层的信息:srcPort=10000,destPort=80。

注意,两个包的srcPort都是10000。这样的可能性很小,只是为了说明最坏的情况。

- 经过Ra,无改变;
- 经过PAT,
Pkt_A1变成:
IP层的信息:srcIP=Aout,destIP=B,protocl=TCP。
TCP层的信息:srcPort=32001,destPort=80。(注意srcIP和srcPort)
Pkt_A2变成:
IP层的信息:srcIP=Aout,destIP=B,protocl=TCP。
TCP层的信息:srcPort=32002,destPort=80。(注意srcIP和srcPort)


两个包转发走了,PAT的translation表上也多了两项:

Prot__inLocl_____inGlbl______outLocl___outGlbl
TCP___A1:10000___Aout:32001__B:80______B:80
TCP___A2:10000___Aout:32002__B:80______B:80



- 此后这个两个包穿过Rb,到达B。在B看来,这是同一个IP host(Aout)发出来的两个HTTP session,交给web service分别予以响应,返回两个包:
Pkt_B1:
IP层:srcIP=B,destIP=Aout,protocl=TCP。
TCP层:srcPort=80,destPort=32001。
Pkt_B2:
IP层的信息:srcIP=B,destIP=Aout,protocl=TCP。
TCP层的信息:srcPort=80,destPort=32002。

- Pkt_B1 和 B2送达PAT
PAT怎么办?当然是用包里携带的信息检索NAT translation表。
现在仔细看看这个NAT表。
推论一,不能允许任意两行内容完全一样(废话吧?)

推论二,不会有任意两行有相同的inLocal IP:_port + outLocal IP:_port。这跟NAT无关,是基本的IP规则。
换句话说(A1:10001,B:80)同一时刻不可能出现在这个table的两个不同的row里。A1可以跟B的TCP=80建立多个连接,但是local port一定不一样。A1和A2都用port=10000跟B:80连接,没问题,他们的IP不同。
推论三,不会有任意两行有相同的inGlobal IP:_port + outGlobal IP:_port。理由同上。

结论:
1。有了inside Global IP+port,outside Global IP+port,及protocl,就可以唯一地确定一个表项;
2。有了inside Local IP+port,outside Local IP+port,及protocl,也可以唯一地确定一个表项;


实际上,B -〉A用的是(1),A -〉B用的是(2)。

回到这个具体的例子,PAT知道把Pkt_B1给A1而不是A2,是因为Pkt_B1的outside Local port=32001,这足以确定用:
TCP___A1:10000___Aout:32001__B:80______B:80
这条entry来确定inside Local IP。


在NAT路由器上也需要一个端口值来标示出session的。对不同的NAT session路由器都会分配一个端口来标示它,以便back的数据包能根据NAT table正确的forward数据包。到这里给出了路由表和NAT表之间如何协调来处理数据包的答案了!
就拿上面的例子来说明:
先说A->B的情况:
·A->B 的数据包先会被路由,但路由器会发现该A这个数据包是需要路由到外部的,则需要进行NAT处理,于是经过处理后NAT路由器会利用A这个数据包的 srcIP,desIP,Protocl,srcPort,desPort这个五元组来作为标签标示NAT session的。
·不过注意,这里正如上面例子中提到的,srcPort可能会被转换(如果是PAT的情况,srcPort肯定被转换掉),但转换后的srcPort是个关键的标签,是会被记录在NAT talbe中的,用来给back的数据包正确转发用的。就是上面例子中B->A所需要的。通过NAT处理好的数据包,携带着新的srcIP和srcPort信息发给B。这样从A到B的过程就结束了。
下面说一下B->A的情况:
·B->A也就是back数据包如何被路由器处理的问题了。路由器在拆包的过程中当然会发现该数据包的目的IP就是自己。所以路由表这时候就应该不起作用了(因为目的已达到而且它自己并没有相应需要服务的上层程序)。此时就该NAT发挥作用了,它会根据除了desIP外因为desIP是路由器端口的IP地址,所以已经没用了)的另外四个字段来从NAT table中查找相应的session来进行转发。
·首先当然是,srcIP(B的IP地址),接下来就是关键的这个desPort了,这个desPort就是先前被NAT路由器转换后的那个port,这个port值在NAT table中是唯一的,可以通过它来区分不同的session,即使是来自同一台主机的同一协议的两个请求,该端口值也是不一样。通过四个字段也就可以唯一标示一个session,当然就可以把数据包发往真正的目的地了——主机A。

下面补充对TCP分组结构(segment)的说明:

===================16=====================32 bit
---------------------------------------------------------------------------------
|======Source port=====|========Destination port=====|
---------------------------------------------------------------------------------
|=============Sequence number ==================|
---------------------------------------------------------------------------------
|=============Acknowledgement number=============|
---------------------------------------------------------------------------------
|Offset| Reserved|U|A|P|R|S|F|=========Window=========|
---------------------------------------------------------------------------------
|========Checksum========|=====Urgent pointer =====|
---------------------------------------------------------------------------------
|===============Option + Padding=================|
---------------------------------------------------------------------------------
|===================Data======================|
---------------------------------------------------------------------------------

Source Port: 识别上层源处理器接收 TCP 服务的点。
Destination Port: 识别上层目标处理器接收 TCP 服务的点。
Sequence Number: 通常指定分配到当前信息中的数据首字节的序号。在连接建立阶段,该字段用于设别传输中的初始序列号。
Acknowledgment Number: 包含数据包发送端期望接收的数据下一字节的序列号。一旦连接成功,该值会一直被发送。
Data Offset: 4 位。TCP 协议头中的32位字序号表示数据开始位置。
Reserved: 6位。预留以备用,必须设置为0。
Control Bits (Flags): 6位。传送各种控制信息。控制位可以是:


U (URG) Urgent pointer field significant.
A (ACK) Acknowledgment field significant.
P (PSH) Push function.
R (RST) Reset the connection.
S (SYN) Synchronize sequence numbers.
F (FIN) No more data from sender.

Window: 16位。指定发送端接收窗口的大小,也就是说,数据可用的八位缓存区大小。
Checksum: 16 位。指出协议头在传输中是否遭到破坏。
Urgent Pointer: 16 位。指向数据包中的第一个重要数据字节。
Option + Padding: 指定各种 TCP 选项。可选项有两种可能形式:单个八位可选类型和八位可选类型,八位可选长度和实际可选数据八位位组。
Data: 包含上层信息。

文章来自 http://blog.chinaunix.net/u/4929/showart_62329.html