Commit 946cedcc authored by Eric Dumazet's avatar Eric Dumazet Committed by David S. Miller

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: default avatarEric Dumazet <eric.dumazet@gmail.com>
CC: Tom Herbert <therbert@google.com>
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parent 27e95a8c
...@@ -231,6 +231,8 @@ enum ...@@ -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; ...@@ -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 *); ...@@ -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[] = { ...@@ -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) ...@@ -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) ...@@ -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) ...@@ -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) ...@@ -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, ...@@ -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) ...@@ -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) ...@@ -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) ...@@ -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) {
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment