Linux网络协议栈基础

时间:2021-01-24 10:30:06
一 套接字
    1、BSD套接字
套接口是连接应用层与传输层的桥梁,也因此可以将套接口细分为两层:应用交互层,传输交互层。
    struct socket结构是协议无关接口,主要与应用层进行交互,它代表一条通信链路的一端。
 Linux网络协议栈基础
                            图1 struct socket
    file指针:是socket与进程之间的连接,进程通过socket描述符(就是文件描述符)来定位特定的socket。下图是涉及file和socket的主要结构体之间的关系图:
 Linux网络协议栈基础
图2 file socket 关系图
    其中sock对应于传输控制块,在socket的实例中会根据协议族和传输层协议的特点,分层次地定义多个结构,以TCP为例,tcp_sock结构是在inet_sock结构基础上构成的,而inet_sock结构又是在sock结构基础上构成的。
    需要注意的是,file结构体是在socket_open的时候创建的,同时会将file中的private_data赋值为socket结构体。private_data是一个void*指针,在设备驱动中被广发应用,能灵活的指向自定义的结构体。详见sock_attack_fd()函数。
套接口类型:SOCK_STREAM, SOCK_DGRAM, SOCK_RAW, SOCK_RDM, SOCK_SEQPACKET, SOCK_DCCP, SOCK_PACKET;注意,这些套接口类型并未与任何的传输层协议相关联,包括proto_ops的三种结构实例:inet_stream_ops, inet_dgram_ops, inet_sockraw_ops也都是协议无关的。socket通过成员结构sock来实现与传输层的连接。
而对于传输层协议,需要一个inet_protosw结构实现proto_ops实例与proto结构实例的关联。inet_protosw结构实例定义在静态数组inetsw_array[]中,在系统初始化的网络子系统初始化时,注册到数组inetsw[]中。
创建套接口时,根据套接口的类型在inetsw中寻找匹配的inet_protosw结构,初始化socket中的proto_ops成员。需要注意的是,这是对于Internet协议族而说,因为inet_protosw是PF_INET协议族对应传输层协议的结构。
2、套接字缓存
网络子系统中用来存储数据的缓冲区叫做套接字缓存,简称SKB。该缓存:(1)能够处理可变长数据,(2)能够很容易地在数据区头尾部添加和移除数据,(3)能尽量避免数据的复制。
套接字缓存与传输控制块struct sock实例直接关联,sock结构中包含:sk_receive_queue和sk_write_queue;它们分别是接收缓存队列和发送缓存队列的链表头。
二 Internet协议族
    Linux协议族的命名方式为:PF_XXX,比如PF_INET代表了Internet协议族。内核中,每个协议族用一个net_proto_family结构实例来表示,在系统初始化时,调用sock_register将结构注册到全局数组net_familly[NPROTO]中。数组长度为41(最新版本),Linux内核集成了40种标准协议,空余的1项(28)可用于自定义协议族。
1、net_proto_family
    不同的协议族,传输层的结构和实现有着很大的不同,各自的套接口创建函数也不同,create()方法正是net_proto_family结构中最关键的成员函数;
网络系统的初始化阶段,可以统一调用sock_register()将特定的net_proto_family 注册到net_familes数组中。在net_proto_family结构中提供了协议族套接口创建的接口。

 Linux网络协议栈基础

图3

    也就是说,如果一个通信协议需要特殊的套接口结构的话,那么就应该归为特殊的协议族。PF_RING在内核中创建了新的套接口结构,也同时需要创建新的协议族——PF_RING。从命名上来看,PF_RING就是协议族RING.
2、inet_protosw
    结构主要成员有:type,struct proto* prot,struct proto_ops *ops;
    其中type标识了套接口的类型,对于Internet协议族共有三种类型SOCK_STREAM,SOCK_DGRAM,SOCK_RAW。对应于socket系统调用中的type参数。
    prot是套接口网络层接口,会赋值给传输层模块struct sock实例的结构成员;
    ops是套接口传输层接口,会被赋值给struct socket的成员ops。
    inetsw_array是存储inet_protosw实例的数组,而inetsw是存储指向实例指针的散列表,通过调用inet_register_protosw()将inet_protosw实例注册到inetsw散列表中。
3、net_protocol
    这是一个与协议相关的结构,定义了协议族中支持的传输层协议以及传输层的报文接收历程。对应于socket系统调用中的protocol参数,是网络层与传输层之间的桥梁。
    内核中为Internet协议族定义了4个net_protocol结构实例:icmp_protocol, udp_protocol, tcp_protocol和igmp_protocol。在Internet协议族初始化时,调用inet_add_protocol()将它们注册到net_protocol结构指针数组inet_protos[MAX_INET_PROTOS]中。
    系统运行时,当有数据报文从网络层流向上层时,就可以通过其上层协议号,在inet_protos[]数组中找到对应的net_protocol结构实例,然后调用该结构中的传输层协议数据报接收处理函数。
4、协议初始化
1)    proto_register()
2)    sock_register()
3)    inet_add_protocol()
4)    inet_register_protosw()
总结:
    1、在进程中,通过socket系统调用建立所需的通信链路时,需要留意socket()的三个主要参数:
1)    参数family标识了通信链路所属的协议族,比如AF_INET指明是internet链路;
2)    参数type标识了通信链路所需的套接口类型,套接口是协议无关的,不同的协议族可以使用相同类型的套接口;Linux BSD主要支持一下几种套接字类型:SOCK_STREAM,SOCK_DGRAM,SOCK_RAW;SOCK_RAW是为底层协议或物理网络提供的套接字类型,可以看到原始IP数据流,PF_RING协议族只能使用该类型的套接字。
3)    参数protocol标识所使用的传输层协议,大多数情况并不需要特别说明,内核可以通过前两个参数得到匹配的protocol值,比如Internet协议族面向连接的传输层协议为TCP,继而创建传输层控制块(也就是特定协议套接字)struct sock实例,比如tcp_sock。
2、各数据结构间的接口

三 PF_RING协议族
    相关数据结构:
    1、ring_family_ops
    等同于Internet协议族中的inet_family_ops,内有成员函数指针ring_create,在创建PF_RING套接口时调用。
    使用Linux提供的sock_register()函数将PF_RING套接字协议族注册到系统的全局数组net_families[]中,在应用层调用socket系统调用时,能根据family,type参数在net_families[]找到ring_create()函数来创建PF_RING套接口。
    2、struct proto ring_proto
    在PF_RING协议中与网络层交互的接口,是struct ring_sock(也就是传输控制块)的结构成员,类似于tcp_sock中的tcp¬_prot。
    3、struct proto_ops ring_ops
    该结构为PF_RING协议族套接字操作集,是PF_RING套接字(BSD套接字也就是协议无关套接字)与传输控制块(即特定协议的套接字)ring_sock之间的接口,该操作集为BSD套接口提供服务,作为一个函数跳转表,最终会调用ring_sock中的ring_proto函数集中的相应函数。