免费注册 查看新帖 |

Chinaunix

  平台 论坛 博客 文库
最近访问板块 发新帖
查看: 6486 | 回复: 7
打印 上一主题 下一主题

关于tcp_v4_do_rcv()中对TCP_LISTEN状态处理的疑问 [复制链接]

论坛徽章:
0
跳转到指定楼层
1 [收藏(0)] [报告]
发表于 2007-04-16 16:44 |只看该作者 |倒序浏览
这个函数中,在sock处于TCP_LISTEN状态时,执行tcp_v4_hnd_req(),但是, 由于在监听状态,
它并没有客户端的信息,所以,它返回总是NULL。这样tcp_rcv_state_process()中对TCP_LISTEN的状态的处理总是得不到执行。而tcp_rcv_state_process()中执行tcp_v4_conn_request()正是建立用于tcp_v4_hnd_req()查找所需要的信息。
        所以,我觉得应该先执行tcp_rcv_state_process(),再执行tcp_v4_hnd_req()。我的理解错在哪里了?请各位指点一下,谢谢。


tcp_v4_do_rcv()
{
   
    ...

    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)) {
                rsk = nsk;
                goto reset;
            }
            return 0;
        }
    }

    TCP_CHECK_TIMER(sk);
    if (tcp_rcv_state_process(sk, skb, skb->h.th, skb->len)) {
        rsk = sk;
        goto reset;
    }
    TCP_CHECK_TIMER(sk);
    return 0;

    ...
}


tcp_rcv_state_process()
{
    ...
    case TCP_LISTEN:
        if(th->ack)
            return 1;

        if(th->syn) {
            //这个函数为tcp_v4_conn_request()

            if(tp->af_specific->conn_request(sk, skb) < 0)
                return 1;

            kfree_skb(skb);
            return 0;
            goto discard;
        }
        goto discard;
    ...
}


[ 本帖最后由 R.wen 于 2007-4-16 16:46 编辑 ]

论坛徽章:
0
2 [报告]
发表于 2007-04-16 18:35 |只看该作者
内核版本: 2.6.21-RC5-rt12

我觉得, 你对tcp_v4_hnd_req()的理解有问题:

tcp_v4_hnd_req()会返回以下三种值, 分别代表三类情况:
1.  == NULL, 报文有问题,  一般是安全问题, 如, 攻击listen socket, 落在窗口外, 等.
2.  != sk, 接收到的是客户端发送的三次握手中的最后一次, 就是ack报文. 处理在tcp_v4_hnd_req() -- >tcp_check_req()中
3.  == sk, 接收到的是三次握手中的第一个报文, 就是syn报文, 这时会落到tcp_rcv_state_process()中处理, 也就是说tcp_rcv_state_process() 在TCP_LISTEN状态时, 只处理syn报文, 其他都是出错情况, 因为ack在tcp_v4_hnd_req()处理完了.

代码分析:

static struct sock *tcp_v4_hnd_req(struct sock *sk, struct sk_buff *skb)
{
        struct tcphdr *th = skb->h.th;
        struct iphdr *iph = skb->nh.iph;
        struct sock *nsk;
        struct request_sock **prev;
        /* 查找出于半连接状态的请求 */
        struct request_sock *req = inet_csk_search_req(sk, &prev, th->source,
                                                       iph->saddr, iph->daddr);
        /* 如果找到,检查报文是否符合协议标准和安全规则, 如果符合所有规则,
           会将连接放到listen complete队列中(accept()需要)。*/
        if (req)
                return tcp_check_req(sk, skb, req, prev);

        /* 如果没有找到, 检查是否在已经建立好的连接表或者time_wait表中能找到连接 */
        nsk = inet_lookup_established(&tcp_hashinfo, skb->nh.iph->saddr,
                                      th->source, skb->nh.iph->daddr,
                                      th->dest, inet_iif(skb));

        /* 如果可以,可能是错误,为了安全,需要关闭这个连接,或者继续处理?? */
        if (nsk) {
                if (nsk->sk_state != TCP_TIME_WAIT) {
                        bh_lock_sock(nsk);
                        return nsk;
                }
                inet_twsk_put(inet_twsk(nsk));
                return NULL;
        }

        /* 防止DDos攻击 */
#ifdef CONFIG_SYN_COOKIES
        if (!th->rst && !th->syn && th->ack)
                sk = cookie_v4_check(sk, skb, &(IPCB(skb)->opt));
#endif

        /* 注意, 最后所有情况都正常,返回sk */
        return sk;
}

论坛徽章:
0
3 [报告]
发表于 2007-04-16 19:40 |只看该作者

回复 #2 xiaozhaoz 的帖子

非常感谢你的回复,但还是有点不明白

》       /* 查找出于半连接状态的请求 */
  》      struct request_sock *req = inet_csk_search_req(sk, &prev, th->source,
   》                                                    iph->saddr, iph->daddr);
      
