文章目錄
RFC792描述了ICMP協議,其中關於重新導向訊息的部分描述如下:
The gateway sends a redirect message to a host in the following situation. A gateway, G1, receives an internet datagram from a host on a network to which the gateway is attached. The gateway, G1, checks its routing table and obtains the address of the next gateway, G2, on the route to the datagram's internet destination network, X. If G2 and the host identified by the internet source address of the datagram are on the same network, a redirect message is sent to the host. The redirect message advises the host to send its traffic for network X directly to gateway G2 as this is a shorter path to the destination. The gateway forwards the original datagram's data to its internet destination.
For datagrams with the IP source route options and the gateway address in the destination address field, a redirect message is not sent even if there is a better route to the ultimate destination than the next address in the source route.
重新導向訊息一般用於通知“這不是一條好的路由”。對於共用介質的網路,特別容易出現這種訊息。如所示:
千萬不要認為ICMP路由重新導向訊息是什麼錯誤訊息或者是一種“額外”的訊息,它可是產生路由轉寄表要素的三種來源之一哦,另外兩種是靜態路由和路由協議產生的動態路由。如果你不相信這一點,在Linux上可以在收到路由重新導向訊息後運行route –C –n查看路由緩衝,實際上重新導向的路由已經開始起作用了(有個前提是你已經允許接收路由重新導向訊息,這個由sysctl來控制)。
前面我們已經理解了ICMP路由重新導向的理論,那麼怎麼合理的利用它呢?如果你想製造一些應用場合,那是很容易的,然而ICMP重新導向在這些場合產生路由並不是最優的做法,我們需要找一種場合,在該場合中,使用ICMP重新導向訊息產生路由即使不是最優的方式,起碼也是一種很不錯的方式。到底是什麼場合呢?那就是OpenVPN在tap模式下實現用戶端子網和伺服器端子網所有N個子網間的互連。拓撲圖如下:
我們知道,tap模式下所有的用戶端節點和一個伺服器節點構成了一個虛擬乙太網路,不管虛擬與否,起碼它是一個乙太網路,遵循所有的標準乙太網路協議,比如乙太網路arp協議以及乙太網路廣播等,在開啟client-to-client的情況下,用戶端M要想訪問用戶端N後面的子網,那麼需要將訪問該子網的網關設定成用戶端N的虛擬IP地址,這是十分顯然的,手工配置一下,瞬間可以證實。然而這是有問題的,對於一個穩定的虛擬私人網路,所有子網的IP段基本是固定的,然而由於各種原因,各個節點被分配的虛擬IP地址可能會發生變化,一旦它們發生了變化,就要想辦法通知所有其它節點更新其到該變化了虛擬IP節點後面子網的路由,這是一件很麻煩的事情,搞不好就會弄錯,當然你可以為某一個固定節點分配固定的IP地址,可是我還是不喜歡這種方式,因為這需要人工來維護這份地址表,也不簡單。
由於我們的需求僅僅是用戶端子網之間互聯互連,那麼完全可以隱藏掉關於虛擬IP地址的配置,使其不體現在配置介面上更好。所有的關於虛擬私人網路的路由都採取默默推送的方式進行,完全隱藏在內部。因此,很直接的方式就是ICMP路由重新導向訊息的利用,使用這種訊息來自動產生路由,這樣只需要OpenVPN伺服器端知道各個OpenVPN用戶端後面的子網並設定路由指向分配給該用戶端的虛擬IP地址即可,而這十分容易,只需要簡單的寫一個client-connect和client-disconnect指令碼即可。然後所有用戶端將訪問其它用戶端子網的路由指向OpenVPN伺服器的虛擬IP地址。這樣當有用戶端發起這樣的訪問時,OpenVPN伺服器會發送ICMP路由重新導向訊息給OpenVPN用戶端,這樣OpenVPN用戶端便自動生效了這個重新導向路由,十分不錯。
這裡有個需要注意的地方,那就是OpenVPN用戶端需要定期執行ip route flush cache來重新整理路由緩衝,這樣做為了發現其它用戶端由於重連等原因更新了虛擬IP地址,從而使OpenVPN伺服器端推送新的ICMP路由重新導向訊息,如果不重新整理緩衝,那麼萬一有長資料在傳輸,比如沒有確認的ping或者udp的syslog,ntp等,就會永遠不可達,重新整理了緩衝之後,這些資料發往OpenVPN伺服器,此時該伺服器就會推送下來新的ICMP重新導向路由。
雜項:鏈路層複用帶來的問題
編程界有IOCCC,我覺得網路界也應該有個類似的,其宗旨在於:
1.用諷刺的手段顯示網路規劃的重要性
2.用反常的配置讓網管精神崩潰
3.顯示網路通訊協定棧不為人知的細節
4.為無意犯錯的高手網管提供避難所
5.為突破常規方法帶來的限制提供解決方案
《西廂計劃》其實可以作為網路界一個典型的IOCCC作品,網路通訊協定棧的設計者和C語言的設計是同一類人,他們都有相同的夢想,都是一類傢伙,那個時代是瘋狂的時代,是駭客的時代,然而我們現在,網路和編程變成了兩回事!C語言可以寫出那麼混亂的代碼,那是因為設計C的傢伙本身就是個駭客,他關注的更多的是C語言本身,是如何讓和他一樣的駭客可以更好的控制電腦,而不是在上面跑什麼應用,而網路通訊協定棧的設計者也是同一類人,他關心的是如何使駭客們可以更方便的通訊,而不是這種通訊能給誰帶來什麼效益。因此網路通訊協定自設計一開始就是開放的,是不設防的。所以才為如今一些瘋狂的人留下了一個糞靶子。
下面是一個關於ICMP路由重新導向的配置
主機1:兩個網卡,同時接到交換器A,然而配置不同網段的IP地址,啟動路由,預設閘道指向主機3
主機2:一塊網卡,將訪問網路A的路由指向主機1的網卡1,該主機的網卡和主機1的網卡1處於同一IP網段
主機3:兩塊網卡,網卡1串連在交換器A,網卡2直連網路A
在主機2上訪問網路A,資料包該怎麼走呢?這是一個問題。
1.首先主機1會發送arp請求,請求主機1網卡1的mac地址;
2.主機1收到arp請求後,預設情況下,網卡1和網卡2均會回複這個請求,因為它們複用了鏈路層
3.假設主機2接受了主機1網卡1的mac地址那麼接下來主機1會發送ICMP重新導向訊息
4.而如果主機2接受了主機1網卡2的mac地址,那麼由於網卡2的IP地址是另一個網段的,接下來將不會發送ICMP重新導向訊息,資料包將由主機1的網卡2接收,然後通過其網卡1轉寄出去給主機3
因此上述的配置導致的資料包路由是不確定的,那麼它受什麼影響呢?實際上有很多因素,在物理層,它受到交換器拓撲的影響,到底主機1的網卡2和網卡2的arp回應哪個會生效,這就要看網線是怎麼串連的,看看誰“離主機2更近”,構造交換器環路可以對其造成影響,另外還受到核心配置的影響,在linux中,可以通過配置網卡的arp_ignore來影響arp回應的發送,另外還受到是否發送和接受ICMP路由重新導向訊息來影響資料包的路由,在linux中可以配置網卡的send/accept_redirect。