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 協議棧鄰居子系統條目創建流程:arp_bind_neighbour 到 neigh_create

編輯:Linux 教學

IP 路由查找確定下一跳後,必須為 dst_entry 綁定鄰居條目以獲取鏈路層地址。arp_bind_neighbour() 觸發鄰居查找或創建流程,最終調用 neigh_create() 分配並初始化 struct neighbour,插入哈希表、綁定協議相關操作向量。本文基於 Linux 2.6.21 源碼完整追蹤創建流程。

一、 綁定入口 arp_bind_neighbour()


int arp_bind_neighbour(struct dst_entry *dst) {
    struct net_device *dev = dst->dev;
    struct neighbour *n = dst->neighbour;

    if (!dev) return -EINVAL;

    if (!n) {  // 未綁定,需查找/創建
        u32 nexthop = ((struct rtable*)dst)->rt_gateway;
        if (dev->flags & (IFF_LOOPBACK | IFF_POINTOPOINT))
            nexthop = 0;  // 本地/點對點無需網關

        n = __neigh_lookup_errno(
            #ifdef CONFIG_ATM_CLIP
            dev->type == ARPHRD_ATM ? clip_tbl_hook :
            #endif
            &arp_tbl, &nexthop, dev);
        if (IS_ERR(n)) return PTR_ERR(n);

        dst->neighbour = n;  // 綁定
    }
    return 0;
}

二、 查找或創建 __neigh_lookup_errno()


struct neighbour *__neigh_lookup_errno(struct neigh_table *tbl, const void *pkey, struct net_device *dev) {
    struct neighbour *n = neigh_lookup(tbl, pkey, dev);  // 哈希查找
    if (n) return n;
    return neigh_create(tbl, pkey, dev);  // 不存在則創建
}

三、 核心創建函數 neigh_create()


struct neighbour *neigh_create(struct neigh_table *tbl, const void *pkey, struct net_device *dev) {
    u32 hash_val;
    int key_len = tbl->key_len;
    int error;
    struct neighbour *n1, *rc, *n = neigh_alloc(tbl);  // 分配內存

    if (!n) return ERR_PTR(-ENOBUFS);

    // 1. 複製關鍵字(IPv4 為 4 字節 IP 地址)
    memcpy(n->primary_key, pkey, key_len);
    n->dev = dev;
    dev_hold(dev);  // 增加設備引用

    // 2. 協議特定初始化(arp_constructor)
    if (tbl->constructor && (error = tbl->constructor(n)) < 0) {
        rc = ERR_PTR(error);
        goto out_neigh_release;
    }

    // 3. 設備特定初始化
    if (n->parms->neigh_setup && (error = n->parms->neigh_setup(n)) < 0) {
        rc = ERR_PTR(error);
        goto out_neigh_release;
    }

    // 4. 初始化時間戳
    n->confirmed = jiffies - (n->parms->base_reachable_time << 1);  // 標記為 STALE 等效

    // 5. 哈希表插入(加寫鎖)
    write_lock_bh(&tbl->lock);

    // 條目數超過 hash_mask+1 觸發擴容
    if (tbl->entries > (tbl->hash_mask + 1))
        neigh_hash_grow(tbl, (tbl->hash_mask + 1) << 1);

    hash_val = tbl->hash(pkey, dev) & tbl->hash_hash;

    // 檢查參數是否已標記死亡
    if (n->parms->dead) {
        rc = ERR_PTR(-EINVAL);
        goto out_tbl_unlock;
    }

    // 遍歷哈希桶,檢查是否已存在(並發創建競爭)
    for (n1 = tbl->hash_buckets[hash_val]; n1; n1 = n1->next) {
        if (dev == n1->dev && !memcmp(n1->primary_key, pkey, key_len)) {
            neigh_hold(n1);  // 增加引用
            rc = n1;
            goto out_tbl_unlock;
        }
    }

    // 插入桶頭
    n->next = tbl->hash_buckets[hash_val];
    tbl->hash_buckets[hash_val] = n;
    n->dead = 0;
    neigh_hold(n);  // 初始引用:哈希表持有

