Linux内核分析 - 网络[十六]:TCP三次握手
下面来看下对每个端口的检查,即//choose a valid port部分的代码。这里要先了解下tcp的内核表组成,udp的表 内核表udptable只是一张hash表,tcp的表则稍复杂,它的名字是tcp_hashinfo,在tcp_init()中被初始化,这个数据结构定义 如下(省略了不相关的数据): struct inet_hashinfo { struct inet_ehash_bucket *ehash; …… struct inet_bind_hashbucket *bhash; …… struct inet_listen_hashbucket listening_hash[INET_LHTABLE_SIZE] ____cacheline_aligned_in_smp; }; 从定义可以看出,tcp表又分成了三张表ehash, bhash, listening_hash,其中ehash, listening_hash对应于 socket处在TCP的ESTABLISHED, LISTEN状态,bhash对应于socket已绑定了本地地址。三者间并不互斥,如一个socket可同时在 bhash和ehash中,由于TIME_WAIT是一个比较特殊的状态,所以ehash又分成了chain和twchain,为TIME_WAIT的socket单独形成 一张表。 回到刚才的代码,现在还只是建立socket连接,使用的就应该是tcp表中的bhash。首先取得内核tcp表的bind表 – bhash,查看是否已有socket占用: 如果没有,则调用inet_bind_bucket_create()创建一个 bind表项tb,并插入到bind表中,跳转至goto ok代码段; 如果有,则跳转至goto ok代码段。 进入ok代码段表明已找到合适的bind表项(无论是创建的还是查找到的),调用inet_bind_hash()赋值源端口inet_num。 for (i = 1; i <= remaining; i++) { port = low + (i + offset) % remaining; head = &hinfo->bhash[inet_bhashfn(net, port, hinfo->bhash_size)]; …… inet_bind_bucket_for_each(tb, node, &head->chain) { if (net_eq(ib_net(tb), net) && tb->port == port) { if (tb->fastreuse >= 0) goto next_port; WARN_ON(hlist_empty(&tb->owners)); if (!check_established(death_row, sk, port, &tw)) goto ok; goto next_port; } } tb = inet_bind_bucket_create(hinfo->bind_bucket_cachep, net, head, port); …… next_port: spin_unlock(&head->lock); } ok: …… inet_bind_hash(sk, tb, port); …… goto out; 在获取到合适的源端口号后,会重建路由项来进行更新: err = ip_route_newports(&rt, IPPROTO_TCP, inet->inet_sport, inet->inet_dport, sk); 函数比较简单,在获 取sport前已经查找过一次路由表,并插入了key=[saddr=0, sport=0, daddr, dport, oif=0]的路由缓存项;现在获取到了 sport,调用ip_route_output_flow()再次更新路由缓存表,它会添加key=[saddr=0, sport, daddr, dport, oif=0]的路由缓存 项。这里可以看出一个策略选择,查询路由表->获取sport->查询路由表,为什么不是获取sport->查询路由表的原因 可能是效率的问题。 if (sport != (*rp)->fl.fl_ip_sport || dport != (*rp)->fl.fl_ip_dport) { struct flowi fl; memcpy(&fl, &(*rp)->fl, sizeof(fl)); fl.fl_ip_sport = sport; fl.fl_ip_dport = dport; fl.proto = protocol; ip_rt_put(*rp); *rp = NULL; security_sk_classify_flow(sk, &fl); return ip_route_output_flow(sock_net(sk), rp, &fl, sk, 0); } (编辑:PHP编程网 - 黄冈站长网) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |