背景
前些日子在IDC實驗docker的時候,為了避免與公司網路衝突,將bridge設定為127.x網段的IP,原以為這樣就OK,後來發現在訪問container內部的服務的時候無法訪問。開始以為iptables的問題,搞了半天,後來,才發現系統對127.x.x.x的包根本不會經過bridge。這兩天補習了一下linux的路由實現,才徹底明白其中緣由。
其實,關於環回介面,TCP/IP詳解中已經描述得很清楚,只是自己沒有去仔細閱讀而已。
TCP/IP關於環回介面的描述
Linu支援環回介面( Loopback Interface),以允許執行在同一臺主機上的客戶程式和伺服器程式通TCP/IP進行通訊。 A 類網路127就是為環回介面預留的 。根據慣例,大多數系統把IP地址127.0.0.1分配給這個介面,並命名為localhost。一個傳給環回介面的IP資料包不能在任何網路上出現。實際上,訪問127.x.x.x的所有IP都是訪問環回介面(lo)。
按理來說,一旦傳輸層檢測到目的端地址是環回地址時,應該可以省略部分傳輸層和所有網路層的邏輯操作。但是大多數的產品還是照樣完成傳輸層和網路層的所有過程,只是當 I P 資料包離開網路層時把它返回給自己。Linux的核心實現就是這樣。
幾個關鍵點:
(1)傳給環回地址(一般是127.0.0.1 )的任何資料均作為IP輸入。
(2)傳給廣播地址或多播地址的資料包復制一份傳給環回介面,然後送到乙太網上。這是因為廣播傳送和多播傳送的定包含主機本身。
(3)任何傳給該主機I P地址的資料均送到環回介面 。
從上面的描述可以明白,訪問127.0.0.1和本機IP(比如192.168.1.10)都是通過lo來完成的。
考慮如下路由表:
儘管我們為172.16.213.0/24和129.0.0.0/8指定了出口裝置(eth0/docker0),但實際上,資料仍然是通過lo來完成的。
Linux的實現
核心預設有兩個路由表(不考慮策略路由):
struct fib_table *ip_fib_local_table;
struct fib_table *ip_fib_main_table;
前者用於本地路由,後都可以由管理員配置。
從上面的可以看到,172.16.213.129,127.0.0.0/8都被認為是本機IP。
linux在進行路由查詢時,先查詢local,再查詢main:
static inline int fib_lookup(const struct flowi *flp, struct fib_result *res) { if (ip_fib_local_table->tb_lookup(ip_fib_local_table, flp, res) && ip_fib_main_table->tb_lookup(ip_fib_main_table, flp, res)) return -ENETUNREACH; return 0; } |
實際上,如果核心認為目標地址是本機IP,就會將包的出口裝置設定為loopback_dev(不管路由表將出口裝置設定成什麼)。
static int ip_route_output_slow(struct rtable **rp, const struct flowi *oldflp) { ... if (res.type == RTN_LOCAL) { if (!fl.fl4_src) fl.fl4_src = fl.fl4_dst; if (dev_out) dev_put(dev_out); dev_out = &loopback_dev; dev_hold(dev_out); fl.oif = dev_out->ifindex; if (res.fi) fib_info_put(res.fi); res.fi = NULL; flags |= RTCF_LOCAL; goto make_route; } |
整個資料流過程:
主要參考
[1]TCP/IP詳解
[2]深入理解linux網路技術內幕
作者:YY哥
出處:http://www.cnblogs.com/hustcat/
本文版權歸作者和部落格園共有,歡迎轉載,但未經作者同意必須保留此段宣告,且在文章頁面明顯位置給出原文連線,否則保留追究法律責任的權利。