目前OS已经知道inet协议族的存在了,但inet自己知道IP层还没有准备好,它还不能为OS服务。
继续看inet_init函数。下面的代码就有趣了。
1386 if (inet_add_protocol(&icmp_protocol, IPPROTO_ICMP) < 0)
1387 printk(KERN_CRIT "inet_init Cannot add ICMP protocol\n");
1388 if (inet_add_protocol(&udp_protocol, IPPROTO_UDP) < 0)
1389 printk(KERN_CRIT "inet_init Cannot add UDP protocol\n");
1390 if (inet_add_protocol(&tcp_protocol, IPPROTO_TCP) < 0)
1391 printk(KERN_CRIT "inet_init Cannot add TCP protocol\n");
1386-1391 三个同样的函数,增加三个protocol。从函数的名称来看,是向inet增加的。从之前的分析来看,我们的inet已经向OS报到了。光是OS知道inet还是不够的,inet下面有一帮小弟,这些小弟现在还没有挂靠到inet,inet还是无法工作的。所以就有了这个函数的三次调用,添加三个小弟.UDP,TCP,ICMP。那么问题又来了,为什么ICMP和TCP、UDP并列了呢?熟悉协议栈的同学一定知道,ICMP和IP协议是一对好基友。IP传输过程中出现了什么状况,都是靠ICMP打小报告,通过上层大哥(TCP UDP)的。把它和TCP UPD并列显然是没有道理的。不过仔细一想,也有道理。ICMP报文是通过什么协议传输的?当然是IP协议了,也就是说ICMP和UDP TCP一样,也是要使用IP协议的,这样一想ICMP虽然为IP协议服务,却也享受了IP协议的服务,果然一对好基友不是!这样说的话,在inet中,ICMP和UDP TCP一样位于IP协议层之上,所以先要准备好ICMP UDP TCP。步子一次一步,别迈太大。
我好奇的是如果把三个协议和inet关联起来的。走去看下tcp_protocol(来自于同一个文件net/ipv4/af_inet.c)的内容,然后看下inet_add_protocol干了什么事情。
tcp_protocol比其他两个协议内容多一些,如下。如果说,现在在一步步向下打通整个协议栈,你可能不信。不过事实就是如此,linux的世界里秩序是非常重要的,一切复杂的东西,都是以简单的道理开始。目前来看,tcp_protocol就是连接inet和IP层的接口。tcp_protocol中的handler被赋值为tcp_v4_rcv,而tcp_v4_rcv函数的原型为 int tcp_v4_rcv(struct sk_buff *skb)。看到了熟悉的sk_buff,也就明白了来龙去脉了,这里真的是IP层向上走的入口了。其他成员暂不提。再去看看inet_add_protocol函数。
1273 static struct net_protocol tcp_protocol = {
1274 .handler = tcp_v4_rcv,
1275 .err_handler = tcp_v4_err,
1276 .gso_send_check = tcp_v4_gso_send_check,
1277 .gso_segment = tcp_tso_segment,
1278 .no_policy = 1,
1279 };
inet_add_protocol函数来自于net/ipv4/protocol.c文件中。这个函数很简单,就喜欢这种简单的函数。我们关键点在65行。把传入的prot赋值给inet_protos对应的位置。inet_protos定义的一个数组,用来记录注册好的协议地址。显然这个数组是经常访问的,要求编译器缓存对齐。通过这个数组,加上我们的协议号,inet就能找到对应的协议数据结构,如上所述的tcp_protocol。
48 struct net_protocol *inet_protos[MAX_INET_PROTOS] ____cacheline_aligned_in_smp;
55 int inet_add_protocol(struct net_protocol *prot, unsigned char protocol)
56 {
57 int hash, ret;
59 hash = protocol & (MAX_INET_PROTOS - 1);
61 spin_lock_bh(&inet_proto_lock);
62 if (inet_protos[hash]) {
63 ret = -1;
64 } else {
65 inet_protos[hash] = prot;
66 ret = 0;
67 }
68 spin_unlock_bh(&inet_proto_lock);
70 return ret;
71 }
面向IP层的接口已经注册好了。下面回来inet_init函数中。
1398 for (r = &inetsw[0]; r < &inetsw[SOCK_MAX]; ++r)
1399 INIT_LIST_HEAD(r);
1401 for (q = inetsw_array; q < &inetsw_array[INETSW_ARRAY_LEN]; ++q)
1402 inet_register_protosw(q);
1398-1399 初始化了inetsw数组中的各个元素。
1401-1402 代码中的注释说,是为inet_create提供socket侧的信息。我们先去看下inetsw_array是什么。定义在同一个文件af_inet.c中。这个数组有三个成员。不难看出,这就是这面提到过的3个控制层的协议。看到前面说过的熟悉的tcp_prot就明白了,tcp_prot果然是给socket用的。其他个成员就不说了。
去看下inet_register_protosw这个函数。
910 static struct inet_protosw inetsw_array[] =
911 {
912 {
913 .type = SOCK_STREAM,
914 .protocol = IPPROTO_TCP,
915 .prot = &tcp_prot,
916 .ops = &inet_stream_ops,
917 .capability = -1,
918 .no_check = 0,
919 .flags = INET_PROTOSW_PERMANENT |
920 INET_PROTOSW_ICSK,
921 },
923 {
924 .type = SOCK_DGRAM,
925 .protocol = IPPROTO_UDP,
926 .prot = &udp_prot,
927 .ops = &inet_dgram_ops,
928 .capability = -1,
929 .no_check = UDP_CSUM_DEFAULT,
930 .flags = INET_PROTOSW_PERMANENT,
931 },
934 {
935 .type = SOCK_RAW,
937 .prot = &raw_prot,
938 .ops = &inet_sockraw_ops,
939 .capability = CAP_NET_RAW,
940 .no_check = UDP_CSUM_DEFAULT,
941 .flags = INET_PROTOSW_REUSE,
942 }
943 };
函数的定义还是在这个文件里。这个函数很简单,因为我们第一次执行时,inetsw数组里的成员都是空的。直接跳到983行执行。把对应的inet_protosw的list成员加入到对应的inetsw的成员列表头中。至此inetsw_array中的成员全部挂靠在inetsw中了。
void inet_register_protosw(struct inet_protosw *p)
948 {
949 struct list_head *lh;
950 struct inet_protosw *answer;
951 int protocol = p->protocol;
952 struct list_head *last_perm;
954 spin_lock_bh(&inetsw_lock);
956 if (p->type >= SOCK_MAX)
957 goto out_illegal;
960 answer = NULL;
961 last_perm = &inetsw[p->type];
962 list_for_each(lh, &inetsw[p->type]) {
963 answer = list_entry(lh, struct inet_protosw, list);
966 if (INET_PROTOSW_PERMANENT & answer->flags) {
967 if (protocol == answer->protocol)
968 break;
969 last_perm = lh;
970 }
972 answer = NULL;
973 }
974 if (answer)
975 goto out_permanent;
983 list_add_rcu(&p->list, last_perm);
1001 }
至此可以有一个大概的轮廓了。
tcp_prot负责tcp_sock数据结构的维护,接收socket层的指令。tcp_prot是放在inetsw数组中。tcp_protocol负责与IP层接洽,tcp_protocol放在inet_protos中。而inetsw,inet_protos都是inet子系统中可见的。
这当中还是有很多疑惑。比如inet_family_ops的成员函数inet_create是如何调用tcp_prot的成员函数的?socket创建完成后,是不是直接由socket调用tcp_prot的成员函数?tcp层是如何把数据传递给IP层的?虽然这些疑惑暂时未解开,那张图片可以补充上一点内容了。
继续看inet_init函数。下面的代码就有趣了。
1386 if (inet_add_protocol(&icmp_protocol, IPPROTO_ICMP) < 0)
1387 printk(KERN_CRIT "inet_init Cannot add ICMP protocol\n");
1388 if (inet_add_protocol(&udp_protocol, IPPROTO_UDP) < 0)
1389 printk(KERN_CRIT "inet_init Cannot add UDP protocol\n");
1390 if (inet_add_protocol(&tcp_protocol, IPPROTO_TCP) < 0)
1391 printk(KERN_CRIT "inet_init Cannot add TCP protocol\n");
1386-1391 三个同样的函数,增加三个protocol。从函数的名称来看,是向inet增加的。从之前的分析来看,我们的inet已经向OS报到了。光是OS知道inet还是不够的,inet下面有一帮小弟,这些小弟现在还没有挂靠到inet,inet还是无法工作的。所以就有了这个函数的三次调用,添加三个小弟.UDP,TCP,ICMP。那么问题又来了,为什么ICMP和TCP、UDP并列了呢?熟悉协议栈的同学一定知道,ICMP和IP协议是一对好基友。IP传输过程中出现了什么状况,都是靠ICMP打小报告,通过上层大哥(TCP UDP)的。把它和TCP UPD并列显然是没有道理的。不过仔细一想,也有道理。ICMP报文是通过什么协议传输的?当然是IP协议了,也就是说ICMP和UDP TCP一样,也是要使用IP协议的,这样一想ICMP虽然为IP协议服务,却也享受了IP协议的服务,果然一对好基友不是!这样说的话,在inet中,ICMP和UDP TCP一样位于IP协议层之上,所以先要准备好ICMP UDP TCP。步子一次一步,别迈太大。
我好奇的是如果把三个协议和inet关联起来的。走去看下tcp_protocol(来自于同一个文件net/ipv4/af_inet.c)的内容,然后看下inet_add_protocol干了什么事情。
tcp_protocol比其他两个协议内容多一些,如下。如果说,现在在一步步向下打通整个协议栈,你可能不信。不过事实就是如此,linux的世界里秩序是非常重要的,一切复杂的东西,都是以简单的道理开始。目前来看,tcp_protocol就是连接inet和IP层的接口。tcp_protocol中的handler被赋值为tcp_v4_rcv,而tcp_v4_rcv函数的原型为 int tcp_v4_rcv(struct sk_buff *skb)。看到了熟悉的sk_buff,也就明白了来龙去脉了,这里真的是IP层向上走的入口了。其他成员暂不提。再去看看inet_add_protocol函数。
1273 static struct net_protocol tcp_protocol = {
1274 .handler = tcp_v4_rcv,
1275 .err_handler = tcp_v4_err,
1276 .gso_send_check = tcp_v4_gso_send_check,
1277 .gso_segment = tcp_tso_segment,
1278 .no_policy = 1,
1279 };
inet_add_protocol函数来自于net/ipv4/protocol.c文件中。这个函数很简单,就喜欢这种简单的函数。我们关键点在65行。把传入的prot赋值给inet_protos对应的位置。inet_protos定义的一个数组,用来记录注册好的协议地址。显然这个数组是经常访问的,要求编译器缓存对齐。通过这个数组,加上我们的协议号,inet就能找到对应的协议数据结构,如上所述的tcp_protocol。
48 struct net_protocol *inet_protos[MAX_INET_PROTOS] ____cacheline_aligned_in_smp;
55 int inet_add_protocol(struct net_protocol *prot, unsigned char protocol)
56 {
57 int hash, ret;
59 hash = protocol & (MAX_INET_PROTOS - 1);
61 spin_lock_bh(&inet_proto_lock);
62 if (inet_protos[hash]) {
63 ret = -1;
64 } else {
65 inet_protos[hash] = prot;
66 ret = 0;
67 }
68 spin_unlock_bh(&inet_proto_lock);
70 return ret;
71 }
面向IP层的接口已经注册好了。下面回来inet_init函数中。
1398 for (r = &inetsw[0]; r < &inetsw[SOCK_MAX]; ++r)
1399 INIT_LIST_HEAD(r);
1401 for (q = inetsw_array; q < &inetsw_array[INETSW_ARRAY_LEN]; ++q)
1402 inet_register_protosw(q);
1398-1399 初始化了inetsw数组中的各个元素。
1401-1402 代码中的注释说,是为inet_create提供socket侧的信息。我们先去看下inetsw_array是什么。定义在同一个文件af_inet.c中。这个数组有三个成员。不难看出,这就是这面提到过的3个控制层的协议。看到前面说过的熟悉的tcp_prot就明白了,tcp_prot果然是给socket用的。其他个成员就不说了。
去看下inet_register_protosw这个函数。
910 static struct inet_protosw inetsw_array[] =
911 {
912 {
913 .type = SOCK_STREAM,
914 .protocol = IPPROTO_TCP,
915 .prot = &tcp_prot,
916 .ops = &inet_stream_ops,
917 .capability = -1,
918 .no_check = 0,
919 .flags = INET_PROTOSW_PERMANENT |
920 INET_PROTOSW_ICSK,
921 },
923 {
924 .type = SOCK_DGRAM,
925 .protocol = IPPROTO_UDP,
926 .prot = &udp_prot,
927 .ops = &inet_dgram_ops,
928 .capability = -1,
929 .no_check = UDP_CSUM_DEFAULT,
930 .flags = INET_PROTOSW_PERMANENT,
931 },
934 {
935 .type = SOCK_RAW,
937 .prot = &raw_prot,
938 .ops = &inet_sockraw_ops,
939 .capability = CAP_NET_RAW,
940 .no_check = UDP_CSUM_DEFAULT,
941 .flags = INET_PROTOSW_REUSE,
942 }
943 };
函数的定义还是在这个文件里。这个函数很简单,因为我们第一次执行时,inetsw数组里的成员都是空的。直接跳到983行执行。把对应的inet_protosw的list成员加入到对应的inetsw的成员列表头中。至此inetsw_array中的成员全部挂靠在inetsw中了。
void inet_register_protosw(struct inet_protosw *p)
948 {
949 struct list_head *lh;
950 struct inet_protosw *answer;
951 int protocol = p->protocol;
952 struct list_head *last_perm;
954 spin_lock_bh(&inetsw_lock);
956 if (p->type >= SOCK_MAX)
957 goto out_illegal;
960 answer = NULL;
961 last_perm = &inetsw[p->type];
962 list_for_each(lh, &inetsw[p->type]) {
963 answer = list_entry(lh, struct inet_protosw, list);
966 if (INET_PROTOSW_PERMANENT & answer->flags) {
967 if (protocol == answer->protocol)
968 break;
969 last_perm = lh;
970 }
972 answer = NULL;
973 }
974 if (answer)
975 goto out_permanent;
983 list_add_rcu(&p->list, last_perm);
1001 }
至此可以有一个大概的轮廓了。
tcp_prot负责tcp_sock数据结构的维护,接收socket层的指令。tcp_prot是放在inetsw数组中。tcp_protocol负责与IP层接洽,tcp_protocol放在inet_protos中。而inetsw,inet_protos都是inet子系统中可见的。
这当中还是有很多疑惑。比如inet_family_ops的成员函数inet_create是如何调用tcp_prot的成员函数的?socket创建完成后,是不是直接由socket调用tcp_prot的成员函数?tcp层是如何把数据传递给IP层的?虽然这些疑惑暂时未解开,那张图片可以补充上一点内容了。