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

63 篇文章 0 订阅

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
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: MQTTAsync_rcv是一个函数,它是MQTT C客户端库中的一个函数,用于接收MQTT消息。这个函数的作用是从MQTT服务器接收消息并将其传递给客户端应用程序。它采用异步方式工作,这意味着它可以在后台接收消息而不会阻塞主线程。当接收到消息时,它将调用注册的回调函数来处理消息。 以下是该函数的原型: ``` int MQTTAsync_rcv(MQTTAsync handle, char **topicName, int *topicLen, MQTTAsync_message **message, long timeout); ``` 其中,参数含义如下: - `handle`:MQTT客户端句柄 - `topicName`:指向指针的指针,用于存储消息的主题名称 - `topicLen`:指向int的指针,用于存储主题名称的长度 - `message`:指向指针的指针,用于存储MQTT消息 - `timeout`:超时时间,单位为毫秒 如果成功接收到消息,则返回MQTTASYNC_SUCCESS,否则返回错误代码。 ### 回答2: MQTTAsync_rcv是MQTT客户端库中的一个接收消息的函数。它用于从服务器接收消息并处理消息的回调函数。 当客户端连接到MQTT服务器并成功订阅了一个或多个主题后,服务器将会发送相应的消息给客户端。MQTTAsync_rcv函数的作用就是接收并处理这些消息。 使用MQTTAsync_rcv函数,我们可以定义一个回调函数来处理接收到的消息。当服务器发送消息到客户端时,MQTTAsync_rcv函数将被触发,回调函数会被调用。 在回调函数中,我们可以根据消息的主题和内容做出相应的处理。例如,可以解析消息内容,更新应用程序的状态,将消息显示在用户界面上,或者调用其他函数来处理该消息。 需要注意的是,MQTTAsync_rcv函数只有在客户端使用MQTTAsync_subscribe函数成功订阅主题后才能正常接收消息。如果未订阅主题,则无法接收到服务器发送的消息。 通过使用MQTTAsync_rcv函数,我们可以实现基于MQTT协议的即时通信功能,使得客户端能够与服务器进行双向通信,达到实时数据传输的目的。 ### 回答3: MQTTAsync_rcv是一个用于接收MQTT消息的函数。MQTT是一种轻量级的通讯协议,主要用于在物联网领域进行设备间的通讯。MQTTAsync_rcv函数是MQTTAsync库中的一个函数,它允许我们在客户端程序中接收来自MQTT服务器的消息。 使用MQTTAsync_rcv函数,我们可以设置一个回调函数来处理接收到的消息。当有消息到达时,MQTTAsync_rcv函数会调用我们设置的回调函数,并将消息内容作为参数传递给回调函数。我们可以在回调函数中进行对消息的处理和分析,比如打印消息内容、保存消息到数据库等等。 MQTTAsync_rcv函数可以接收不同类型的消息,如发布消息、订阅确认消息等。我们可以根据自己的需求来选择接收哪种类型的消息,并在回调函数中进行相应的处理。 使用MQTTAsync_rcv函数需要先建立与MQTT服务器的连接,并创建一个MQTT订阅。在订阅消息后,我们可以调用MQTTAsync_rcv函数来接收MQTT服务器发送过来的消息。 总之,MQTTAsync_rcv函数是一个功能强大的函数,它使我们能够方便地接收并处理MQTT服务器发送过来的消息,为我们在物联网设备间构建通讯系统提供了很大的便利性。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值