GRE TUNNEL

我之前寫過一篇 介紹 tunnel 的文章 ,只是作了大致的介紹。裏面多數 tunnel 是很容易理解的,由於它們可能是一對一的,換句話說,是直接從一端到另外一端。好比 IPv6 over IPv4 的 tunnel,也就是 SIT,它的原理以下圖所示:html

顯然,除了端點的 host A 和 host B以外,中間通過的任何設備都是看不到裏面的 IPv6 的頭,對於它們來講,通過 sit 發出的包和其它的 IPv4 的包沒有任何區別。網絡

GRE tunnel 卻不同了,它的原理從根本上和 sit,ipip 這樣的 tunnel 就不同。除了外層的 IP 頭和內層的 IP 頭之間多了一個 GRE 頭以外,它最大的不一樣是, tunnel 不是創建在最終的 host 上,而是在中間的 router 上 !換句話說,對於端點 host A 和 host B 來講,該 tunnel 是透明的(對比上面的 sit tunnel)。這是網上不少教程裏沒有直接告訴你的。理解這一點很是關鍵,正是由於它這麼設計的,因此它才能解決 ipip tunnel 解決不了的問題。函數

因此,通過 GRE tunnel 發送的包(從 host A 發送到 host B)大致過程是這樣子的(仍然借用了 LARTC 中的例子 ):ui

咱們能夠看出,從 host A 發出的包其實就是一個很普通的 IP 包,除了目的地址不直接可達外。該 GRE tunnel 的一端是創建在 router A上,另外一段是創建在 router B上,因此添加外部的 IP 頭是在 router A 上完成的,而去掉外面的 IP 頭是在 router B上完成的,兩個端點的 host 上幾乎什麼都不用作(除了配置路由,把發送到 10.0.2.0 的包路由到 router A)!.net

這麼設計的好處也就很容易看出來了,ipip tunnel 是端對端的,通訊也就只能是點對點的,而 GRE tunnel 卻能夠進行多播。設計

如今讓咱們看看Linux內核是怎麼實現的,咱們必須假設 router A 和 B 上都是運行的 Linux,而 host A 和 B上運行什麼是無所謂的。指針

發送過程是很簡單的,由於 router A 上配置了一條路由規則,凡是發往 10.0.2.0 網絡的包都要通過 netb 這個 tunnel 設備,在內核中通過 forward 以後就最終到達這個 GRE tunnel 設備的 ndo_start_xmit(),也就是 ipgre_tunnel_xmit() 函數。這個函數所作的事情無非就是經過 tunnel 的 header_ops 構造一個新的頭,並把對應的外部 IP 地址填進去,最後發送出去。router

稍微難理解的是接收過程,即 router B 上面進行的操做。這裏須要指出的一點是,GRE tunnel 本身定義了一個新的 IP proto,也就是 IPPROTO_GRE。當 router B 收到從 router A 過來的這個包時,它暫時還不知道這個是 GRE 的包,它首先會把它看成普通的 IP 包處理。由於外部的 IP 頭的目的地址是該路由器的地址,因此它本身會接收這個包,把它交給上層,到了 IP 層以後才發現這個包不是 TCP,UDP,而是 GRE,這時內核會轉交給 GRE 模塊處理。htm

GRE 模塊註冊了一個 hook:blog

C:
  1. static const struct gre_protocol ipgre_protocol = {
  2.         . handler     = ipgre_rcv,
  3.         . err_handler = ipgre_err,
  4. } ;

 

因此真正處理這個包的函數是 ipgre_rcv() 。ipgre_rcv() 所作的工做是:經過外層IP 頭找到對應的 tunnel,而後剝去外層 IP 頭,把這個「新的」包從新交給 IP 棧去處理,像接收到普通 IP 包同樣。到了這裏,「新的」包處理和其它普通的 IP 包已經沒有什麼兩樣了:根據 IP 頭中目的地址轉發給相應的 host。注意:這裏我所謂的「新的」包其實並不新,內核用的仍是同一個copy,只是skbuff 裏相應的指針變了。

以上就是Linux 內核對 GRE tunnel 的整個處理過程。另外,GRE 的頭以下所示(圖片來自 這裏 ):

IP header in GRE tunnel

GRE header

轉自:http://www.tuicool.com/articles/VNzY7n

相關文章
相關標籤/搜索