Linux内核ARP的处理函数分析(arp_rcv, arp_send)

来源:互联网 发布:thinksns app源码 编辑:程序博客网 时间:2024/05/21 01:47

Linux内核ARP的处理函数分析(arp_rcv, arp_send)

int arp_rcv(struct sk_buff *skb, struct net_device *dev,
                     struct packet_type *pt)
   当系统的网络驱动程序收到一个arp包的时候,调用这个函数处理。简单来说,arp_rev
发回本机器或者它代理的其他机器的网卡硬件地址(mac address),并且将发送者的网卡硬件地址放在自己的缓存(arp cache)中。
实现过程:
* 检查硬件地址长度(一般为6 字节)和协议地址长度(4 字节)是否正确。
* 调用skb_linearize 来初始化skb,这样我们就可以用skb->nh.iph来访问IP  header.
    if (in_dev == NULL ||
        arp->ar_hln != dev->addr_len    ||
        dev->flags & IFF_NOARP ||
        skb->pkt_type == PACKET_OTHERHOST ||
        skb->pkt_type == PACKET_LOOPBACK ||
        arp->ar_pln != 4)
            goto out;
   
    if ((skb = skb_share_check(skb, GFP_ATOMIC)) == NULL)
            goto out_of_mem;
   
    if (skb_is_nonlinear(skb)) {
            if (skb_linearize(skb, GFP_ATOMIC) != 0)
                    goto freeskb;
            arp = skb->nh.arph;
            arp_ptr= (unsigned char *)(arp+1);
    }
* 判断硬件地址类型(ethernet :1)和协议地址的类型(ip :0x800)。
* 判断arp包的类型,arp请求为1,arp应答为2。
    if (arp->ar_hrd != __constant_htons(ARPHRD_ETHER) &&
        arp->ar_hrd != __constant_htons(ARPHRD_IEEE802))
            goto out;
    if (arp->ar_pro != __constant_htons(ETH_P_IP))
        goto out;
    ... ...
    if (arp->ar_op != __constant_htons(ARPOP_REPLY) &&
        arp->ar_op != __constant_htons(ARPOP_REQUEST))
        goto out;
* 取得arp包各项值,包括源ethernet地址(sha)、源ip地址(sip)、目的ethernet地址(tha)   和目的ip地址(tip)。
* 如果目的地址是loopback或者是muticast地址,将这个arp包扔掉。
    sha=arp_ptr;
    arp_ptr += dev->addr_len;
    memcpy(&sip, arp_ptr, 4);
    arp_ptr += 4;
    tha=arp_ptr;
    arp_ptr += dev->addr_len;
    memcpy(&tip, arp_ptr, 4);
   
    if (LOOPBACK(tip) || MULTICAST(tip))
        goto out;
* 如果源地址为0的话,这个arp包使用来检测IP地址冲突的,则以本机的ip 地址为源地址和    目的地址发回一个arp应答。
    /* Special case: IPv4 duplicate address detection packet (RFC2131) */
    if (sip == 0) {
            if (arp->ar_op == __constant_htons(ARPOP_REQUEST) &&
                inet_addr_type(tip) == RTN_LOCAL)
                    arp_send(ARPOP_REPLY,ETH_P_ARP,tip,dev,tip,sha,dev->dev_addr,dev->dev_addr);
            goto out;
    }
* 如果是arp请求,调用ip_route_input找到目的地址(tip)的路由。
    (a)如果tip是本机地址,则调用neigh_event_ns来更新arp 缓存(arp_tbl),记下对方
       的ip和硬件地址。然后调用arp_send发回应答。
         既然对方发给我arp请求,很可能他会和我通信,存下他的地址会缩短下次处理的时间,
       这是保留发送者的ip和硬件地址的原因。
