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);
} (编辑:应用网_阳江站长网) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |

