tcp_v4_send_synack()用于发送SYNACK段,在tcp_v4_conn_request()中被调用。

首先调用tcp_make_synack()构造SYNACK段,主要是构造TCP报头和初始化skb中的一些字段

/*
* 该结构主要描述双方的地址、所支持的TCP选项等
tcp_request_sock包含inet_request_sock,inet_request_sock包含request_sock
*/
struct inet_request_sock {
struct request_sock req;
/*
* 本地端口号 本地IP地址 对端IP地址
*/
#define ir_loc_addr req.__req_common.skc_rcv_saddr
#define ir_rmt_addr req.__req_common.skc_daddr
#define ir_num req.__req_common.skc_num
#define ir_rmt_port req.__req_common.skc_dport
#define ir_v6_rmt_addr req.__req_common.skc_v6_daddr
#define ir_v6_loc_addr req.__req_common.skc_v6_rcv_saddr
#define ir_iif req.__req_common.skc_bound_dev_if
#define ir_cookie req.__req_common.skc_cookie
#define ireq_net req.__req_common.skc_net
#define ireq_state req.__req_common.skc_state
#define ireq_family req.__req_common.skc_family
/*
* 发送窗口扩大因子,即要把TCP首部中指定的滑动窗口大小
* 左移snd_wscale位后,作为真正的滑动窗口大小。在TCP
* 首部中,滑动窗口大小值为16位的,而snd_wscale的值最大
* 只能为14。所以,滑动窗口最大可被扩展到30位,在协议栈
* 的实际实现中,可以看到窗口大小被置为5840,扩大因子为2,
* 即实际的窗口大小为5840<<2=23360B
*/
kmemcheck_bitfield_begin(flags);
u16 snd_wscale : 4,
/*
* 接收窗口扩大因子
*/
rcv_wscale : 4,
/*
* 标识TCP段是否存在TCP时间戳选项
*/
tstamp_ok : 1,
/*
* 标识是否支持SACK,支持则该选项能出现在SYN段中
*/
sack_ok : 1,
/*
* 标识是否支持窗口扩大因子,如果支持该选项也只能出现
* 在SYN段中
*/
wscale_ok : 1,
/*
* 标志是否启用了显式拥塞通知
*/
ecn_ok : 1,
/*
* 标识已接收到第三次握手的ACK段,但是由于服务器繁忙
* 或其他原因导致未能建立起连接,此时可根据该标志重新
* 给客户端发送SYN+ACK段,再次进行连接的建立。该标志
* 的设置同时受sysctl_tcp_abort_on_overflow的控制
*/
acked : 1,
no_srccheck: 1;
kmemcheck_bitfield_end(flags);
u32 ir_mark;
//服务器端在接收到SYN后,会解析SKB中的ip选项字段,见tcp_v4_save_option
union {
struct ip_options_rcu *opt;
struct sk_buff *pktopts;
};
};
static const struct tcp_request_sock_ops tcp_request_sock_ipv4_ops = {
    .mss_clamp    =    TCP_MSS_DEFAULT,
#ifdef CONFIG_TCP_MD5SIG
    .req_md5_lookup    =    tcp_v4_md5_lookup,
    .calc_md5_hash    =    tcp_v4_md5_hash_skb,
#endif
    .init_req    =    tcp_v4_init_req,
#ifdef CONFIG_SYN_COOKIES
    .cookie_init_seq =    cookie_v4_init_sequence,
#endif
    .route_req    =    tcp_v4_route_req,
    .init_seq    =    tcp_v4_init_sequence,
    .send_synack    =    tcp_v4_send_synack,
};

