Android开发经验谈让前端飞Web前端之路

NAT探珠

2020-03-25  本文已影响0人  noosphere

概要

NAT的全称是 Network Address Translation, 其实看英文名,就知道它的大概意思:网络地址转换。

严格来说,

那么NAT规则其实定义的就是这4者的映射关系,告诉路由器应该怎么去转换(Translation)

那么什么时候会用到NAT技术呢,答案是,从一个网络到另外一个网络,有一些文章在写NAT的时候,常讲的是外网(互联网)到私网(私域子网)的通信的时候,其实这只是其中一种场景,实际上,只要是两个网络间需要通信,都可以用NAT来做。

下面这个图讲的就是互联网和私网这个场景的NAT

    \ | /                  .                               /
   +---------------+  WAN     .           +-----------------+/
   |Regional Router|----------------------|Stub Router w/NAT|---
   +---------------+          .           +-----------------+\
                              .                      |        \
                              .                      |  LAN
                              .               ---------------
                        Stub border

        Figure 1: A typical NAT operation scenario

这个图是从NAT的规范rfc.2663 复制过来的,其中WAN就是广域网,LAN是一个局域网,LAN通过路由器连接到WAN,那么要在WAN和LAN之间通信,那就通过的在路由器里面做数据包的NAT转换。

NAT的地址转换技术有很多种类,但这些技术一般都具备下面这3个特征:

那么透明地址分配的规则是这个这些技术里面的关键点,这种地址分配又分为:

NAT的种类

NAT根据他的通信种类,又分为4类:

这么多NAT,对我们最经常需要用到的是传统NAT(出站NAT),下面我们重点讲这种NAT技术

传统NAT(出站NAT)

而传统的NAT又分为:

NAPT的技术有根据地址转换过程阶段分为

BASIC NAT

下面这个图分别演示了路由器在转换过程中的SNAT和DNAT

image

SNAT阶段:由于私网对外只有一个IP(一般是拨号拿来的或者运营商给静态分配的),私网的主机A在给公网发出去请求的给Web Server的时候,它的这个请求的数据包先经过路由器,路由器对会把它的地址改为私网对外的唯一的对外的公网地址202.20.65.5,然后在发给Web Server

DNAT阶段: Web Server收到请求后,开始响应,这个时候它响应的数据包的目标地址就变成了它私网的唯一的对外的公网地址202.20.65.5,但是,路由器收到这个数据包后,要把他转给这个包发起者192.168.1.2,就必须包目的地址改为192.168.1.2然后再发出去,这样主机A才会接受这个包(这里涉及同一个子网到数据链路层包广播的问题)

那么,这里又有一个问题,在DNAT阶段,路由器是怎么知道,这个报应该把他的目的地址转换位192.168.1.2呢?这里就涉及到上面所说会话管理的问题,路由器(或者说NAT网关)在会话开始的阶段,在第一个个包发出去,它就确认了会话的方向(第一个包的方向就是会话的方向)和对应关系(这里讲的会话的定义是,一小撮用来表达怎么用来完成一个转换的流量集合,这个集合定义为一个会话)

到这里,又涉及到一个概念是Session flow和packet low的对比(NAT用的Session flow),这里不细讲了,可以直接看rfc规范的这部分

在传输层里面:

  1. 如果是TCP协议,每个TCP会话的第一个数据包尝试建立一个会话 并包含连接启动信息。(这里涉及到3次握手的过程。TCP Session的确立,是在第一个包含这SYN标志位,而有没有ACK标志位出现后就确认,在TCP的协议里,除了第一个SYNC包,除了其他的包都必须包含ACK标志位)

2. 如果是UDP又是另外一码事,这里不细讲,看rfc规范吧

还有关于TCP和UDP详细的会话结束的定义和生命周期,看rfc规范这里

总的来讲,就是NAT会建立一个session或者connection 跟踪的机制,来跟踪哪个报应该转换到内部的哪个个主机IP

image

这个track table 如果,是外网IP足够多的话,那就用静态分配技术,使内外网IP一对一,如果外网IP不够多,那就用动态分配,如何根据这个对照表来动态转换

NAPT

大家看上面这个图很快就会发现另外一个问题,如果内网里面有两个主机同时请求Web Server,那怎么转换呢,这个时候,可以把端口作为额外信息加入这个映射表,这个就是NAPT了

image

看上图,即使主机A和主机B请求的源端口一样,路由器也可以将他们转换成不一样的的端口,如何在发出去,如何收到Server回复的数据包的时候,再按照转换的规则,原路转换回去就好

docker和wireshark抓包实例演示

下面我们用docker建一个有子网络位192.168.5.0的子网的容器,如何在容器它里面去访问一个网站,并通过wireshark来抓这个包看具体是怎么转换的

# 新建一个桥接子网
docker network create -d bridge --subnet 192.168.5.0/24 --gateway 192.168.5.1 test_bridge1

