本文主要分析:服务器端接收到SYN包时的处理路径。
内核版本:3.6
Author:zhangskd @ csdn blog
接收入口
1. 状态为ESTABLISHED时,用tcp_rcv_established()接收处理。
2. 状态为LISTEN时,说明这个sock处于监听状态,用于被动打开的接收处理,包括SYN和ACK。
3. 当状态不为ESTABLISHED或TIME_WAIT时,用tcp_rcv_state_process()处理。
-
int tcp_v4_do_rcv(struct sock *sk, struct sk_buff *skb)
-
{
-
struct sock *rsk;
-
-
#ifdef CONFIG_TCP_MD5SIG
-
-
-
-
-
if (tcp_v4_inbound_md5_hash(sk, skb))
-
goto discard;
-
#endif
-
-
-
if (sk->sk_state == TCP_ESTABLISHED) {
-
struct dst_entry *dst = sk->sk_rx_dst;
-
sock_rps_save_rxhash(sk, skb);
-
-
if (dst) {
-
if (inet_sk(sk)->rx_dst_ifindex != skb->skb_iif || dst->ops->check(dst, 0) == NULL) {
-
dst_release(dst);
-
sk->sk_rx_dst = NULL;
-
}
-
}
-
-
-
if (tcp_rcv_established(sk, skb, tcp_hdr(skb), skb->len)) {
-
rsk = sk;
-
goto reset;
-
}
-
return 0;
-
}
-
-
-
if (skb->len < tcp_hdrlen(skb) || tcp_checksum_complete(skb))
-
goto csum_err;
-
-
-
if (sk->sk_state == TCP_LISTEN) {
-
-
-
-
-
-
struct sock *nsk = tcp_v4_hnd_req(sk, skb);
-
-
if (! nsk)
-
goto discard;
-
-
if (nsk != sk) {
-
sock_rps_save_rxhash(nsk, skb);
-
-
if (tcp_child_process(sk, nsk, skb)) {
-
rsk = nsk;
-
goto reset;
-
}
-
return 0;
-
}
-
} else
-
sock_rps_save_rx(sk, skb);
-
-
-
if (tcp_rcv_state_process(sk, skb, tcp_hdr(skb), skb->len)) {
-
rsk = sk;
-
goto reset;
-
}
-
return 0;
-
-
reset:
-
tcp_v4_send_reset(rsk, skb);
-
-
discard:
-
kfree_skb(skb);
-
return 0;
-
-
csum_err:
-
TCP_INC_STATS_BH(sock_net(sk), TCP_MIB_INERRS);
-
goto discard;
-
}
当收到客户端发送的SYN包时,会进入tcp_rcv_state_process()进行处理。
-
-
-
-
-
-
-
int tcp_rcv_state_process(struct sock *sk, struct sk_buff *skb, const struct tcphdr *th,
-
unsigned int len)
-
{
-
struct tcp_sock *tp = tcp_sk(sk);
-
struct inet_connection_sock *icsk = inet_csk(sk);
-
int queued = 0;
-
-
tp->rx_opt.saw_tstamp = 0;
-
-
switch(sk->sk_state) {
-
case TCP_CLOSE:
-
goto discard;
-
-
case TCP_LISTEN:
-
-
-
-
if (th->ack)
-
return 1;
-
-
if (th->rst)
-
goto discard;
-
-
if (th->syn) {
-
if (th->fin)
-
goto discard;
-
-
-
if (icsk->icsk_af_ops->conn_request(sk, skb) < 0)
-
return 1;
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
kfree_skb(skb);
-
return 0;
-
}
-
-
goto discard;
-
...
-
}
-
...
-
discard:
-
__kfree_skb(skb);
-
}
-
return 0;
-
}
处理SYN包
SYN包的处理是地址族相关的,我们要研究的是IPv4。
-
-
-
-
-
struct inet_connection_sock_af_ops {
-
...
-
int (*conn_request) (struct sock *sk, struct sk_buff *skb);
-
...
-
};
-
-
const struct inet_connection_sock_af_ops ipv4_specific = {
-
...
-
.conn_request = tcp_v4_conn_request,
-
...
-
};
服务器端处理接收到的SYN包。
-
int tcp_v4_conn_request(struct sock *sk, struct sk_buff *skb)
-
{
-
struct tcp_extend_values tmp_ext;
-
struct tcp_options_received tmp_opt;
-
const u8 *hash_location;
-
struct request_sock *req;
-
struct inet_request_sock *ireq;
-
struct tcp_sock *tp = tcp_sk(sk);
-
struct dst_entry *dst = NULL;
-
__be32 saddr = ip_hdr(skb)->saddr;
-
__be32 daddr = ip_hdr(skb)->daddr;
-
__u32 isn = TCP_SKB_CB(skb)->when;
-
bool want_cookie = false;
-
-
-
-
-
if (skb_rtable(skb)->rt_flags & (RTCF_BROADCAST | RTCF_MULTICAST))
-
goto drop;
-
-
-
-
-
if (inet_csk_reqsk_queue_is_full(sk) && ! isn) {
-
-
-
want_cookie = tcp_syn_flood_action(sk, skb, "TCP");
-
-
if (! want_cookie)
-
goto drop;
-
}
-
-
-
-
-
-
-
if (sk_acceptq_is_full(sk) && inet_csk_reqsk_queue_young(sk) > 1)
-
goto drop;
-
-
-
req = inet_reqsk_alloc(&tcp_request_sock_ops);
-
if (! req)
-
goto drop;
-
-
#ifdef CONFIG_TCP_MD5SIG
-
tcp_rsk(req)->af_specific = &tcp_request_sock_ipv4_ops;
-
#endif
-
-
tcp_clear_options(&tmp_opt);
-
tmp_opt.mss_clamp = TCP_MSS_DEFAULT;
-
tmp_opt.user_mss = tp->rx_opt.user_mss;
-
tcp_parse_options(skb, &tmp_opt, &hash_location, 0, NULL);
-
-
-
-
-
-
if (tmp_opt.cookie_plus > 0 && tmp_opt.saw_tstamp && ! tp->rx_opt.cookie_out_never &&
-
(sysctl_tcp_cookie_size > 0 || (tp->cookie_values != NULL &&
-
tp->cookie_values->cookie_desired > 0))) {
-
u8 *c;
-
u32 *mess = &tmp_ext.cookie_bakery[COOKIE_DIGEST_WORDS];
-
int l = tmp_opt.cookie_plus - TCPOLEN_COOKIE_BASE;
-
-
if (tcp_cookie_generator(&tmp_ext.cookie_bakery[0]) != 0)
-
goto drop_and_release;
-
-
-
*mess++ ^= (__force u32) daddr;
-
*mess++ ^= (__force u32) saddr;
-
-
-
c = (u8 *) mess;
-
while (l-- > 0)
-
*c++ ^= *hash_location++;
-
-
want_cookie = false;
-
tmp_ext.cookie_out_never = 0;
-
tmp_ext.cookie_plus = tmp_opt.cookie_plus;
-
} else if (! tp->rx_opt.cookie_in_always) {
-
-
tmp_ext.cookie_out_never = 1;
-
tmp_ext.cookie_plus = 0;
-
} else {
-
goto drop_and_release;
-
}
-
-
tmp_ext.cookie_in_always = tp->rx_opt.cookie_in_always;
-
-
-
-
if (want_cookie && ! tmp_opt.saw_tstamp)
-
tcp_clear_options(&tmp_opt);
-
-
tmp.tstamp_ok = tmp_opt.saw_tstamp;
-
-
-
tcp_openreq_init(req, &tmp_opt, skb);
-
ireq->loc_addr = daddr;
-
ireq->rmt_addr = saddr;
-
ireq->no_srccheck = inet_sk(sk)->transparent;
-
ireq->opt = tcp_v4_save_options(sk, skb);
-
-
if (security_inet_conn_request(sk, skb, req))
-
goto drop_and_free;
-
-
-
if (! want_cookie || tmp_opt.tstamp_ok)
-
TCP_ECN_create_request(req, skb);
-
-
if (want_cookie) {
-
isn = cookie_v4_init_sequence(sk, skb, &req->mss);
-
req->cookie_ts = tmp_opt.tstamp_ok;
-
-
} else if (! isn) {
-
struct flowi4 fl4;
-
-
-
-
-
-
-
-
-
if (tmp_opt.saw_tstamp && tcp_death_row.sysctl_tw_recycle &&
-
(dst = inet_csk_route_req(sk, &fl4, req)) != NULL && fl4.daddr = saddr) {
-
if (! tcp_peer_is_proven(req, dst, true)) {
-
NET_INC_STATS_BH(sock_net(sk), LINUX_MIB_PAWSPASSIVEREJECTED);
-
goto drop_and_release;
-
}
-
} else if (! 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)) {
-
-
-
-
-
LIMIT_NETDEBUG(KERN_DEBUG pr_fmt("drop open request from %pI4/%u\n"), &saddr,
-
ntohs(tcp_hdr(skb)->source));
-
}
-
-
isn = tcp_v4_init_sequence(skb);
-
}
-
-
tcp_rsk(req)->snt_isn = isn;
-
tcp_rsk(req)->snt_synack = tcp_time_stamp;
-
-
-
if (tcp_v4_send_synack(sk, dst, req, (struct request_values *)&tmp_ext,
-
skb_get_queue_mapping(skb), want_cookie) || want_cookie)
-
goto drop_and_free;
-
-
-
inet_csk_reqsk_queue_hash_add(sk, req, TCP_TIMEOUT_INIT);
-
return 0;
-
-
drop_and_release:
-
dst_release(dst);
-
-
drop_and_free:
-
reqsk_free(req);
-
-
drop:
-
return 0;
-
}
队列长度
判断半连接队列是否满了。
-
static inline int inet_csk_reqsk_queue_is_full(const struct sock *sk)
-
{
-
return reqsk_queue_is_full(&inet_csk(sk)->icsk_accept_queue);
-
}
半连接队列的最大长度为:2^max_qlen_log。
-
static inline int reqsk_queue_is_full(const struct request_sock_queue *queue)
-
{
-
return queue->listen_opt->qlen >> queue->listen_opt->max_qlen_log;
-
}
判断全连接队列是否满了,全连接队列的最大长度为:sk->sk_max_ack_backlog。
-
static inline bool sk_acceptq_is_full(const struct sock *sk)
-
{
-
return sk->sk_ack_backlog > sk->sk_max_ack_backlog;
-
}
获取未重传过SYNACK的半连接个数。
-
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 requst_sock_queue *queue)
-
{
-
return queue->listen_opt->qlen_young;
-
}
初始序列号
根据源IP、目的IP、源端口、目的端口计算出本端的初始序列号isn。
-
static inline __u32 tcp_v4_init_sequence(const struct sk_buff *skb)
-
{
-
return secure_tcp_sequence_number(ip_hdr(skb)->daddr, ip_hdr(skb)->saddr,
-
tcp_hdr(skb)->dest, tcp_hdr(skb)->source);
-
}
-
-
__u32 secure_tcp_sequence_number(__be32 saddr, __be32 daddr, __be16 sport, __be16 dport)
-
{
-
u32 hash[MD5_DIGEST_WORDS];
-
-
hash[0] = (__force u32) saddr;
-
hash[1] = (__force u32) daddr;
-
hash[2] = ((__force u16) sport << 16) + (__force u16) dport;
-
hash[3] = net_secret[15];
-
-
md5_transform(hash, net_secret);
-
-
return seq_scale(hash[0]);
-
}
-
#define MD5_DIGEST_WORDS 4
-
#define MD5_MESSAGE_BYTES 64
-
-
static u32 net_secret[MD5_MESSAGE_BYTES / 4] __cacheline_aligned;
-
-
static int __init net_secret_init(void)
-
{
-
get_random_bytes(net_secret, sizeof(net_secret));
-
return 0;
-
}
-
-
-
-
-
-
-
-
void get_random_bytes(void *buf, int bytes) {};
-
-
static u32 seq_scale(u32 seq)
-
{
-
return seq + (ktime_to_ns(ktime_get_real()) >> 6);
-
}
最终使用MD5。
Message Digest Algorithm 5,消息摘要算法第五版。是一种散列函数,用于提供消息的完整性保护。
除了MD5外,比较著名的还有SHA1。
void md5_transform(__u32 *hash, __u32 const *in) {}
阅读(1519) | 评论(0) | 转发(1) |