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

相关文章
|
2月前
|
网络协议 安全 测试技术
手撕测试tcp服务器效率工具——以epoll和io_uring对比为例
手撕测试tcp服务器效率工具——以epoll和io_uring对比为例
41 2
|
3天前
|
网络协议 安全 Linux
IDEA通过内网穿透实现固定公网地址远程SSH连接本地Linux服务器
IDEA通过内网穿透实现固定公网地址远程SSH连接本地Linux服务器
|
6天前
|
域名解析 网络协议 Linux
TCP/IP协议及配置、IP地址、子网掩码、网关地址、DNS与DHCP介绍
TCP/IP协议及配置、IP地址、子网掩码、网关地址、DNS与DHCP介绍
|
1月前
|
网络协议 Java
【开源视频联动物联网平台】J2mod库写一个Modbus TCP 服务器
【开源视频联动物联网平台】J2mod库写一个Modbus TCP 服务器
65 0
|
2月前
|
网络协议 Java API
【JavaEE初阶】 TCP服务器与客户端的搭建
【JavaEE初阶】 TCP服务器与客户端的搭建
|
2月前
|
弹性计算 监控 网络安全
ECS实例问题之公网IP地址无法访问如何解决
ECS实例指的是在阿里云ECS服务中创建的虚拟计算环境,用户可在此环境中运行应用程序和服务;本合集将介绍ECS实例的创建、管理、监控和维护流程,及常见问题处理方法,助力用户保障实例的稳定运行。
|
2月前
|
域名解析 弹性计算 网络协议
DNS服务器问题之翻译为 TCP/IP 地址如何解决
DNS服务器是负责将域名转换为IP地址的服务,它是互联网上实现域名解析的关键基础设施;本合集将探讨DNS服务器的工作原理、配置方法和常见问题处理,帮助用户理解和优化DNS服务的使用。
38 7
|
2月前
|
安全
解决window服务器打开网页都要添加信任地址的问题
解决window服务器打开网页都要添加信任地址的问题
|
2月前
|
数据采集 网络协议 搜索推荐
网络编程【TCP单向通信、TCP双向通信、一对多应用、一对多聊天服务器】(二)-全面详解(学习总结---从入门到深化)
网络编程【TCP单向通信、TCP双向通信、一对多应用、一对多聊天服务器】(二)-全面详解(学习总结---从入门到深化)
53 0
|
3月前
|
网络协议 安全 Linux
腾讯三面:一台服务器,最大支持的TCP连接数是多少?
一个 TCP 对象占用的大小,等于它所包含的一些数据结构占用大小的总和,也是就把上面这些数据结构的大小累加起来,就是一个 TCP 连接占用的大小了。实际过程中的 TCP 连接,肯定不是静止状态的,还会进行发送数据和接收数据了,那么这些过程还是会额外消耗更多的内存资源的,8 GB 物理内存的服务器实际并发很难达到百万级别。
59 2