内核arp请求

 

Linux 内核 网络地址转换函数 in_aton、 in4_pton 和 in6_pton

 

#ifndef _LINUX_INET_H
#define _LINUX_INET_H
 
#include <linux/types.h>
 
/*
 * These mimic similar macros defined in user-space for inet_ntop(3).
 * See /usr/include/netinet/in.h .
 */
#define INET_ADDRSTRLEN        (16)
#define INET6_ADDRSTRLEN    (48)
 
extern __be32 in_aton(const char *str);
extern int in4_pton(const char *src, int srclen, u8 *dst, int delim, const char **end);
extern int in6_pton(const char *src, int srclen, u8 *dst, int delim, const char **end);
#endif    /* _LINUX_INET_H */

 

 

/**
 * in4_pton - convert an IPv4 address from literal to binary representation
 * @src: the start of the IPv4 address string ,地址字符串
 * @srclen: the length of the string, -1 means strlen(src),字符串长度,可以填写-1
 * @dst: the binary (u8[4] array) representation of the IPv4 address,地址缓存
 * @delim: the delimiter of the IPv4 address in @src, -1 means no delimiter,分隔符
 * @end: A pointer to the end of the parsed string will be placed here,可以填写NULL
 *
 * Return one on success, return zero when any error occurs
 * and @end will point to the end of the parsed string.
 *
 * 成功返回1,出错返回0
 */
int in4_pton(const char *src, int srclen, u8 *dst, int delim, const char **end);
 
用例:
    __be32 saddr;
    ret = in4_pton("192.168.1.1", strlen("192.168.1.1"), &addr, -1, NULL);

三个函数头文件都是<linux/inet.h>.,使用是只要包含该头文件就可以了。

 

 

arp_send_dst(ARPOP_REQUEST, ETH_P_ARP, target, dev, saddr,
             dst_hw, dev->dev_addr, NULL, dst);

 

dev_hard_header

dev->header_ops         = &eth_header_ops

static inline int dev_hard_header(struct sk_buff *skb, struct net_device *dev,
                  unsigned short type,
                  const void *daddr, const void *saddr,
                  unsigned int len)
{
    if (!dev->header_ops || !dev->header_ops->create)
        return 0;

    return dev->header_ops->create(skb, dev, type, daddr, saddr, len);
}

 

/**
 * eth_header - create the Ethernet header
 * @skb:    buffer to alter
 * @dev:    source device
 * @type:    Ethernet type field
 * @daddr: destination address (NULL leave destination address)
 * @saddr: source address (NULL use device source address)
 * @len:   packet length (<= skb->len)
 *
 *
 * Set the protocol type. For a packet of type ETH_P_802_3/2 we put the length
 * in here instead.
 */
int eth_header(struct sk_buff *skb, struct net_device *dev,
           unsigned short type,
           const void *daddr, const void *saddr, unsigned int len)
{
    struct ethhdr *eth = (struct ethhdr *)skb_push(skb, ETH_HLEN);
    if (type != ETH_P_802_3 && type != ETH_P_802_2)
        eth->h_proto = htons(type);
    else
        eth->h_proto = htons(len);
    /*
     *      Set the source hardware address.
     */
    if (!saddr)
        saddr = dev->dev_addr;
    memcpy(eth->h_source, saddr, ETH_ALEN);
    if (daddr) {
        memcpy(eth->h_dest, daddr, ETH_ALEN);
        return ETH_HLEN;
    }
    /*
     *      Anyway, the loopback-device should never use this function...
     */
    if (dev->flags & (IFF_LOOPBACK | IFF_NOARP)) {
        memset(eth->h_dest, 0, ETH_ALEN);
        return ETH_HLEN;
    }
    return -ETH_HLEN;
}

 

 

arp_create


     dev_kfree_skb_any(skb);
     //struct sk_buff *skb;
     __be32 src_addr, dst_addr;
     in4_pton("10.10.10.8", strlen(DEST_IP),(u8 *)&dst_addr, '\0', NULL);
     in4_pton("10.10.10.7", strlen(SRC_IP), (u8 *)&src_addr, '\0', NULL);
     skb = arp_create(ARPOP_REQUEST, ETH_P_ARP,  dst_addr, dev, src_addr,NULL, dev->dev_addr, NULL);
