Linux内核用结构体struct net_device表示一个网络设备接口,该结构体的成员hard_start_xmit是一个函数指针,用于完成数据报在网络上的发送工作,其原型是:
int (*hard_start_xmit)( struct sk_buff *skb, struct net_device *dev );
skb是待发送的数据缓冲区,dev是该网络设备接口本身的一个指针。环回设备接口由于是把数据报发给本机,所以其发送数据报函数比较特殊,它把skb稍
加处理后,又转回给协议栈的数据报接收函数netif_rx。其发送函数的函数名是loopback_xmit。
首先,loopback_xmit调用skb_orphan把skb孤立,使它跟发送socket和协议栈不再有任何联系,也即对本机来说,这个skb的
数据内容已经发送出去了,而skb相当于已经被释放掉了。skb_orphan所做的实际事情是,首先从skb->sk(发送这个skb的那个
socket)的sk_wmem_alloc减去skb->truesize,也即从socket的已提交发送队列的字节数中减去这个skb,表示
这个skb已经发送出去了,同时,如果有进程在这个socket上写等待,则唤醒这些进程继续发送数据报,然后把socket的引用计数减1,最后,令
sk->destructor和skb->sk都为NULL,使skb完全孤立。实际上,对于环回设备接口来说,数据的发送工作至此已经全部
完成,接下来,只要把这个实际上还未被释放的skb传回给协议栈的接收函数即可。
在传回去之前,还需要做一些工作,因为协议栈在发送数据报时,为数据报打以太网首部时,skb->data是指向以太网首部的开始位置的,网络设备
接口传回的数据报是需要已经剥离了以太网首部的,所以令skb->data加上ETH_HLEN(14),令skb->mac.raw指向原
来data的位置,即以太网首部。然后,需要确定skb->pkt_type的值,即该数据报的类型,如果以太网首部中的目的mac地址是个组播或
广播地址,则skb->pkt_type为PACKET_BROADCAST或PACKET_MULTICAST,关于MAC地址类型的判断方法以
后再详细分析。否则,如果目的mac地址不等于本接口设备的mac地址,则skb->pkt_type为PACKET_OTHERHOST,否则就
为默认值PACKET_HOST。最后,设置skb->protocol(ETH_P_IP,ETH_P_ARP或其它),设置
skb->dev,再更新一下统计值,把skb交回给协议协栈即可。
struct net_device还有一些跟协议栈的处理流程关系比较密切的成员函数,下面一一介绍。
hard_header,该成员被以太网设备驱动程序用于为每一个待发送数据报构建以太网首部,系统中所有以太网设备驱动程序共享一个函数即
eth_header。所有数据报在传递给该函数之前,其skb头部预留了以太网首部的空间,data成员指向网络层首部,eth_header将
data成员指向以太网首部,并为以太网首部填入目的以太网地址,源以太网地址和网络层协议类型(ETH_P_IP或ETH_P_ARP),该函数在协议
栈中主要有两处被用到,一是ARP模块中,发送ARP请求或应答前,构建以太网首部用;另一处是在IP数据发送过程中,构建以太网首部用。
hard_header_cache,用于创建以太网首部的高速缓冲,每一个邻居节点都有一个struct hh_cache
*hh成员,用于缓冲该邻居节点的以太网首部,有了这个缓冲,以后再向这个邻居发数IP数据的时候,不必再调用hard_header构建以太网首部,而
是直接从hh中拷贝即可。
阅读(3261) | 评论(0) | 转发(0) |