参考Linux内核收包流程,函数调用链是:
硬件中断 -->do_IRQ–>handle_irq–>e1000_intr_msix_rx–>__napi_schedule(&adapter->napi)–>
____napi_schedule–>__raise_softirq_irqoff(NET_RX_SOFTIRQ)
do_IRQ–>irq_exit–>do_softirq–>call_softirq–>__do_softirq–>
net_rx_action->e1000e_poll–>e1000_receive_skb->napi_gro_receive–>
netif_receive_skb–>__netif_receive_skb–>__netif_receive_skb_core–>
deliver_skb–>ip_rcv–>NF_HOOK(NF_INET_PRE_ROUTING)–>
ip_rcv_finish–>dst_input–>ip_local_deliver–>
NF_HOOK(NF_INET_LOCAL_IN)–>ip_local_deliver_finish–>ipprot->handler()
如果ovs 或者普通的网桥如何报文在哪里接收呢?通过梳理代码发现上述调用流程中的__netif_receive_skb_core函数有个rx_handler,ovs bridge等都是通过设置该函数指针实现对报文的接管。
内核版本4.4
__netif_receive_skb_core函数中如下片段
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;
设置rx_handler的接口是netdev_rx_handler_register
int netdev_rx_handler_register(struct net_device *dev,
rx_handler_func_t *rx_handler,
void *rx_handler_data)
{
ASSERT_RTNL();
if (dev->rx_handler)
return -EBUSY;
/* Note: rx_handler_data must be set before rx_handler */
rcu_assign_pointer(dev->rx_handler_data, rx_handler_data);
rcu_assign_pointer(dev->rx_handler, rx_handler);
return 0;
}
EXPORT_SYMBOL_GPL(netdev_rx_handler_register);
梳理流程发现ovs的rx_handler指针是netdev_frame_hook;bridge的为br_handle_frame
在添加接口的时候调用设置,例如对于bridge,rx_handler的参数skb的skb->dev是桥下的设备,不是桥设备,桥设备(比如br0)是没有注册rx_handle这个函数的。