鄰居子系統是 Linux 網絡協議棧中負責三層地址到鏈路層地址映射的核心模塊,支援 ARP (IPv4)、NDP (IPv6)、ATMARP 等協議。本文基於 Linux 2.6.21 源碼,系統解析 arp_init() 初始化流程、neigh_table 表結構、neighbour 條目結構、哈希表組織、參數配置體系。
arp_init()
void __init arp_init(void) {
// 1. 鄰居表初始化(核心)
neigh_table_init(&arp_tbl);
// 2. 註冊協議包類型(接收 ARP 數據包)
dev_add_pack(&arp_packet_type);
// 3. 創建 /proc/net/arp 等 procfs 入口
arp_proc_init();
// 4. 註冊 sysctl 參數樹
#ifdef CONFIG_SYSCTL
neigh_sysctl_register(NULL, &arp_tbl.parms, NET_IPV4, NET_IPV4_NEIGH, "ipv4", NULL);
#endif
// 5. 註冊網絡設備事件通知器(設備上下線同步清理條目)
register_netdevice_notifier(&arp_netdev_notifier);
}
struct neigh_table
struct neigh_table {
struct neigh_table *next; // 全局鏈表指針
int family; // 協議族:AF_INET, AF_INET6, AF_DECnet...
int entry_size; // 條目大小:sizeof(neighbour) + key_len
int key_len; // 關鍵字長度:IPv4=4, IPv6=16
__u32 (*hash)(const void *pkey, const struct net_device *dev); // 哈希函數
int (*constructor)(struct neighbour *); // 協議構造函數
int (*pconstructor)(struct pneigh_entry *); // 代理條目構造
void (*pdestructor)(struct pneigh_entry *);
void (*proxy_redo)(struct sk_buff *skb); // 代理 ARP 重發
char *id; // 名稱:"arp_cache", "ndisc_cache"
struct neigh_parms parms; // 默認參數配置
// GC 參數緊隨 parms 之後(內存佈局兼容)
int gc_interval; // 常規 GC 間隔
int gc_thresh1; // 閾值1
int gc_thresh2; // 閾值2
int gc_thresh3; // 閾值3(硬限制)
unsigned long last_flush; // 最後強制刷新時間
struct timer_list gc_timer; // 垃圾回收定時器
struct timer_list proxy_timer; // 代理定時器
struct sk_buff_head proxy_queue; // 代理隊列
int entries; // 當前條目總數
rwlock_t lock; // 表級鎖
unsigned long last_rand; // 最後隨機化時間
struct neigh_parms *parms_list; // 參數配置鏈表
kmem_cache_t *kmem_cachep; // 條目 slab 緩存
struct neigh_statistics *stats; // 統計計數器
struct neighbour **hash_buckets; // 哈希桶數組
unsigned int hash_mask; // 桶掩碼 (size-1)
__u32 hash_rnd; // 哈希隨機種子
unsigned int hash_chain_gc; // GC 掃描桶索引
struct pneigh_entry **phash_buckets; // 代理條目哈希表
#ifdef CONFIG_PROC_FS
struct proc_dir_entry *pde; // procfs 目錄
#endif
};
struct neighbour
struct neighbour {
struct neighbour *next; // 哈希桶鏈表指針
struct neigh_table *tbl; // 所屬表
struct neigh_parms *parms; // 參數配置(可繼承默認、可設備覆蓋)
struct net_device *dev; // 出接口設備
unsigned long used; // 最後使用時間
unsigned long confirmed; // 最後確認可達時間
unsigned long updated; // 最後更新時間
__u8 flags; // 標志位
__u8 nud_state; // 狀態機:NUD_INCOMPLETE, REACHABLE, STALE, DELAY, PROBE, FAILED, PERMANENT, NOARP
__u8 type; // 目標類型:RTN_LOCAL, UNICAST, MULTICAST, BROADCAST...
__u8 dead; // GC 標記
atomic_t probes; // 剩餘探測次數
rwlock_t lock; // 條目級鎖
// 鏈路層地址緩存(緊湊對齊)
unsigned char ha[(MAX_ADDR_LEN + sizeof(unsigned long) - 1) & ~(sizeof(unsigned long) - 1)];
struct hh_cache *hh; // 硬件頭緩存鏈表
atomic_t refcnt; // 引用計數
int (*output)(struct sk_buff *skb); // 輸出函數指針(狀態相關)
struct sk_buff_head arp_queue; // 待解析數據包隊列
struct timer_list timer; // 狀態定時器
struct neigh_ops *ops; // 協議操作向量
u8 primary_key[0]; // 彈性數組:關鍵字(IP 地址)
};
struct neigh_parms
struct neigh_parms {
struct neigh_parms *next; // 全局鏈表
struct neigh_table *tbl; // 所屬表
int (*neigh_setup)(struct neighbour *); // 設備特定初始化
void (*neigh_cleanup)(struct neighbour *);
// 核心時間參數 (jiffies)
int base_reachable_time; // 基礎可達時間 (默認 30*HZ)
int reachable_time; // 實際可達時間 (隨機化後)
int retrans_time; // 重傳間隔 (默認 1*HZ)
int base_reachable_time; // 重複字段?實為 gc_staletime
int delay_probe_time; // STALE→DELAY 延遲 (默認 5*HZ)
int gc_staletime; // 失效條目最大存活 (默認 60*HZ)
int queue_len; // arp_queue 最大長度 (默認 3)
int ucast_probes; // 單播探測次數 (默認 3)
int mcast_probes; // 多播探測次數 (默認 3)
int app_probes; // 應用層探測次數 (默認 0)
int proxy_qlen; // 代理隊列長度
int proxy_delay; // 代理延遲
int dead; // 參數集是否失效
};
neigh_table_init()
void __init neigh_table_init(struct neigh_table *tbl) {
// 1. 分配 slab 緩存
tbl->entry_size = ALIGN(tbl->entry_size, sizeof(unsigned long));
tbl->kmem_cachep = kmem_cache_create(tbl->id, tbl->entry_size, 0,
SLAB_HWCACHE_ALIGN | SLAB_PANIC, NULL);
// 2. 初始化默認參數
tbl->parms.tbl = tbl;
tbl->parms.neigh_setup = NULL;
tbl->parms.neigh_cleanup = NULL;
tbl->parms.base_reachable_time = 30 * HZ;
tbl->parms.retrans_time = 1 * HZ;
tbl->parms.gc_staletime = 60 * HZ;
tbl->parms.delay_probe_time = 5 * HZ;
tbl->parms.queue_len = 3;
tbl->parms.ucast_probes = 3;
tbl->parms.mcast_probes = 3;
tbl->parms.app_probes = 0;
tbl->parms_list = &tbl->parms;
// 3. 哈希表分配(初始 32 桶)
tbl->hash_buckets = kcalloc(32, sizeof(struct neighbour *), GFP_KERNEL);
tbl->hash_mask = 31;
tbl->hash_rnd = get_random_int();
get_random_bytes(&tbl->hash_rnd, sizeof(tbl->hash_rnd));
// 4. 初始化 GC 定時器
init_timer(&tbl->gc_timer);
tbl->gc_timer.data = (unsigned long)tbl;
tbl->gc_timer.function = neigh_periodic_timer;
tbl->gc_timer.expires = jiffies + 1;
add_timer(&tbl->gc_timer);
// 5. 加入全局表鏈表
write_lock_irq(&neigh_tbl_lock);
tbl->next = neigh_tables;
neigh_tables = tbl;
write_unlock_irq(&neigh_tbl_lock);
}
struct neigh_table arp_tbl = {
.family = AF_INET,
.entry_size = sizeof(struct neighbour) + 4, // IPv4 地址 4 字節
.key_len = 4,
.hash = arp_hash,
.constructor = arp_constructor,
.pconstructor = arp_pconstructor,
.pdestructor = arp_pdestructor,
.proxy_redo = arp_proxy_redo,
.id = "arp_cache",
};
arp_hash
static __u32 arp_hash(const void *pkey, const struct net_device *dev) {
const u32 *addr = pkey;
return (*addr ^ (dev->ifindex << 8)) * arp_tbl.hash_rnd;
}
neigh_tables 鏈表串聯,neigh_find_table(family) 查找。tbl->parms → 設備參數 in_dev->arp_parms → 條目參數 neigh->parms(clone 複製),支持 per-interface、per-neighbour 精細調優。kmem_cachep 加速頻繁分配/釋放條目,entry_size 包含彈性 primary_key。rcu_read_lock,寫端加 tbl->lock,查找路徑無鎖。neigh_statistics 記錄 allocs、destroys、hash_grows、lookups、hits、res_failed、gc_runs 等,/proc/net/stat/arp_cache 導出。