/*
* 服务端用来处理客户端连接请求的函数
*/
//服务器端收到SYN后,创建连接控制块request_sock。也就是收到第一步SYN的时候只是建立的连接控制块request_sock,当收到第三次ack的时候,才创建新的struct sock
int tcp_v4_conn_request(struct sock *sk, struct sk_buff *skb)
{
/* Never answer to SYNs send to broadcast or multicast */
if (skb_rtable(skb)->rt_flags & (RTCF_BROADCAST | RTCF_MULTICAST))
goto drop; return tcp_conn_request(&tcp_request_sock_ops,
&tcp_request_sock_ipv4_ops, sk, skb); drop:
tcp_listendrop(sk);
return 0;
}
//服务器端收到SYN后,创建连接控制块request_sock
/。也就是收到第一步SYN的时候只是建立的连接控制块request_sock
,当收到第三次ack的时候,才创建新的struct sock
*/
int tcp_conn_request(struct request_sock_ops *rsk_ops,
const struct tcp_request_sock_ops *af_ops,
struct sock *sk, struct sk_buff *skb)
{
struct tcp_fastopen_cookie foc = { .len = -1 };
__u32 isn = TCP_SKB_CB(skb)->tcp_tw_isn;
struct tcp_options_received tmp_opt;
struct tcp_sock *tp = tcp_sk(sk);
struct net *net = sock_net(sk);
struct sock *fastopen_sk = NULL;
struct dst_entry *dst = NULL;
struct request_sock *req;
bool want_cookie = false;
/*如果启用了cookie机制,则会在第三步收到ACK的时候在tcp_v4_hnd_req中
的cookie_v4_check对之前发送的ack+syn进行检查,检查过程见cookie_v4_check
*/struct flowi fl; /* TW buckets are converted to open requests without
* limitations, they conserve resources and peer is
* evidently real one.
*/
*/
/*
* 如果SYN请求连接队列已满并且isn为零,则需做特别处理。
* 这里的isn就是TCP_SKB_CB(skb)->when,而TCP_SKB_CB(skb)->when
* 在TCP接收处理一开始就被清零,因此这里isn为零总是成立
*/
if ((net->ipv4.sysctl_tcp_syncookies == 2 ||//sysctl_tcp_syncookies=2无条件生成syncookie
inet_csk_reqsk_queue_is_full(sk)) && !isn) {//或者请求队列太长, 并且当前不是timewait
want_cookie = tcp_syn_flood_action(sk, skb, rsk_ops->slab_name);//sysctl_tcp_syncookies>0, 并未当前socket打印一次告警
if (!want_cookie)//队列满了,但不使用syncookie,则丢弃
goto drop;
} /* Accept backlog is full. If we have already queued enough
* of warm entries in syn queue, drop request. It is better than
* clogging syn queue with openreqs with exponentially increasing
* timeout.
*/
/*
* 如果连接队列长度已达到上限且SYN请求队列中至少有一个握手过程中
* 没有重传过的段,则丢弃当前连接请求.
* 如果半连接队列中未重传的请求块数量大于1,
* 则表示未来可能有2个完成的连接,这些新完成
* 的连接要放到连接队列中,但此时连接队列已满
* 。如果在接收到三次握手中最后的ACK后连接队列
* 中没有空闲的位置,会忽略接收到的ACK包,连接
* 建立会推迟,所以此时最好丢掉部分新的连接请
* 求,空出资源以完成正在进行的连接建立过程。
* 还要注意,这个判断并没有考虑半连接队列是否
* 已满的问题。从这里可以看出,即使开启了
* SYN cookies机制并不意味着一定可以完成连接的建立。
* static inline int inet_csk_reqsk_queue_young(const struct sock *sk)
{
    return reqsk_queue_len_young(&inet_csk(sk)->icsk_accept_queue);
}static inline int reqsk_queue_len_young(const struct request_sock_queue *queue)
{
    return atomic_read(&queue->young);
}
static inline bool sk_acceptq_is_full(const struct sock *sk)
{
    return sk->sk_ack_backlog > sk->sk_max_ack_backlog;
}

*/
if (sk_acceptq_is_full(sk) && inet_csk_reqsk_queue_young(sk) > 1) {//accept队列满,但是syn队列依然有可能被accept的连接,此时丢弃
NET_INC_STATS(sock_net(sk), LINUX_MIB_LISTENOVERFLOWS);
goto drop;
} /*
* 可以接收并处理连接请求,调用inet_reqsk_alloc()分配一个连接请求
* 块,用于保存连接请求信息,同时初始化在建立连接过程中用来发送
* ACK、RST段的操作集合,以便在建立连接过程中能方便地调用这些接口
*/
//rsk_ops ===tcp_request_sock_ops
req = inet_reqsk_alloc(rsk_ops, sk, !want_cookie);//分配request_sock, 进入TCP_NEW_SYN_RECV状态
if (!req)
goto drop;
//af_ops====tcp_request_sock_ipv4_opss
tcp_rsk(req)->af_specific = af_ops;//tcp_request_sock_ipv4_ops
/*
* 清除TCP选项后初始化mss_clamp和user_mss。
*/
tcp_clear_options(&tmp_opt);
tmp_opt.mss_clamp = af_ops->mss_clamp;//TCP_MSS_DEFAULT=536
tmp_opt.user_mss = tp->rx_opt.user_mss;//listen sock设置的或是tw的 /*
* 解析SYN段中的TCP选项
*/
tcp_parse_options(skb, &tmp_opt, 0, want_cookie ? NULL : &foc);//开启syncookie后则不用考虑fastopen, syncookie不允许使用tcp扩展 if (want_cookie && !tmp_opt.saw_tstamp) //开启syncookie,但是不带timestamp
tcp_clear_options(&tmp_opt);//清除wscale,sack_ok等选项,因为没地方存
/*
* 初始化该连接中是否启用时间戳的选项tstamp_ok
*/
tmp_opt.tstamp_ok = tmp_opt.saw_tstamp;
/*
* 根据接收到SYN段中的选项和序号来初始化连接请求块信息
*/
tcp_openreq_init(req, &tmp_opt, skb, sk); /* Note: tcp_v6_init_req() might override ir_iif for link locals */
inet_rsk(req)->ir_iif = inet_request_bound_dev_if(sk, skb);
/*
* 初始化TCP层次的连接请求信息块,包括目的地址、源地址,
* 并调用tcp_v4_save_options从IP层私有控制块中获取IP
* 选项保存到传输控制块的opt中,包括MSS、窗口扩大
* 因子、显式拥塞通知等
*/
af_ops->init_req(req, sk, skb);//tcp_v4_init_req 会调用tcp_v4_save_options if (security_inet_conn_request(sk, skb, req))
goto drop_and_free; if (!want_cookie && !isn) {//不需要生成syncookie,也不是从timewait recycle的新的sock
/* VJ's idea. We save last timestamp seen
* from the destination in peer table, when entering
* state TIME-WAIT, and check against it before
* accepting new connection request.
*
* If "isn" is not zero, this request hit alive
* timewait bucket, so that all the necessary checks
* are made in the function processing timewait state.
*/
/*
* 进入TIMEWAIT状态时,从对端信息块中获取时间戳,在新的
* 连接请求之前检测PAWS
*/
if (tcp_death_row.sysctl_tw_recycle) {
bool strict; dst = af_ops->route_req(sk, &fl, req, &strict); //tcp_v4_route_req
//当起了快速回收tw_recycle的时候,这里可能有问题,可能连接建立不上,针对TCP时间戳PAWS漏洞的代码。 见:http://blog.chinaunix.net/uid-736168-id-376061.html
//针对TCP时间戳PAWS漏洞,造成服务器端收到SYN的时候不回收SYN+ACK,解决办法是对方不要发送时间戳选项,同时关闭tcp_timestamps见tcp_v4_conn_request
if (dst && strict &&
!tcp_peer_is_proven(req, dst, true,
tmp_opt.saw_tstamp)) {
NET_INC_STATS(sock_net(sk), LINUX_MIB_PAWSPASSIVEREJECTED);
goto drop_and_release;
/*
1 tcp的option有 time stamp字段. 2 tcp_tw_recycle有设置。 3 在路由表中是否存在完全相同的流(如果打开了xfrm的话,
还要比较端口,默认xfrm应该是打开的),如果存在则直接返回. 4 并且数据包的源地址和新请求的源地址相同. 5 根据路由表以及源地址能够查找到保存的peer
(这个可以看我以前的blog,也就是保存了一些连接统计信息). 6 当前时间(接收到syn)比最后一次的时间(time stamp)小于60秒. 7 已经存在peer的最近一次时间戳要大于当前请求进来的时间戳.
从上面可以看到,上面的条件中1/2都是 server端可以控制的,而其他的条件,
都是很容易就满足的,因此我们举个例子。 如果客户端是NAT出来的,并且我们server端有打开tcp_tw_recycle ,
并且time stamp也没有关闭,那么假设第一个连接进来,然后关闭,此时这个句柄处于time wait状态,然后很快(小于60秒)又一个客户端(相同的源地址,如果打开了xfrm还要相同的端口号)发一个syn包,此时linux内核就会认为这个数据包异常的,因此就会丢掉这个包,并发送rst。 而现在大部分的客户端都是NAT出来的,因此建议tw_recycle还
是关闭,或者说server段关闭掉time stamp(/proc/sys/net/ipv4/tcp_timestamps).
*/
}
}
/* Kill the following clause, if you dislike this way. */
//如果没开启sysctl_tw_recycle和syncookie,最后1/4的syn请求需要验证过去的连接信?
/*
* 未启动syncookies的情况下受到synflood攻击,则丢弃接收到的段
*/
else if (!net->ipv4.sysctl_tcp_syncookies &&
(sysctl_max_syn_backlog - inet_csk_reqsk_queue_len(sk) <
(sysctl_max_syn_backlog >> 2)) &&
!tcp_peer_is_proven(req, dst, false,
tmp_opt.saw_tstamp)) {//如果不存在tcp metric或者过去的连接信息则丢弃
/* Without syncookies last quarter of
* backlog is filled with destinations,
* proven to be alive.
* It means that we continue to communicate
* to destinations, already remembered
* to the moment of synflood.
*/
pr_drop_req(req, ntohs(tcp_hdr(skb)->source),
rsk_ops->family);
goto drop_and_release;
} isn = af_ops->init_seq(skb);//tcp_v4_init_sequence,根据四元组,随机数,当前高精度时间来生成isn
}
if (!dst) {
dst = af_ops->route_req(sk, &fl, req, NULL);//tcp_v4_route_req
if (!dst)
goto drop_and_free;
} tcp_ecn_create_request(req, skb, sk, dst); if (want_cookie) {
/*
* 如果启动了syncookies,则每60秒警告一次可能受
* synflood攻击,同时由客户端IP地址、客户端端口、
* 服务器IP地址、服务器端口、客户端初始序列号
* 等要素经hash运算后加密得到服务端初始化序列号
*/
//如果开启了syncookie选项,则需要检查收到的第三步ack和这个isn值是否一致
isn = cookie_init_sequence(af_ops, sk, skb, &req->mss);
//cookie_v4_init_sequence生成syncookie,并作为ack的起始序号
req->cookie_ts = tmp_opt.tstamp_ok;
if (!tmp_opt.tstamp_ok)
inet_rsk(req)->ecn_ok = 0;
} tcp_rsk(req)->snt_isn = isn;
tcp_rsk(req)->txhash = net_tx_rndhash();
tcp_openreq_init_rwin(req, sk, dst);//设置初始化rwnd
if (!want_cookie) {
tcp_reqsk_record_syn(sk, req, skb);//如果设置保存TCP_SAVE_SYN标记,则保存
fastopen_sk = tcp_try_fastopen(sk, skb, req, &foc, dst);
//验证后创建fastopen sock,并把数据部分放入接收队列中
}
if (fastopen_sk) {//验证并创建fastsocket成功, 进入TCP_SYN_RCV状态
af_ops->send_synack(fastopen_sk, dst, &fl, req,
&foc, TCP_SYNACK_FASTOPEN);//tcp_v4_send_synac
/* Add the child socket directly into the accept queue */
inet_csk_reqsk_queue_add(sk, req, fastopen_sk);//添加到等待accept的队列
sk->sk_data_ready(sk);
bh_unlock_sock(fastopen_sk);
sock_put(fastopen_sk);
} else {
tcp_rsk(req)->tfo_listener = false;
/*
* 将连接请求块保存到其父传输控制块中的散列表中
*/
if (!want_cookie)
inet_csk_reqsk_queue_hash_add(sk, req, TCP_TIMEOUT_INIT);//插入ehash,并设置定时器 /*
* 调用__tcp_v4_send_synack()组织并发送SYN+ACK段给客户端;如果
* 启用了syncookies,则是根据序号来判断三次握手的,因此无需保存
* 连接请求,直接将其释放
*/ //如果是cookie,则真正的tcp_request_sock在第三步ack的时候在cookie_v4_check中创建
af_ops->send_synack(sk, dst, &fl, req, &foc,
!want_cookie ? TCP_SYNACK_NORMAL :
TCP_SYNACK_COOKIE);//tcp_v4_send_synack
if (want_cookie) {
reqsk_free(req);//启用syncookie的话,可以直接释放req
return 0;
}
}
reqsk_put(req);
return 0; drop_and_release:
dst_release(dst);
drop_and_free:
reqsk_free(req);
drop:
tcp_listendrop(sk);
return 0;
/*
* Send a SYN-ACK after having received a SYN.
* This still operates on a request_sock only, not on a big
* socket.
*/
/*
synack发送
tcp_make_synack主要是根据需要设置synack的tcp选项, 使用syncookie的时候服务端不保存状态,会把tcp扩展项编码到timestamp中,把syncookie作为seq回传;
对于fastopen请求,则会设置好fastopen cookie tcp选项回传,并对接收到的数据部分进行ack
因为synack不用分片并且必须有路由缓存,直接调用ip_build_and_send_pkt()来发送
*/
static int tcp_v4_send_synack(const struct sock *sk, struct dst_entry *dst,
struct flowi *fl,
struct request_sock *req,
struct tcp_fastopen_cookie *foc,
enum tcp_synack_type synack_type)
{
const struct inet_request_sock *ireq = inet_rsk(req);
struct flowi4 fl4;
int err = -1;
struct sk_buff *skb; /* First, grab a route. */ /* 路由为空则查路由 */
if (!dst && (dst = inet_csk_route_req(sk, &fl4, req)) == NULL)
return -1;
/* 构造syn+ack包 */
skb = tcp_make_synack(sk, dst, req, foc, synack_type); if (skb) {/* 生成校验码 */
__tcp_v4_send_check(skb, ireq->ir_loc_addr, ireq->ir_rmt_addr); err = ip_build_and_send_pkt(skb, sk, ireq->ir_loc_addr,
ireq->ir_rmt_addr,
ireq->opt);
err = net_xmit_eval(err);
} return err;
}
/**
* tcp_make_synack - Prepare a SYN-ACK.
* sk: listener socket
* dst: dst entry attached to the SYNACK
* req: request_sock pointer
*
* Allocate one skb and build a SYNACK packet.
* @dst is consumed : Caller should not use it again.
*/
struct sk_buff *tcp_make_synack(const struct sock *sk, struct dst_entry *dst,
struct request_sock *req,
struct tcp_fastopen_cookie *foc,
enum tcp_synack_type synack_type)
{
struct inet_request_sock *ireq = inet_rsk(req);
const struct tcp_sock *tp = tcp_sk(sk);
struct tcp_md5sig_key *md5 = NULL;
struct tcp_out_options opts;
struct sk_buff *skb;
int tcp_header_size;
struct tcphdr *th;
u16 user_mss;
int mss;
/* 分配skb用于发送SYNACK **/
skb = alloc_skb(MAX_TCP_HEADER, GFP_ATOMIC);
if (unlikely(!skb)) {
dst_release(dst);
return NULL;
}
/* Reserve space for headers. */ /*
拓展headroom,为MAC、IP、TCP协议头预留空间
保留头部空间 */
skb_reserve(skb, MAX_TCP_HEADER); switch (synack_type) {
case TCP_SYNACK_NORMAL:/* skb关联控制块 */
skb_set_owner_w(skb, req_to_sk(req));
break;
case TCP_SYNACK_COOKIE:
/* Under synflood, we do not attach skb to a socket,
* to avoid false sharing.
*/
break;
case TCP_SYNACK_FASTOPEN:
/* sk is a const pointer, because we want to express multiple
* cpu might call us concurrently.
* sk->sk_wmem_alloc in an atomic, we can promote to rw.
*/
skb_set_owner_w(skb, (struct sock *)sk);
break;
}
/* 保存路由缓存的地址 */
skb_dst_set(skb, dst); /* 设置路由缓存 */
/* 从路由缓存中获取本端的通告MSS */
mss = dst_metric_advmss(dst); /* mss取从路由表中查询的mss与user_mss之间的较小值 */
user_mss = READ_ONCE(tp->rx_opt.user_mss);
if (user_mss && user_mss < mss)
mss = user_mss; memset(&opts, 0, sizeof(opts));
#ifdef CONFIG_SYN_COOKIES
if (unlikely(req->cookie_ts))
skb->skb_mstamp.stamp_jiffies = cookie_init_timestamp(req);
else
#endif
skb_mstamp_get(&skb->skb_mstamp); /* 获取时间戳 */ #ifdef CONFIG_TCP_MD5SIG
rcu_read_lock();
md5 = tcp_rsk(req)->af_specific->req_md5_lookup(sk, req_to_sk(req));
#endif
skb_set_hash(skb, tcp_rsk(req)->txhash, PKT_HASH_TYPE_L4);
tcp_header_size = tcp_synack_options(req, mss, skb, &opts, md5, foc) +
sizeof(*th);/* 设置tcp选项 */
/* 构造填充tcp头 */
skb_push(skb, tcp_header_size);
skb_reset_transport_header(skb); th = (struct tcphdr *)skb->data;
memset(th, 0, sizeof(struct tcphdr));
th->syn = 1;
th->ack = 1;
tcp_ecn_make_synack(req, th);
th->source = htons(ireq->ir_num);/* 源端口 */
th->dest = ireq->ir_rmt_port;/* 目的端口 */
/* Setting of flags are superfluous here for callers (and ECE is
* not even correctly set)
*//* 初始化skb中的一些控制字段 */
tcp_init_nondata_skb(skb, tcp_rsk(req)->snt_isn,
TCPHDR_SYN | TCPHDR_ACK); th->seq = htonl(TCP_SKB_CB(skb)->seq);
/* XXX data is queued and acked as is. No buffer/window check */
th->ack_seq = htonl(tcp_rsk(req)->rcv_nxt); /* RFC1323: The window in SYN & SYN/ACK segments is never scaled. */
th->window = htons(min(req->rsk_rcv_wnd, 65535U)); /* 设置窗口 */
/* 把TCP选项实例tcp_out_options写到skb中 */
tcp_options_write((__be32 *)(th + 1), NULL, &opts);
th->doff = (tcp_header_size >> 2); /* 设置首部长度 */
__TCP_INC_STATS(sock_net(sk), TCP_MIB_OUTSEGS); #ifdef CONFIG_TCP_MD5SIG
/* Okay, we have all we need - do the md5 hash if needed */
if (md5)
tcp_rsk(req)->af_specific->calc_md5_hash(opts.hash_location,
md5, req_to_sk(req), skb);
rcu_read_unlock();
#endif /* Do not fool tcpdump (if any), clean our debris */
skb->tstamp.tv64 = 0;
return skb;

如果SYNACK段使用SYN Cookie,并且使用时间戳选项,则把TCP选项信息保存在SYNACK段中tsval的低6位

/*
* when syncookies are in effect and tcp timestamps are enabled we encode
* tcp options in the lower bits of the timestamp value that will be
* sent in the syn-ack.
* Since subsequent timestamps use the normal tcp_time_stamp value, we
* must make sure that the resulting initial timestamp is <= tcp_time_stamp.
*/
/* There is no TS_OPT_TIMESTAMP:
* if ACK contains timestamp option, we already know it was
* requested/supported by the syn/synack exchange.
*/
#define TSBITS 6
#define TSMASK (((__u32)1 << TSBITS) - 1) __u32 cookie_init_timestamp(struct request_sock *req)
{
struct inet_request_sock *ireq;
u32 ts, ts_now = tcp_time_stamp;
u32 options = 0; ireq = inet_rsk(req); options = ireq->wscale_ok ? ireq->snd_wscale : TS_OPT_WSCALE_MASK;
if (ireq->sack_ok)
options |= TS_OPT_SACK;
if (ireq->ecn_ok)
options |= TS_OPT_ECN; ts = ts_now & ~TSMASK;
ts |= options;
if (ts > ts_now) {
ts >>= TSBITS;
ts--;
ts <<= TSBITS;
ts |= options;
}
return ts;
}

赋值TCP选项实例tcp_out_options,用于构造SYNACK段。

/* Set up TCP options for SYN-ACKs. */
static unsigned int tcp_synack_options(struct request_sock *req,
unsigned int mss, struct sk_buff *skb,
struct tcp_out_options *opts,
const struct tcp_md5sig_key *md5,
struct tcp_fastopen_cookie *foc)
{
struct inet_request_sock *ireq = inet_rsk(req);
unsigned int remaining = MAX_TCP_OPTION_SPACE; #ifdef CONFIG_TCP_MD5SIG
if (md5) {
opts->options |= OPTION_MD5;
remaining -= TCPOLEN_MD5SIG_ALIGNED; /* We can't fit any SACK blocks in a packet with MD5 + TS
* options. There was discussion about disabling SACK
* rather than TS in order to fit in better with old,
* buggy kernels, but that was deemed to be unnecessary.
*/
ireq->tstamp_ok &= !ireq->sack_ok;
}
#endif /* We always send an MSS option. */
opts->mss = mss;/* Max Segment Size选项 */
remaining -= TCPOLEN_MSS_ALIGNED; if (likely(ireq->wscale_ok)) {/* Window Scaling选项 */
opts->ws = ireq->rcv_wscale;
opts->options |= OPTION_WSCALE;
remaining -= TCPOLEN_WSCALE_ALIGNED;
}
if (likely(ireq->tstamp_ok)) {/* 时间戳选项 */
opts->options |= OPTION_TS;
opts->tsval = tcp_skb_timestamp(skb);
opts->tsecr = req->ts_recent;
remaining -= TCPOLEN_TSTAMP_ALIGNED;
}
if (likely(ireq->sack_ok)) {/* SACK Permit选项 */
opts->options |= OPTION_SACK_ADVERTISE;
if (unlikely(!ireq->tstamp_ok))
remaining -= TCPOLEN_SACKPERM_ALIGNED;
}
if (foc != NULL && foc->len >= 0) {
u32 need = foc->len; need += foc->exp ? TCPOLEN_EXP_FASTOPEN_BASE :
TCPOLEN_FASTOPEN_BASE;
need = (need + 3) & ~3U; /* Align to 32 bits */
if (remaining >= need) {
opts->options |= OPTION_FAST_OPEN_COOKIE;
opts->fastopen_cookie = foc;
remaining -= need;
}
} return MAX_TCP_OPTION_SPACE - remaining;/* TCP选项长度 */

server端TCP在收到SYN请求后进行的处理总结如下:
1、创建一个比较小的数据结构request_sock并保存连接信息
2、将request_sock加入到syn table中,以便ACK到来时能够找到相应的连接信息
3、创建SYN|ACK包并发送出去,同时设置重传定时器以避免SYN|ACK包丢失

tcp syn-synack-ack 服务端发送syn-ack的更多相关文章

