2009-02-18 118 views
16

這可以被認爲是this earlier SO question的延續。Linux:如何強制使用特定的網絡接口?

理想情況下,我想監獄一個進程只使用某個接口,無論如何。它將進行TCP連接,發送UDP數據報,並偵聽UDP廣播。目前,我正在做的是:

  1. 確定要使用的接口的IP。
  2. 創建知識產權政策規則路由從界面來該IP
  3. 創建另一個IP策略規則路由所有數據包從IP來該接口
  4. 設置默認的路由表中的每個規則的所有數據包

現在,這很有效,但客戶端過程也必須願意一起玩。也就是說,它需要綁定到它想要使用的接口的特定IP,我想我也需要設置SO_BINDTODEVICE。 (但是,我一直在閱讀有關使用TCP或UDP時SO_BINDTODEVICE實際上是否工作的衝突信息。)幸運的是,客戶端應用程序是Python,並且我可以擴展套接字類以透明地完成所有這些。但我不確定這是一個完整的解決方案,特別是在接收廣播方面。

我的問題是:

  1. 是否SO_BINDTODEVICE做什麼,我想在這裏?還是隻對原始插座有效?有人評論說,「套接字上的SO_BINDTODEVICE不能保證套接字只接收在該物理接口的導線/天線上到達的數據包。」如果這確實如此,那麼SO_BINDTODEVICE呢?

  2. 有沒有辦法做到這一點,使本地IP不必是唯一的?除了一個接口上的DHCP服務器可能爲其分配一個正在被另一個接口使用的IP的事實之外,這不會成爲問題,因此會混淆路由表。

  3. 如何只接收來自特定接口的廣播?綁定到一個特定的IP似乎使它忽略廣播,這是有道理的,但不是我所期待的。

我在Ubuntu 8.04上運行w/Linux kernel 2.6.26。能夠通過兩個不同的接口同時訪問兩個不同網絡上的相同子網是一個不可協商的要求,因此使其(大部分)免於「不這樣做」。 :)

回答

3

在經歷了艱難的週末之後,我很高興提出一個解決方案,解決了我以前討論過的大部分問題,幾乎沒有任何麻煩。

有一個名爲net.ipv4.conf.all.rp_filter其可以被設置爲0,以禁止源驗證的sysctl:

 
    rp_filter - INTEGER 
     2 - do source validation by reversed path, as specified in RFC1812 
      Recommended option for single homed hosts and stub network 
      routers. Could cause troubles for complicated (not loop free) 
      networks running a slow unreliable protocol (sort of RIP), 
      or using static routes. 

     1 - (DEFAULT) Weaker form of RP filtering: drop all the packets 
      that look as sourced at a directly connected interface, but 
      were input from another interface. 

     0 - No source validation. 

這也可以在每個接口的基礎使用設定的/ proc/sys/net/ipv4/conf/<interface>/rp_filter。

正如一位海報解釋的那樣,它使得IP路由「不太確定性」,因爲來自一個子網的數據包不能保證始終走出相同的接口。在這種情況下,這正是它所需要的。請做額外的研究,以確定這是否真的是你想要的。

廣播仍然存在問題,原因我不明白,但我終於滿意這個問題,我希望它可以幫助別人。

+0