# 新建一个名位box 1的容器,并加入这个网络
docker run --name box1 -p 8082:80 -it --rm  --network=test_bridge1 busybox sh

如何我们在宿主机,看看这个私有网络的网卡是哪个

route -n
内核 IP 路由表
目标            网关            子网掩码        标志  跃点   引用  使用 接口
192.168.5.0     0.0.0.0    255.255.255.0   U     0      0        0 br-6edc31410314

接着,用wireshark监控这个网卡

并在box 1这个容器里面区去访问外网

wget www.baidu.com

然后下面这个截图是wireshark的抓包信息

image

可以看到,前面是访问dns,如何接下来就是建立TCP连接,最后是发起http的Get请求

可以看到info里面是49982->80,这个就是NAPT,主机box1用的端口是49982去访问百度的80

可以展开这个握手包的传输层看看:

image

然后服务器发回握手的第二个包是确认包,我们再看看他的传输层:

image

server返回来的包的目的端口是49982,这个就是NAT的映射关系

实际上,上面这个例子,有几层的NAT,有容器到子网,如何子网到物理机的无线网卡,那么即使你在分别抓这3个地方的包,他们的端口映射是一样的。

上面这个端口的情况,下面我们来看一下ping 百度的其中一个ip

ping 182.61.200.6

抓容器的包和子网网桥的包和以及宿主机的包对比一下

image image image

容器本身和网桥里面抓包的source ip 是一样的(这个是容器的veth pair实现的机制所致),IP是192.168.5.2(容器IP)

而宿主机发出去的source ip以及被NAT网关转换成192.168.3.3,同时也可以看到,回复的ICMP包的地址也同样被按原路转换回去了

查看宿主机的NAT规则

上面我们创建容器的时候,绑定了容器box1的80端口到宿主机的8082端口

docker run --name box1 -p 8082:80 -it --rm  --network=test_bridge1 busybox sh

那这个绑定是怎么回事呢?为什么这样绑定,发给宿主机的8082端口的数据包就会发到容器呢?答案就是NAT,严格来说是宿主机在它的iptables里面增加了DNAT规则,告诉宿主机的iptable,碰到8082的包就发给宿主机的80吧,下面就是这条规则

> sudo iptables -S -t nat | grep 8082
-A DOCKER ! -i br-6edc31410314 -p tcp -m tcp --dport 8082 -j DNAT --to-destination 192.168.5.2:80

上面是规则,下面把路由表格列出来

> sudo iptables -L -t nat
Chain PREROUTING (policy ACCEPT)
target     prot opt source               destination         
KUBE-SERVICES  all  --  anywhere             anywhere             /* kubernetes service portals */
DOCKER     all  --  anywhere             anywhere             ADDRTYPE match dst-type LOCAL

Chain INPUT (policy ACCEPT)
target     prot opt source               destination         

Chain OUTPUT (policy ACCEPT)
target     prot opt source               destination         
KUBE-SERVICES  all  --  anywhere             anywhere             /* kubernetes service portals */
DOCKER     all  --  anywhere            !localhost/8          ADDRTYPE match dst-type LOCAL
...
Chain DOCKER (2 references)
target     prot opt source               destination         
RETURN     all  --  0.0.0.0/0            0.0.0.0/0                 
DNAT       tcp  --  0.0.0.0/0            0.0.0.0/0            tcp dpt:27017 to:172.18.0.2:27017
DNAT       tcp  --  0.0.0.0/0            0.0.0.0/0            tcp dpt:8081 to:172.17.0.2:80
DNAT       tcp  --  0.0.0.0/0            0.0.0.0/0            tcp dpt:8082 to:192.168.5.2:80
DNAT       tcp  --  0.0.0.0/0            0.0.0.0/0            tcp dpt:8083 to:192.168.5.3:80
DNAT       tcp  --  0.0.0.0/0            0.0.0.0/0            tcp dpt:8085 to:192.168.6.3:80
DNAT       tcp  --  0.0.0.0/0            0.0.0.0/0            tcp dpt:8084 to:192.168.5.4:80

用conntrack看当前链接的映射关系

sudo conntrack -L         
tcp      6 32841 ESTABLISHED src=192.168.3.3 dst=49.51.200.100 sport=58710 dport=443 src=49.51.200.100 dst=192.168.3.3 sport=443 dport=58710 [ASSURED] mark=0 use=1
tcp      6 74 TIME_WAIT src=10.1.1.1 dst=10.1.1.32 sport=33634 dport=8082 src=10.1.1.32 dst=10.1.1.1 sport=8082 dport=33634 [ASSURED] mark=0 use=1
tcp      6 31706 ESTABLISHED src=192.168.3.3 dst=49.51.200.100 sport=55282 dport=443 src=49.51.200.100 dst=192.168.3.3 sport=443 dport=55282 [ASSURED] mark=0 use=1
...

上一篇下一篇

猜你喜欢

热点阅读