1. 程式人生 > >linux路由核心實現分析(一)----鄰居子節點

linux路由核心實現分析(一)----鄰居子節點

有三種路由結構:

1,neigh_table{} 結構和 neighbour{} 結構

 儲存和本機物理上相鄰的主機地址資訊表,通常稱為鄰居子節點,指的是和本機相鄰只有

 一跳的機器,其中 neigh_table{} 作為資料結構連結串列來表示 neighbour{} 表示相鄰的機器節點

2, 路由規則的儲存,判斷了一個到達一個網路地址必須經過怎樣的路由,使用 fib_table 來表示

3, 提供了路由地址的快取機制,使用 rtable 連結串列來表示.

neigh_table 結構

struct neigh_table

{

struct neigh_table *next;

int family;

int entry_size;

int key_len;

__u32 (*hash)(const void *pkey, const struct net_device *);

int (*constructor)(struct neighbour *);

int (*pconstructor)(struct pneigh_entry *);

void (*pdestructor)(struct pneigh_entry *);

void (*proxy_redo)(struct sk_buff *skb);

char *id;

struct neigh_parms parms;

int gc_interval;

int gc_thresh1;

int gc_thresh2;

int gc_thresh3;

unsigned long last_flush;

struct timer_list gc_timer;

struct timer_list proxy_timer;

struct sk_buff_head proxy_queue;

atomic_t entries;

rwlock_t lock;

unsigned long last_rand;

struct kmem_cache *kmem_cachep;

struct neigh_statistics *stats;

struct neighbour **hash_buckets;

unsigned int hash_mask;

__u32 hash_rnd;

unsigned int hash_chain_gc;

struct pneigh_entry **phash_buckets;

#ifdef CONFIG_PROC_FS

struct proc_dir_entry *pde;

#endif

};

struct proc_dir_entry *pde

這個成員是 linux 2.6 中添加了對 proc 檔案系統的支援,但是我沒有找到 proc 檔案系統中

有直接相關於鄰居節點的資訊,估計是和其他結構一起向用戶態提供了路由資訊,有可能

是輸出到 /proc/net/route 裡面.

struct neigh_table *next; // 下一個鄰居表 , 實際上就是 ARP 報文到達的下一臺機器

int family;// 地址族,對於乙太網而言就是 AF_INET

int entry_size; // 入口長度 , 也就是一個鄰居結構的大小 , 初始化為 sizeof(neighbour)+4(4 為一個 IP 地址的長度 )

// 雜湊關鍵值長度 IP 地址的長度,為 4

int key_len;

__u32 (*hash)(const void *pkey, const struct net_device *); 構造出存放和檢索這個 neigh_table neighbour 的雜湊函式  

// 允許鄰居的上限,根據網路的型別,大小會有所變化,例如 C 類地址,鄰居限制就應該小於 255

int gc_thresh3

// 雜湊陣列,存入其中的鄰居,在一個 neigh_table 裡面,最多可以有 32 neighbour 結構的連結串列.

struct neighbour **hash_buckets;

int entries // 整個 neigh_table 中鄰居的數量

unsigned int hash_mask; // 雜湊陣列大小的掩碼

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;

__u8 type;

__u8 dead;

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];

};

struct neigh_table *tbl;// 所在的鄰居表,指向上層的 neigh_table 結構

struct net_device *dev;// 鄰居所對應的網路裝置介面指標

int (*output)(struct sk_buff *skb);// 找到合適的鄰居節點之後,系統將呼叫這個函式指標,

使用結構中的 dev 裝置,將資料包傳送出去,如果協議

族是 AF_INET ,將呼叫 dev_queue_xmit 函式傳送資料

u8 primary_key[0];// 雜湊關鍵字

// 這段程式碼完成函式指標的轉換( net/ipv4/arp.c

static struct neigh_ops arp_hh_ops = {

.family = AF_INET,

.solicit = arp_solicit,

.error_report = arp_error_report,

.output = neigh_resolve_output,

.connected_output = neigh_resolve_output,

.hh_output = dev_queue_xmit,

.queue_xmit = dev_queue_xmit,

};

鄰居節點相關的操作:

查詢到路由後,會呼叫 arp_bind_neighbour 繫結一個鄰居項

int arp_bind_neighbour(struct dst_entry *dst)

{

struct net_device *dev = dst->dev;

struct neighbour *n = dst->neighbour;

if (dev == NULL)

return -EINVAL;

// 如果這個鄰居不存在,則執行 __neigh_lookup_errno

if (n == NULL) {

__be32 nexthop = ((struct rtable*)dst)->rt_gateway;

if (dev->flags&(IFF_LOOPBACK|IFF_POINTOPOINT))

nexthop = 0;

n = __neigh_lookup_errno(

//ATM 網路和乙太網絡呼叫了不同的 neigh_table, 作為乙太網絡將呼叫 &arp_tbl 作為 neigh_table 的入口

#if defined(CONFIG_ATM_CLIP) || defined(CONFIG_ATM_CLIP_MODULE)

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 函式

static inline 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_lookup 函式

struct neighbour *neigh_lookup(struct neigh_table *tbl, const void *pkey,

struct net_device *dev)

{

struct neighbour *n;

int key_len = tbl->key_len;

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

NEIGH_CACHE_STAT_INC(tbl, lookups);

read_lock_bh(&tbl->lock);

// 以下程式碼可以看出,通過指定的 neigh_table 入口,找到 hash_buckets,

// 因為所有的 neighbour 連結串列是經過雜湊的,所以再通過傳入的雜湊值作為

// 下標最後找到連結串列頭,然後在往下遍歷,直到找到相對應的 neighbour 結構

// 為止

for (n = tbl->hash_buckets[hash_val & tbl->hash_mask]; n; n = n->next) {

if (dev == n->dev && !memcmp(n->primary_key, pkey, key_len)) {

neigh_hold(n);

NEIGH_CACHE_STAT_INC(tbl, hits);

break;

}

}

read_unlock_bh(&tbl->lock);

return n;

}

如果到鄰居表中尋找對應的鄰居項,如果不存在,則新建一項。繼續跟進 呼叫 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) {

rc = ERR_PTR(-ENOBUFS);

goto out;

}

// 每個 neighbour 的雜湊就是在這裡計算的,實際上我們可以看出,

// 所謂的雜湊值就是目的 IP

memcpy(n->primary_key, pkey, key_len);

n->dev = dev;

dev_hold(dev);

if (tbl->constructor && (error = tbl->constructor(n)) < 0) {

rc = ERR_PTR(error);

goto out_neigh_release;

}

if (n->parms->neigh_setup &&

(error = n->parms->neigh_setup(n)) < 0) {

rc = ERR_PTR(error);

goto out_neigh_release;

}

n->confirmed = jiffies - (n->parms->base_reachable_time << 1);

write_lock_bh(&tbl->lock);

if (atomic_read(&tbl->entries) > (tbl->hash_mask + 1))

neigh_hash_grow(tbl, (tbl->hash_mask + 1) << 1);

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

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);

rc = n;

out:

return rc;

out_tbl_unlock:

write_unlock_bh(&tbl->lock);

out_neigh_release:

neigh_release(n);

goto out;

}