假定客户端执行主动打开,发送syn包到服务器,服务器执行完该包的第一次握手操作后,调用af_ops->send_synack向客户端发送syn+ack包,该回调实际调用tcp_v4_send_synack函数;
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)
{
/* 第一次握手的服务器处理 */ /* 发送syn+ack */
af_ops->send_synack(sk, dst, &fl, req, &foc,
!want_cookie ? TCP_SYNACK_NORMAL :
TCP_SYNACK_COOKIE);
return ;
}
tcp_v4_send_synack完成路由查找,构造syn+ack包,构造ip包,然后发送出去;
/*
* Send a SYN-ACK after having received a SYN.
* This still operates on a request_sock only, not on a big
* socket.
*/
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 = -;
struct sk_buff *skb; /* First, grab a route. */
/* 路由为空则查路由 */
if (!dst && (dst = inet_csk_route_req(sk, &fl4, req)) == NULL)
return -; /* 构造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); /* 构造ip包并发送 */
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函数完成skb分配,tcp首部的构造;
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;
int mss; /* 分配skb */
skb = alloc_skb(MAX_TCP_HEADER, GFP_ATOMIC);
if (unlikely(!skb)) {
dst_release(dst);
return NULL;
}
/* Reserve space for headers. */
/* 保留头部空间 */
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与user_mss之间的较小值 */
mss = tcp_mss_clamp(tp, dst_metric_advmss(dst)); memset(&opts, , 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 /* 设置hash */
skb_set_hash(skb, tcp_rsk(req)->txhash, PKT_HASH_TYPE_L4);
/* 设置tcp选项 */
tcp_header_size = tcp_synack_options(req, mss, skb, &opts, md5, foc) +
sizeof(*th); /* 构造填充tcp头 */
skb_push(skb, tcp_header_size);
skb_reset_transport_header(skb); th = (struct tcphdr *)skb->data;
memset(th, , sizeof(struct tcphdr));
/* 设置syn+ack标记 */
th->syn = ;
th->ack = ;
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,标志为syn+ack */
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_options_write((__be32 *)(th + ), NULL, &opts); /* 设置首部长度 */
th->doff = (tcp_header_size >> );
__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 = ;
return skb;
}