本文分析了桥上包RX处理流程。注意本文是openwrt版本ipq5018 soc的内核,openwrt和高通都可能修改了一些逻辑。
在桥添加接口的函数(net/bridge/br_if.c)中:
int br_add_if(struct net_bridge *br, struct net_device *dev)
调用
struct net_bridge_port *p;
err = netdev_rx_handler_register(dev, br_handle_frame, p);
给子接口注册一个rx handler。这样子接口收到包之后,就会调用br_handle_frame进行处理。
在 __netif_receive_skb_core()中,会调用rx_handler。
rx_handler = rcu_dereference(skb->dev->rx_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:
goto another_round;
case RX_HANDLER_EXACT:
deliver_exact = true;
case RX_HANDLER_PASS:
break;
default:
BUG();
}
}
定义在net/bridge/br_input.c
先计算出bridge port:
p = br_port_get_rcu(skb->dev);
如果定义了br_should_route_hook指针,则调用这个函数处理skb。ebtable(net/bridge/netfilter/ebtable_broute.c)的broute功能会设置br_should_route_hook为ebt_broute。
switch (p->state) {
case BR_STATE_FORWARDING:
rhook = rcu_dereference(br_should_route_hook);
if (rhook) {
if ((*rhook)(skb)) {
*pskb = skb;
return RX_HANDLER_PASS;
}
dest = eth_hdr(skb)->h_dest;
}
/* fall through */
case BR_STATE_LEARNING:
if (ether_addr_equal(p->br->dev->dev_addr, dest))
skb->pkt_type = PACKET_HOST;
BR_HOOK(NFPROTO_BRIDGE, NF_BR_PRE_ROUTING,
dev_net(skb->dev), NULL, skb, skb->dev, NULL,
br_handle_frame_finish);
break;
}
return RX_HANDLER_CONSUMED;
最后,使用BR_HOOK(),调用bridge netfilter的NF_BR_PRE_ROUTING hook,然后再调用br_handle_frame_finish。
bool br_netfilter_run_hooks(void)
{
return brnf_call_iptables | brnf_call_ip6tables | brnf_call_arptables |
brnf_call_ebtables | brnf_call_custom;
}
static inline int
BR_HOOK(uint8_t pf, unsigned int hook, struct net *net, struct sock *sk,
struct sk_buff *skb, struct net_device *in, struct net_device *out,
int (*okfn)(struct net *, struct sock *, struct sk_buff *))
{
if (!br_netfilter_run_hooks())
return okfn(net, sk, skb);
return NF_HOOK(pf, hook, net, sk, skb, in, out, okfn);
}
如果没有打开br_netfilter,则直接 调用okfn,否则走正常的NF_HOOK流程。
net/bridge/br_input.c
计算出桥:br = p->br;
如果子接口地址学习打开,则更新fdb
if (p->flags & BR_LEARNING)
br_fdb_update(br, p, eth_hdr(skb)->h_source, vid, false);
处理multicast帧,注意,这里不是转发组播帧,只是学习组播帧的内容,用于multicast snooping。
if (!is_broadcast_ether_addr(dest) && is_multicast_ether_addr(dest) &&
br_multicast_rcv(br, p, skb, vid))
goto drop;
接口开始处理帧转发或者local in
/* The packet skb2 goes to the local host (NULL to skip). */
skb2 = NULL;
if (br->dev->flags & IFF_PROMISC)
skb2 = skb;
dst = NULL;
if (IS_ENABLED(CONFIG_INET) && skb->protocol == htons(ETH_P_ARP))
br_do_proxy_arp(skb, br, vid, p);
if (skb->protocol == htons(ETH_P_PAE)) {
skb2 = skb;
/* Do not forward 802.1x/EAP frames */
skb = NULL;
} else if (is_broadcast_ether_addr(dest)) {
skb2 = skb;
unicast = false;
} else if (is_multicast_ether_addr(dest)) {
br_multicast_handle_hook_t *multicast_handle_hook =
rcu_dereference(br_multicast_handle_hook);
if (!__br_get(multicast_handle_hook, true, p, skb))
goto out;
mdst = br_mdb_get(br, skb, vid);
if ((mdst || BR_INPUT_SKB_CB_MROUTERS_ONLY(skb)) &&
br_multicast_querier_exists(br, eth_hdr(skb))) {
if ((mdst && mdst->mglist) ||
br_multicast_is_router(br))
skb2 = skb;
br_multicast_forward(mdst, skb, skb2);
skb = NULL;
if (!skb2)
goto out;
} else
skb2 = skb;
unicast = false;
br->dev->stats.multicast++;
} else {
pdst = __br_get(get_dst_hook, NULL, p, &skb);
if (pdst) {
if (!skb)
goto out;
} else {
dst = __br_fdb_get(br, dest, vid);
if ((p->flags & BR_ISOLATE_MODE) ||
(dst && dst->is_local)) {
skb2 = skb;
/* Do not forward the packet since it's local.*/
skb = NULL;
}
}
}
if (skb) {
if (dst) {
dst->used = jiffies;
pdst = dst->dst;
}
if (pdst)
br_forward(pdst, skb, skb2);
else
br_flood_forward(br, skb, skb2, unicast);
}
if (skb2)
return br_pass_frame_up(skb2);
out:
return 0;
drop:
kfree_skb(skb);
goto out;
总结如下:skb是要转发的帧,skb2是要上到本地协议栈的帧。
如果是广播包,则skb2 = skb,
如果是多播包,
multicast_handle_hook,如果这个hook定义了,执行这个hook,如果hook返回false,则直接跳出去,不进行转发处理。
mdst = br_mdb_get(br, skb, vid);
如果mdst是空,则进行广播处理。否则,调用 br_multicast_forward(mdst, skb, skb2) 处理。
如果是单播包
则查找fdb,
调用:
br_forward(pdst, skb, skb2);
br_flood_forward(br, skb, skb2, unicast);
br_pass_frame_up(skb2);
等进行转发处理。
net/bridge/br_forward.c
遍历桥的port,执行转发。