舞觅泪眸 / 通信协议 / 2.6.24.4内核网络接收数据包分析

分享

   

2.6.24.4内核网络接收数据包分析

2011-08-10  舞觅泪眸
在2.6.24.4中所有的网卡,不管是否支持napi,都是通过struct napi_struct结构进行。所有我们先说一下这个结果。 struct napi_struct{ struct list_head poll_list; unsigned long state; int weight; int (*poll)(struct napi_struct *,int); } 对应支持napi的网卡,自己填充这个结构体;而非napi网卡,则使用per cpu的softnet_data->backlog,这个结构的初始化在net_dev_init()中完成。 我们先说一下非napi机制的网卡: 网卡接收到数据包后dma到内核空间,然后调用netif_rx()将数据包挂接到softnet_data->input_pkt_queue中,如果backlog这个napi_struct没有被调度,则napi_schedule(&backlog). napi_schedule()会将backlog的poll_list挂接到softnet_data->poll_list上,同时触发软中断NET_RX_SOFTIRQ。 NET_RX_SOFTIRQ软中断,调用相应的函数net_rx_action()。 对应napi机制的网卡: 网卡初始化是会自己初始化一个自己的数据包接收队列,当有数据包到达时,将数据包dma到自己的数据包队列中,如果自己的napi没有调度,则napi_schedule(mynapi),这里的mynapi是网卡自己的napi_struct.napi_schedule()会将网卡自己的poll_list挂接到softnet_data->poll_list上,同时出发软中断NET_RX_SOFTIRQ。NET_RX_SOFTIRQ软中断,调用相应的函数net_rx_action()。 net_rx_action(): 首先获取softnet_data->poll_list,通过遍历poll_list,获取每个poll_list对应的napi_struct结构(container_of实现),然后根据napi_struct的weight调用poll函数,如果是非napi网卡,这里的napi_struct是backlog,所以poll函数就是process_backlog;如果是napi的网卡,则会使自己的poll函数。 napi网卡的poll函数就是从自己数据包队列中dequeue出一个skb,然后调用netif_receive_skb(). 非napi的process_backlog会获取softnet_data->input_pkt_queue,然后对队列input_pkt_queue进行dequeue操作,获得一个skb,之后调用netif_receive_skb(skb)。 netif_receive_skb(): 对skb做一些准备工作,例如设置mac_len等,调用deliver_skb()给所有的注册ptype_all类型的协议处理handle,然后是网桥和VLAN的处理,之后会给注册的相应协议的ptype_base的handle。这里假设是ip协议,则会调用相应的ip协议handle的处理函数ip_rcv。 ip_rcv(): 对skb做一些检查工作后,会转入netfilter的NF_IP_PRE_ROUTING的hook点,调用所有在该点注册的hook函数。比如说如果开启了conntrack,则会在这里进行数据包重组。之后调用ip_rcv_finish(). ip_rcv_finish(): 首先调用ip_route_input()决定数据包的路由,初始化skb->dst,调用dst_input(skb). dst_input(): 实际上是调用skb->dst->input(skb),对应input的初始化在route.c中。如果是发往本地的数据包dst->input=ip_local_deliver;如果是转发的数据包dst->input=ip_forward; 本地流程: ip_local_deliver(): 首先是对分片的数据包重组,会转入netfilter的NF_IP_LOCAL_IN的hook点,调用所有在该点注册的hook函数。之后会调用ip_local_deliver_finish(),之后就到第四层了。 转发流程: ip_forward(): 做一些源路由等方面的检查后,会转入netfilter的NF_IP_FORWARD的hook点,调用所有在该点注册的hook函数。之后会调用ip_forward_finish(). ip_forward_finish(): 调用dst_output(). dst_output(): skb->dst->output(skb).一般output=ip_output. ip_output(): 设置skb的dev为发包的dev,同时设置skb->protocol,会转入netfilter的NF_IP_POST_ROUTING的hook点,调用所有在该点注册的hook函数。之后会调用ip_finish_output(). ip_finish_output(): 检查一下数据包是否需要分片,如果需要分片,则进行ip_fragement(),之后调用ip_finish_output2(). ip_finish_output2(): 根据neighbour,调用dst->neighbour->output. 到这为止,数据包会经过dev_queue_xmit放入dev的qdisc中。之后就是流控出队列

    本站是提供个人知识管理的网络存储空间,所有内容均由用户发布,不代表本站观点。请注意甄别内容中的联系方式、诱导购买等信息,谨防诈骗。如发现有害或侵权内容,请点击一键举报。

    0条评论

    发表

    请遵守用户 评论公约

    类似文章 更多
    喜欢该文的人也喜欢 更多

    ×
    ×

    ¥.00

    微信或支付宝扫码支付:

    开通即同意《个图VIP服务协议》

    全部>>