数据报文去哪儿了

背景

今天遇到一个诡异的现象,当接口附加一个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()接收后直接丢弃,并且没有在任何地方做丢包统计的动作。

感受

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

相关推荐
若风的雨9 分钟前
【deekseek】P2P通信路由过程
服务器·网络协议·p2p
Python私教29 分钟前
征服Rust:从零到独立开发的实战进阶
服务器·开发语言·rust
zizisuo32 分钟前
面试篇:Spring Security
网络·数据库·安全
玉笥寻珍33 分钟前
Web安全渗透测试基础知识之HTTP参数污染篇
网络·网络协议·安全·web安全·http
GCKJ_08241 小时前
观成科技:加密C2框架Vshell流量分析
网络·科技·信息与通信
曼岛_2 小时前
[架构之美]linux常见故障问题解决方案(十九)
linux·运维·架构
大神的风范2 小时前
从0开始学linux韦东山教程第三章问题小结(4)
linux·服务器
大蚂蚁2号3 小时前
windows文件共享另一台电脑资源管理器网络文件夹无法找到机器
运维·服务器·网络
Lw老王要学习3 小时前
Linux数据库篇、第一章_02_MySQL的使用增删改查
linux·运维·数据库·mysql·云计算·it
LetsonH3 小时前
Home Assistant 米家集成:开启智能家居新体验
网络·智能家居