linux内核查询arp表,linux内核学习笔记------ARP:地址解析协议

当发送ARP请求时,发送方填入发送方以太网地址、发送方ip地址以及目标ip地址。目标主机接收到这个ARP广播包时,会在响应报文中填上自己的以太网地址。ARP报文格式如下:

硬件类型

协议类型

硬件地址长度

协议地址长度

操作码

src硬件地址

src ip地址

dst硬件地址

目标ip地址

1、硬件类型都是以ARPHDR_开头

2、操作码包括:ARPOP_REQUEST,ARPOP_REPLY

ARP报文像ip数据报一样,要是作为数据封装在以太网帧中发送。ARP会在初始化的时候注册ARP报文类型:

static struct packet_type arp_packet_type __read_mostly = {

.type =cpu_to_be16(ETH_P_ARP),

.func =arp_rcv,

};ARP模块的初始化是通过arp_init完成,该函数在ipv4协议栈初始化函数inet_init调用。

void __init arp_init(void)

{

neigh_table_init(&arp_tbl);

dev_add_pack(&arp_packet_type);

arp_proc_init();

#ifdef CONFIG_SYSCTL

neigh_sysctl_register(NULL, &arp_tbl.parms, NET_IPV4,

NET_IPV4_NEIGH, "ipv4", NULL, NULL);

#endif

register_netdevice_notifier(&arp_netdev_notifier);

}该函数首先初始化ARP协议的邻居表,然后在协议栈中注册ARP协议,最后建立proc对象,注册事件通知。ARP协议是通过哈希表组织起来的,除了ARP协议还有ip协议等;

在ARP中,根据不同的介质,提供了多种邻居项函数指针表的实例,例如通用的arp_generic_ops,支持缓存首部硬件首部arp_hh_ops,不支持ARP的arp_direct_ops以及支持业余无线电设备等的arp_broken_ops。在初始化邻居项时,会根据访问邻居项的输出网络设备的特性使用合适的邻居项函数指针表的实例。

在需要获取目标的mac地址时就会调用arp_solicit发送ARP请求:

static void arp_solicit(struct neighbour *neigh, struct sk_buff *skb)

{

......

__be32 target = *(__be32*)neigh->primary_key;

int probes = atomic_read(&neigh->probes);

struct in_device *in_dev = in_dev_get(dev);

......

switch (IN_DEV_ARP_ANNOUNCE(in_dev)) {

default:

case 0:/* By default announce any local IP */

if (skb && inet_addr_type(dev_net(dev), ip_hdr(skb)->saddr) == RTN_LOCAL)

saddr = ip_hdr(skb)->saddr;

break;

case 1:/* Restrict announcements of saddr in same subnet */

if (!skb)

break;

saddr = ip_hdr(skb)->saddr;

if (inet_addr_type(dev_net(dev), saddr) == RTN_LOCAL) {

/* saddr should be known to target */

if (inet_addr_onlink(in_dev, target, saddr))

break;

}

saddr = 0;

break;

case 2:/* Avoid secondary IPs, get a primary/preferred one */

break;

}

if (in_dev)

in_dev_put(in_dev);

if (!saddr)

saddr = inet_select_addr(dev, target, RT_SCOPE_LINK);

if ((probes -= neigh->parms->ucast_probes) < 0) {

if (!(neigh->nud_state&NUD_VALID))

printk(KERN_DEBUG "trying to ucast probe in NUD_INVALID\n");

dst_ha = neigh->ha;

read_lock_bh(&neigh->lock);

} else if ((probes -= neigh->parms->app_probes) < 0) {

#ifdef CONFIG_ARPD

neigh_app_ns(neigh);

#endif

return;

}

arp_send(ARPOP_REQUEST, ETH_P_ARP, target, dev, saddr,

dst_ha, dev->dev_addr, NULL);

......

}

arp会根据arp_announce系统参数来选择源ip地址,检测ARP请求报文重传次数是否达到上限,如果是,则停止发送,否则就会调用arp_send发送请求。

void arp_send(int type, int ptype, __be32 dest_ip,

struct net_device *dev, __be32 src_ip,

const unsigned char *dest_hw, const unsigned char *src_hw,

const unsigned char *target_hw)

{

struct sk_buff *skb;

/*

*No arp on this interface.

*/

if (dev->flags&IFF_NOARP)

return;

skb = arp_create(type, ptype, dest_ip, dev, src_ip,

dest_hw, src_hw, target_hw);

if (skb == NULL) {

return;

}

arp_xmit(skb);

}arp_send比较简单,如果网络设备无需arp支持,则直接返回,否则就会调用arp_create创建arp请求报文,并调用arp_xmit把请求报文发送出去。arp_create逻辑比较见到那,就不贴出源码了。

在以前学习邻居项的时候,在创建邻居项时也就是neigh_create有这么一句话:

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

rc = ERR_PTR(error);

goto out_neigh_release;

}tbl_constructor就是arp协议中的arp_constructor,当然如果是别的协议就另说了

static int arp_constructor(struct neighbour *neigh)

{

__be32 addr = *(__be32*)neigh->primary_key;

......

neigh->type = inet_addr_type(dev_net(dev), addr);

parms = in_dev->arp_parms;

__neigh_parms_put(neigh->parms);

neigh->parms = neigh_parms_clone(parms);

if (!dev->header_ops) {

neigh->nud_state = NUD_NOARP;

neigh->ops = &arp_direct_ops;

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

} else {

switch (dev->type) {

default:

break;

case ARPHRD_ROSE:

#if defined(CONFIG_AX25) || defined(CONFIG_AX25_MODULE)

case ARPHRD_AX25:

#if defined(CONFIG_NETROM) || defined(CONFIG_NETROM_MODULE)

case ARPHRD_NETROM:

#endif

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->header_ops->cache)

neigh->ops = &arp_hh_ops;

else

neigh->ops = &arp_generic_ops;

if (neigh->nud_state&NUD_VALID)

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

else

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

}

}arp_constructor主要就是初始化neighbor结构,根据不同的硬件接口,启用不同的邻居项的函数操作表。如果是组播类型是不需要arp支持的,如果网络设备不需要arp支持或者回环设备,则设置邻居项状态NUD_NOARP,并将广播地址作为硬件地址存储到邻居项中。然后根据网络设备支持缓存硬件首部接口来设置邻居项函数表,在根据邻居项的状态设置输出接口。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值