  1. TCP服务端收到syn但是不回复syn ack问题分析

    文章转载自:https://blog.csdn.net/jueshengtianya/article/details/52130667 最近在分析客户的一个问题时遇到了一种奇怪的情况,客户在服务端开启 ...

  2. tcp syn-synack-ack 服务端 接收 SYN tcp_v4_do_rcv分析

    rcv 分析: /* The socket must have it's spinlock held when we get * here, unless it is a TCP_LISTEN soc ...

  3. 记一次压测问题定位:connection reset by peer,TCP三次握手后服务端发送RST_网络_c359719435的专栏-CSDN博客 https://blog.csdn.net/c359719435/article/details/80300433

    记一次压测问题定位:connection reset by peer,TCP三次握手后服务端发送RST_网络_c359719435的专栏-CSDN博客 https://blog.csdn.net/c3 ...

  4. 异步tcp通信——APM.Core 服务端概述

    为什么使用异步 异步线程是由线程池负责管理,而多线程,我们可以自己控制,当然在多线程中我们也可以使用线程池.就拿网络扒虫而言,如果使用异步模式去实现,它使用线程池进行管理.异步操作执行时,会将操作丢给 ...

  5. 网络编程 UDP协议 TCP局域网客户端与服务端上传下载电影示例

    UDP协议 (了解) 称之为数据包协议,又称不可靠协议. 特点: 1) 不需要建立链接. 2) 不需要知道对方是否收到. 3) 数据不安全 4) 传输速度快 5)能支持并发 6) 不会粘包 7) 无需 ...

  6. Query通过Ajax向PHP服务端发送请求并返回JSON数据

    Query通过Ajax向PHP服务端发送请求并返回JSON数据 服务端PHP读取MYSQL数据,并转换成JSON数据,传递给前端Javascript,并操作JSON数据.本文将通过实例演示了jQuer ...

  7. Diango之通过form表单向服务端发送数据

    通过form表单向服务端发送数据 表单元素 表单:form></form>表单用于向服务器传输数据.另外一种向服务端传输数据的方式为ajax. form属性: action:提交表单 ...

  8. jQuery通过Ajax向PHP服务端发送请求并返回JSON数据

    SON(JavaScript Object Notation) 是一种轻量级的数据交换格式.易于人阅读和编写,同时也易于机器解析和生成.JSON在前后台交互的过程中发挥着相当出色的作用.请接着往下看教 ...

  9. socket小程序写一个客户端,实现给服务端发送hello World字符串,将客户端发送的数据变成大写后返回

    写一个客户端,实现给服务端发送hello World字符串,将客户端发送的数据变成大写后返回 本机id是192.168.xx.xy 服务端 import socket soc = socket.soc ...

  10. TCP协议下的服务端并发,GIL全局解释器锁,死锁,信号量,event事件,线程q

    TCP协议下的服务端并发,GIL全局解释器锁,死锁,信号量,event事件,线程q 一.TCP协议下的服务端并发 ''' 将不同的功能尽量拆分成不同的函数,拆分出来的功能可以被多个地方使用 TCP服务 ...

随机推荐

  1. Dubbo部分知识点总结

    Dubbo部分 Dubbo工作原理 dubbo工作原理第一层:service层,接口层,给服务提供者和消费者来实现的第二层:config层,配置层,主要是对dubbo进行各种配置的第三层:proxy层 ...

  2. MeteoInfoLab脚本示例:FY-3C全球火点HDF数据

    FY-3C全球火点HDF数据包含一个FIRES二维变量,第一维是火点数,第二维是一些属性,其中第3.4列分别是火点的纬度和经度.下面的脚本示例读出所有火点经纬度并绘图.脚本程序: #Add data ...

  3. Hybrid App中原生页面 VS H5页面

    Hybrid App中原生页面 VS H5页面   现有3类主流APP,分别为:Web App.Hybrid App(混合模式移动应用,Hybrid有"混合的"意思). Nativ ...

  4. docker-阿里云加速

    系统版本 centos7 阿里云登录 ->容器镜像服务->镜像加速器 复制下面的直接执行即可     sudo mkdir -p /etc/docker sudo tee /etc/doc ...

  5. Golang数组和切片的区别

    大纲 数组是固定大小 切片不是动态数组,可以扩容 区别 定义方式不一样 初始化方法不一样 package main import "fmt" func main() { // -- ...

  6. nginx安全:配置ssl证书(https证书)

    一,配置https证书的意义 https协议是由SSL+http协议构建的安全协议,支持加密传输和身份认证, 安全性比http要更好,因为数据的加密传输,更能保证数据的安全性和完整性 例如:不使用ht ...

  7. java之集合容器(Collection,Map)

    首先我们要了解什么是集合? 正所谓容器,比如说杯子是装水的容器,衣柜是装衣服的容器,那么集合就是装数据的容器. 集合有什么特点呢? 1.集合长度是可变的 2.集合用来存储对象 集合和数组有什么区别呢? ...

  8. 使用ML.NET模型生成器来完成图片性别识别

    什么是ML.NET? ML.NET 使你能够在联机或脱机场景中将机器学习添加到 .NET 应用程序中. 借助此功能,可以使用应用程序的可用数据进行自动预测. 机器学习应用程序利用数据中的模式来进行预测 ...

  9. D. Maximum Distributed Tree 解析(思維、DFS、組合、貪心、DP)

    Codeforce 1401 D. Maximum Distributed Tree 解析(思維.DFS.組合.貪心.DP) 今天我們來看看CF1401D 題目連結 題目 直接看原題比較清楚,略. 前 ...

  10. exe打包成安装文件(界面美观)

    前言 在开发windows桌面应用过程中,软件交付时,一般都是交付安装包. 安装文件的优点 显得更正规,安装界面也可展示软件特点介绍,可自动生成桌面图标等: 安装包体积要比软件小,方便下载. 探索之路 ...