路由器|为什么我在公司里访问不了家里的电脑?( 二 )



整个过程下来 , NAT悄悄的改了IP数据包的发送和接收端IP地址 , 但对真正的发送方和接收方来说 , 他们却对这件事情 , 一无所知 。
这就是NAT的工作原理 。
NAPT的原理
到这里 , 相信大家都有一个很大的疑问 。
局域网里并不只有一台机器 , 局域网内每台机器都在NAT下留下的映射信息都会是 192.168.xx.xx -> 20.20.20.20 , 发送消息是没啥事 , 但接收消息的时候就不知道该回给谁了 。
【路由器|为什么我在公司里访问不了家里的电脑?】
这问题相当致命 , 因此实际上大部分时候不会使用普通的NAT 。
那怎么办呢?
问题出在我们没办法区分内网里的多个网络连接 。
于是乎:
我们可以加入其他信息去区分内网里的各个网络连接 , 很自然就能想到端口 。
但IP数据包(网络层)本身是没有端口信息的 。 常见的传输层协议TCP和UDP数据报文里才有端口的信息 。


于是流程就变成了下面这样子:
当你准备发送数据包的时候 , 你的电脑内核协议栈就会先构造一个TCP或者UDP数据报头 , 里面写入端口号 , 比如发送端口是5000 , 接收端口是3000 , 然后在这个基础上 , 加入IP数据报头 , 填入发送端和接收端的IP地址 。
那数据包长这样 。

假设 , 发送端IP地址填的就是192.168.30.5 , 接收端IP地址就是30.30.30.30 。
将数据包发到NAT路由器中 。
此时NAT路由器会将IP数据包里的源IP地址和端口号修改一下 , 从192.168.30.5:5000改写成20.20.20.20:6000 。 并且还会在NAT路由器内部留下一条 192.168.30.5:5000 -> 20.20.20.20:6000的映射记录 。 之后数据包经过公网里各个路由器的转发 , 发到了接收端30.30.30.30:3000 , 到这里发送流程结束 。

接收端响应时 , 就会在数据包里填入发送端地址是30.30.30.30:3000 , 将接收端是20.20.20.20:6000 , 发往NAT路由器 。 NAT路由器发现下自己之前留下过这么一条 192.168.30.5:5000 -> 20.20.20.20:6000的记录 , 就会将这个数据包的目的IP地址和端口修改一下 , 变回原来的192.168.30.5:5000 。之后将其转发给你的电脑上 。

如果局域网内有多个设备 , 他们就会映射到不同的公网端口上 , 毕竟端口最大可达65535 , 完全够用 。 这样大家都可以相安无事 。
像这种同时转换IP和端口的技术 , 就是NAPT(Network Address Port Transfer  网络地址端口转换 ) 。
看到这里 , 问题就来了 。
那这么说只有用到端口的网络协议才能被NAT识别出来并转发?
但这怎么解释ping命令?ping基于ICMP协议 , 而ICMP协议报文里并不带端口信息 。 我依然可以正常的ping通公网机器并收到回包 。

事实上针对ICMP协议 , NAT路由器做了特殊处理 。 ping报文头里有个Identifier的信息 , 它其实指的是放出ping命令的进程id 。
对NAT路由器来说 , 这个Identifier的作用就跟端口一样 。
另外 , 当我们去抓包的时候 , 就会发现有两个Identifier , 一个后面带个BE(Big Endian) , 另一个带个LE(Little Endian) 。
其实他们都是同一个数值 , 只不过大小端不同 , 读出来的值不一样 。 就好像同样的数字345 , 反着读就成了543 。 这是为了兼容不同操作系统(比如linux和Windows)下大小端不同的情况 。

内网穿透是什么
看到这里 , 我们大概也发现了 。 使用了NAT上网的话 , 前提得内网机器主动请求公网IP , 这样NAT才能将内网的IP端口转成外网IP端口 。
反过来公网的机器想主动请求内网机器 , 就会被拦在NAT路由器上 , 此时由于NAT路由器并没有任何相关的IP端口的映射记录 , 因此也就不会转发数据给内网里的任何一台机器 。