文章详情

  • 游戏榜单
  • 软件榜单
关闭导航
热搜榜
热门下载
热门标签
php爱好者> php文档>LINUX下如果发送和接受ARP

LINUX下如果发送和接受ARP

时间:2010-11-22  来源:rainballdh

一.发送一个ARP数据包

1.首先, 调用arp_send()函数创建和发送一个ARP数据包。 

 

 

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)
{
    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_xmit()函数来发送arp数据包。

2. Now, 我们看一下arp_create()函数的源码。

首先, 看一下arp头部结构:

 


struct arphdr
{
    unsigned short    ar_hrd;        /* format of hardware address    */
    unsigned short    ar_pro;        /* format of protocol address    */
    unsigned char    ar_hln;        /* length of hardware address    */
    unsigned char    ar_pln;        /* length of protocol address    */
    unsigned short    ar_op;        /* ARP opcode (command)    */
}

struct sk_buff *arp_create(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)
{
    struct sk_buff *skb;
    struct arphdr *arp;
    unsigned char *arp_ptr;

    /*
      * dev->addr_len 就是代表 以太网地址长度
      * 4 就是代表 ip地址长度
      */
    skb = alloc_skb(sizeof(struct arphdr)+ 2*(dev->addr_len+4)
                + LL_RESERVED_SPACE(dev), GFP_ATOMIC);
    if (skb == NULL)
        return NULL;

    skb_reserve(skb, LL_RESERVED_SPACE(dev));
    skb->nh.raw = skb->data;
    arp = (struct arphdr *) skb_put(skb,sizeof(struct arphdr) + 2*(dev->addr_len+4));
    skb->dev = dev;
    skb->protocol = htons(ETH_P_ARP);
    if (src_hw == NULL)
        src_hw = dev->dev_addr;
               /* if dest_hw == NULL, we create a broadcast packet */
    if (dest_hw == NULL)
        dest_hw = dev->broadcast;

    /*
     * Fill eth header
     */
    if (dev->hard_header &&
     dev->hard_header(skb,dev,ptype,dest_hw,src_hw,skb->len) < 0)
        goto out;

     /*
     * Now fill arp header
     */
    switch (dev->type) {
    default:
        arp->ar_hrd = htons(dev->type);
        arp->ar_pro = htons(ETH_P_IP);
        break;
         /* … */
    }
    arp->ar_hln = dev->addr_len;
    arp->ar_pln = 4;
    arp->ar_op = htons(type);

    /*
     * arp_ptr equal ((unsignec char *)(arp) + sizeof(struct arphdr))
     */
    arp_ptr=(unsigned char *)(arp+1);
    memcpy(arp_ptr, src_hw, dev->addr_len); /* src mac */
    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); /* dst mac */
    else
        memset(arp_ptr, 0, dev->addr_len);
    arp_ptr+=dev->addr_len;
    memcpy(arp_ptr, &dest_ip, 4);
    return skb;
out:
    kfree_skb(skb);
    return NULL;
}


3.arp 数据包创建完成后,调用arp_xmit()函数来发送。

 

/*
 *    Send an arp packet.
 */
void arp_xmit(struct sk_buff *skb)
{
    /* Send it off, maybe filter it using firewalling first. */
    NF_HOOK(NF_ARP, NF_ARP_OUT, skb, NULL, skb->dev, dev_queue_xmit);
}

 

二. 接受ARP数据包。

1. 当arp请求包或者响应包到达时,我们调用arp_rcv()函数。arp_rcv判断这个arp请求是不是询问本机或者本机代理的硬件地址,如果是的话,调用arp_send发回arp应答。另外arp_rcv还尽量保留对方机器的mac addres。

 

