linux内核的邻居表,linux协议栈之邻居子系统详细介绍

在前面已经分析过,查找到路由后,会调用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;

//如果邻居项不存在

if (n == NULL) {

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

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

nexthop = 0;

n = __neigh_lookup_errno(

#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()

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

}

从上面可以看到,它会先到邻居表中寻找对应的邻居项,如果不存在,则新建一项。继续跟进

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;

}

//从此可以看到,哈希键值就是目的IP

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

n->dev = dev;

dev_hold(dev);

/* Protocol specific setup. */

//初始化函数

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

rc = ERR_PTR(error);

goto out_neigh_release;

}

/* Device specific setup. */

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

//如果总数超过了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_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;

}

在函数里,会调用tbl->constructor()进行初始化。在arp_tbl结构中,为constructor赋值为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;

neigh->type = inet_addr_type(addr);

rcu_read_lock();

in_dev = rcu_dereference(__in_dev_get(dev));

if (in_dev == NULL) {

rcu_read_unlock();

return -EINVAL;

}

parms = in_dev->arp_parms;

__neigh_parms_put(neigh->parms);

neigh->parms = neigh_parms_clone(parms);

rcu_read_unlock();

//dev->hard_header.是为被赋值勤

if (dev->hard_header == NULL) {

neigh->nud_state = NUD_NOARP;

neigh->ops = &arp_direct_ops;

neigh->output = neigh->ops->queue_xmit;

} else {

#if 1

switch (dev->type) {

default:

break;

case ARPHRD_ROSE:

neigh->ops = &arp_broken_ops;

neigh->output = neigh->ops->output;

return 0;

#endif

;}

#endif

if (neigh->type == RTN_MULTICAST) {

neigh->nud_state = NUD_NOARP;

arp_mc_map(addr, neigh->ha, dev, 1);

} else if (dev->flags&(IFF_NOARP|IFF_LOOPBACK)) {

neigh->nud_state = NUD_NOARP;

memcpy(neigh->ha, dev->dev_addr, dev->addr_len);

} else if (neigh->type == RTN_BROADCAST || dev->flags&IFF_POINTOPOINT) {

neigh->nud_state = NUD_NOARP;

memcpy(neigh->ha, dev->broadcast, dev->addr_len);

}

if (dev->hard_header_cache)

neigh->ops = &arp_hh_ops;

else

neigh->ops = &arp_generic_ops;

//如果邻居项是可用状态,则调用connected_output里的函数

if (neigh->nud_state&NUD_VALID)

neigh->output = neigh->ops->connected_output;

else

//如果邻居项不可用

neigh->output = neigh->ops->output;

}

return 0;

}

在网卡驱动那一章,我们是调用alloc_etherdev()来构建网卡的net_device结构的,在allocetherdev()调用alloc_etherdev对各函数指针赋值

void ether_setup(struct net_device *dev)(drivers/net/net_init.c)

{

/* Fill in the fields of the device structure with ethernet-generic values.

This should be in a common file instead of per-driver.*/

dev->change_mtu= eth_change_mtu;

dev->hard_header= eth_header;

dev->rebuild_header = eth_rebuild_header;

dev->set_mac_address = eth_mac_addr;

dev->hard_header_cache= eth_header_cache;

dev->header_cache_update= eth_header_cache_update;

dev->hard_header_parse= eth_header_parse;

dev->type= ARPHRD_ETHER;

dev->hard_header_len = ETH_HLEN;

dev->mtu= 1500; /* eth_mtu */

dev->addr_len= ETH_ALEN;

dev->tx_queue_len= 1000;/* Ethernet wants good queues */

memset(dev->broadcast,0xFF, ETH_ALEN);

/* New-style flags. */

dev->flags= IFF_BROADCAST|IFF_MULTICAST;

}

所以,neigh->output就指向了arp_hh_opsàoutput

Arp_hh_ops的结构如下:

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,

};

由此可以看到,最终的数据都会流到neigh_resolve_output

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值