tcp服务器监听任意地址的问题-阿里云开发者社区

开发者社区> 科技小能手> 正文

tcp服务器监听任意地址的问题

简介:
+关注继续查看
如果服务器监听地址任意地址,客户端只要连接该服务器上配置的任何地址都能连接进来(前提是arp_ignore为0,否则最后一跳的arp解析可能会失败,详情参见前面的文章),
但是看linux协议栈的实现代码时发现内核是通过五元组的哈西来寻找套结字结构从而实现一个连接和套结字绑定的,如果一个服务器监听任意地址,而客户端连接一个确定的地址,因为连接的目的地址和监听地址不同,哈希后是不可能找到监听套结字的,这是怎么处理的呢?
实际上在__tcp_v4_lookup中有两类查询,第一类是首先进行的查询,查询已经建立的连接,就是利用五元组来查询,如果找不到的话,就会进行第二类查询,就是在所有的监听套结字上进行查询:
static struct sock *__tcp_v4_lookup_listener(struct hlist_head *head, u32 daddr,
                         unsigned short hnum, int dif)
{
    ...//head仅仅是和端口相关的链表
    sk_for_each(sk, node, head) {
        struct inet_opt *inet = inet_sk(sk);
        if (inet->num == hnum && !ipv6_only_sock(sk)) {
            __u32 rcv_saddr = inet->rcv_saddr;  //取到服务器监听的地址,如果监听任意地址的话则为0
            score = (sk->sk_family == PF_INET ? 1 : 0);
            if (rcv_saddr) {
                if (rcv_saddr != daddr) //如果监听确定的地址,则严格进行判断
                    continue;
                score+=2;
            }
            if (sk->sk_bound_dev_if) { //如果绑定了确定的设备,则严格进行判断
                if (sk->sk_bound_dev_if != dif)
                    continue;
                score+=2;
            }
            if (score == 5) //严格经过了上述两个约束条件的判断,直接返回
                return sk;
            if (score > hiscore) { //scope越大越精确
                hiscore = score;
                result = sk;
            }
        }
    }
    return result;  //返回一个任意的可用套结字
}
找到了监听套结字,接下来需要创建一个用户套结字,该用户套结字的源地址就是连接套结字的目的地址。tcp_v4_rcv后面要调用tcp_v4_do_rcv,当服务器收到第一个syn包的时候以及后续的握手包,是监听套结字进行处理的:
if (sk->sk_state == TCP_LISTEN) {
    struct sock *nsk = tcp_v4_hnd_req(sk, skb);
    if (!nsk)
        goto discard;
    if (nsk != sk) { //说明生成了新的套结字,接下来进入处理新的通信套结字
        if (tcp_child_process(sk, nsk, skb))
            goto reset;
        return 0;
    }
}
如果一切合理,则会进入tcp三次握手的状态机中。看一下tcp_v4_hnd_req函数,只有在真的创建了一个通信套接字的时候才会返回新的通信套结字:
static struct sock *tcp_v4_hnd_req(struct sock *sk, struct sk_buff *skb)
{
    ...//如果服务器收到了syn包,那么它会分配一个连接请求并且将之放入一个容器,所谓的syn-flood就是服务器收到了大量的没有下文的syn包,导致内存被大量占用.在服务器收到syn的时候会调用conn_request回调函数将请求加入容器
    struct open_request *req = tcp_v4_search_req(tp, &prev, th->source, iph->saddr, iph->daddr);
    if (req)
        return tcp_check_req(sk, skb, req, prev);//到了这一步说明syn已经收到了,这个函数中在state为syn-rcv情况下发送syn-ack,返回NULL,在ack收到后创建通信套结字并且返回它。
    ...
    return sk;
}
在创建通信套结字的时候会设置通信套结字的源地址,相关的回调函数中有以下的代码:
newinet->daddr          = req->af.v4_req.rmt_addr;
newinet->rcv_saddr    = req->af.v4_req.loc_addr;
newinet->saddr          = req->af.v4_req.loc_addr;
这就设置了通信套结字的地址信息,这些信息是在服务器收到syn的时候初始化的:
req->af.v4_req.loc_addr = daddr;
req->af.v4_req.rmt_addr = saddr;

根本不需要判断daddr是否是自己的ip就可以放心的进行设置,因为路由模块做完了这一切判断,如果不是自己ip地址,数据包那是根本不可能被投递到上层的。



 本文转自 dog250 51CTO博客,原文链接:http://blog.51cto.com/dog250/1271754

版权声明:本文内容由阿里云实名注册用户自发贡献,版权归原作者所有,阿里云开发者社区不拥有其著作权,亦不承担相应法律责任。具体规则请查看《阿里云开发者社区用户服务协议》和《阿里云开发者社区知识产权保护指引》。如果您发现本社区中有涉嫌抄袭的内容,填写侵权投诉表单进行举报,一经查实,本社区将立刻删除涉嫌侵权内容。

相关文章
【Boost】boost库asio详解2——io_service::run函数无任务时退出的问题
io_service::work类可以使io_service::run函数在没有任务的时候仍然不返回,直至work对象被销毁。 [cpp] view plain copy    print? void test_asio_nowork()   {       b...
1195 0
我的博客即将入驻“云栖社区”,诚邀技术同仁一同入驻。专注于网站安全问题
我的博客即将入驻“云栖社区”,诚邀技术同仁一同入驻。专注于网站安全问题
710 0
就是要你懂 TCP-- 最经典的TCP性能问题
# 就是要你懂 TCP-- 最经典的TCP性能问题 ### 问题描述 某个PHP服务通过Nginx将后面的tair封装了一下,让其他应用可以通过http协议访问Nginx来get、set 操作tair 上线后测试一切正常,每次操作几毫秒,但是有一次有个应用的value是300K,这个时候set一次需要300毫秒以上。 在没有任何并发压力单线程单次操作也需要这么久,这个延迟是没有道
3351 0
Linux netcat对tcp/udp的连接和监听
netcat是一个用于TCP/UDP连接和监听的linux工具, 主要用于网络传输及调试领域。 netcat 可以打开TCP连接发送UDP报文,监听在TCP和UDP端口,以及TCP端口扫描,并将错误消息输出到屏幕上。
2120 0
TCP TIME_WAIT状态解析及问题解决
版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/zhaobryant/article/details/80557158 一、TCP四次挥手过程 TCP在建立连接时需要握手,同理,在关闭连接的时候也需要握手。
2028 0
Eclipse 中部署应用到任意服务器
在之前的文章《在 Intellij IDEA 中部署 Java 应用到 阿里云 ECS》中讲解了如何将一个本地应用部署到阿里云 ECS 上去,有些读者反馈目前还有一些测试机器是在经典网络,甚至是在本地机房中,咨询是否可以通过 Cloud Toolkit 插件将应用部署到这些服务器上去?最新版本的 Cloud Toolkit 已经发布,完全支持啦。
1638 0
23706
文章
0
问答
文章排行榜
最热
最新
相关电子书
更多
《2021云上架构与运维峰会演讲合集》
立即下载
《零基础CSS入门教程》
立即下载
《零基础HTML入门教程》
立即下载