linux网络协议初始化

简介:

  当linux系统启动后,会加载image文件到内存,然后解压、安装文件系统、内存管理以及其他关键系统。当完成初始任务后,开始执行init程序。

            网络的初始化是通过函数inet_init来实现的。

            先来看下内核网络代码实现层和理论模型的关系,

d0e1213629d7e84f8182d2bf56a621d2db778c53

1.1.1 套接字层

 

地址族其实就是套接字接口的种类, 每种套接字种类有自己的通信寻址方法。 Linux 将不同的地址族抽象统一为 BSD 套接字接口,应用程序关心的是 BSD 套接字接口。

  为 了 支 持 多 个 地 址 族 , 定 义 了 变 量 : static struct net_proto_family *net_families[NPROTO], NPROTO 根据版本不一样定义不一样。其中NPROTO可以是PF_UNIX( 1)、 PF_INET( 2)、 PF_NETLINK( 16)。

在初始化inet_init()中,会调用 (void)sock_register(&inet_family_ops); 其中inet_family_ops是struct net_proto_family的结构体对象如下,指定了PF_INET地址族的.create函数:

static const struct net_proto_family inet_family_ops = {

        .family = PF_INET,

        .create = inet_create,

        .owner  = THIS_MODULE,

};

那么调用socket()创建PF_INET地址族的套接字时,内核使用的创建函数就是inet_create。确定了socket的创建函数。

1.1.2     套接字与inet层

我们先来看下数组inetsw,该数组是在系统初始化时候初始化的,通过套接字类型(例如SOCK_STREAM等)索引。

inet_register_protosw()函数位于net/ipv4/af_inet.c文件中, 注册inet套接字的回调函数

inetsw_array是一个全局静态数组,对象是结构体inet_protosw,结构体static struct inet_protosw inetsw_array[]已经定义如下,系统在初始化的时候会读取inetsw_array来填写inetsw数组(在网络初始化inet_init函数中,调用inet_register_protosw函数来完成),因此系统中所有inet套接字都在inetsw数组中。

当socket调用的时候,将使用第二个参数type到这个数组中查找对应的inet_protosw对象。将套接字和相关操作进行了关联。

static struct inet_protosw inetsw_array[] =

{

        {               

                .type =       SOCK_STREAM,

                .protocol =   IPPROTO_TCP,

                .prot =       &tcp_prot, 

                .ops =        &inet_stream_ops,

                .flags =      INET_PROTOSW_PERMANENT | 

                              INET_PROTOSW_ICSK,       

        },              

 

        {               

                .type =       SOCK_DGRAM,

                .protocol =   IPPROTO_UDP,

                .prot =       &udp_prot, 

                .ops =        &inet_dgram_ops,

                .flags =      INET_PROTOSW_PERMANENT,  

       },               

其中.type是套接字类型,而.protocol是协议,分别对应socket函数的第二和第三个参数。ops是该类型套接字的操作函数。

proto结构体里面存放的是不同协议(IPPROTO_TCP, IPPROTO_UDP)的操作函数集,也就是具体协议的实现。而struct proto_ops是根据套接字的类型(SOCK_STREAM, SOCK_DGRAM...)不同而组成不同的套接字管理函数集,面向的套接字。

proto对象赋值给inet_protosw结构体中的prot指针,从而建立两者的联系

1.1.3 协议链表

此外,结构体proto对象维护了一个链表,串连在proto_list全局链表。

inet_init函数,定义在net/ipv4/af_inet.c文件中,

先调用proto_register函数,来注册内嵌协议。函数是在 net/core/sock.c 中定义的,注册struct proto对象,第一个参数传的是协议,第二个传的是内存分配方式,如果是1表示在高速缓存中分配空间,0则在内存中分配。因为tcp这些协议经常使用,所以分配在高速缓存里面。将tcp_proto对象添加到全局链表proto_list里面,其他的 udp_proto、raw_proto、ping_proto都会在初始化的时候添加到proto_list链表里面。

            使用频繁,将其放在slab中便于快速分配使用。

1.1.4     inet层与ip层

结构体net_protocolinet层和网络层(IP层)之间的连接,结构体定义在include/net/protocol.h文件中

struct net_protocol {   