對於`net.ipv4.conf.all.rp_filter` sysctl變量,沒有IPv6 equivlant;但是相應的功能以netfilter的`rpfilter`模塊的形式存在。它記錄在[iptables-extensions](http://ipset.netfilter.org/iptables-extensions.man.html)手冊頁中。 [將rp_filter移動到netfilter](http://www.strlen.de/talks/rpfilter.pdf)注意到`rp_filter`可能會在未來的內核中完全從路由緩存代碼中移除。 – 2013-07-31 13:03:57

4

至於我的一般性問題,似乎有幾個方法可以做到這一點:

  • ,涉及從每個進程的路由表發生變化,合作的複雜的方式。這是我上面描述的方式。它具有它在用戶空間中工作的一個優點。我已經在上面添加了一些附註,並在下面回答了我的具體問題。

  • 如果設置了SO_BINDTODEVICE,編寫一個自定義內核模型,該模型完全忽略路由表。但客戶程序仍然需要致電setsockopt(SOL_SOCKET, SO_BINDTODEVICE, dev)。這個選項絕對不適合心臟病。

  • 虛擬化的過程。這可能不適合很多人,它會帶來一系列令人頭疼的問題,主要是配置問題。但值得一提的是。

選項1和2需要進程選擇加入我們希望的工作。這可以通過創建一個動態庫來緩解,該庫會劫持socket()調用來創建套接字,然後在返回描述符之前立即將其綁定到設備。這在here中有更詳細的描述。

做一些研究和大量的谷歌搜索後,我可以借鑑如何在Linux內核2.6.26的行爲一些結論。請注意,這些可能都是特定於實現的行爲,甚至可能是特定於內核的行爲。在決定根據單點數據實施功能之前,測試您自己的平臺。

  1. SO_BINDTODEVICE確實做它說,至少爲UDP。

  2. 爲每個接口獨立IP似乎是必要的,因爲我們使用的路由表。定製的內核模塊可以繞過這個限制。

  3. 要接收特定接口上的廣播,首先使用SO_BINDTODEVICE綁定到設備,然後使用通常的bind()調用綁定到廣播地址。設備綁定需要在其他任何事情之前完成。然後,套接字將只接收到達該接口的廣播。

我通過首先創建一個套接字來測試它。然後我使用setsockopt(SOL_SOCKET, SO_BINDTODEVICE, dev)將它綁定到特定的接口。最後,我將它綁定到廣播地址。從另一臺計算機,我發送了一個廣播,通過非綁定接口接收。設備綁定的套接字沒有收到這個廣播,這是有道理的。刪除setsockopt(SOL_SOCKET, SO_BINDTODEVICE, dev)呼叫,將收到廣播。

還應該提到的是,你可以使用setsockopt(SOL_SOCKET, SO_REUSEADDR, 1)這裏。請注意,SO_REUSEADDR的語義隨廣播地址而變化。具體來說,如果兩個套接字都被設置爲SO_REUSEADDR,那麼將兩個套接字綁定到同一臺機器上的廣播地址和同一端口是合法的。

UPDATE:SO_BINDTODEVICE與廣播似乎充滿了危險,即,廣播幀的接收。我觀察在一個接口上收到的廣播幀,但同時在另一個接口上消失。看起來它們受本地路由表影響,但不受IP策略規則的影響。但是,我對此並不是100%確定的,如果你想繼續這樣做,只是提及它作爲一個調查點。所有這一切說:使用風險自負。爲了時間的關係,我在接口上打開了一個原始套接字,並自己解析了以太網和IP標頭。

+0

非常豐富的問答,很好的工作! – 2009-02-20 16:53:38

2

不是您的問題的直接答案,而只是一個參考。正如你上面提到的,這個解決方案可能對你需要/想要做的事情有太多的工作。

我個人喜歡創建一個網絡協議棧鉤內核模塊,讓我做到這一點的想法。這樣我就可以完全控制來自用戶空間的多播和單播幀。你必須使用類似netlink sockets的東西來發送/接收來自你的驅動程序和用戶空間應用程序的數據,但它工作得很好,而且速度非常快。

你也可以通過這種方式掛鉤到任何級別的堆棧......以太網或IP。因此完全控制你發送/接收的內容。

下面是一個示例article,討論掛鉤到netfilter堆棧。
注:這篇文章掛鉤到IP棧,它也是舊的。我知道這些API已經發生了變化,但很多這篇文章仍然適用於實踐和理論。 如果你想掛接到橋接層,你可以使用類似的機制,但指定

BR_LOCAL_IN instead of NF_IP_LOCAL_IN 

注:這是非常相似的界面上打開原始套接字。你必須自己建立你的框架。

2

您可以嘗試將進程的網絡名稱空間限制爲單一接口。您需要使用CONFIG_NETNS(大多數現代發行版的內核)和一些腳本來爲您完成任務。 Sample configuration

相關問題