|
背景IPsec在兩個通信實體之間建立安全的數(shù)據(jù)傳輸通道, 但它卻與網絡中廣泛存在的NAT設備(以及PAT)有天生的不兼容性(incompatible)。 我們以一個TCP報文為例來看看在不同IPsec的不同模式(Transport和Tunnel)和協(xié)議(AH和ESP)下,這種不兼容是如何發(fā)生的。 先來看Transport模式
對AH協(xié)議,由于其Authenticate范圍是整個IP報文,所以如果兩個IPsec之間存在NAT設備,修改了報文IP Header中的地址,就會導致接收方的Authenticate失敗。 再來看Tunnel模式
對AH協(xié)議, Tunnel模式和Transport模式沒什么不同,Authenticate范圍包含了外層IP Header,因此同樣會造成接收方Authenticate失敗。 這樣看起來,ESP-Tunnel似乎成為了在有NAT設備環(huán)境下,唯一可行的協(xié)議-模式組合。但即使是這種組合也是有缺點的:它只能支持一對一的NAT(NAT設備后面只有一臺內網主機)。在很多組網中,NAT設備通常作為網關使用,其背后可能有很多臺主機。這時地址轉換就不夠了,它還需要端口轉換,顯然,NAT設備對ESP-Tunnel的報文是無能為力的,因為TCP部分已經被加密了,已經沒有端口字段了。 UDP-EncapsulateIPsec采用的辦法是在ESP Header前加上一個UDP Header, 這個方法同時適用于ESP-Transport和ESP-Tunnel模式。
UDP Header是有端口字段的,有了端口,NAT設備便可以進行端口轉換。RFC3948中規(guī)定UDP Header中的端口要使用和IKE協(xié)商時相同的端口號,這個端口號在RFC3947中規(guī)定為4500. 在下面這樣的拓撲中,NAT設備背后有兩臺內網主機,它們都與Server建立IPsec連接。
Host 1與Host 2發(fā)出的IPsec報文都附加了一個UDP Header。NAT網關替換該報文的Source IP和Source Port。 還有一個問題, 對于ESP-Transport模式, 內層TCP報文的checksum校驗的問題如何解決呢?要知道,經過NAT設備之后,報文的IP地址發(fā)生了變化,這勢必導致接收端校驗失敗。IPsec采用的方法是在IKE協(xié)商時,就將自己原始IP地址信息發(fā)給對端,這樣Server在解密出TCP報文后,可以根據(jù)這個信息修正checksum NAT-T 協(xié)商過程IPsec的通信實體之間需要在IKE時完成協(xié)商才能使用上面UDP-Encapsulate,完成NAT-T。 在IKE的PHASE1
在IKE的PHASE2
為了更好的說明我用虛擬機搭建了下面這個拓撲,用來展示IPsec的NAT-T協(xié)商過程
其中Alice和Carol上運行Strongswan, 而Moon作為NAT設備。配置IPsec為Transport模式,使用IKEv1進行協(xié)商 探測支持 NAT-TIPsec的兩端在PHASE1的消息1和消息2中會通過交換vendor ID payload來向對方通告自己支持NAT, 其內容正是字符串'rfc3947'
探測是否存在 NAT在IKE PHASE1的消息3和消息4,通信雙方會交換自己的和自己眼中對方的IP和Port的哈希值,如果中間存在NAT設備,則該值一定與該報文本身的IP和Port計算出的值不一致。
改變端口從500到4500IKE PHASE1的前4個消息都是使用Sport=Dport=500進行通信。但當探測到NAT設備存在時,作為Initiator的Alice就再消息5需要將端口切換到Sport=Dport=4500, 作為Responder的Carol在收到該消息后,如果解密成功,也會使用新的4500端口
在此之后,后續(xù)的IKE PHASE2和業(yè)務流量都會使用4500端口進行UDP-Encapsulate。為了與業(yè)務流量進行區(qū)分,IKE階段的流量緊隨UDP Header后的是一個32bit全為0的Non-ESP Marker (業(yè)務流量的這個地方是填寫的是非零的SPI)
內核相關實現(xiàn)內核使用xfrm框架完成IPsec報文收發(fā)功能。普通情況下, IP根據(jù)協(xié)議字段分流IPsec報文和TCP UDP報文。
在NAT-T場景中,IPsec為報文進行了UDP-Encapsulate,那么,接收端看到的就是一個UDP報文了,會調用udp_rcv()進行報文接收。那么此時又如何進入xfrm框架呢? 答案是:Strongswan通過設置UDP套接字UDP_ENCAP選項,內核為套接字綁定一個回調函數(shù)xfrm4_udp_encap_rcv() int udp_lib_setsockopt(struct sock *sk, int level, int optname, char __user *optval, unsigned int optlen, int (*push_pending_frames)(struct sock *)) { // code omitted switch (optname) { case UDP_ENCAP: switch (val) { case 0: case UDP_ENCAP_ESPINUDP: case UDP_ENCAP_ESPINUDP_NON_IKE: up->encap_rcv = xfrm4_udp_encap_rcv; // code omitted } } 而在udp_rcv()接收過程中,最終會調用到該回調函數(shù)
REFRFC 3947 Negotiation of NAT-Traversal in the IKE |
|
|