start_xmit(skb,dev);

 

 

/* Create and send an arp packet. */
static void arp_send_dst(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 dst_entry *dst)
{
    struct sk_buff *skb;

    /* 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)
        return;

    skb_dst_set(skb, dst_clone(dst));
    arp_xmit(skb);
}

 

static inline void skb_dst_set(struct sk_buff *skb, struct dst_entry *dst)
{
        skb->_skb_refdst = (unsigned long)dst;
}

 

我们常见的IP地址都是以点分十进制格式表示,例如“172.18.1.231”。而在程序中基本是以如下的结构表示一个IP:

struct in_addr {
         __be32     s_addr; //其实就是一个32bit的数字
};


   它和点分十进制格式的IP地址可以通过一组API实现相互转换:

int inet_aton(const char *cp,struct in_addr *inp) 无效的地址cp则返回0;否则返回非0
char *inet_ntoa(struct in_addr in) 将一个32位的IP地址转换成点分十进制字符串。

 

 

arp_hdr

static inline struct arphdr *arp_hdr(const struct sk_buff *skb)
{
        return (struct arphdr *)skb_network_header(skb);
}

 

__netif_receive_skb_core

1)处理 ptype_all 上所有的 packet_type->func() ,典型场景就是抓包
2) 处理vlan报文
3)rx_handler函数处理,例如网桥
4)处理ptype_base上所有的 packet_type->func() ,数据包传递给上层协议层处理,例如ip_rcv函数; 

static int __netif_receive_skb_core(struct sk_buff *skb, bool pfmemalloc) // 将skb传递到上层 
{
    struct packet_type *ptype, *pt_prev;
    rx_handler_func_t *rx_handler;
    struct net_device *orig_dev;
    struct net_device *null_or_dev;
    bool deliver_exact = false;//默认不精确传递
    int ret = NET_RX_DROP;//默认收报失败
    __be16 type;
 
    net_timestamp_check(!netdev_tstamp_prequeue, skb);//记录收包时间,netdev_tstamp_prequeue为0,表示可能有包延迟 
 
    trace_netif_receive_skb(skb);
 
    orig_dev = skb->dev;//记录收包设备 
 
    skb_reset_network_header(skb);//重置network header,此时skb指向IP头(没有vlan的情况下)
    if (!skb_transport_header_was_set(skb))
        skb_reset_transport_header(skb);
    skb_reset_mac_len(skb);
  
     // 留下一个节点,最后一次向上层传递时,不需要再inc引用,回调中会free
 这样相当于少调用了一次free
 
     
    pt_prev = NULL;
 
another_round:
    skb->skb_iif = skb->dev->ifindex;//设置接收设备索引号 
 
    __this_cpu_inc(softnet_data.processed);//处理包数统计 
 
    if (skb->protocol == cpu_to_be16(ETH_P_8021Q) ||
        skb->protocol == cpu_to_be16(ETH_P_8021AD)) {//vxlan报文处理,剥除vxlan头
        skb = skb_vlan_untag(skb);//剥除vxlan头
        if (unlikely(!skb))
            goto out;
    }
 
#ifdef CONFIG_NET_CLS_ACT
    if (skb->tc_verd & TC_NCLS) {
        skb->tc_verd = CLR_TC_NCLS(skb->tc_verd);
        goto ncls;
    }
#endif
 
    if (pfmemalloc)此类报文不允许ptype_all处理,即tcpdump也抓不到
        goto skip_taps;
       //先处理 ptype_all 上所有的 packet_type->func()           
       //所有包都会调func,对性能影响严重!所有有的钩子是随模块加载挂上的。
    list_for_each_entry_rcu(ptype, &ptype_all, list) {//遍历ptye_all链表
        if (!ptype->dev || ptype->dev == skb->dev) {//上面的paket_type.type 为 ETH_P_ALL,典型场景就是tcpdump抓包所使用的协议
            if (pt_prev)//pt_prev提高效率
                ret = deliver_skb(skb, pt_prev, orig_dev);//此函数最终调用paket_type.func()
            pt_prev = ptype;
        }    
    }
 
skip_taps:
#ifdef CONFIG_NET_CLS_ACT
    if (static_key_false(&ingress_needed)) {
        skb = handle_ing(skb, &pt_prev, &ret, orig_dev);
        if (!skb)
            goto out;
    }
 
    skb->tc_verd = 0;
ncls:
#endif
    if (pfmemalloc && !skb_pfmemalloc_protocol(skb))//不支持使用pfmemalloc 
        goto drop;
 
    if (skb_vlan_tag_present(skb)) {// 如果是vlan包 
        if (pt_prev) {/* 处理pt_prev */
            ret = deliver_skb(skb, pt_prev, orig_dev);
            pt_prev = NULL;
        }
        if (vlan_do_receive(&skb))/* 根据实际的vlan设备调整信息,再走一遍 */
            goto another_round;
        else if (unlikely(!skb))
            goto out;
    }
