zoukankan      html  css  js  c++  java
  • Internet 网络协议族

    1、linux目前支持多种协议族,每个协议族用一个net_porto_family结构实例来表示,在初始化时,会调用sock_register()函数初始化注册到net_families[NPROTO]中去;

    同时出现了一个地址族的概念,目前协议族和地址族是一 一 对应关系。历史上曾经有一个协议族支持多个地址族,实际上从未实现过。在socket.h文件中PF_XX和AF_XX 值一样

    2、由于不同协议族的结构差别很大,为了封装统一,以便在初始化时,可以统一接口,于是就有了net_proto_family。其用sock_register统一注册,初始化钩子,具体初始化,其实现见钩子实现,类似于VFS 的实现方式。一种很好的设计思想。

    /*ops->create在应用程序创建套接字的时候,引起系统调用,从而在函数__sock_create中执行ops->create  netlink为netlink_family_ops
    应用层创建套接字的时候,内核系统调用sock_create,然后执行该函数
    pf_inet的net_families[]为inet_family_ops,对应的套接口层ops参考inetsw_array中的inet_stream_ops inet_dgram_ops inet_sockraw_ops,
    传输层操作集分别为tcp_prot udp_prot raw_prot
    netlink的net_families[]netlink_family_ops,对应的套接口层ops为netlink_ops
    family协议族通过sock_register注册  传输层接口tcp_prot udp_prot netlink_prot等通过proto_register注册   
    IP层接口通过inet_add_protocol(&icmp_protocol等注册 ,这些组成过程参考inet_init函数*/
    
    struct net_proto_family {//操作集参考inetsw_array
        int        family;
        int        (*create)(struct net *net, struct socket *sock,
                      int protocol, int kern);协议族的套接字创建函数指针,每个协议族实现都不同
        struct module    *owner;
    };
    

     

    Internet 协议族的net_proto_family结构实例为inet_family_ops,创建套接字socket时,其调用接口为inet_create().

    2、inet_protosw 结构

    /* This is used to register socket interfaces for IP protocols.  */
    struct inet_protosw {
    	struct list_head list;/* 初始化时将相同的type的inet_protosw散列在同一个链表*/
    
            /* These two fields form the lookup key.  */
    	unsigned short	 type;	   /* This is the 2nd argument to socket(2). 表示套接口字的类型,对于Internet 协议族有三种类型 SOCK_STREAM SOCK_DGRAM SOCK_RAW 对于与应用层socket函数的第二个参数type*/
    	unsigned short	 protocol; /* This is the L4 protocol number.  */
    
    	struct proto	 *prot; /*套接口网络层口,tcp为tcp_port udp为udp_port 原始套接字为raw_port*/
    	const struct proto_ops *ops;/* 套接口传输层接口,tcp为inet_stream_ops,udp 为inet_dgram_ops,原始套接字为inet_sockraw_ops*/
      
    	unsigned char	 flags;      /* See INET_PROTOSW_* below.  */
    };
    #define INET_PROTOSW_REUSE 0x01         /* Are ports automatically reusable? 端口重用*/
    #define INET_PROTOSW_PERMANENT 0x02  /* Permanent protocols are unremovable. 协议不能被替换卸载*/
    #define INET_PROTOSW_ICSK      0x04  /* Is this an inet_connection_sock? 是不是为连接类型的接口*/
    View Code

     tcp 不能被替换卸载切为连接型套接字,udp 不能被替换和卸载,rawsocket端口可以重用。

    /* Upon startup we insert all the elements in inetsw_array[] into
     * the linked list inetsw.
     在初始化的时候我们会将上面数组中的的元素按套接字类型插入static struct list_head inetsw[SOCK_MAX];链表数组中
     */  
     /* 
      * inetsw_array数组包含三个inet_protosw结构的实例,分别对应
      * TCP、UDP和原始套接字。在Internet协议族初始化函数inet_init()中
      * 调用inet_register_protosw()将inetsw_array数组中
      * 的inet_protosw结构实例,以其type值为key组织到散列表inetsw中,
      * 也就是说各协议族中type值相同而protocol值不同的inet_protosw结构
      * 实例,在inetsw散列表中以type为关键字连接成链表,通过inetsw
      * 散列表可以找到所有协议族的inet_protosw结构实例。
      */ //ipv4_specific是TCP传输层到网络层数据发送以及TCP建立过程的真正OPS,在tcp_prot->init中被赋值给inet_connection_sock->icsk_af_ops
    static struct inet_protosw inetsw_array[] =   //这个和应用层创建套接字相关,个人我理解是属于套接口层,为了把套接口层和传输层衔接起来(tcp_protocol udp_protol icmp_protocol)
    {
    	{  
    		.type =       SOCK_STREAM, //在inet_create的时候,用它做为关键字,把下面这几个成员联系在一起
    		.protocol =   IPPROTO_TCP,
    
        //tcp_prot udp proto raw_proto头添加到的proto_list中,通过遍历该链表就可以知道有哪些传输层协议添加到该链表中
    //协议最终都是通过inet_init中的proto_register添加到proto_list链表中的。family协议族通过sock_register注册  
    //传输层接口tcp_prot udp_prot netlink_prot等通过proto_register注册   
    //IP层接口通过inet_add_protocol(&icmp_protocol等注册 ,这些组成过程参考inet_init函数
    		.prot =       &tcp_prot,//传输层操作集  在inet_create中的sk_alloc中赋值 
    // 先执行ops中的函数,然后执行prot中对应的函数 proto结构为网络接口层,
    //结构中的操作实现传输层的操作和从传输层到网络层调用的跳转,
    //在proto结构中的某些成员跟proto_ops结构中的成员对应,比如connect()等
    		.ops =        &inet_stream_ops,//套接口层操作集,也就是协议族操作集
    // 用来区分协议族(netlink family(ops为netlink_ops)或者 inet family) 
    // ops在创建套接字的时候被赋值,例如netlink赋值的地方在__netlink_create  pf_net赋值的地方在inet_create中
    		.no_check =   0, //为0表示始终进行校验和操作
    		.flags =      INET_PROTOSW_PERMANENT |
    			      INET_PROTOSW_ICSK,
    	},
    
    	{
    		.type =       SOCK_DGRAM,
    		.protocol =   IPPROTO_UDP,
    		.prot =       &udp_prot,//传输层操作集  在inet_create中的sk_alloc中赋值  先执行ops中的函数,然后执行prot中对应的函数
    		.ops =        &inet_dgram_ops,//套接口层操作集 用来区分协议族(netlink family(ops为netlink_ops)或者 inet family) 
    // ops在创建套接字的时候被赋值,例如netlink赋值的地方在__netlink_create  pf_net赋值的地方在inet_create中
    		.no_check =   UDP_CSUM_DEFAULT,
    		.flags =      INET_PROTOSW_PERMANENT,
           },
    
    
           {
    	       .type =       SOCK_RAW,  //原始套接口
    	       .protocol =   IPPROTO_IP,	/* wild card */
    	       .prot =       &raw_prot,//传输层操作集  在inet_create中的sk_alloc中赋值  先执行ops中的函数,然后执行prot中对应的函数
    	       .ops =        &inet_sockraw_ops,//套接口层操作集  
    //用来区分协议族(netlink family(ops为netlink_ops)或者 inet family)  ops在创建套接字的时候被赋值,
    //例如netlink赋值的地方在__netlink_create  pf_net赋值的地方在inet_create中
    	       .no_check =   UDP_CSUM_DEFAULT,
    	       .flags =      INET_PROTOSW_REUSE,
           }
    };
    

     

    3、net_protocol 结构

    net_protocol 结构定义了传输层协议(包含icmp igmp协议)以及传输层的报文接收例程,此结构是网络层和传输层之间的桥梁。

    /* 
     * inet_add_protocol函数用于将上述结构的实例(指针)
     * 存储到inet_protos 数组中
     * update:
     *  net_protocol是一个非常重要的结构,定义了协议族中支持的传输层协议以及传输层的报文接收实例。此结构是网络层和 传输层之间的桥梁,当网络数据包从网络层流向传输层时,
     * 会调用此结构中的传输层协议数据时,会调用此结构中的传输层协议数据报接收处理函数。
     *
     * 内核中为Internet协议族定义了4个net_protocol结构实例---
     * icmp_protocol、udp_protocol、tcp_protocol和igmp_protocol
     * ,分别与ICMP、UDP、TCP和IGMP协议一一对应。在Internet协议族
     * 初始化时,调用inet_add_protocol()将它们注册到net_protocol
     * 结构指针数组inet_protos[MAX_INET_PROTOS]中。在系统运行
     * 过程中,随时可以用内核模块加载/卸载方式,调用函数inet_add_protocol()
     * /inet_del_protocol()将net_protocol结构实例注册到inet_protos[]数组中,
     * 或从中删除。
     *///ops = rcu_dereference(inet_protos[proto]);通过该函数获取对应的协议ops
    /* This is used to register protocols. */
    struct net_protocol {
    	void			(*early_demux)(struct sk_buff *skb);
    	  /* 分组将传递到该函数进行进一步处理*/
        /*
         * 传输层协议数据包接收处理函数指针,当网络层接收IP数据包
         * 之后,根据IP数据包所指示传输层协议,调用对应传输层
         * net_protocol结构的该例程接收报文。
         * TCP协议的接收函数为tcp_v4_rcv(),UDP协议的接收函数为
         * udp_rcv(),IGMP协议为igmp_rcv(),ICMP协议为icmp_rcv()。
         */
    	int			(*handler)(struct sk_buff *skb);
    	   /* 
            * 在接收到ICMP错误信息并需要传递到更高层时,
            * 调用该函数
            */
        /*
         * 在ICMP模块中接收到差错报文后,会解析差错报文,并根据
         * 差错报文中原始的IP首部,调用对应传输层的异常处理
         * 函数err_handler。TCP协议为tcp_v4_err(),UDP为
         * udp_err(),IGMP则无。
         */
    	void			(*err_handler)(struct sk_buff *skb, u32 info);
    	   /*
         * no_policy标识在路由时是否进行策略路由。TCP和UDP默认不进行
         * 策略路由。
         */
    	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;
    };
    

     初始化后的inet_protos 如下:

    4、Internet协议族的初始化

    Internet协议初始化函数为inet_init ,通过fs_initcall调用,加载到内核中;

    /设备物理层的初始化net_dev_init
     TCP/IP协议栈初始化inet_init  传输层的协议初始化也在这里面
     传输层初始化proto_init  只是为了显示各种协议用的
     套接口层初始化sock_init  netfilter_init在套接口层初始化的时候也初始化了

    static int __init inet_init(void)
    {
        struct inet_protosw *q;
        struct list_head *r;
        int rc = -EINVAL;
    
        sock_skb_cb_check_size(sizeof(struct inet_skb_parm));
    
        rc = proto_register(&tcp_prot, 1);
        if (rc)
            goto out;
    
        rc = proto_register(&udp_prot, 1);
        if (rc)
            goto out_unregister_tcp_proto;
    
        rc = proto_register(&raw_prot, 1);
        if (rc)
            goto out_unregister_udp_proto;
    
        rc = proto_register(&ping_prot, 1);
        if (rc)
            goto out_unregister_raw_proto;
    
        /*
         *    Tell SOCKET that we are alive...
         */
    
        (void)sock_register(&inet_family_ops);
    
    #ifdef CONFIG_SYSCTL
        ip_static_sysctl_init();
    #endif
    
        /*
         *    Add all the base protocols.
         */
    //这里面有每种协议传输层的接收函数,
        if (inet_add_protocol(&icmp_protocol, IPPROTO_ICMP) < 0)
            pr_crit("%s: Cannot add ICMP protocol\n", __func__);
        if (inet_add_protocol(&udp_protocol, IPPROTO_UDP) < 0)
            pr_crit("%s: Cannot add UDP protocol\n", __func__);
        if (inet_add_protocol(&tcp_protocol, IPPROTO_TCP) < 0)
            pr_crit("%s: Cannot add TCP protocol\n", __func__);
    #ifdef CONFIG_IP_MULTICAST
        if (inet_add_protocol(&igmp_protocol, IPPROTO_IGMP) < 0)
            pr_crit("%s: Cannot add IGMP protocol\n", __func__);
    #endif
    
        /* Register the socket-side information for inet_create. */
        for (r = &inetsw[0]; r < &inetsw[SOCK_MAX]; ++r)
            INIT_LIST_HEAD(r);
    
        for (q = inetsw_array; q < &inetsw_array[INETSW_ARRAY_LEN]; ++q)
            inet_register_protosw(q);//把inetsw_array结构中的节点添加到inetsw表中,以type为索引 为套接字层所用
    
        /*
         *    Set the ARP module up
         */
    
        arp_init();
    
        /*
         *    Set the IP module up
         */
    
        ip_init();
    
        tcp_v4_init()//创建一个tcp套接字用来发送rst ack 字段
    
        /* Setup TCP slab cache for open requests. */
        tcp_init();
    
        /* Setup UDP memory threshold */
        udp_init();
    
        /* Add UDP-Lite (RFC 3828) */
        udplite4_register();
    
        ping_init();
    
        /*
         *    Set the ICMP layer up
         */
    /*由于协议栈本身有发送ICMP数据报的需求,所以,需要在协议栈中创建内核态的原始套接字,用于发送ICMP数据报,这个事情在协议栈初始化时,
            由 icmp_init函数完成。它为每个CPU都创建一个icmp_socket,创建工作由sock_create_kern函数完成,创建流程跟应用层 创建socket完全一致。*/ if (icmp_init() < 0) panic("Failed to create the ICMP control socket.\n"); /* * Initialise the multicast router */ #if defined(CONFIG_IP_MROUTE) if (ip_mr_init()) pr_crit("%s: Cannot init ipv4 mroute\n", __func__); #endif if (init_inet_pernet_ops()) pr_crit("%s: Cannot init ipv4 inet pernet ops\n", __func__); /* * Initialise per-cpu ipv4 mibs */ if (init_ipv4_mibs()) pr_crit("%s: Cannot init ipv4 mibs\n", __func__); ipv4_proc_init(); ipfrag_init(); dev_add_pack(&ip_packet_type); ip_tunnel_core_init(); rc = 0; out: return rc; out_unregister_raw_proto: proto_unregister(&raw_prot); out_unregister_udp_proto: proto_unregister(&udp_prot); out_unregister_tcp_proto: proto_unregister(&tcp_prot); goto out; } fs_initcall(inet_init);
    http代理服务器(3-4-7层代理)-网络事件库公共组件、内核kernel驱动 摄像头驱动 tcpip网络协议栈、netfilter、bridge 好像看过!!!! 但行好事 莫问前程 --身高体重180的胖子
  • 相关阅读:
    Eclipse在线安装spring-tool-suit插件
    使用Eclipse构建Maven项目
    uwsgi+flask环境中安装matplotlib
    开启flask调试
    linux进入软连接所指向的原目录
    eclipse打不开,报错 "java was started with exit code=13"
    gnuplot 的安装
    使用tcp_probe时最初没有输出,先卸载后加载模块之后就有了。
    一个简单的socket程序运行与抓包查看
    如何查看文件是dos格式还是unix格式的?
  • 原文地址:https://www.cnblogs.com/codestack/p/9193533.html
Copyright © 2011-2022 走看看