(b)如果tip是本机需要forward的ip,也进行类似的操作,但是如果proxy_delay 不等于 0
的话,不马上发回应答,而是将这个arp包先放在arp_tbl中的proxy_queue 中,设置定时
器,以后再发送。另外,调用pneigh_lookup判断tip的硬件地址是否在常驻缓存,也要此时发回应答。
    if (arp->ar_op == __constant_htons(ARPOP_REQUEST) &&
        ip_route_input(skb, tip, sip, 0, dev) == 0) {
   
        rt = (struct rtable*)skb->dst;
        addr_type = rt->rt_type;
   
        if (addr_type == RTN_LOCAL) {
            n = neigh_event_ns(&arp_tbl, sha, &sip, dev);
            if (n) {
                int dont_send = 0;
                if (IN_DEV_ARPFILTER(in_dev))
                    dont_send |= arp_filter(sip,tip,dev);
                if (!dont_send)
                    arp_send(ARPOP_REPLY,ETH_P_ARP,sip,dev,tip,sha,dev->dev_addr,sha);
   
                neigh_release(n);
            }
            goto out;
        } else if (IN_DEV_FORWARD(in_dev)) {
            if ((rt->rt_flags&RTCF_DNAT) ||
                (addr_type == RTN_UNICAST  && rt->u.dst.dev != dev &&
                 (IN_DEV_PROXY_ARP(in_dev) || pneigh_lookup(&arp_tbl, &tip, dev, 0)))) {
                n = neigh_event_ns(&arp_tbl, sha, &sip, dev);
                if (n)
                    neigh_release(n);
   
                if (skb->stamp.tv_sec == 0 ||
                    skb->pkt_type == PACKET_HOST ||
                    in_dev->arp_parms->proxy_delay == 0) {
                    arp_send(ARPOP_REPLY,ETH_P_ARP,sip,dev,tip,sha,dev->dev_addr,sha);
                } else {
                    pneigh_enqueue(&arp_tbl, in_dev->arp_parms, skb);
                    in_dev_put(in_dev);
                    return 0;
                }
                goto out;
            }
        }
    }
* 处理完arp请求后,arp_rcv不管收到的是请求还是应答,根据sip调用__neigh_lookup来查
找arp_tbl,找不到则插入一个新的entry,存入对方的ip和硬件地址,最后调用neigh_update 来更新这个entry的状态。
    n = __neigh_lookup(&arp_tbl, &sip, dev, 0);
    if (n) {
        int state = NUD_REACHABLE;
        int override = 0;
        /* If several different ARP replies follows back-to-back,
           use the FIRST one. It is possible, if several proxy
           agents are active. Taking the first reply prevents
           arp trashing and chooses the fastest router.
         */
        if (jiffies - n->updated >= n->parms->locktime)
            override = 1;
        /* Broadcast replies and request packets
           do not assert neighbour reachability.
         */
        if (arp->ar_op != __constant_htons(ARPOP_REPLY) ||
            skb->pkt_type != PACKET_HOST)
            state = NUD_STALE;
        neigh_update(n, sha, state, override, 1);
        neigh_release(n);
    }
