数据报文去哪儿了

背景

今天遇到一个诡异的现象,当接口附加一个IP时,主IP业务正常,附加IP死活不行,tcpdump抓包确可以正常抓到到业务的报文,但是在PREROUTING raw添加规则确没有命中,说明报文没有到netfilter框架内,Scapy打流测试,通过bpftrace 跟踪kfree_skb,没有捕获大量的kfree_skb 调用,才引发今天的问题,数据报文去哪儿了?

结论

由于上层设备发送报文时,将附加IP的MAC地址填错导致,但是tcpdump可以明确报文已经到了主机,但是为什么却没有往netfilter框架递送,bpftrace 为什么没有捕获大量的kfree_skb? 简单的讲错误的MAC地址,正确的IP 是否可以正常通信?为什么?

内核源码分析

本文使用内核版本 5.10, 网卡驱动 e1000e

我们知道当网卡工作在直接模式(Direct Model)时,网卡只接收自己MAC地址的帧,此模式下通过scapy打流不匹配的目的mac地址时,数据帧直接被网卡层面丢弃,bpftrace 此时无法捕获kfree_skb事件。

当使用tcpdump工具时会将网卡设置为混杂模式(Promiscuous Model),不匹配自己的MAC地址也会接收交给网卡驱动处理。

cpp 复制代码
/**
 * e1000_receive_skb - helper function to handle Rx indications
 * @adapter: board private structure
 * @netdev: pointer to netdev struct
 * @staterr: descriptor extended error and status field as written by hardware
 * @vlan: descriptor vlan field as written by hardware (no le/be conversion)
 * @skb: pointer to sk_buff to be indicated to stack
 **/
static void e1000_receive_skb(struct e1000_adapter *adapter,
			      struct net_device *netdev, struct sk_buff *skb,
			      u32 staterr, __le16 vlan)
{
	u16 tag = le16_to_cpu(vlan);

	e1000e_rx_hwtstamp(adapter, staterr, skb);

	skb->protocol = eth_type_trans(skb, netdev);

	if (staterr & E1000_RXD_STAT_VP)
		__vlan_hwaccel_put_tag(skb, htons(ETH_P_8021Q), tag);

	napi_gro_receive(&adapter->napi, skb);
}

这里e1000e网卡驱动接收报文,此处注意eth_type_trans(skb, netdev)方法

cpp 复制代码
/**
 * eth_type_trans - determine the packet's protocol ID.
 * @skb: received socket data
 * @dev: receiving network device
 *
 * The rule here is that we
 * assume 802.3 if the type field is short enough to be a length.
 * This is normal practice and works for any 'now in use' protocol.
 */
__be16 eth_type_trans(struct sk_buff *skb, struct net_device *dev)
{
	unsigned short _service_access_point;
	const unsigned short *sap;
	const struct ethhdr *eth;

	skb->dev = dev;
	skb_reset_mac_header(skb);

	eth = (struct ethhdr *)skb->data;
	skb_pull_inline(skb, ETH_HLEN);

    /* 此处目的MAC不是设备DEV地址时,命中条件 */
	if (unlikely(!ether_addr_equal_64bits(eth->h_dest,
					      dev->dev_addr))) {
        /* 检测目的MAC是否为多播或者组播 */
		if (unlikely(is_multicast_ether_addr_64bits(eth->h_dest))) {
			if (ether_addr_equal_64bits(eth->h_dest, dev->broadcast))
				skb->pkt_type = PACKET_BROADCAST;
			else
				skb->pkt_type = PACKET_MULTICAST;
		} else {
			skb->pkt_type = PACKET_OTHERHOST;
		}
	}

    if (likely(eth_proto_is_802_3(eth->h_proto)))
		return eth->h_proto;
    .....
}

当我们的目的MAC地址与自身dev地址不匹配时,会将pkt_type = PACKET_OTHERHOST 然后返回eth->proto, 这里我们使用的IP报文,也就是将来会使用ip_rcv()处理。

然后通过NAPI接口,__netif_receive_skb_core,等一系列调用最终调用到ip_rcv_core()

cpp 复制代码
/*
 * 	Main IP Receive routine.
 */
static struct sk_buff *ip_rcv_core(struct sk_buff *skb, struct net *net)
{
	const struct iphdr *iph;
	u32 len;

	/* When the interface is in promisc. mode, drop all the crap
	 * that it receives, do not try to analyse it.
	 */
	if (skb->pkt_type == PACKET_OTHERHOST)
		goto drop;

    ......

drop:
	kfree_skb(skb);
out:
	return NULL;
}

这里有明确的注释说明,混杂模式的报文即pkt_type为PACKET_OTHERHOST值,直接丢弃,并且此处的drop,内核协议栈层面并没有做任何的丢包统计。

总结

经过分析我们可以总结我们遇到的问题,

1,通过scapy打流测试,为什么bpftrace没有捕获到大量的kfree_skb事件?

这是因为网卡工作在直接模式(Direct Model)网卡将目的MAC不是自己的直接丢弃,验证这个想象,可以直接使用tcpdump 工具抓包,此时bpftrace 可以捕获大量的kfree_skb事件。

2,tcpdump 捕获到去往自己IP的报文,为什么没有到netfilter框架?

这是因为tcpdump将网卡设置为混杂模式(Promiscuous Model)网卡驱动接收报文并将报文类型置为PACKET_OTHERHOST,当ip_rcv_core()接收后直接丢弃,并且没有在任何地方做丢包统计的动作。

感受

工作中遇到的每个小问题,背后都蕴藏着大量知识,只有平时多积累总结,才能游刃有余解决所面对的问题。

相关推荐
大树886 小时前
金刚石散热越强,管路越先见顶
大数据·运维·服务器·人工智能·ai
摇滚侠6 小时前
Linux CentOS7 rpm 安装 MySQL 5.7
linux·运维·mysql
霸道流氓气质7 小时前
领域驱动设计(DDD)在 Spring Boot 微服务中的实践指南
运维·spring boot·微服务
小宇宙Zz7 小时前
Maven依赖冲突
java·服务器·maven
Inhand陈工8 小时前
基于台达PLC与映翰通IG502的智慧水产养殖精准投喂与远程运维解决方案
运维·人工智能·物联网·阿里云·信息与通信
网络研究院8 小时前
2026年网络安全
网络·安全·法律·法规·趋势·发展
酣大智8 小时前
ARP代理--工作原理
运维·网络·arp·arp代理
treesforest8 小时前
AI安全系统如何识别异常访问?IP风险识别正在成为关键能力
网络·人工智能·tcp/ip·安全·web安全
shushangyun_8 小时前
2026年快消品B2B系统推荐:支持终端门店订货、促销政策自动化的工具?
java·运维·网络·数据库·人工智能·spring·自动化
古城小栈8 小时前
Unix 与 Linux 异同小叙
linux·服务器·unix