查看内核源代码,我发现在处理 ARP 请求时,net_device
会直接从 中检索skb
,然后似乎将回复发送到该设备(arp.c#L679):
static int arp_process(struct net *net, struct sock *sk, struct sk_buff *skb)
{
struct net_device *dev = skb->dev;
...
arp_send_dst(ARPOP_REPLY, ETH_P_ARP,
sip, dev, tip, sha,
dev->dev_addr, sha,
reply_dst);
}
但是如果数据包通过了桥接器,那么skb
就得到了它的设备用桥接设备覆盖:
static int br_pass_frame_up(struct sk_buff *skb)
{
...
skb->dev = brdev;
...
}
难道不应该如何检索原始设备并将回复发送到那里吗?
答案1
如果帧从桥接端口到达,则意味着现在有一个新桥 FDB 条目指示在向此 MAC 地址发送帧时使用同一网桥端口:回复将发送到查询到达的同一网桥端口。
因此,从 ARP 层的角度来看,数据包在桥接接口上被接收,并且应答也在桥接接口上发回。
当回复到达下层:桥接器时,桥接器代码会在其转发数据库(又名FIB)并选择前一个传入桥接端口作为传出桥接端口,而不是将答案泛洪到其所有端口(因为可以找到这样一个最近的条目):
netdev_tx_t br_dev_xmit(struct sk_buff *skb, struct net_device *dev) { ... } else if ((dst = br_fdb_find_rcu(br, dest, vid)) != NULL) { br_forward(dst->dst, skb, false, true); } else { br_flood(br, skb, BR_PKT_UNICAST, false, true);
与之前相反,skb 的设备桥接口被桥端口覆盖:
static void __br_forward(const struct net_bridge_port *to, struct sk_buff *skb, bool local_orig) { ... skb->dev = to->dev; ...