对于第一个报文syn报文,我觉得这个查找好像只能是返回NULL,它能查找到匹配的req吗?
因为如下代码中rmt_addr,loc_addr等域都没被初始化(在tcp_rcv_state_process中),
它们能相等吗?
struct request_sock *inet_csk_search_req(const struct sock *sk,
                     struct request_sock ***prevp,
                     const __be16 rport, const __be32 raddr,
                     const __be32 laddr)
{
    const struct inet_connection_sock *icsk = inet_csk(sk);
    struct listen_sock *lopt = icsk->icsk_accept_queue.listen_opt;
    struct request_sock *req, **prev;

    for (prev = &lopt->syn_table[inet_synq_hash(raddr, rport, lopt->hash_rnd,
                            lopt->nr_table_entries)];
         (req = *prev) != NULL;
         prev = &req->dl_next) {
        const struct inet_request_sock *ireq = inet_rsk(req);
  
       //这里,它能相等吗?
        if (ireq->rmt_port == rport &&
            ireq->rmt_addr == raddr &&
            ireq->loc_addr == laddr &&
            AF_INET_FAMILY(req->rsk_ops->family)) {
            BUG_TRAP(!req->sk);
            *prevp = prev;
            break;
        }
    }

    return req;
}

论坛徽章:
0
4 [报告]
发表于 2007-04-16 19:59 |只看该作者
对呀, 是返回NULL,
因为if(req) 所以如果返回inet_csk_search_req() == NULL, 函数会继续执行下去.

注意看这个函数的最后一条语句:

        /* 注意, 最后所有情况都正常,返回sk */
        return sk;

论坛徽章:
0
5 [报告]
发表于 2007-04-16 22:10 |只看该作者
原帖由 xiaozhaoz 于 2007-4-16 19:59 发表于 4楼  
对呀, 是返回NULL,
因为if(req) 所以如果返回inet_csk_search_req() == NULL, 函数会继续执行下去.

注意看这个函数的最后一条语句:

        /* 注意, 最后所有情况都正常,返回sk */
        return sk;



太感谢了,呵呵。 就是忽略了这一行让我困惑了好久。

谢谢xiaozhaoz!

希望可以多多交流

论坛徽章:
8
羊年新春福章
日期:2015-03-19 02:03:312015亚冠之北京国安
日期:2015-06-16 22:04:45程序设计版块每日发帖之星
日期:2015-06-23 22:20:00每日论坛发贴之星
日期:2015-06-23 22:20:002015亚冠之首尔
日期:2015-06-24 19:18:072015亚冠之广州恒大
日期:2015-08-06 10:29:442015亚冠之柏太阳神
日期:2015-11-02 11:21:0515-16赛季CBA联赛之辽宁
日期:2015-12-09 15:05:02
6 [报告]
发表于 2013-03-31 14:29 |只看该作者
本帖最后由 firocu 于 2013-03-31 14:36 编辑

好。

我有个疑问在 tcp_v4_do_rcv()这个函数中:
if (sk->sk_state == TCP_LISTEN) {
                struct sock *nsk = tcp_v4_hnd_req(sk, skb);
                if (!nsk)
                        goto discard;
你们说tcp_v4_hnd_req()会处理client发送来的ACK包,可是tcp/ip协议上应该是TCP_LISTEN只会受到SYN包。之后变到SYN_RCVD,才会接受ACK包。
为什么,在TCP_LISTEN状态也会接受ACK包呢?难道说是,之前收到SYN包,没有将LISTN 状态更新到SYN_RCVD吗?

论坛徽章:
8
羊年新春福章
日期:2015-03-19 02:03:312015亚冠之北京国安
日期:2015-06-16 22:04:45程序设计版块每日发帖之星
日期:2015-06-23 22:20:00每日论坛发贴之星
日期:2015-06-23 22:20:002015亚冠之首尔
日期:2015-06-24 19:18:072015亚冠之广州恒大
日期:2015-08-06 10:29:442015亚冠之柏太阳神
日期:2015-11-02 11:21:0515-16赛季CBA联赛之辽宁
日期:2015-12-09 15:05:02
7 [报告]
发表于 2013-03-31 15:00 |只看该作者
回复 6# firocu
原来,listen state 下收到ack就是个错误,这里的代码就是处理这种状况的。我本来以为这个函数收到ack是正确的,理所应当的事。

   

论坛徽章:
8
羊年新春福章
日期:2015-03-19 02:03:312015亚冠之北京国安
日期:2015-06-16 22:04:45程序设计版块每日发帖之星
日期:2015-06-23 22:20:00每日论坛发贴之星
日期:2015-06-23 22:20:002015亚冠之首尔
日期:2015-06-24 19:18:072015亚冠之广州恒大
日期:2015-08-06 10:29:442015亚冠之柏太阳神
日期:2015-11-02 11:21:0515-16赛季CBA联赛之辽宁
日期:2015-12-09 15:05:02
8 [报告]
发表于 2013-04-01 00:34 |只看该作者
本帖最后由 firocu 于 2013-04-01 00:37 编辑

我又看了下,源码,发现。

linux 内核中对tcp/ip协议中listen 状态到syn_recv状态的转换过程是与tcp/ip协议,不一致的。
协议中是:
listen state -----recv syn,send syn+ack----> syn_recv state -------recv ack----------------------------->established state
linux kernel:
listen state ------recv syn, send syn+ack -----> listen state(没变)---recv ack --->syn_recv state--->established state

理想和现实总是有差别的,但不影响功能!!!
您需要登录后才可以回帖 登录 | 注册

本版积分规则 发表回复

  

北京盛拓优讯信息技术有限公司. 版权所有 京ICP备16024965号-6 北京市公安局海淀分局网监中心备案编号:11010802020122 niuxiaotong@pcpop.com 17352615567
未成年举报专区
中国互联网协会会员  联系我们:huangweiwei@itpub.net
感谢所有关心和支持过ChinaUnix的朋友们 转载本站内容请注明原作者名及出处

清除 Cookies - ChinaUnix - Archiver - WAP - TOP