/*如果一个dev被添加到一个bridge(做为bridge的一个接口),这个接口设备的rx_handler将被设置为br_handle_frame函数,这是在br_add_if函数中设置的,而br_add_if (net/bridge/br_if.c)是在向网桥设备上添加接口时设置的。进入br_handle_frame也就进入了bridge的逻辑代码。*/
    rx_handler = rcu_dereference(skb->dev->rx_handler);/* 如果有注册handler,那么调用,比如网桥模块 */
    if (rx_handler) {
        if (pt_prev) {
            ret = deliver_skb(skb, pt_prev, orig_dev);
            pt_prev = NULL;
        }
        switch (rx_handler(&skb)) {
        case RX_HANDLER_CONSUMED:/* 已处理,无需进一步处理 */
            ret = NET_RX_SUCCESS;
            goto out;
        case RX_HANDLER_ANOTHER:/* 修改了skb->dev,在处理一次 */
            goto another_round;
        case RX_HANDLER_EXACT:/* 精确传递到ptype->dev == skb->dev */
            deliver_exact = true;
        case RX_HANDLER_PASS:
            break;
        default:
            BUG();
        }
    }
 
    if (unlikely(skb_vlan_tag_present(skb))) {/* 还有vlan标记,说明找不到vlanid对应的设备 */
        if (skb_vlan_tag_get_id(skb))/* 存在vlanid,则判定是到其他设备的包 */
            skb->pkt_type = PACKET_OTHERHOST;
        /* Note: we might in the future use prio bits
         * and set skb->priority like in vlan_do_receive()
         * For the time being, just ignore Priority Code Point
         */
        skb->vlan_tci = 0;
    }
 
    /* deliver only exact match when indicated */
    null_or_dev = deliver_exact ? skb->dev : NULL;//指定精确传递的话,就精确传递,否则向未指定设备的指定协议全局发送一份
 
    type = skb->protocol;/* 设置三层协议,下面提交都是按照三层协议提交的 */
    list_for_each_entry_rcu(ptype,&ptype_base[ntohs(type) & PTYPE_HASH_MASK], list) {
        if (ptype->type == type &&
            (ptype->dev == null_or_dev || ptype->dev == skb->dev ||
             ptype->dev == orig_dev)) {
            if (pt_prev)
                ret = deliver_skb(skb, pt_prev, orig_dev);//上层传递
            pt_prev = ptype;
        }
    }
    if (pt_prev) {
        if (unlikely(skb_orphan_frags(skb, GFP_ATOMIC)))
            goto drop;
        else
                //使用pt_prev这里就不需要deliver_skb来inc应用数了,  func执行内部会free,减少了一次skb_free
 
        
            ret = pt_prev->func(skb, skb->dev, pt_prev, orig_dev);/* 传递到上层*/
    } else {
drop:
        if (!deliver_exact)
            atomic_long_inc(&skb->dev->rx_dropped);//网卡丢包计数
        else
            atomic_long_inc(&skb->dev->rx_nohandler);
        kfree_skb(skb);
        /* Jamal, now you will not able to escape explaining
         * me how you were going to use this. :-)
         */
        ret = NET_RX_DROP;
    }
out:
    return ret;
}
 

 

posted on 2021-11-24 16:45  tycoon3  阅读(191)  评论(0编辑  收藏  举报

导航