TCP/IP协议栈初始化(十) ICMP带来的一段插曲 2

来源:互联网 发布:网络翻译 编辑:程序博客网 时间:2024/06/10 09:28

继续inet_create函数的下半部分。

321     rcu_read_unlock();323     BUG_TRAP(answer_prot->slab != NULL);325     err = -ENOBUFS;326     sk = sk_alloc(net, PF_INET, GFP_KERNEL, answer_prot);327     if (sk == NULL)328         goto out;330     err = 0;331     sk->sk_no_check = answer_no_check;332     if (INET_PROTOSW_REUSE & answer_flags)333         sk->sk_reuse = 1;335     inet = inet_sk(sk);336     inet->is_icsk = (INET_PROTOSW_ICSK & answer_flags) != 0;338     if (SOCK_RAW == sock->type) {339         inet->num = protocol;340         if (IPPROTO_RAW == protocol)341             inet->hdrincl = 1;342     }344     if (ipv4_config.no_pmtu_disc)345         inet->pmtudisc = IP_PMTUDISC_DONT;346     else347         inet->pmtudisc = IP_PMTUDISC_WANT;349     inet->id = 0;351     sock_init_data(sock, sk);353     sk->sk_destruct    = inet_sock_destruct;354     sk->sk_family      = PF_INET;355     sk->sk_protocol    = protocol;356     sk->sk_backlog_rcv = sk->sk_prot->backlog_rcv;358     inet->uc_ttl    = -1;359     inet->mc_loop   = 1;360     inet->mc_ttl    = 1;361     inet->mc_index  = 0;362     inet->mc_list   = NULL;364     sk_refcnt_debug_inc(sk);366     if (inet->num) {372         inet->sport = htons(inet->num);374         sk->sk_prot->hash(sk);375     }377     if (sk->sk_prot->init) {378         err = sk->sk_prot->init(sk);379         if (err)380             sk_common_release(sk);381     }382 out:383     return err;384 out_rcu_unlock:385     rcu_read_unlock();386     goto out;387 }

321 跟上半部分的rcu lock对应,这里表明已经读完了。可以解锁了。
323 一个指针判断。作为得到的协议,如果已经注册过了,如那么它的slab多半不会是空的,如果是空的,说明没有协议自身没有内存资源,就输出一个警告出来。
325 初始化一个错误码。这个我们不会用到,因为我们都是假设不会出错的,我们的流程都是正常的。:)
326 又来了一个sk_alloc函数。跟上一篇中遇到的sock_alloc,很相像。发现没有,内核中也是满满的套路。对应的是 struct socket与struct sock,善于分析总结的人已经发现了。socket比较长,是面向高层一些的,sock比较短,就是面向低层一些的。同样sock_alloc用于分配struct socket的,而sk_alloc是用来分配struct sock的。
有时候取舍很难,这个函数要不看下呢?看的话,与主线偏离的更远了,写的人一不注意,这成了主线。还是看下吧,毕竟关键的数据结构还没有串联起来。

326     sk = sk_alloc(net, PF_INET, GFP_KERNEL, answer_prot);

先看下,传入的参数。
- net,就是一开始传入的init_net的命名空间。
- PF_INET,表示inet协议子族,这个已经遇到过了。
- GFP_KERNEL,是分配内存时标识,表示我们需要是一般的内核内存。关于它还有许多类似的内存类型标识,有兴趣的可以去看下。
- anser_prot, 就是上回说到的raw_prot。
函数定义在net/core/sock.c中。

929 struct sock *sk_alloc(struct net *net, int family, gfp_t priority,930               struct proto *prot)931 {932     struct sock *sk;934     sk = sk_prot_alloc(prot, priority | __GFP_ZERO, family);935     if (sk) {936         sk->sk_family = family;941         sk->sk_prot = sk->sk_prot_creator = prot;942         sock_lock_init(sk);943         sk->sk_net = get_net(net);944     }946     return sk;947 }

函数很简单,调用sk_prot_alloc分配内存空间。分配到了就初始化一下。sk_prot_alloc更简单,判断协议的slab是不是为空,如果空的话,就从系统中另行分配内存空间。分配的空间的大小,是slab对应的单元的大小,去net/ipv4/raw.c中看下raw_prot定义,发现的它对象大小就是sizeof(struct raw_sock)。
注意到941行,把sk的两个成员变量赋以了传入的prot,这个后面还会遇到。
942,把sk的锁初始化了。
943, 把对net的引用加1。这是Linux中记录对象是否有人使用的方法,当没有人使用时,这个对象占用的资源就可以释放了。
函数执行完了。同样假定得到了一个正常有资源的sk。
回到inet_create函数中。327-334对sk的进行初始化。这当中需要注意以下2点:
answer_no_check其余是赋以了raw_prot对应的在inetsw中元素的no_check标识。这个值为0,表示每次数据包到达时,协议都要自己进行一次校验和计算。
同样answer_flags,是对应是INET_PROTOSW_REUSE。也就是sk要支持复用。这又是为什么呢?别忘记了,我们现在正在创建的是ICMP协议的内核socket,ICMP与IP一起工作,当然要支持复用了。
335, 这个函数仅仅是把struct sock类型转换成了struct inet_sock。为什么能这么转换,因为当在调用sk_prot_alloc生成的sk实例,其实大小是raw_sock,而raw_sock是内含了struct inet_sock,而struct inet_sock又内含了struct sock。可以参考我另一篇《 linux源码-TCP/IP协议栈学习预备(1) 数据结构之各socket之间的关系 》中描述的关系。
336, 我们的ICMP socket是非连接socket,is_icsk被置0。
338-349 完成对inet成员的进一步初始化。
inet->num 给inet socket的本地端口。这里被初始化为协议类型号。也就是IPPROTO_ICMP。我们只是知道系统会保留一些端口号,80留给http协议,并没有听说过会1号端口留给ICMP协议。
341,hdrincl选项用来指示数据中是否包含头部。ICMP是使用的IP协议,数据中包含了IP的包首部。也就收到的raw数据包减去IP的首部长度,剩下的部分才是ICMP协议的报文部分。
344-347,这当中的选项不太清楚,根据源码中注释猜测,是接受用户配置选项,来决定ICMP socket是否支持MTU(Maximum Transmit Unit)的发现机制。
349, 现在还没有开始工作,把记录分片包的数据的变量id置0。
351,进一步初始化sk。别看传入的是sock和sk,几乎全是针对sk的初始化。既然是init_data,多半是接受发送有关的数据结构。主要做了以下事情,具体的内容在遇到时再说下。
初始化三个队列:接受队列,写队列,出错队列。
初始化各个状态变量值
初始化各个锁
继续回到inet_create中。下面一直到364行,进一步初始化sk与协议相关的成员。
366-375 如果有端口号,上面已经看到过被赋1,给inet的源端口赋上,被把sk加入到协议的散列表中。这里的散列表是定义在net/ipv4/raw.c中raw_v4_htable。
377 如果协议自身定义了初始化函数就去执行它。这里我们执行的是来自net/ipv4/raw.c上的raw_init。这个函数只是置0了一个成员变量filter。
至此inet_create执行完成了。我们的ICMP socket从inet层、控制层都准备好了。也是该返回主线的时候了。

0 0
原创粉丝点击