linux内核网络协议栈--2层报文处理(十七)
版本说明
Linux版本: 3.10.103
网卡驱动: ixgbe
网络协议注册
- br_add_if主要是注册桥处理函数br_handle_frame给skb->edv->rx_handler。
- netdev_create–>netdev_rx_handler_register主要是注册接收函数netdev_frame_hook,此处是给openvswitch的datapath用的。
报文接收
网卡调用__netif_receivve_skb_core后,会调用skb->dev->rx_handler处理2层协议函数,此处只关注桥的,暂不关注ovs的。
br_handle_frame
- 首先判定mac是否本地,是本地的报文则进入到NFPROTO_BRIDGE的NF_HOOK,即NF_BR_LOCAL_IN,进入处理链,最终完成后调用br_handle_local_finish完成操作,等着进入再上一层的协议。
- 如果不是本地,则进入转发流程,如果直接转发状态,则调用br_should_route_hook,即ebt_route;如果是学习状态是NF_HOOK,即NF_BR_PRE_ROUTING,进入处理链,最终完成调用br_handle_frame_finish。
- 然后调用br_fdb_update学习源MAC地址,更新MAC地址表,__br_fdb_get或者目的MAC对应的端口。
- 如果查找到了端口,调用br_forward转发到对应的端口。 找不到端口,调用br_flood_forward进行广播。
- br_forward继续调用NF_HOOK的NF_BR_FORWARD,完成后调用br_forward_finish,继续调用NF_HOOK的NF_BR_POST_ROUTING,最终调用br_dev_queue_push_xmit。
- br_dev_queue_push_xmit–>dev_queue_xmit进行L2的发送。
报文发送
dev_queue_xmit
- netdev_pick_tx主要是选择发送设备设置的映射关系,查找当前CPU对应的队列,如果多个则计算hash,确定队列。
- 支持入队的话,调用__dev_xmit_skb进行下一步的操作。不支持队列的话调用dev_hard_start_xmit进行数据的发送。
__dev_xmit_skb
- 如果队列的状态是非活跃的,则丢包。
- 如果是支持bypass,则调用sch_direct_xmit直接发送数据包,没有发送完成,继续调用__qdisc_run。
- 其他情况入队并且调用__qdisc_run。
__qdisc_run
- while循环调用qdisc_restart,进行的操作为skb出队,并且调用sch_direct_xmit直接发送数据包,没有发送完成,返回值非0,继续参与循环。
- 一直循环到数据包发送完毕,或者权重耗完或者需要重新调度,此时调用__netif_schedule–>__netif_reschedule,将当前的队列记录在CPU的softnet_data,触发软中断NET_TX_SOFTIRQ,调用net_tx_action。
sch_direct_xmit
- 直接调用dev_hard_start_xmit–>ops->ndo_start_xmit–>ixgbe_xmit_frame发送数据包。
- 如果发送成功,则返回值为队列剩下的数据包量。
- 如果被自己CPU锁了则报错,被其他CPU锁了则重新入队,等以后再发送。
- 如果busy的话重新入队,等以后发送。
- 所以其实就是发送完成则返回成功0,否则发送非0。
L2报文处理流程图:
原文链接:https://zhaozhanxu.com/2016/07/13/Linux/2016-07-13-Linux-Kernel-Pkts_Processing2/