        int                     (*early_demux)(struct sk_buff *skb);

        int                     (*early_demux_handler)(struct sk_buff *skb);

        int                     (*handler)(struct sk_buff *skb);

        void                    (*err_handler)(struct sk_buff *skb, u32 info);

        unsigned int            no_policy:1,

                                netns_ok:1,              

                                /* does the protocol do more stringent

                                 * icmp tag validation than simple

                                 * socket lookup?

                                 */

                                icmp_strict_tag_validation:1;

};

            相关协议(TCP)如下:

/* thinking of making this const? Don't.

 * early_demux can change based on sysctl.                                                                                               

 */

static struct net_protocol tcp_protocol = {

        .early_demux    =       tcp_v4_early_demux,

        .early_demux_handler =  tcp_v4_early_demux,      

        .handler        =       tcp_v4_rcv,

        .err_handler    =       tcp_v4_err,

        .no_policy      =       1,

        .netns_ok       =       1,

        .icmp_strict_tag_validation = 1,                                                                                                

};

     其中handler是回调函数。在inet_init函数中,通过函数inet_add_protocol(&tcp_protocol, IPPROTO_TCP),将协议接收函数添加到inet_protos[protocol] 全局链表中,从而完成IP层和传输层的链接。

1.1.5 ip层

 

报文从设备层送到上层之前,要区分是 IP 报文还是 ARP 报文。该过程由一个数据结构来抽象,叫 packet_type{},定义在linux/netdevice.h。

函数 dev_add_pack() 用于将指定的协议 (packet_type) 注册到系统中。

该函数定义在:net/core/dev.c文件中,将协议句柄增加到网络栈中。

Packet_type结构体定义在include/linux/netdevice.h文件中

struct packet_type {

        __be16                  type;   /* This is really htons(ether_type). */

        struct net_device       *dev;   /* NULL is wildcarded here           */

        int                     (*func) (struct sk_buff *,

                                         struct net_device *,

                                         struct packet_type *,

                                         struct net_device *);

        bool                    (*id_match)(struct packet_type *ptype,

                                            struct sock *sk);

        void                    *af_packet_priv;

        struct list_head        list;

};

其中type是协议类型,定义在include/uapi/linux/if_ether.h文件中如下,dev表示什么设备可以使用,func表示回调函数,list是链表头

#define ETH_P_LOOP      0x0060          /* Ethernet Loopback packet     */

#define ETH_P_PUP       0x0200          /* Xerox PUP packet             */

#define ETH_P_PUPAT     0x0201          /* Xerox PUP Addr Trans packet  */

#define ETH_P_TSN       0x22F0          /* TSN (IEEE 1722) packet       */

#define ETH_P_ERSPAN2   0x22EB          /* ERSPAN version 2 (type III)  */

#define ETH_P_IP        0x0800          /* Internet Protocol packet     */

#define ETH_P_X25       0x0805          /* CCITT X.25                   */

#define ETH_P_ARP       0x0806          /* Address Resolution packet    */

#define ETH_P_BPQ       0x08FF          /* G8BPQ AX.25 Ethernet Packet  [ NOT AN OFFICIALLY REGISTERED ID ] */                                  

#define ETH_P_IEEEPUP   0x0a00          /* Xerox IEEE802.3 PUP packet */

#define ETH_P_IEEEPUPAT 0x0a01          /* Xerox IEEE802.3 PUP Addr Trans packet */

#define ETH_P_BATMAN    0x4305          /* B.A.T.M.A.N.-Advanced packet [ NOT AN OFFICIALLY REGISTERED ID ] */                                  

#define ETH_P_DEC       0x6000          /* DEC Assigned proto           */

#define ETH_P_DNA_DL    0x6001          /* DEC DNA Dump/Load            */

#define ETH_P_DNA_RC    0x6002          /* DEC DNA Remote Console       */

#define ETH_P_DNA_RT    0x6003          /* DEC DNA Routing              */

#define ETH_P_LAT       0x6004          /* DEC LAT                      */

#define ETH_P_DIAG      0x6005          /* DEC Diagnostics              */

#define ETH_P_CUST      0x6006          /* DEC Customer use             */

#define ETH_P_SCA       0x6007          /* DEC Systems Comms Arch       */

#define ETH_P_TEB       0x6558          /* Trans Ether Bridging         */

#define ETH_P_RARP      0x8035          /* Reverse Addr Res packet      */

#define ETH_P_ATALK     0x809B          /* Appletalk DDP                */

#define ETH_P_AARP      0x80F3          /* Appletalk AARP               */

#define ETH_P_8021Q     0x8100          /* 802.1Q VLAN Extended Header  */

#define ETH_P_ERSPAN    0x88BE          /* ERSPAN type II               */

#define ETH_P_IPX       0x8137          /* IPX over DIX                 */

#define ETH_P_IPV6      0x86DD          /* IPv6 over bluebook           */

#define ETH_P_PAUSE     0x8808          /* IEEE Pause frames. See 802.3 31B */

#define ETH_P_SLOW      0x8809          /* Slow Protocol. See 802.3ad 43B */

#define ETH_P_WCCP      0x883E          /* Web-cache coordination protocol

