Windows XP Windows 7 Windows 2003 Windows Vista Windows 綜合教學 Linux 系統教學
Windows 10 Windows 8 Windows 2008 Windows NT Windows Server 電腦軟體教學
您现在的位置: Windows教程網 >> Windows教程 >  >> Linux 系統教學 >> Linux 教學

Linux 協議棧鄰居子系統核心流程:neigh_update 狀態機與地址更新

編輯:Linux 教學

neigh_update() 是鄰居子系統更新鄰居條目狀態、鏈路層地址、輸出路徑的核心函數,由 ARP/NDP 接收處理、用戶態配置、內核探測邏輯調用。本文基於 Linux 2.6.21 源碼詳細解析參數校驗、狀態轉換邏輯、地址比較覆蓋策略、定時器管理、輸出函數綁定全流程。

一、 函數原型與關鍵參數


int neigh_update(struct neighbour *neigh, const u8 *lladdr, u8 new_state, u32 flags)

/* flags 位定義 */
#define NEIGH_UPDATE_F_ADMIN       0x01  // 管理員靜態設置
#define NEIGH_UPDATE_F_OVERRIDE    0x02  // 強制覆蓋地址
#define NEIGH_UPDATE_F_WEAK_OVERRIDE 0x04  // 弱覆蓋(僅 CONNECTED→STALE)
#define NEIGH_UPDATE_F_OVERRIDE_ISROUTER 0x08 // 更新 isrouter 標志

二、 核心邏輯流程圖

1. 權限檢查與無效狀態處理


write_lock_bh(&neigh->lock);
if (!(flags & NEIGH_UPDATE_F_ADMIN) && (old & (NUD_NOARP | NUD_PERMANENT)))
    goto out;  // 非管理員不可修改永久/無ARP條目

if (!(new & NUD_VALID)) {  // 轉為無效狀態(FAILED/INCOMPLETE)
    neigh_del_timer(neigh);
    if (old & NUD_CONNECTED)
        neigh_suspect(neigh);  // 輸出函數改為 solicited
    neigh->nud_state = new;
    goto out;
}

2. 鏈路層地址比較與覆蓋決策


// 無地址設備(如隧道)直接使用現有地址
if (!dev->addr_len) lladdr = neigh->ha;

// 有新地址且舊地址有效:比較是否相同
if (lladdr && (old & NUD_VALID) && !memcmp(lladdr, neigh->ha, dev->addr_len))
    lladdr = neigh->ha;  // 相同則忽略新地址

// 無新地址:若舊地址有效則保留,否則報錯
if (!lladdr) {
    if (!(old & NUD_VALID)) { err = -EINVAL; goto out; }
    lladdr = neigh->ha;
}

3. 狀態轉換核心邏輯


// 連接狀態重置 confirmed 時間戳
if (new & NUD_CONNECTED)
    neigh->confirmed = jiffies;
neigh->updated = jiffies;

int update_isrouter = flags & NEIGH_UPDATE_F_OVERRIDE_ISROUTER;

// 舊地址有效時的覆蓋判斷
if (old & NUD_VALID) {
    if (lladdr != neigh->ha && !(flags & NEIGH_UPDATE_F_OVERRIDE)) {
        // 地址變了但無強制覆蓋標志
        update_isrouter = 0;
        if ((flags & NEIGH_UPDATE_F_WEAK_OVERRIDE) && (old & NUD_CONNECTED)) {
            // 弱覆蓋:CONNECTED 狀態下允許降級為 STALE
            lladdr = neigh->ha;
            new = NUD_STALE;
        } else
            goto out;  // 拒絕更新
    } else {
        // 地址相同或強制覆蓋
        if (lladdr == neigh->ha && new == NUD_STALE &&
            ((flags & NEIGH_UPDATE_F_WEAK_OVERRIDE) || (old & NUD_CONNECTED)))
            new = old;  // 保持原狀態
    }
}

4. 狀態變更時的定時器與輸出函數重綁


if (new != old) {
    neigh_del_timer(neigh);
    if (new & NUD_IN_TIMER) {  // REACHABLE / DELAY / PROBE
        neigh_hold(neigh);
        neigh->timer.expires = jiffies + 
            ((new & NUD_REACHABLE) ? neigh->parms->reachable_time : 0);
        add_timer(&neigh->timer);
    }
    neigh->nud_state = new;
}

// 更新鏈路層地址
if (lladdr != neigh->ha) {
    memcpy(&neigh->ha, lladdr, dev->addr_len);
    neigh_update_hhs(neigh);  // 更新硬件頭緩存
    if (!(new & NUD_CONNECTED))
        neigh->confirmed = jiffies - (neigh->parms->base_reachable_time << 1);
    notify = 1;  // 觸發 ARPD 通知
}

if (new == old) goto out;

// 綁定輸出函數
if (new & NUD_CONNECTED)
    neigh_connect(neigh);   // 輸出 → neigh_resolve_output (正常發送)
else
    neigh_suspect(neigh);   // 輸出 → neigh_suspect_output (發起探測)

// 釋放 arp_queue 中排隊的 skb
if ((old & NUD_VALID) == 0 && (new & NUD_VALID))
    neigh_release_skb(neigh);

三、 狀態轉換總結表

舊狀態新狀態觸發場景關鍵動作
INCOMPLETEREACHABLE收到 ARP Reply綁定 lladdr、啟動 reachable 定時器、綁定 connect 輸出、發送排隊 skb
STALEDELAY上層發送數據啟動 delay_probe_time 定時器
DELAYPROBE定時器超時發送單播探測、啟動 retrans 定時器
PROBEREACHABLE收到探測回應同 INCOMPLETE→REACHABLE
REACHABLESTALEreachable_time 過期無定時器,標記 STALE
任何FAILED探測失敗/管理設置清理定時器、綁定 suspect 輸出、GC 回收
任何PERMANENT管理員靜態添加永不老化、無定時器、refcnt 永不歸零

四、 關鍵輔助函數

  • neigh_connect():neigh->ops->output = neigh_resolve_output,hh_output 可用時走快路徑。
  • neigh_suspect():output = neigh_suspect_output,發送數據前先發起探測。
  • neigh_update_hhs():遍歷 neigh->hh 硬件頭緩存鏈表,更新目的 MAC 地址。
  • neigh_release_skb():將 neigh->arp_queue 中排隊的 skb 通過 neigh->ops->output 發送。
Copyright © Windows教程網 All Rights Reserved