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);

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM