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;

}

  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值