赵占旭的博客

Linux内核报文收发-2层报文处理

版本说明


Linux版本: 3.10.103
网卡驱动: ixgbe

网络协议注册


  • br_add_if主要是注册桥处理函数br_handle_frameskb->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_BRIDGENF_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_HOOKNF_BR_FORWARD,完成后调用br_forward_finish,继续调用NF_HOOKNF_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。

avatar

注意:所有文章非特别说明皆为原创。为保证信息与源同步,转载时请务必注明文章出处!谢谢合作 :-)

原始链接:http://zhaozhanxu.com/2016/07/13/Linux/2016-07-13-Linux-Kernel-Pkts_Processing2/

许可协议: "署名-非商用-相同方式共享 4.0" 转载请保留原文链接及作者。