Linux TCP包头设置SEQ值,linux 协议栈tcp的rst报文中,seq的选取问题

之前在《深入理解并行编程》的群里,有个小米的兄弟问了一个问题,服务器A发包给服务器B,Seq是1,但是在未能收到服务器B的报文回复的情况下,发送了

rst,但是rst报文中,对应的seq是1461,一堆人都在猜测,为什么seq跳变了,由于当时只看到一半的图片,所以我让他发送完整报文出来之后,我

发现其实rst的seq不是1的原因,并不是因为跳变,而是正常的,因为发送给B的报文,长度为1460,但是这个报文没有得到回复,所以在超时之后,应用程序关闭了这条连接,

导致内核协议栈发送了一个rst报文,而rst报文选取seq的时候,并不是选取的确定已经发送的seq,而是当前连接已经用掉的seq,也就是当前seq,哪怕这个报文没有收到回复,也会使用。

具体看代码:

/* We get here when a process closes a file descriptor (either due to

* an explicit close() or as a byproduct of exit()'ing) and there

* was unread data in the receive queue. This behavior is recommended

* by RFC 2525, section 2.17. -DaveM

*/

void tcp_send_active_reset(struct sock *sk, gfp_t priority)

{

struct sk_buff *skb;

/* NOTE: No TCP options attached and we never retransmit this. */

skb = alloc_skb(MAX_TCP_HEADER, priority);

if (!skb) {

NET_INC_STATS(sock_net(sk), LINUX_MIB_TCPABORTFAILED);

return;

}

/* Reserve space for headers and prepare control bits. */

skb_reserve(skb, MAX_TCP_HEADER);

tcp_init_nondata_skb(skb, tcp_acceptable_seq(sk),

TCPHDR_ACK | TCPHDR_RST);//注意传入的标志是rst,不是fin,可以具体参考tcp_send_fin 是怎么传参数的

/* Send it off. */

if (tcp_transmit_skb(sk, skb, 0, priority))

NET_INC_STATS(sock_net(sk), LINUX_MIB_TCPABORTFAILED);

TCP_INC_STATS(sock_net(sk), TCP_MIB_OUTRSTS);

}

其中关注下报文的init过程:

/* Constructs common control bits of non-data skb. If SYN/FIN is present,

* auto increment end seqno.

*/

static void tcp_init_nondata_skb(struct sk_buff *skb, u32 seq, u8 flags)

{

struct skb_shared_info *shinfo = skb_shinfo(skb);

skb->ip_summed = CHECKSUM_PARTIAL;

skb->csum = 0;

TCP_SKB_CB(skb)->tcp_flags = flags;

TCP_SKB_CB(skb)->sacked = 0;

shinfo->gso_segs = 1;

shinfo->gso_size = 0;

shinfo->gso_type = 0;

TCP_SKB_CB(skb)->seq = seq;

if (flags & (TCPHDR_SYN | TCPHDR_FIN))//我们本文的标志是 TCPHDR_ACK | TCPHDR_RST ,

seq++;//此处+1 ,但进不来 TCP_SKB_CB(skb)->end_seq = seq;//所以本文应该是传入seq是多少就发送多少 }

那么传入的seq是多少呢?

/* SND.NXT, if window was not shrunk.

* If window has been shrunk, what should we make? It is not clear at all.

* Using SND.UNA we will fail to open window, SND.NXT is out of window. :-(

* Anything in between SND.UNA...SND.UNA+SND.WND also can be already

* invalid. OK, let's make this for now:

*/

static inline __u32 tcp_acceptable_seq(const struct sock *sk)

{

const struct tcp_sock *tp = tcp_sk(sk);

if (!before(tcp_wnd_end(tp), tp->snd_nxt))

return tp->snd_nxt;

else

return tcp_wnd_end(tp);

}

注释写得比较清楚,如果窗口没有shrunk,也就是tp->snd_nxt 没有out of window 的话,则取得就是tp->snd_nxt,而这个值,就是报文长度+1了,也就是1461.

如果不是rst的方式结束,而是fin的方式结束,那么这个seq则应该为多少呢?

我们来关注下 tcp_send_fin 函数,看看它怎么使用seq的,

void tcp_send_fin(struct sock *sk)

{

struct tcp_sock *tp = tcp_sk(sk);

struct sk_buff *skb = tcp_write_queue_tail(sk);

int mss_now;

/* Optimization, tack on the FIN if we have a queue of

* unsent frames. But be careful about outgoing SACKS

* and IP options.

*/

mss_now = tcp_current_mss(sk);

if (tcp_send_head(sk) != NULL) {//说明还有空间,tcp_send_head返回值为sk->sk_send_head,

TCP_SKB_CB(skb)->tcp_flags |= TCPHDR_FIN;

TCP_SKB_CB(skb)->end_seq++;

tp->write_seq++;

} else {

/* Socket is locked, keep trying until memory is available. */

for (;;) {

skb = alloc_skb_fclone(MAX_TCP_HEADER,

sk->sk_allocation);//申请一个skb

if (skb)

break;

yield();

}

/* Reserve space for headers and prepare control bits. */

skb_reserve(skb, MAX_TCP_HEADER);

/* FIN eats a sequence byte, write_seq advanced by tcp_queue_skb(). */

tcp_init_nondata_skb(skb, tp->write_seq,

TCPHDR_ACK | TCPHDR_FIN);//fin包占用一个seq

tcp_queue_skb(sk, skb);

}

__tcp_push_pending_frames(sk, mss_now, TCP_NAGLE_OFF);

}

不管有没有空间,其实发送出去的序列号就是当前报文的下一个,因为fin也占用一个seq,所以这个seq也是上次发完的seq+报文长度+1.

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值