forked from mirrors/linux
		
	tcp: Change possible SYN flooding messages
"Possible SYN flooding on port xxxx " messages can fill logs on servers. Change logic to log the message only once per listener, and add two new SNMP counters to track : TCPReqQFullDoCookies : number of times a SYNCOOKIE was replied to client TCPReqQFullDrop : number of times a SYN request was dropped because syncookies were not enabled. Based on a prior patch from Tom Herbert, and suggestions from David. Signed-off-by: Eric Dumazet <eric.dumazet@gmail.com> CC: Tom Herbert <therbert@google.com> Signed-off-by: David S. Miller <davem@davemloft.net>
This commit is contained in:
		
							parent
							
								
									27e95a8c67
								
							
						
					
					
						commit
						946cedccbd
					
				
					 6 changed files with 40 additions and 50 deletions
				
			
		| 
						 | 
					@ -231,6 +231,8 @@ enum
 | 
				
			||||||
	LINUX_MIB_TCPDEFERACCEPTDROP,
 | 
						LINUX_MIB_TCPDEFERACCEPTDROP,
 | 
				
			||||||
	LINUX_MIB_IPRPFILTER, /* IP Reverse Path Filter (rp_filter) */
 | 
						LINUX_MIB_IPRPFILTER, /* IP Reverse Path Filter (rp_filter) */
 | 
				
			||||||
	LINUX_MIB_TCPTIMEWAITOVERFLOW,		/* TCPTimeWaitOverflow */
 | 
						LINUX_MIB_TCPTIMEWAITOVERFLOW,		/* TCPTimeWaitOverflow */
 | 
				
			||||||
 | 
						LINUX_MIB_TCPREQQFULLDOCOOKIES,		/* TCPReqQFullDoCookies */
 | 
				
			||||||
 | 
						LINUX_MIB_TCPREQQFULLDROP,		/* TCPReqQFullDrop */
 | 
				
			||||||
	__LINUX_MIB_MAX
 | 
						__LINUX_MIB_MAX
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -96,7 +96,8 @@ extern int sysctl_max_syn_backlog;
 | 
				
			||||||
 */
 | 
					 */
 | 
				
			||||||
struct listen_sock {
 | 
					struct listen_sock {
 | 
				
			||||||
	u8			max_qlen_log;
 | 
						u8			max_qlen_log;
 | 
				
			||||||
	/* 3 bytes hole, try to use */
 | 
						u8			synflood_warned;
 | 
				
			||||||
 | 
						/* 2 bytes hole, try to use */
 | 
				
			||||||
	int			qlen;
 | 
						int			qlen;
 | 
				
			||||||
	int			qlen_young;
 | 
						int			qlen_young;
 | 
				
			||||||
	int			clock_hand;
 | 
						int			clock_hand;
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -460,6 +460,9 @@ extern int tcp_write_wakeup(struct sock *);
 | 
				
			||||||
extern void tcp_send_fin(struct sock *sk);
 | 
					extern void tcp_send_fin(struct sock *sk);
 | 
				
			||||||
extern void tcp_send_active_reset(struct sock *sk, gfp_t priority);
 | 
					extern void tcp_send_active_reset(struct sock *sk, gfp_t priority);
 | 
				
			||||||
extern int tcp_send_synack(struct sock *);
 | 
					extern int tcp_send_synack(struct sock *);
 | 
				
			||||||
 | 
					extern int tcp_syn_flood_action(struct sock *sk,
 | 
				
			||||||
 | 
									const struct sk_buff *skb,
 | 
				
			||||||
 | 
									const char *proto);
 | 
				
			||||||
extern void tcp_push_one(struct sock *, unsigned int mss_now);
 | 
					extern void tcp_push_one(struct sock *, unsigned int mss_now);
 | 
				
			||||||
extern void tcp_send_ack(struct sock *sk);
 | 
					extern void tcp_send_ack(struct sock *sk);
 | 
				
