tcp rst报文_TCP报文格式

tcp rst报文_TCP报文格式RESET报文的接收和检查处理。客户端握手阶段对于TCP客户端,在发送完SYN报文之后,如果接收到的回复报文同时设置了ACK和RST标志,在检查完ACK的合法性之后,处理RST标志,关闭套接口。对于ACK确认序号,其应当大于第一个未确认序号(snd_una),并且,确认序号不应大于未发送数据的序号(snd_nxt)。通常情况下ACK确认序号应当等于snd_una加一(SYN占用一个序号),但是,如果SYN报文中带有数据(例如:TFO),ACK确认序号会更大。以上情况向对端发送reset报文,但是,如果

大家好,又见面了,我是你们的朋友全栈君。如果您正在找激活码,请点击查看最新教程,关注关注公众号 “全栈程序员社区” 获取激活教程,可能之前旧版本教程已经失效.最新Idea2022.1教程亲测有效,一键激活。

Jetbrains全系列IDE稳定放心使用

RESET报文的接收和检查处理。

客户端握手阶段

对于TCP客户端,在发送完SYN报文之后,如果接收到的回复报文同时设置了ACK和RST标志,在检查完ACK的合法性之后,处理RST标志,关闭套接口。对于ACK确认序号,其应当大于第一个未确认序号(snd_una),并且,确认序号不应大于未发送数据的序号(snd_nxt)。

通常情况下ACK确认序号应当等于snd_una加一(SYN占用一个序号),但是,如果SYN报文中带有数据(例如:TFO),ACK确认序号会更大。以上情况向对端发送reset报文,但是,如果当前报文不仅只有ACK标志位,还设置了RST位,将不发送reset报文。

