内核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 = ð_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; }