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