NAT探珠

概要

NAT的全稱是 Network Address Translation, 其實看英文名,就知道它的大概意思:網絡地址轉換。

嚴格來說,

  • 這個Address,包括了IP地址和端口。
  • 而這個Translation,則包括來來源地址和目的地址,以及來源端口和目的端口這4者之間的轉換

那麼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個特徵:

  • 透明地址分配,就是定義外網和內網的地址的映射關係
  • 通過地址轉換進行透明路由(這裏的路由指的是數據包的轉發,而不是指交換路由信息)
  • ICMP的錯誤包的payload轉換

那麼透明地址分配的規則是這個這些技術裏面的關鍵點,這種地址分配又分爲:

  • 靜態地址分配:這是一種one-to-one的映射關係,這種映射關係明確、簡單,不需要在地址轉換翻譯的過程中管理會話,發出去和回覆回來的數據包都有明確的關係。
  • 動態地址分配:非one-to-one的映射關係,一個或者少量的外網地址分配個一個私網的一堆主機,這種情況,則需要通過會話來明確這個映射關係,那麼就需要管理會話,這樣才知道回覆的數據包對應的是哪臺主機發出去的(通過會話來查找映射關係,映射關係會話開始的時候就確立了)

NAT的種類

NAT根據他的通信種類,又分爲4類:

  • 傳統NAT或出站NAT: 會話從私有網絡發起,是單向的,出站方向的這種會話
  • 雙向NAT:會話可以從無論是公有網絡還是私有網絡發起都行,是雙向的
  • 兩次NAT:兩次NAT是NAT的一個變種,它在轉換過程中,把數據包裏面源地址和目標地址都給改了,這種情況是因爲兩個網絡的地址有衝突,比如兩個網絡都是同一個網段,而且主機的地址都一樣
  • 多宿主NAT:解決NAT路由的單點故障問題而衍生出來的一種NAT技術

這麼多NAT,對我們最經常需要用到的是傳統NAT(出站NAT),下面我們重點講這種NAT技術

傳統NAT(出站NAT)

而傳統的NAT又分爲:

  • Basic NAT,需要一個外部的IP池拿來和做轉換,不需要端口參與,要求對每一個當前連接都要對應一個公網IP地址,這種NAT在轉換的階段,指修改IP、ip頭的checksum以及一切更高級別和IP相關的checksum(如TCP、UDP、ICMP的header的checksum)的值(使數據包看起來合法),一般來講你有足夠多的外網ip可以用來對應你的內網主機,那就用這種,
  • NAPT, NAPT是Net Address Port Translation, 需要端口參與到映射關係裏,爲什麼需要端口參數呢,因爲你的外網ip數量不足以對應到你內網的主機數量,所以需要共享外網IP.

NAPT的技術有根據地址轉換過程階段分爲

  • DNAT,數據包發出去的時候,修改的是目標IP地址,所以叫做Destination NAT,簡稱DNAT
  • SNAT, 數據包發出去的時候,修改的是源IP地址,所以叫做Source NAT,簡稱SNAT(但是隻看縮寫的話,SNAT會有很多其他的定義,有一些廠商,比如WatchGuard,SNAT代表的是Static NAT)

BASIC NAT

下面這個圖分別演示了路由器在轉換過程中的SNAT和DNAT

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

這個track table 如果,是外網IP足夠多的話,那就用靜態分配技術,使內外網IP一對一,如果外網IP不夠多,那就用動態分配,如何根據這個對照表來動態轉換

NAPT

大家看上面這個圖很快就會發現另外一個問題,如果內網裏面有兩個主機同時請求Web Server,那怎麼轉換呢,這個時候,可以把端口作爲額外信息加入這個映射表,這個就是NAPT了

看上圖,即使主機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的抓包信息

可以看到,前面是訪問dns,如何接下來就是建立TCP連接,最後是發起http的Get請求

可以看到info裏面是49982->80,這個就是NAPT,主機box1用的端口是49982去訪問百度的80

可以展開這個握手包的傳輸層看看:

然後服務器發回握手的第二個包是確認包,我們再看看他的傳輸層:

server返回來的包的目的端口是49982,這個就是NAT的映射關係

實際上,上面這個例子,有幾層的NAT,有容器到子網,如何子網到物理機的無線網卡,那麼即使你在分別抓這3個地方的包,他們的端口映射是一樣的。

上面這個端口的情況,下面我們來看一下ping 百度的其中一個ip

ping 182.61.200.6

抓容器的包和子網網橋的包和以及宿主機的包對比一下

容器本身和網橋裏面抓包的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
...

擴展閱讀:

新手該如何選擇雲服務器
個人玩家和中小企業雲服務器選擇對比

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章