			||||||
extern void tcp_send_delayed_ack(struct sock *sk);
 | 
					extern void tcp_send_delayed_ack(struct sock *sk);
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -254,6 +254,8 @@ static const struct snmp_mib snmp4_net_list[] = {
 | 
				
			||||||
	SNMP_MIB_ITEM("TCPDeferAcceptDrop", LINUX_MIB_TCPDEFERACCEPTDROP),
 | 
						SNMP_MIB_ITEM("TCPDeferAcceptDrop", LINUX_MIB_TCPDEFERACCEPTDROP),
 | 
				
			||||||
	SNMP_MIB_ITEM("IPReversePathFilter", LINUX_MIB_IPRPFILTER),
 | 
						SNMP_MIB_ITEM("IPReversePathFilter", LINUX_MIB_IPRPFILTER),
 | 
				
			||||||
	SNMP_MIB_ITEM("TCPTimeWaitOverflow", LINUX_MIB_TCPTIMEWAITOVERFLOW),
 | 
						SNMP_MIB_ITEM("TCPTimeWaitOverflow", LINUX_MIB_TCPTIMEWAITOVERFLOW),
 | 
				
			||||||
 | 
						SNMP_MIB_ITEM("TCPReqQFullDoCookies", LINUX_MIB_TCPREQQFULLDOCOOKIES),
 | 
				
			||||||
 | 
						SNMP_MIB_ITEM("TCPReqQFullDrop", LINUX_MIB_TCPREQQFULLDROP),
 | 
				
			||||||
	SNMP_MIB_SENTINEL
 | 
						SNMP_MIB_SENTINEL
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -808,20 +808,38 @@ static void tcp_v4_reqsk_destructor(struct request_sock *req)
 | 
				
			||||||
	kfree(inet_rsk(req)->opt);
 | 
						kfree(inet_rsk(req)->opt);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static void syn_flood_warning(const struct sk_buff *skb)
 | 
					/*
 | 
				
			||||||
 | 
					 * Return 1 if a syncookie should be sent
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					int tcp_syn_flood_action(struct sock *sk,
 | 
				
			||||||
 | 
								 const struct sk_buff *skb,
 | 
				
			||||||
 | 
								 const char *proto)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	const char *msg;
 | 
						const char *msg = "Dropping request";
 | 
				
			||||||
 | 
						int want_cookie = 0;
 | 
				
			||||||
 | 
						struct listen_sock *lopt;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#ifdef CONFIG_SYN_COOKIES
 | 
					#ifdef CONFIG_SYN_COOKIES
 | 
				
			||||||
	if (sysctl_tcp_syncookies)
 | 
						if (sysctl_tcp_syncookies) {
 | 
				
			||||||
		msg = "Sending cookies";
 | 
							msg = "Sending cookies";
 | 
				
			||||||
	else
 | 
							want_cookie = 1;
 | 
				
			||||||
 | 
							NET_INC_STATS_BH(sock_net(sk), LINUX_MIB_TCPREQQFULLDOCOOKIES);
 | 
				
			||||||
 | 
						} else
 | 
				
			||||||
#endif
 | 
					#endif
 | 
				
			||||||
		msg = "Dropping request";
 | 
							NET_INC_STATS_BH(sock_net(sk), LINUX_MIB_TCPREQQFULLDROP);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	pr_info("TCP: Possible SYN flooding on port %d. %s.\n",
 | 
						lopt = inet_csk(sk)->icsk_accept_queue.listen_opt;
 | 
				
			||||||
				ntohs(tcp_hdr(skb)->dest), msg);
 | 
						if (!lopt->synflood_warned) {
 | 
				
			||||||
 | 
							lopt->synflood_warned = 1;
 | 
				
			||||||
 | 
							pr_info("%s: Possible SYN flooding on port %d. %s. "
 | 
				
			||||||
 | 
								" Check SNMP counters.\n",
 | 
				
			||||||
 | 
								proto, ntohs(tcp_hdr(skb)->dest), msg);
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return want_cookie;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					EXPORT_SYMBOL(tcp_syn_flood_action);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/*
 | 
					/*
 | 
				
			||||||
 * Save and compile IPv4 options into the request_sock if needed.
 | 
					 * Save and compile IPv4 options into the request_sock if needed.
 | 
				
			||||||
| 
						 | 
					@ -1235,11 +1253,7 @@ int tcp_v4_conn_request(struct sock *sk, struct sk_buff *skb)
 | 
				
			||||||
	__be32 saddr = ip_hdr(skb)->saddr;
 | 
						__be32 saddr = ip_hdr(skb)->saddr;
 | 
				
			||||||
	__be32 daddr = ip_hdr(skb)->daddr;
 | 
						__be32 daddr = ip_hdr(skb)->daddr;
 | 
				
			||||||
	__u32 isn = TCP_SKB_CB(skb)->when;
 | 
						__u32 isn = TCP_SKB_CB(skb)->when;
 | 
				
			||||||
#ifdef CONFIG_SYN_COOKIES
 | 
					 | 
				
			||||||
	int want_cookie = 0;
 | 
						int want_cookie = 0;
 | 
				
			||||||
#else
 | 
					 | 
				
			||||||
#define want_cookie 0 /* Argh, why doesn't gcc optimize this :( */
 | 
					 | 
				
			||||||
#endif
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
	/* Never answer to SYNs send to broadcast or multicast */
 | 
						/* Never answer to SYNs send to broadcast or multicast */
 | 
				
			||||||
	if (skb_rtable(skb)->rt_flags & (RTCF_BROADCAST | RTCF_MULTICAST))
 | 
						if (skb_rtable(skb)->rt_flags & (RTCF_BROADCAST | RTCF_MULTICAST))
 | 
				
			||||||
| 
						 | 
					@ -1250,14 +1264,9 @@ int tcp_v4_conn_request(struct sock *sk, struct sk_buff *skb)
 | 
				
			||||||
	 * evidently real one.
 | 
						 * evidently real one.
 | 
				
			||||||
	 */
 | 
						 */
 | 
				
			||||||
	if (inet_csk_reqsk_queue_is_full(sk) && !isn) {
 | 
						if (inet_csk_reqsk_queue_is_full(sk) && !isn) {
 | 
				
			||||||
		if (net_ratelimit())
 | 
							want_cookie = tcp_syn_flood_action(sk, skb, "TCP");
 | 
				
			||||||
			syn_flood_warning(skb);
 | 
							if (!want_cookie)
 | 
				
			||||||
#ifdef CONFIG_SYN_COOKIES
 | 
								goto drop;
 | 
				
			||||||
		if (sysctl_tcp_syncookies) {
 | 
					 | 
				
			||||||
			want_cookie = 1;
 | 
					 | 
				
			||||||
		} else
 | 
					 | 
				
			||||||
#endif
 | 
					 | 
				
			||||||
		goto drop;
 | 
					 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	/* Accept backlog is full. If we have already queued enough
 | 
						/* Accept backlog is full. If we have already queued enough
 | 
				
			||||||
| 
						 | 
					@ -1303,9 +1312,7 @@ int tcp_v4_conn_request(struct sock *sk, struct sk_buff *skb)
 | 
				
			||||||
		while (l-- > 0)
 | 
							while (l-- > 0)
 | 
				
			||||||
			*c++ ^= *hash_location++;
 | 
								*c++ ^= *hash_location++;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#ifdef CONFIG_SYN_COOKIES
 | 
					 | 
				
			||||||
		want_cookie = 0;	/* not our kind of cookie */
 | 
							want_cookie = 0;	/* not our kind of cookie */
 | 
				
			||||||
#endif
 | 
					 | 
				
			||||||
		tmp_ext.cookie_out_never = 0; /* false */
 | 
							tmp_ext.cookie_out_never = 0; /* false */
 | 
				
			||||||
		tmp_ext.cookie_plus = tmp_opt.cookie_plus;
 | 
							tmp_ext.cookie_plus = tmp_opt.cookie_plus;
 | 
				
			||||||
	} else if (!tp->rx_opt.cookie_in_always) {
 | 
						} else if (!tp->rx_opt.cookie_in_always) {
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -531,20 +531,6 @@ static int tcp_v6_rtx_synack(struct sock *sk, struct request_sock *req,
 | 
				
			||||||
	return tcp_v6_send_synack(sk, req, rvp);
 | 
						return tcp_v6_send_synack(sk, req, rvp);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static inline void syn_flood_warning(struct sk_buff *skb)
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
#ifdef CONFIG_SYN_COOKIES
 | 
					 | 
				
			||||||
	if (sysctl_tcp_syncookies)
 | 
					 | 
				
			||||||
		printk(KERN_INFO
 | 
					 | 
				
			||||||
		       "TCPv6: Possible SYN flooding on port %d. "
 | 
					 | 
				
			||||||
		       "Sending cookies.\n", ntohs(tcp_hdr(skb)->dest));
 | 
					 | 
				
			||||||
	else
 | 
					 | 
				
			||||||
#endif
 | 
					 | 
				
			||||||
		printk(KERN_INFO
 | 
					 | 
				
			||||||
		       "TCPv6: Possible SYN flooding on port %d. "
 | 
					 | 
				
			||||||
		       "Dropping request.\n", ntohs(tcp_hdr(skb)->dest));
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
static void tcp_v6_reqsk_destructor(struct request_sock *req)
 | 
					static void tcp_v6_reqsk_destructor(struct request_sock *req)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	kfree_skb(inet6_rsk(req)->pktopts);
 | 
						kfree_skb(inet6_rsk(req)->pktopts);
 | 
				
			||||||
| 
						 | 
					@ -1179,11 +1165,7 @@ static int tcp_v6_conn_request(struct sock *sk, struct sk_buff *skb)
 | 
				
			||||||
	struct tcp_sock *tp = tcp_sk(sk);
 | 
						struct tcp_sock *tp = tcp_sk(sk);
 | 
				
			||||||
	__u32 isn = TCP_SKB_CB(skb)->when;
 | 
						__u32 isn = TCP_SKB_CB(skb)->when;
 | 
				
			||||||
	struct dst_entry *dst = NULL;
 | 
						struct dst_entry *dst = NULL;
 | 
				
			||||||
#ifdef CONFIG_SYN_COOKIES
 | 
					 | 
				
			||||||
	int want_cookie = 0;
 | 
						int want_cookie = 0;
 | 
				
			||||||
#else
 | 
					 | 
				
			||||||
#define want_cookie 0
 | 
					 | 
				
			||||||
#endif
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if (skb->protocol == htons(ETH_P_IP))
 | 
						if (skb->protocol == htons(ETH_P_IP))
 | 
				
			||||||
		return tcp_v4_conn_request(sk, skb);
 | 
							return tcp_v4_conn_request(sk, skb);
 | 
				
			||||||
| 
						 | 
					@ -1192,14 +1174,9 @@ static int tcp_v6_conn_request(struct sock *sk, struct sk_buff *skb)
 | 
				
			||||||
		goto drop;
 | 
							goto drop;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if (inet_csk_reqsk_queue_is_full(sk) && !isn) {
 | 
						if (inet_csk_reqsk_queue_is_full(sk) && !isn) {
 | 
				
			||||||
		if (net_ratelimit())
 | 
							want_cookie = tcp_syn_flood_action(sk, skb, "TCPv6");
 | 
				
			||||||
			syn_flood_warning(skb);
 | 
							if (!want_cookie)
 | 
				
			||||||
#ifdef CONFIG_SYN_COOKIES
 | 
								goto drop;
 | 
				
			||||||
		if (sysctl_tcp_syncookies)
 | 
					 | 
				
			||||||
			want_cookie = 1;
 | 
					 | 
				
			||||||
		else
 | 
					 | 
				
			||||||
#endif
 | 
					 | 
				
			||||||
		goto drop;
 | 
					 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if (sk_acceptq_is_full(sk) && inet_csk_reqsk_queue_young(sk) > 1)
 | 
						if (sk_acceptq_is_full(sk) && inet_csk_reqsk_queue_young(sk) > 1)
 | 
				
			||||||
| 
						 | 
					@ -1249,9 +1226,7 @@ static int tcp_v6_conn_request(struct sock *sk, struct sk_buff *skb)
 | 
				
			||||||
		while (l-- > 0)
 | 
							while (l-- > 0)
 | 
				
			||||||
			*c++ ^= *hash_location++;
 | 
								*c++ ^= *hash_location++;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#ifdef CONFIG_SYN_COOKIES
 | 
					 | 
				
			||||||
		want_cookie = 0;	/* not our kind of cookie */
 | 
							want_cookie = 0;	/* not our kind of cookie */
 | 
				
			||||||
#endif
 | 
					 | 
				
			||||||
		tmp_ext.cookie_out_never = 0; /* false */
 | 
							tmp_ext.cookie_out_never = 0; /* false */
 | 
				
			||||||
		tmp_ext.cookie_plus = tmp_opt.cookie_plus;
 | 
							tmp_ext.cookie_plus = tmp_opt.cookie_plus;
 | 
				
			||||||
	} else if (!tp->rx_opt.cookie_in_always) {
 | 
						} else if (!tp->rx_opt.cookie_in_always) {
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
		Loading…
	
		Reference in a new issue