深入理解Linux网络技术内幕学习笔记第十三章:协议处理函数

单独一个封包通常在链路层成为帧(frame),在网络层叫数据包(packet),传输层叫段(segment),应用层叫消息(message)。

Ethernet的链路层的选择:

LLC(逻辑链路控制):在LLC中,包头内含有一个字段,指出SSAP(Source Service Access Point,来源地服务访问点)的协议和DSAP(Destination Service Access Point,目的地服务访问点)的协议。每个字段只有8位,其中两位分别用于指示多播,局域网地址还是全球唯一地址。剩下6位指示协议,这样能指示的协议太少了,因此没有普及。

SNAP:在LLC上改进而来,在SSAP和DSAP中提供了一个特殊值,改值指示另外五个字节用来表示协议。

数据在网络协议栈上穿行时,在最低的软件层上L2上,所用的硬件决定所用的协议。如果帧是在Ethernet接口上被接收,接收者知道其包含了一个Ethernet报头,而Token Ring接口则知道其包含了一个Token Ring 报头。

设备驱动(L2上的操作)如何启用L3协议:

当设备驱动接收一帧时,会将其保存在一个sk_buff数据结构内,然后对protocol初始化.。netif_receive_skb函数根据protocol来确定相关的协议处理函数。特殊情况下,一个封包会传给多个处理函数,如当封包嗅探器运行时(如tcpdump)。

协议处理函数组织:

上图是内核内部使用的协议,由驱动直接赋值,不是从报头解析出来的。

用一个hash存储,16个列表组织成一个数组。每个数组元素都存放一个冲突链(那些协议类型相同,但处理函数不一样的)。

上图中的ptype_all所指的列表表示ETH_P_ALL协议,协议嗅探器都会在这个添加到这个列表。

协议函数注册:

对每个网络协议而言,无论其所在的分层,都有一个初始化函数,要么在引导期间执行(内建在内核时),要么在模块加载时执行(编译成模块时)。改初始化函数用于分配数据结构,通知其他子系统有关该协议的存在,在内核注册一个协议处理函数。

内核调用dev_add_pack,来注册一个协议处理函数。该函数会接收一个struct packet_type参数。

struct packet_type{

    unsigned short type;//协议类型

    struct net_device *dev;//设备的指针,该协议为此设备而开启,置为NULL时,指所用设备

    int (*func) (struct sk_buff *,struct net_device *,struct packet_type *);//当netif_receive_skb处理一个帧时,会调用此函数,其第三个参数由PF_PACKET套接字使用,以访问af_packet_priv字段。

    void *af_packet_priv;//由PF_PACKET套接字使用。该指针指向与packet_type结构的建立者相关联的sock数据结构(啥意思啊)。此字段允许dev_queue_xmit_nit(第十章的内容,但我好像没记录)不把缓冲区传递给传送者,而且通过PF_PACKET接收函数把输入数据传递给正确的套接字。(没看懂??)

    struct list_head *list;//

}

dev_add_pack首先检查新增的协议处理函数是否为协议嗅探器(ETH_P_ALL)。若是,则添加ptype_all所指的列表,并递增嗅探器数目netdev_nit++。否则,添加到ptype_base所指的16个列表之一。

dev_remove_pack用于删除一个协议处理函数。

Ethernet与IEEE802.3帧:

802.2和802.3不是纯粹的Ethernet,但通常都称为Ethernet标准。

Ethernet报头的定义:

struct ether{

    unsigned char h_dest[ETH_ALEN];//目的eth地址(即mac地址)

    unsigned char h_source[ETH_ALEN];//源eth地址

    unsigned short h_proto;//L3的协议类型或者封包长度

}_ _ATTRIBUTE_ _((packed));

eth_type_trans函数完成协议和长度的区分。第十章知道,netif_rx负责把帧拷贝到队列,然后设置NET_RX_SOFTIRQ标识,让内核知道新帧的到来。在调用netif_rx之前,会先调用eth_type_trans函数做一些初始化工作:设置封包类型及协议。

设置封包类型:

eth_type_trans把skb->pkt_type设置为如下值:

PACKET_HOST:帧的目的地址就是该接口。

PACKET_BROADCAST:链路层广播地址。

PACKET_MULTICAST:链路层多播地址。

PACKET_OTHERHOST:该帧并非传给该接口,但不会马上丢弃,可能会有一些嗅探器像想查看该帧。

Ethernet地址有六个字节,第一个字节的最高两个位有特殊意义。位0区分多播和单播地址。广播地址是多播的特殊情况。位1是区分局部地址和全球唯一地址。

逻辑控制链路LLC:

LLC可以为上层提供不同的服务类型:①无连接,②有连接,③无连接但有一些有连接的优点。如图13-8,LLC有三个新字段,SSAP,DSAP,CTL(控制)。

Linux的LLC实现有三个组件组成:

  •     两个状态机。用于记录局部SAP以及建立再其上的连接状态。

  •     LLC接收函数。

  •     AF_LLC套接字接口。可用于用户空间的LLC层的顶部建立协议或服务。

使用struct llc_sap定义局部SAP,其中一些字段如下:

  • struct llc_addr laddr ;//SAP标识符。

  • int (*rcv_func)(...);//函数处理。当一个SAP通过PF_LLC套接字开启时,此字段为NULL,SAP是内核开启时,此字段指向内核提供的函数。

局部SAP由llc_sap_open建立,并插入到llc_sap_list列表中。

每当输入帧被eth_type_trans识别为LLC报头时,会导致llc_rcv处理函数被选中,该处理函数会根据DSAP字段选出正确的协议处理程序:即为内核开启的SAP调用llc_sap_open注册的rcv_func函数,为PF_LLC套接字开启的SAP把数据输入到正确的状态机。如下图所示:

子网访问协议(SNAP):

使用struct datalink_proto来描述SNAP协议,其中一些字段如下:

unsigned short header_length ;//数据链路报头的长度

unsigned char type[8];//协议标识符。只使用了5个字节

void (*request)(...);//此函数指针会对报头初始化(只初始化协议ID),然后把帧传给802.2代码。

void (*rcvfunc)(...);//入口帧的处理函数。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值