Linux tcp之sync cookie

1、正常的tcp连接过程中,客户端向服务端发送一个sync包,服务端会新建一个request请求,然后将请求存放在socket的半连接队列里(syn_table),然后向客户端返回sync+ack;客户端收到sync后再次向服务端发送ack,服务端收到ack后,将request请求从半连接队列移到全连接队列里(icsk_accept_queue);

2、在sync恶意攻击的场景下,攻击方不断的发sync包,发完就退出,这样server端的半连接队列很快就会越来越大,服务端就会需要消耗额外cpu和内存资源来维护这些半连接队列,直至系统崩溃;

针对这种问题,新增了sync cookie功能,为了支持该功能,内核新增了一个tcp_syncookies参数,先看下内核文档对该参数的描述:

tcp_syncookies - BOOLEAN
        Only valid when the kernel was compiled with CONFIG_SYN_COOKIES
        Send out syncookies when the syn backlog queue of a socket
        overflows. This is to prevent against the common 'SYN flood attack'
        Default: 1

        Note, that syncookies is fallback facility.
        It MUST NOT be used to help highly loaded servers to stand
        against legal connection rate. If you see SYN flood warnings
        in your logs, but investigation shows that they occur
        because of overload with legal connections, you should tune
        another parameters until this warning disappear.
        See: tcp_max_syn_backlog, tcp_synack_retries, tcp_abort_on_overflow.

        syncookies seriously violate TCP protocol, do not allow
        to use TCP extensions, can result in serious degradation
        of some services (f.e. SMTP relaying), visible not by you,
        but your clients and relays, contacting you. While you see
        SYN flood warnings in logs not being really flooded, your server
        is seriously misconfigured.

        If you want to test which effects syncookies have to your
        network connections you can set this knob to 2 to enable
        unconditionally generation of syncookies.


从描述里可以看到,这个参数是在syn backlog queue溢出时使用的,这里的queue指的就是全连接队列,这里的队列长度取决于listen系统调用的第二个参数(backlog长度)及系统参数/proc/sys/net/ipv4/tcp_max_syn_backlog,详情看reqsk_queue_alloc实现。tcp_syncookies默认值为1,即只有等socket的全连接队列溢出时才起作用,设定为2表示强制使用sync cookie,0表示关闭sync cookie。

2、原理:

1)、server在收到sync包后,判断是否需要使用sync cookie,如果需要使用,则server根据流的源/宿ip、源/宿端口、序列号以及mss值hash成一个cookie,然后作为sync+ack报文的初始序列号发送给client,server本地不保存相关的request_sock信息;
2)、client收到sync+ack后,将序列号加1,然后返回ack给server;
3)、server收到ack后,先从半连接队列里找request_sock,如果找不到,则进行sync cookie判断,判断方法是:
   a)、将包的ack_seq序列号减1,减完1后的序列号值即为server一开始初始化创建的cookie值; 
   b)、根据收到的ack包的源/宿ip、源/宿端口、及序列号减1重新hash一个cookie值,然后跟上一步得到的cookie比较,并得到mss值,则正常创建request_sock,然后更新相关信息,将sk->sk_state置为ESTABLISHED状态,request_sock保存到连接队列里。

3、server端实现:

1)、收到sync报文,分配sync cookie

tcp_v4_rcv
    tcp_v4_do_rcv
        tcp_v4_hnd_req
            tcp_rcv_state_process
                tcp_v4_conn_request
                    tcp_conn_request

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)
{
	/* TW buckets are converted to open requests without
	* limitations, they conserve resources and peer is
	* evidently real one.
	*/
	//判断是否强制使用sync cookie,或者sync queue是否已经溢出
	//want_cookie表示需要使用sync cookie功能
	if ((sysctl_tcp_syncookies == 2 ||
		inet_csk_reqsk_queue_is_full(sk)) && !isn) {
		want_cookie = tcp_syn_flood_action(sk, skb, rsk_ops->slab_name);
		if (!want_cookie)
			goto drop;
		}

	//如果需要使用sync cookie,则根据流的信息hash一个初始序列号
	if (want_cookie) {
		isn = cookie_init_sequence(af_ops, sk, skb, &req->mss);
		req->cookie_ts = tmp_opt.tstamp_ok;
		if (!tmp_opt.tstamp_ok)
			inet_rsk(req)->ecn_ok = 0;
	}
						
	//将hash生成的序列号作为sync+ack包的初始序列号
	tcp_rsk(req)->snt_isn = isn;
				
	if (!fastopen) {
		//如果使用sync cookie,则不保存request_sock信息,直接释放掉.
		if (err || want_cookie)
			goto drop_and_free;

		tcp_rsk(req)->listener = NULL;
		af_ops->queue_hash_add(sk, req, TCP_TIMEOUT_INIT);
	}

	return 0;
}

2)、server收到ack,分配requeset_sock

tcp_v4_rcv
    tcp_v4_do_rcv
        tcp_v4_hnd_req      

static struct sock *tcp_v4_hnd_req(struct sock *sk, struct sk_buff *skb)
{
	#ifdef CONFIG_SYN_COOKIES
	//收到client的ack时,找不到request_sock,检查cookie值
	if (!th->syn)
		sk = cookie_v4_check(sk, skb, &(IPCB(skb)->opt));
	#endif
	return sk;
}	

            cookie_v4_check
                __cookie_v4_check

int __cookie_v4_check(const struct iphdr *iph, const struct tcphdr *th, u32 cookie)
{
	//先将序列号减1
	__u32 seq = ntohl(th->seq) - 1;
	//获得mss id
	__u32 mssind = check_tcp_syn_cookie(cookie, iph->saddr, iph->daddr,
					th->source, th->dest, seq);
	//如果得到的mss在msstab范围内,则认为检查通过,说明使用sync cookie有个限制;
	//就是mss的值只能固定msstab里定义的那几种
	return mssind < ARRAY_SIZE(msstab) ? msstab[mssind] : 0;
}

                get_cookie_sock
                    tcp_v4_syn_recv_sock(这里面新创一个request_sock,然后将状态置为SYNC_RECV)
                        inet_csk_reqsk_queue_add(将requeset_sock加到全连接队列里)

4、缺陷:

1)、使用sync cookie后,由于server在收到sync报文时不保存request_sock信息,所以有一些在sync里协商的tcp option字段无法使用,比如WScale(窗口扩大因子)、sack等;
mss信息本来也是在sync里协商的,因为server在生成cookie时会带上mss信息,等收到ack后再计算得到mss,所以mss信息可以保留下来;

2)、mss的类型只能固定几种。

  • 0
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值