2023年7月31日发(作者:)
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_protnetlink的net_families[]netlink_family_ops,对应的套接⼝层ops为netlink_opsfamily协议族通过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_opsstatic 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 */ (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 protocoln", __func__); if (inet_add_protocol(&udp_protocol, IPPROTO_UDP) < 0) pr_crit("%s: Cannot add UDP protocoln", __func__); if (inet_add_protocol(&tcp_protocol, IPPROTO_TCP) < 0) pr_crit("%s: Cannot add TCP protocoln", __func__);#ifdef CONFIG_IP_MULTICAST if (inet_add_protocol(&igmp_protocol, IPPROTO_IGMP) < 0) pr_crit("%s: Cannot add IGMP protocoln", __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 mrouten", __func__);#endif if (init_inet_pernet_ops()) pr_crit("%s: Cannot init ipv4 inet pernet opsn", __func__); /* * Initialise per-cpu ipv4 mibs */ if (init_ipv4_mibs()) pr_crit("%s: Cannot init ipv4 mibsn", __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);
发布者:admin,转转请注明出处:http://www.yc00.com/xiaochengxu/1690782962a425054.html
评论列表(0条)