static int tcp_rcv_synsent_state_process(struct sock *sk, struct sk_buff *skb,
                     const struct tcphdr *th)
{
    struct inet_connection_sock *icsk = inet_csk(sk);
    struct tcp_sock *tp = tcp_sk(sk);
    ...

    if (th->ack) {
        /* rfc793:
         * "If the state is SYN-SENT then
         *    first check the ACK bit
         *      If the ACK bit is set
         *    If SEG.ACK =< ISS, or SEG.ACK > SND.NXT, send
         *        a reset (unless the RST bit is set, if so drop
         *        the segment and return)"
         */
        if (!after(TCP_SKB_CB(skb)->ack_seq, tp->snd_una) ||
            after(TCP_SKB_CB(skb)->ack_seq, tp->snd_nxt))
            goto reset_and_undo;

第一个SYN报文复用了retrans_stamp字段,记录其发送的时间戳。所以回复报文中TCP时间戳选项中返回的时间戳应当位于retrans_stamp和当前时间之间,否则记录PAWS错误。

只有在以上ACK报文判断合法之后,才能检查RST标志位,认为是一个合法的RST,执行关闭连接。

        if (tp->rx_opt.saw_tstamp && tp->rx_opt.rcv_tsecr &&
            !between(tp->rx_opt.rcv_tsecr, tp->retrans_stamp,
                 tcp_time_stamp(tp))) {
            NET_INC_STATS(sock_net(sk),
                    LINUX_MIB_PAWSACTIVEREJECTED);
            goto reset_and_undo;
        }

        /* Now ACK is acceptable.
         *
         * "If the RST bit is set
         *    If the ACK was acceptable then signal the user "error:
         *    connection reset", drop the segment, enter CLOSED state,
         *    delete TCB, and return."
         */
        if (th->rst) {
            tcp_reset(sk);
            goto discard;
        }

服务端握手阶段

对于TCP服务器端,在接收到三次握手的第三个ACK报文时,由函数tcp_check_req进行检查。在经过序号检查、PAWS检查之后,如果发现此报文设置了TCP_FLAG_RST或者TCP_FLAG_SYN标志位,判断为非法报文,跳转到embryonic_reset。

struct sock *tcp_check_req(struct sock *sk, struct sk_buff *skb,
               struct request_sock *req, bool fastopen, bool *req_stolen)
{
    ...
    /* RFC793: "second check the RST bit" and
     *     "fourth, check the SYN bit"
     */
    if (flg & (TCP_FLAG_RST|TCP_FLAG_SYN)) {
        __TCP_INC_STATS(sock_net(sk), TCP_MIB_ATTEMPTFAILS);
        goto embryonic_reset;
    }

对于TFO,如果仅是错误的设置了SYN标志位,复位当前的连接请求,但是不复位本地的TFO连接。否则,如果设置了RST标志位,需要复位本地的TFO连接。对于非TFO的情况,接收到RST报文,由accept队列(icsk_accept_queue)中删除连接请求结构。

embryonic_reset:
    if (!(flg & TCP_FLAG_RST)) {
        /* Received a bad SYN pkt - for TFO We try not to reset
         * the local connection unless it's really necessary to
         * avoid becoming vulnerable to outside attack aiming at
         * resetting legit local connections.
         */
        req->rsk_ops->send_reset(sk, skb);
    } else if (fastopen) { /* received a valid RST pkt */
        reqsk_fastopen_remove(sk, req, true);
        tcp_reset(sk);
    }
    if (!fastopen) {
        inet_csk_reqsk_queue_drop(sk, req);
        __NET_INC_STATS(sock_net(sk), LINUX_MIB_EMBRYONICRSTS);
    }

通信阶段

除了以上的reset报文处理,在TCP通信过程中,函数tcp_validate_incoming也将检查报文的RST标志,进行相应处理。与以上的事先检查不同,即使PAWS检查没有通过,RST标志也是有效的。

static bool tcp_validate_incoming(struct sock *sk, struct sk_buff *skb,
                  const struct tcphdr *th, int syn_inerr)
{
    struct tcp_sock *tp = tcp_sk(sk);
    bool rst_seq_match = false;

    /* RFC1323: H1. Apply PAWS check first. */
    if (tcp_fast_parse_options(sock_net(sk), skb, th, tp) &&
        tp->rx_opt.saw_tstamp &&
        tcp_paws_discard(sk, skb)) {
        if (!th->rst) {
            NET_INC_STATS(sock_net(sk), LINUX_MIB_PAWSESTABREJECTED);
            ...
            goto discard;
        }
        /* Reset is accepted even if it did not pass PAWS. */
    }

如果序号检查没有通过,只有tcp_reset_check检查通过之后,才会处理报文的RST位,复位当前连接。

    /* Step 1: check sequence number */
    if (!tcp_sequence(tp, TCP_SKB_CB(skb)->seq, TCP_SKB_CB(skb)->end_seq)) {
        /* RFC793, page 37: "In all states except SYN-SENT, all reset
         * (RST) segments are validated by checking their SEQ-fields."
         * And page 69: "If an incoming segment is not acceptable,
         * an acknowledgment should be sent in reply (unless the RST
         * bit is set, if so drop the segment and return)".
         */
        if (!th->rst) {
            ...
        } else if (tcp_reset_check(sk, skb)) {
            tcp_reset(sk);
        }
        goto discard;
    }

即使以上的序号检查通过,还是需要满足以下条件才能复位本地连接:

1) 当前报文的序号等于本地连接下一个要接收的序号;
2) 或者,通过tcp_reset_check函数的检查

    /* Step 2: check RST bit */
    if (th->rst) {
        /* RFC 5961 3.2 (extend to match against (RCV.NXT - 1) after a
         * FIN and SACK too if available):
         * If seq num matches RCV.NXT or (RCV.NXT - 1) after a FIN, or
         * the right-most SACK block,
         * then
         *     RESET the connection
         * else
         *     Send a challenge ACK
         */
        if (TCP_SKB_CB(skb)->seq == tp->rcv_nxt ||
            tcp_reset_check(sk, skb)) {
            rst_seq_match = true;

如果以上两个条件都不成立,对于包含SACK块的SACK报文,找到其中所有块中最大的序号,如果最大序号等于报文的序号,也认为是有效的RST报文,复位本地TCP连接。

        } else if (tcp_is_sack(tp) && tp->rx_opt.num_sacks > 0) {
            struct tcp_sack_block *sp = &tp->selective_acks[0];
            int max_sack = sp[0].end_seq;
            int this_sack;

            for (this_sack = 1; this_sack < tp->rx_opt.num_sacks; ++this_sack) {
                max_sack = after(sp[this_sack].end_seq,
                         max_sack) ?
                    sp[this_sack].end_seq : max_sack;
            }

            if (TCP_SKB_CB(skb)->seq == max_sack)
                rst_seq_match = true;
        }

        if (rst_seq_match)
            tcp_reset(sk);

复位报文合法性检查函数tcp_reset_check如下,如果复位报文的序号等于待接收序号减一(rcv_nxt – 1),Mac OSX会发生这种情况,在FIN报文之后紧跟一个RST报文,由于在接收到FIN之后,RCV.NXT增加了一,但是Mac OSX发出的这个RST报文与之前FIN具有相同的序号,即RCV.NXT-1。这种情况下,如果套接口状态为TCPF_CLOSE_WAIT、TCPF_LAST_ACK或者TCPF_CLOSING,即确认本地接收到了FIN报文,认为此RST有效。

/* Accept RST for rcv_nxt - 1 after a FIN.
 * When tcp connections are abruptly terminated from Mac OSX (via ^C), a
 * FIN is sent followed by a RST packet. The RST is sent with the same
 * sequence number as the FIN, and thus according to RFC 5961 a challenge
 * ACK should be sent. However, Mac OSX rate limits replies to challenge
 * ACKs on the closed socket. In addition middleboxes can drop either the
 * challenge ACK or a subsequent RST.
 */
static bool tcp_reset_check(const struct sock *sk, const struct sk_buff *skb)
{
    struct tcp_sock *tp = tcp_sk(sk);

    return unlikely(TCP_SKB_CB(skb)->seq == (tp->rcv_nxt - 1) &&
            (1 << sk->sk_state) & (TCPF_CLOSE_WAIT | TCPF_LAST_ACK |
                           TCPF_CLOSING));
}

连接断开阶段

在连接断开过程中,如果本地已经停止接收(RCV_SHUTDOWN),又接收到数据,当做接收到了reset报文,关闭TCP连接,并发送reset报文到对端。

int tcp_rcv_state_process(struct sock *sk, struct sk_buff *skb)
{
    ...
    if (!th->ack && !th->rst && !th->syn)
        goto discard;

    if (!tcp_validate_incoming(sk, skb, th, 0))
        return 0;
    ...
    /* step 7: process the segment text */
    switch (sk->sk_state) {
    case TCP_CLOSE_WAIT:
    case TCP_CLOSING:
    case TCP_LAST_ACK:
        if (!before(TCP_SKB_CB(skb)->seq, tp->rcv_nxt))
            break;
        /* fall through */
    case TCP_FIN_WAIT1:
    case TCP_FIN_WAIT2:
        /* RFC 793 says to queue data in these states,
         * RFC 1122 says we MUST send a reset.
         * BSD 4.4 also does reset.
         */
        if (sk->sk_shutdown & RCV_SHUTDOWN) {
            if (TCP_SKB_CB(skb)->end_seq != TCP_SKB_CB(skb)->seq &&
                after(TCP_SKB_CB(skb)->end_seq - th->fin, tp->rcv_nxt)) {
                NET_INC_STATS(sock_net(sk), LINUX_MIB_TCPABORTONDATA);
                tcp_reset(sk);
                return 1;
            }
        }

内核版本 5.0

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请联系我们举报,一经查实,本站将立刻删除。

发布者:全栈程序员-站长,转载请注明出处:https://javaforall.net/186968.html原文链接:https://javaforall.net

(0)
全栈程序员-站长的头像全栈程序员-站长


相关推荐

  • UFT常识

    UFT常识UFT也用了一段时间了,自己竟然还老犯同样的错误,遇到同样的问题。自己还不能单独解决,对不起导师,对不起自己。下面对UFT中的一些基本问题总结一下。1.关于DataTable导入:DataTable.ImportSheet(FileName,SheetSource,SheetDest)FileName: ThefullorrelativepathoftheExce

    2022年5月8日
    79
  • 海思Hi3798MV310芯片处理器参数介绍「建议收藏」

    海思Hi3798MV310芯片处理器参数介绍「建议收藏」Hi3798MV310是用于IPTV/OTT机顶盒市场的支持4KP60解码的超高清高性能SOC芯片。集成4核64位高性能CortexA53处理器和多核高性能2D/3D加速引擎;支持H.265/AVS24Kx2K@P6010bit超高清视频解码,高性能的H.265高清视频编码,HDR视频解码及显示,HDR转SDR,BT.2020,Dolby和DTS音频处理;内置USB2.0…

    2022年6月28日
    192
  • 格兰杰因果关系检验命令_超声波传感器应用实例及原理

    格兰杰因果关系检验命令_超声波传感器应用实例及原理诺贝尔经济学奖获得者,计量经济学大师克莱夫·格兰杰(CliveGranger)从预测的角度给出了因果关系的一种描述性定义,这就是我们现在所熟知的Granger因果关系。格兰杰因果关系检验是计量经济方法中常用的一种检验方法

    2022年10月23日
    0
  • linux popd 命令,Linux shell中的pushd和popd命令「建议收藏」

    linux popd 命令,Linux shell中的pushd和popd命令「建议收藏」在linux的shell中可以使用pushd和popd命令方便地在多个目录之间切换。通过使用pushd和popd能够极大地提高效率。下面介绍下简单使用方法:0、使用cd-进行目录切换一般,Shell中都可以通过cd-命令回到之前的目录,下面是一个例子:$pwd/home/lfqy$cd/$cd-/home/lfqy$实际上,cd-中,-就相当于变量$OLDPWD。cd-就相当…

    2022年6月15日
    26
  • 《智慧城市时空大数据平台建设技术大纲(2019版)》解析——未完,待续

    《智慧城市时空大数据平台建设技术大纲(2019版)》解析——未完,待续2月18日,自然资源部办公厅发布关于印发《智慧城市时空大数据平台建设技术大纲(2019版)》我们先来回顾下之前国家对于智慧城市建设的指导性纲要文件:1、2015年,《智慧城市时空信息云平台建设技术大纲》2、2017年,《智慧城市时空大数据与云平台建设技术大纲》3、2019年,《智慧城市时空大数据平台建设技术大纲》从2015年开始,智慧城市历经从“时空信息云平台”到“时空大数据与云平…

    2022年6月6日
    155
  • 保姆级-红米AC2100之breed不死后台刷写openwrt官方版&第三方改良版「建议收藏」

    保姆级-红米AC2100之breed不死后台刷写openwrt官方版&第三方改良版「建议收藏」刷机有风险!!!后果自负准备1.红米AC21002.基础的电脑操作breed不死后台第一步:环境准备进入小米路由器原始的管理页,miwifi.com或者192.168.31.1登录之后,检查固件版本第二步:降级这里必须降级,我们降到到2.0.7降级包地址链接提取码:tenk然后等几分钟连接上降级后的wifi,正常是redmi开头无密码连上后重新进入后台192.168.31.1自行设置向导,这里忽略然后检查一下系统版本是否降级成功第三步:写入breed此时注意浏览器

    2022年10月29日
    0

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

关注全栈程序员社区公众号