    write_unlock_bh(&tbl->lock);
    NEIGH_PRINTK2("neigh %p is created.\n", n);
    return n;

out_tbl_unlock:
    write_unlock_bh(&tbl->lock);
out_neigh_release:
    neigh_release(n);
    return rc;
}

四、 ARP 協議構造函數 arp_constructor()


static int arp_constructor(struct neighbour *neigh) {
    u32 addr = *(u32*)neigh->primary_key;
    struct net_device *dev = neigh->dev;
    struct in_device *in_dev;
    struct neigh_parms *parms;

    // 1. 確定地址類型:本地/單播/廣播/組播
    neigh->type = inet_addr_type(addr);

    // 2. 獲取接口 IPv4 配置
    rcu_read_lock();
    in_dev = rcu_dereference(__in_dev_get(dev));
    if (!in_dev) {
        rcu_read_unlock();
        return -EINVAL;
    }

    // 3. 綁定鄰居參數(包含 gc_staletime、retrans_time 等)
    parms = in_dev->arp_parms;
    __neigh_parms_put(neigh->parms);  // 釋放默認參數
    neigh->parms = neigh_parms_clone(parms);
    neigh_parms_data_state_cleanall(neigh->parms);

    rcu_read_lock();

    // 4. 設置操作向量
    neigh->ops = &arp_hh_ops;  // 包含 output、connected_output、solicit 等
    neigh->output = neigh_blackhole;  // 初始輸出函數:丟棄並報錯

    return 0;
}

五、 關鍵操作向量 arp_hh_ops


static const struct neigh_ops arp_hh_ops = {
    .family         = AF_INET,
    .solicit        = arp_solicit,        // 發送 ARP 請求
    .error_report   = arp_error_report,   // ICMP 錯誤上報
    .output         = neigh_resolve_output, // 統一解析輸出
    .connected_output = neigh_resolve_output, // REACHABLE 狀態輸出
    .hh_output      = dev_queue_xmit,     // 硬件頭緩存快速輸出
    .queue_xmit     = dev_queue_xmit,     // 普通輸出
};

六、 內存分配 neigh_alloc()


struct neighbour *neigh_alloc(struct neigh_table *tbl) {
    struct neighbour *n = kmem_cache_alloc(tbl->kmem_cachep, GFP_ATOMIC);
    if (!n) return NULL;

    memset(n, 0, sizeof(*n) + tbl->key_len);  // key_len 附加在結構體後
    n->tbl = tbl;
    atomic_set(&n->refcnt, 1);
    rwlock_init(&n->lock);
    skb_queue_head_init(&n->arp_queue);
    setup_timer(&n->timer, neigh_timer_handler, (unsigned long)n);
    return n;
}

七、 流程總結圖


arp_bind_neighbour(dst)
    ↓
__neigh_lookup_errno(arp_tbl, &nexthop, dev)
    ↓
neigh_lookup() --哈希查找--> 存在? --Yes--> neigh_hold() 返回
    ↓ No
neigh_create()
    ↓
neigh_alloc() --kmem_cache_alloc--> 初始化 refcnt、lock、arp_queue、timer
    ↓
memcpy primary_key (IP 地址)
    ↓
tbl->constructor() --arp_constructor()--> inet_addr_type、綁定 in_dev->arp_parms、設置 ops
    ↓
n->parms->neigh_setup() --通常為空--
    ↓
n->confirmed 初始化
    ↓
write_lock_bh(tbl->lock)
    ↓
tbl->entries > hash_mask+1? --Yes--> neigh_hash_grow()
    ↓
hash_val = tbl->hash(pkey, dev) & hash_mask
    ↓
遍歷桶鏈表 --已存在?--Yes--> neigh_hold() 返回
    ↓ No
插入桶頭,tbl->entries++
    ↓
write_unlock_bh
    ↓
返回新建 neighbour*
Copyright © Windows教程網 All Rights Reserved