阿八博客
  • 100000+

    文章

  • 23

    评论

  • 20

    友链

  • 最近新加了很多技术文章,大家多来逛逛吧~~~~
  • 喜欢这个网站的朋友可以加一下QQ群,我们一起交流技术。

CONNTRACK的tcp序列号调整扩展功能

欢迎来到阿八个人博客网站。本 阿八个人博客 网站提供最新的站长新闻,各种互联网资讯。 喜欢本站的朋友可以收藏本站,或者加QQ:我们大家一起来交流技术! URL链接:https://www.abboke.com/jsh/2019/1010/116520.html

当报文的tcp负载长度发生变化的时候(记住这里强调的是tcp负载的长度),就会影响发送方向(被修改的报文是谁发出的,那么这里的发送发现就指该方向)的发送序列号和应答方向报文的应答序列号
比较常见的是ALG功能,syn代理功能需要序列号调整
tcp序列号发生变化后,与序列号相关的sack选项也需要进行调整

seqadj扩展功能

连接跟踪使用seqadj扩展功能实现序列号调整,在连接跟踪上添加一个序列号调整控制块nf_conn_seqadj:


1.连接跟踪创建时如果存在helper扩展功能或者启动了synproxy都会为ct添加seqadj扩展控制块
helper添加seqadj扩展功能时会进行初始化,将成员初始化为0
假设第一个syn报文的序列号为5000,那么其seqadj扩展控制块的内容如上图左边的值,即全为0(上图只显示了一个方向,另外一个方向类似)

2.当第一次发生tcp负载长度变化时的报文的序列号是10000,此时会设置nf_ct_seqadj控制块:

初始化seqadj控制块

在nf_nat_setup_info函数构建nat信息时,如果连接跟踪信息发生变化,并且ct存在help,那么会为该ct添加一个seqadj控制块

在confirm函数中进行序列号的调整

confirm的优先级为NF_IP_PRI_CONNTRACK_CONFIRM,最低的优先级,也就是说最后执行的函数

     * other_way->correction_pos记录的是发送方向发生改变的报文的原始序列号
而tcph->ack_seq是接收方对修改序列号 * 后的报文的应答,这里减去other_way->offset_before后即为对原始发送序列号报文的应答序列号
*/ if (after(ntohl(tcph->ack_seq) - other_way->offset_before, other_way->correction_pos)) ackoff = other_way->offset_after; else ackoff = other_way->offset_before; newack = htonl(ntohl(tcph->ack_seq) - ackoff); inet_proto_csum_replace4(&tcph->check, skb, tcph->ack_seq, newack, false); pr_debug("Adjusting ack number from %u->%u, ack from %u->%u\n", ntohl(tcph->seq), ntohl(newseq), ntohl(tcph->ack_seq), ntohl(newack)); tcph->ack_seq = newack; //进行sack选项的调整
res = nf_ct_sack_adjust(skb, protoff, tcph, ct, ctinfo);out: spin_unlock_bh(&ct->lock); return res;}

情况1:

当前已经发生过一次tcp负载长度变化,after(ntohl(tcph->seq), this_way->correction_pos)为真,seqoff = this_way->offset_after;也就是为10,修改tcph->seq=tcph->seq+10

情况3:

当前已经发生过两次tcp负载长度变化,after(ntohl(tcph->seq), this_way->correction_pos)为真,seqoff = this_way->offset_after;也就是为5,修改tcph->seq=tcph->seq+5

情况5:

当前已经发生过两次tcp负载长度变化,但是当前的报文的序列号为6000,小于this_way->correction_pos
这种情况就是报文严重迷路了导致的,after(ntohl(tcph->seq), this_way->correction_pos)为假,seqoff = this_way->offset_before;也就是为10,会实际修改tcph->seq=tcph->seq+10
这种修改是错误的,因为序列号6000的报文不需要修改序列号
也就是说netfilter不能正确处理两次tcp负载长度变化之前的迷路报文

应答序列号的调整

应答序列号表示接受方期望收到的下一个字节序列号
它等于接收方收到的报文的发送序列号加上报文的长度
接收方还可以合并应答,也就是说应答报文不一定和接收的报文对应起来
而且发送方在接收应答序列号时,只关注比当前已经应答过的序列号大的序列号即可

正常情况下一对一应答报文

情况1:

当前发送方向已经发生过一次tcp负载长度变化,接收方收到的tcph->seq=10000,假设报文长度为100
假设本次应答报文就是应答了序列号为10000的报文,由于长度边长了10,所以应答序列号为tcph->ack_seq=10110
tcph->ack_seq-other_way->offset_before=10110-0=10110大于other_way->correction_pos
所以after(ntohl(tcph->ack_seq) - other_way->offset_before,other_way->correction_pos) 为真,seqoff = this_way->offset_after为10,修改tcph->ack_seq=tcph->ack_seq-10=10100
这个值与发送方的正常的期望应答序列号一致,没有问题

情况3:

当前发送方向已经发生过两次tcp负载长度变化,假设本次应答序列号为20000的报文,假设报文长度为100
那么接收方收到的报文的序列号为20010(netfilter给增加一个10个字节),由于本次报文的长度在netfilter中减少了5个字节,所以实际接收方收到的报文长度为95个字节,应答序列号为tcph->ack_seq=20105
tcph->ack_seq-other_way->offset_before=20105 - 5=20100大于other_way->correction_pos
所以after(ntohl(tcph->ack_seq) - other_way->offset_before,other_way->correction_pos) 为真,seqoff = this_way->offset_after为5,修改tcph->ack_seq=tcph->ack_seq-5=26105-5=26100
这个值与发送方的正常的期望应答序列号一致,没有问题

情况5:

当前发送方向已经发生过一次tcp负载长度变化,发送方情况如上图所示,此时应答方最大应答到序列号为6000
假设本次应答接下来1000个字节
那么应答序列号tcph->ack_seq=7000
tcph->ack_seq-other_way->offset_before=7000 - 0=7000小于other_way->correction_pos
所以after(ntohl(tcph->ack_seq) - other_way->offset_before,other_way->correction_pos) 为假,seqoff = this_way->offset_before为0,修改tcph->ack_seq不会发生变化,没有问题

情况2:

当前发送方向已经发生过两次tcp负载长度变化,发送方情况如上图所示,此时应答方最大应答到序列号为6000
假设发送方发送的序列号已经到了25000了
本次应答接下来2000个字节
那么应答序列号tcph->ack_seq=8000
该应答序列号没有包含了对发生长度变化的10000序列号报文的应答,所以实际应答应该要比序列号不变,即真实到发送端的应答序列号为8000
tcph->ack_seq-other_way->offset_before=8000- 5=7095小于other_way->correction_pos
所以after(ntohl(tcph->ack_seq) - other_way->offset_before,other_way->correction_pos) 为假,seqoff = this_way->offset_before为10,修改tcph->ack_seq=tcph->ack_seq-10=7095,与预期不相符,有点问题,发生了报文的部分应答问题

情况4:

((op[1] - 2) % TCPOLEN_SACK_PERBLOCK) == 0)//长度减去2必须是TCPOLEN_SACK_PERBLOCK的整数倍
nf_ct_sack_block_adjust(skb, tcph, optoff + 2, optoff+op[1], &seqadj->seq[!dir]);/* sack处理的是应答序列号,所以是反方向的序列号控制块 */ optoff += op[1]; } } return 1;}

sack序列号调整与应答序列号调整类似:

相关文章

暂住......别动,不想说点什么吗?
  • 全部评论(0
    还没有评论,快来抢沙发吧!