3.5 ICMP不可达报文的处理

来源:互联网 发布:美国最新非农数据分析 编辑:程序博客网 时间:2024/05/17 06:16

  在三次握手阶段有两种情况TCP会收到ICMP“目的不可达”报文:

1、client端通过connect系统调用发送SYN请求到server端后,server没有进程在相应的地址或端口处理请求,这时client端会收到ICMP不可达报文

2、client端通过connect系统调用发送SYN请求后崩溃,server端收到SYN后发送SYN|ACK,client端收到SYN|ACK后会给server发送ICMP不可达报文

  TCP在收到ICMP目的不可达报文后是如何处理的呢?Linux的TCP在系统启动时会向IP层注册一个数据结构——tcp_protocol:

1552 static const struct net_protocol tcp_protocol = {1553     .early_demux    =   tcp_v4_early_demux,1554     .handler    =   tcp_v4_rcv,    1555     .err_handler    =   tcp_v4_err,1556     .no_policy  =   1,   1557     .netns_ok   =   1,   1558 };
  其中,tcp_v4_rcv是TCP协议入口函数,而tcp_v4_err则是ICMP报文在TCP层的处理函数:

 326 void tcp_v4_err(struct sk_buff *icmp_skb, u32 info) 327 {        328     const struct iphdr *iph = (const struct iphdr *)icmp_skb->data; 329     struct tcphdr *th = (struct tcphdr *)(icmp_skb->data + (iph->ihl << 2)); 330     struct inet_connection_sock *icsk; 331     struct tcp_sock *tp; 332     struct inet_sock *inet; 333     const int type = icmp_hdr(icmp_skb)->type; 334     const int code = icmp_hdr(icmp_skb)->code;... 348     sk = inet_lookup(net, &tcp_hashinfo, iph->daddr, th->dest, 349             iph->saddr, th->source, inet_iif(icmp_skb));... 377     icsk = inet_csk(sk); 378     tp = tcp_sk(sk); 379     req = tp->fastopen_rsk; 380     seq = ntohl(th->seq);...389     switch (type) {... 399     case ICMP_DEST_UNREACH: 400         if (code > NR_ICMP_UNREACH) 401             goto out; 402  403         if (code == ICMP_FRAG_NEEDED) { /* PMTU discovery (RFC1191) */... 418             goto out; 419         }... 440         skb = tcp_write_queue_head(sk);  //获取在队列中的首包 441         BUG_ON(!skb); 442  443         remaining = icsk->icsk_rto - min(icsk->icsk_rto, 444                 tcp_time_stamp - TCP_SKB_CB(skb)->when);  //计算重传时间 445  446         if (remaining) { 447             inet_csk_reset_xmit_timer(sk, ICSK_TIME_RETRANS, 448                           remaining, TCP_RTO_MAX);  //设置重传定时器,超时后重传首包 449         } else { 450             /* RTO revert clocked out retransmission. 451              * Will retransmit now */ 452             tcp_retransmit_timer(sk);  //立即重传首包 453         } 454  455         break;... 471     switch (sk->sk_state) { 472         struct request_sock *req, **prev; 473     case TCP_LISTEN:  //情况2会进入这里 474         if (sock_owned_by_user(sk)) 475             goto out; 476  477         req = inet_csk_search_req(sk, &prev, th->dest,  //查找request_sock 478                       iph->daddr, iph->saddr); 479         if (!req) 480             goto out; 481  482         /* ICMPs are not backlogged, hence we cannot get 483            an established socket here. 484          */ 485         WARN_ON(req->sk); 486  487         if (seq != tcp_rsk(req)->snt_isn) { 488             NET_INC_STATS_BH(net, LINUX_MIB_OUTOFWINDOWICMPS); 489             goto out; 490         } 491  492         /* 493          * Still in SYN_RECV, just remove it silently. 494          * There is no good way to pass the error to the newly 495          * created socket, and POSIX does not want network 496          * errors returned from accept(). 497          */ 498         inet_csk_reqsk_queue_drop(sk, req, prev);  //移除request_sock 499         NET_INC_STATS_BH(sock_net(sk), LINUX_MIB_LISTENDROPS); 500         goto out; 501  502     case TCP_SYN_SENT:  //情况1会进入这里 503     case TCP_SYN_RECV:  /* Cannot happen. 504                    It can f.e. if SYNs crossed, 505                    or Fast Open. 506                  */ 507         if (!sock_owned_by_user(sk)) { 508             sk->sk_err = err; 509  510             sk->sk_error_report(sk); //调用sock_def_error_report唤醒在connect系统调用中睡眠的进程 511  512             tcp_done(sk);  //释放TCP连接相关的一些资源,比如定时器 513         } else { 514             sk->sk_err_soft = err; 515         } 516         goto out; 517     }... 542  543 out: 544     bh_unlock_sock(sk); 545     sock_put(sk); 546 }
  403:这种情况是需要分片导致的ICMP报文,这与路径MTU有关,通常不会出现在三次握手过程中。关于路径MTU会在后续章节中讨论

  507:如果socket没有被进程访问,则唤醒在connect系统调用中睡眠的进程,并设置TCP状态机为TCP_CLOSE,释放一些资源

  514:情况1和情况2都不会走到这里

  可见,tcp_v4_err函数能够在ICMP不可达报文到达后即时的设置TCP的状态机,释放一些资源,并唤醒睡眠的进程,从而使TCP的连接资源能够及时的回收。


0 0
原创粉丝点击