                                         * defined in draft-wilson-wrec-wccp-v2-00.txt */

每种协议通过向ptype_base[]数组张注册进行登记,维护了一张全局的ptype_base[]数组,每一个包网络层使用的是哪种协议都可以来这里来查,查到匹配的协议以后,就调用对应的处理函数。inet_init函数通过调用dev_add_pack(&ip_packet_type)将IP协议添加到ptype_base[]数组中而成为一员,而且对应的处理函数是ip_rcv.

static struct packet_type ip_packet_type __read_mostly = {

        .type = cpu_to_be16(ETH_P_IP),

        .func = ip_rcv, 

};

            最后以网上一张很不错的图来结尾:

a4f95608abaae16c8de842e4d546f74189df7a0d

 

 

 

目录
相关文章
|
8天前
|
iOS开发 MacOS Windows
|
15天前
|
监控 安全 Linux
【专栏】Linux中六个常用的网络命令:ping、traceroute、netstat、nmap、ifconfig和ip
【4月更文挑战第28天】本文介绍了Linux中六个常用的网络命令:ping、traceroute、netstat、nmap、ifconfig和ip,以及它们在测试网络连通性、追踪路由、查看网络状态、安全扫描和接口配置等场景的应用。通过学习和运用这些命令,系统管理员和网络爱好者能更有效地诊断和管理网络问题,确保网络稳定运行。
|
16天前
|
网络协议 算法 Linux
【Linux】深入探索:Linux网络调试、追踪与优化
【Linux】深入探索:Linux网络调试、追踪与优化
|
2天前
|
网络协议 Linux 网络架构
|
5天前
|
域名解析 网络协议 Linux
linux网络配置详解
linux网络配置详解
14 0
|
6天前
|
网络协议 Java Linux
【探索Linux】P.29(网络编程套接字 —— 简单的TCP网络程序模拟实现)
【探索Linux】P.29(网络编程套接字 —— 简单的TCP网络程序模拟实现)
12 0
|
6天前
|
存储 网络协议 算法
【探索Linux】P.28(网络编程套接字 —— 简单的UDP网络程序模拟实现)
【探索Linux】P.28(网络编程套接字 —— 简单的UDP网络程序模拟实现)
12 0
|
6天前
|
网络协议 算法 Linux
【探索Linux】P.27(网络编程套接字 —— UDP协议介绍 | TCP协议介绍 | UDP 和 TCP 的异同)
【探索Linux】P.27(网络编程套接字 —— UDP协议介绍 | TCP协议介绍 | UDP 和 TCP 的异同)
14 0
|
6天前
|
存储 算法 网络协议
【探索Linux】P.26(网络编程套接字基本概念—— socket编程接口 | socket编程接口相关函数详细介绍 )
【探索Linux】P.26(网络编程套接字基本概念—— socket编程接口 | socket编程接口相关函数详细介绍 )
12 0
|
6天前
|
存储 网络协议 Unix
【探索Linux】P.25(网络编程套接字基本概念 —— 预备知识)
【探索Linux】P.25(网络编程套接字基本概念 —— 预备知识)
6 0