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包丢失

最新文章

  1. 请问如何查看mysql 的端口号?
  2. Extjs 4.2 Grid增删改及后台交互(Java)
  3. 不可变String
  4. android中的坐标系以及获取坐标的方法
  5. 【转】 Live555
  6. POJ 3243 Clever Y (求解高次同余方程A^x=B(mod C) Baby Step Giant Step算法)
  7. Couldn&#39;t get lock for %t/vertx.log
  8. IOS 客户端测试入门.pdf
  9. CppCMS URL使用
  10. 即时通信系统Openfire分析之七:集群配置
  11. Apache+PHP+MySQL+phpMyAdmin环境搭建
  12. MobaXterm
  13. Windows10远程报错:由于CredSSP加密Oracle修正
  14. 在嵌入式设备中使用 JavaScript 的前景
  15. HDU1237
  16. DNS实战--1
  17. Git简易的命令入门
  18. proposal-cancelable-promises
  19. GUI_鼠标事件
  20. 多进程vs多线程

热门文章

  1. 为了运行十年前的代码,程序员们甚至翻出了一台 1977 年的 Apple II
  2. 16.深入k8s:Informer使用及其源码分析
  3. 源码安装中./configure的使用
  4. 初探RT-Thread系统在GD32E103x芯片上的使用,点亮LED灯
  5. WSL2 + Docker + IDEA 开发到发布一步到位
  6. 线程池CachedThreadPool
  7. 创建Sqlite数据库(二)
  8. golang开发:http请求redirect的问题
  9. JavaScript实现基于数组的栈
  10. eclipse配置springMVC