int arp_rcv(struct sk_buff *skb, struct net_device *dev, struct packet_type *pt)
{
    struct arphdr *arp;
    /* ARP header, plus 2 device addresses, plus 2 IP addresses. */
    if (!pskb_may_pull(skb, (sizeof(struct arphdr) +
                 (2 * dev->addr_len) +
                 (2 * sizeof(u32)))))
        goto freeskb;
    arp = skb->nh.arph;
    if (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 freeskb;
    if ((skb = skb_share_check(skb, GFP_ATOMIC)) == NULL)
        goto out_of_mem;
    return NF_HOOK(NF_ARP, NF_ARP_IN, skb, dev, NULL, arp_process);
freeskb:
    kfree_skb(skb);
out_of_mem:
    return 0;

}


2.各项检验完成后,通过NF_HOOK来调用arp_process()函数。

 

static int arp_process(struct sk_buff *skb)
{
    struct net_device *dev = skb->dev;
    struct in_device *in_dev = in_dev_get(dev);
    struct arphdr *arp;
    unsigned char *arp_ptr;
    struct rtable *rt;
    unsigned char *sha, *tha;
    u32 sip, tip;
    u16 dev_type = dev->type;
    int addr_type;
    struct neighbour *n;
    /* arp_rcv below verifies the ARP header and verifies the device
     * is ARP'able.
     */
    if (in_dev == NULL)
        goto out;
    arp = skb->nh.arph;
    switch (dev_type) {
    default:    
        if (arp->ar_pro != htons(ETH_P_IP) ||
         htons(dev_type) != arp->ar_hrd)
            goto out;
        break;
        ...
    }
    ...    
    /*
     * arp_ptr equal ((unsignec char *)(arp) + sizeof(struct arphdr))
     */
    arp_ptr= (unsigned char *)(arp+1);
    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);
/*

* 基本校验通过后,函数具体的处理入口。这里的处理思路是:如果是请求本机的包则将回送响应;

* 如果是请求其它机器的信息,则函数将作为代理转发请求。

* 如果是对本机所发请求的响应或请求本机的MAC地址,则需要在系统的缓存中加入一个条目.

* (假定情况是有人请求本机mac地址,则对方随后可能会与本机交互,因此如果本机缓存了对方地址,

* 则会很省时.由于本机不在对方的缓存中,则对方的地址也可能不在本机的缓存中.)*/    
if (arp->ar_op == htons(ARPOP_REQUEST) &&/* ARP请求 */
     ip_route_input(skb, tip, sip, 0, dev) == 0) {/*在路由表中找到目标IP得路由信息,并保存在skb->dst中 */
        rt = (struct rtable*)skb->dst;
        addr_type = rt->rt_type;
        if (addr_type == RTN_LOCAL) {/* 本地ARP */
            n = neigh_event_ns(&arp_tbl, sha, &sip, dev);
            if (n) {
                int dont_send = 0;
                if (!dont_send)
                    dont_send |= arp_ignore(in_dev,dev,sip,tip);
                if (!dont_send && IN_DEV_ARPFILTER(in_dev))
                    dont_send |= arp_filter(sip,tip,dev); 
                if (!dont_send)/*发送arp响应消息告知本机mac地址*/
                    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)) {/*若是转发的请求,则做proxy ARP代理*/
            if ((rt->rt_flags&RTCF_DNAT) ||
             (addr_type == RTN_UNICAST && rt->u.dst.dev != dev &&
             (arp_fwd_proxy(in_dev, rt) || 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 == LOCALLY_ENQUEUED || 
                 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;
            }
        }
    }



    /* Update our ARP tables */

    n = __neigh_lookup(&arp_tbl, &sip, dev, 0);
    ...
    if (n) {
        int state = NUD_REACHABLE;
        int override;
        /* 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.
         */
        override = time_after(jiffies, n->updated + n->parms->locktime);
        /* Broadcast replies and request packets
         do not assert neighbour reachability.
         */
        if (arp->ar_op != htons(ARPOP_REPLY) ||
         skb->pkt_type != PACKET_HOST)
            state = NUD_STALE;
        neigh_update(n, sha, state, override ? NEIGH_UPDATE_F_OVERRIDE : 0);
        neigh_release(n);
    }
out:
    if (in_dev)
        in_dev_put(in_dev);
    kfree_skb(skb);
    return 0;
}


3.调用arp_send()函数发送ARP响应包或者作为代理发送ARP包。

相关阅读 更多 +
排行榜 更多 +
辰域智控app

辰域智控app

系统工具 下载
网医联盟app

网医联盟app

运动健身 下载
汇丰汇选App

汇丰汇选App

金融理财 下载