Networking

當電纜與 Busybox 連接/斷開時如何取消配置/重新配置 eth 介面

  • November 29, 2020

我在 Raspberry Pi 上執行基於 Busybox 和 musl 的 Alpine Linux。

當我執行時ifup eth0,它也會udhcpc在該界面的後台啟動。這意味著如果在我執行時連接了乙太網電纜ifup,它會立即獲得一個 IP 地址,如果它沒有連接,它將在後台旋轉,直到我連接它,然後它會收到一個 IP 地址。然而,這是一次性的事情。

如果它獲得了一個 IP 地址,然後我拔下電纜,它會保留 IP 地址,如果我嘗試發出一些網路請求,例如ping google.com,它會很高興地嘗試通過此介面進行操作,直到超時。如果我有多個網路介面,這尤其是一個問題,在這種情況下,它將繼續嘗試使用第一個而忽略另一個可能具有 Internet 連接的介面。

什麼是適當的輕量級解決方案來檢測電纜已斷開或連接失去並取消配置介面,然後在再次連接後重新配置它?

以防萬一,這也是我的/etc/network/interfaces文件

auto lo
iface lo inet loopback

auto eth0
iface eth0 inet dhcp

auto eth1
iface eth1 inet dhcp

在通常的系統上,該命令ip monitor link dev ethX可用於對載體狀態變化作出反應的事件驅動方法(例如:NO-CARRIER)。

正如我對 Q/A The difference between ip link down and physical link actuator 所做的回答中所述, busybox的最小實現不提供它,因此名為ethX的介面的運營商狀態也可在以下位置獲得:

/sys/class/net/ethX/carrier
  • 0 表示無載體,即NO-CARRIER介面上的標誌
  • 1 表示承運人

該值僅在介面啟動(使用ip link set ethX up)時可用,否則在讀取時返回錯誤。運營商狀態操作狀態在幾乎所有設置中都是等效的(包括這種情況,有關詳細資訊,請參見上一個連結),因此使用操作狀態更容易*,*它/sys/class/net/ethX/operstate可以返回:

  • lowerlayerdown對於無載體(介面向上),
  • up對於承運人,
  • down當介面在管理上關閉時。

為了替換ip monitor link dev ethX,可以定期輪詢/sys/class/net/ethX/operstate以檢索相同的資訊,例如在下面的 shell 函式中,該函式每 2 秒輪詢一次作為參數給出的介面,並且只輸出更改:

stateevent () {
       local fileoperstate=/sys/class/net/"$1"/operstate
       local operstate oldoperstate=$(cat "$fileoperstate")

       while sleep 2; do
               operstate=$(cat "$fileoperstate")
               if [ "$oldoperstate" = "$operstate" ]; then
                       continue
               fi
               oldoperstate="$operstate"
               echo "$operstate"
       done
}

OP 的願望最終是失去斷開介面的路由,因此沒有超時,也不會與雙宿主 RPi 發生衝突(有解決方案可以在自己的路由表中處理兩個預設網關,但它很複雜,尤其是與 DHCP 集成,並且需要自己的方法,並且仍然需要處理載波失去)。

從一些測試來看,失去地址(這也會失去路由)比失去路由更容易,或者這些路由(特別是核心的自動 LAN 路由)以後可能無法正確恢復。這意味著在極少數情況下,RPi 是自己的客戶端,它不應該連接到由 DHCP 設置的自己的地址,而是堅持使用在lo介面上分配的地址,如 127.0.0.1 或添加在那裡的任何其他地址。

Busybox的內置udhcpc有一個有用的功能可以幫助輕鬆解決所有這些問題:

信號:

USR1續租

USR2解除租約

因此,只需使用一個簡單的信號讓正在執行的udhcpc自己完成工作:release 將刪除地址(由於介面已斷開連接,因此無法通知 DHCP 伺服器,但這不是問題),renew 將添加它(並且路線)回來。

這是一個事件循環函式,它將執行此操作,事件來自前一個的輸出:

eventloop () {
       local intf=$1
       local state

       while read -r state; do
               case "$state" in
                       lowerlayerdown)
                                       pkill -USR2 -f "(^|/)udhcpc( | .* )-i $intf( |$)"
                               ;;
                       up)
                                       pkill -USR1 -f "(^|/)udhcpc( | .* )-i $intf( |$)"
                               ;;
                       down)
                               ;;
               esac
       done
}

如果介面被管理性關閉(即使只使用ip link set eth0 down),則無需執行任何操作:核心已經刪除了路由,並且 LAN 路由稍後將自行添加回來,由預設路由完成,一旦發送到udhcpc的信號界面已備份。

您可以擁有一個名為manageudhcpc.sh (以通常的#!/bin/sh, 開頭)的 shell 腳本,包括上面的兩個 shell 函式並以:

stateevent "$1" | eventloop "$1"

並執行它兩次(它永遠不會返回,所以分叉它):

./manageudhcpc.sh eth0 &
./manageudhcpc.sh eth1 &

我把它留給OP:

  • 集成此腳本以進行引導
  • 看看在介面的初始啟動時要做什麼,因為udhcpc在剛使用未連接的介面啟動時可能會有不同的行為,等待其初始租約。

筆記:

  • 在 Alpine 3.12 和 Alpine edge LXC 容器上成功測試,在兩個網路上為 DHCP 設置了兩個介面,
  • 用於查找正確udhcpc的非常保守的擴展正則表達式可能必鬚根據確切的 Alpine 版本進行更改(我必須在 3.12 和 edge 之間更改它,以便它適用於兩者)。可以使用它的pidfile代替。

引用自:https://unix.stackexchange.com/questions/621785