void arp_send(int type, int ptype, u32 dest_ip,
struct net_device *dev, u32 src_ip,
unsigned char *dest_hw, unsigned char *src_hw,
unsigned char *target_hw)
當系統的網絡驅動程序收到一個arp包的時候,調用這個函數處理。簡單來說,arp_rev
發回本機器或者它代理的其他機器的網卡硬件地址(mac address),並且將發送者的網卡硬件地址放在自己的緩存(arp cache)中。
實現過程:
(1) 分配一個sk_buff,用來構造ARP包,除了必要的空間以外,還預留了15字節作備用。
調用skb_reserve空出前面的15字節和網絡包頭部。
skb = alloc_skb(sizeof(struct arphdr)+ 2*(dev->addr_len+4)
+ dev->hard_header_len + 15, GFP_ATOMIC);
if (skb == NULL)
return;
skb_reserve(skb, (dev->hard_header_len+15)&~15);
skb->nh.raw = skb->data;
arp = (struct arphdr *) skb_put(skb,sizeof(struct arphdr) + 2*(dev->addr_len+4));
skb->dev = dev;
skb->protocol = __constant_htons (ETH_P_ARP);
+ 如果從參數傳進來的源硬件地址為空的話,則設為設備的硬件地址;
如果從參數傳進來的目的硬件地址為空的話,則設為廣播地址。
if (src_hw == NULL)
src_hw = dev->dev_addr;
if (dest_hw == NULL)
dest_hw = dev->broadcast;
+ 調用網卡設備本身的MAC頭部構造函數,填好這個ARP包的ethernet目的硬件地址和源硬件地址。
ethernet的缺省構造函數為eth_header,請參見net_init.c中的ether_setup函數。
if (dev->hard_header &&
dev->hard_header(skb,dev,ptype,dest_hw,src_hw,skb->len) ar_hrd = htons(dev->type);
arp->ar_pro = __constant_htons(ETH_P_IP);
+ 填寫硬件地址長度(一般為6 字節)和協議地址長度(4 字節);
填寫arp包的類型,arp請求為1,arp應答為2。
arp->ar_hln = dev->addr_len;
arp->ar_pln = 4;
arp->ar_op = htons(type);
填寫arp包的數據,包括源ethernet地址(sha)、源ip地址(sip)、目的ethernet地址(tha) 和目的ip地址(tip)。
arp_ptr=(unsigned char *)(arp+1);
memcpy(arp_ptr, src_hw, dev->addr_len);
arp_ptr+=dev->addr_len;
memcpy(arp_ptr, &src_ip,4);
arp_ptr+=4;
if (target_hw != NULL)
memcpy(arp_ptr, target_hw, dev->addr_len);
else
memset(arp_ptr, 0, dev->addr_len);
arp_ptr+=dev->addr_len;
memcpy(arp_ptr, &dest_ip, 4);
+ 最後,調用dev_queue_xmit將這個ARP包放在發送隊列中準備發送。
dev_queue_xmit(skb);

0 0
原创粉丝点击
热门问题 老师的惩罚 人脸识别 我在镇武司摸鱼那些年 重生之率土为王 我在大康的咸鱼生活 盘龙之生命进化 天生仙种 凡人之先天五行 春回大明朝 姑娘不必设防,我是瞎子 新手机了微信怎么办 微信收藏空间已满怎么办 微信收藏空间满了怎么办 qq步数上传不了怎么办 qq发送的文件失效了怎么办 苹果手机微信数据损坏怎么办 微信文件被清理怎么办 老婆与别人聊暧昧话题怎么办 微信不小心删了聊天记录怎么办 当聊天没话题了怎么办 老婆和别人频繁聊天老公怎么办 qq上把人屏蔽了怎么办 注册微信验证码发不出去怎么办 两个人在一起没有话题怎么办 两个人聊天没话题怎么办 qq邮箱限制信用卡账单怎么办 和朋友聊天没话题怎么办 跟朋友聊天没话题怎么办 和朋友聊天找不到话题怎么办 qq加好友忽略了怎么办 微信加好友收不到验证消息怎么办 陌陌距离乱了怎么办 qq不能点赞了怎么办 快递发货地址写错了怎么办 订的海鲜不发货怎么办 拉人进群频繁了怎么办 qq群邀请过于频繁怎么办 qq一直被拉进群怎么办 qq号被冻结解封不了怎么办 2018qq自动进群怎么办 手机视频传到电脑倒着怎么办 qq群图片过期了怎么办 q号加不了群怎么办 q号加不了好友怎么办 q号被冻结了怎么办 qq群200人满了怎么办 畅聊之火消失了怎么办 手机版WPS打开文档空白怎么办 空白表格怎么打印不出来怎么办 微信朋友太少怎么办 js和CSS加载失败怎么办