Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os 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>

authored by

Eric Dumazet and committed by
David S. Miller
946cedcc 27e95a8c

+40 -50
+2
include/linux/snmp.h
··· 231 231 LINUX_MIB_TCPDEFERACCEPTDROP, 232 232 LINUX_MIB_IPRPFILTER, /* IP Reverse Path Filter (rp_filter) */ 233 233 LINUX_MIB_TCPTIMEWAITOVERFLOW, /* TCPTimeWaitOverflow */ 234 + LINUX_MIB_TCPREQQFULLDOCOOKIES, /* TCPReqQFullDoCookies */ 235 + LINUX_MIB_TCPREQQFULLDROP, /* TCPReqQFullDrop */ 234 236 __LINUX_MIB_MAX 235 237 }; 236 238
+2 -1
include/net/request_sock.h
··· 96 96 */ 97 97 struct listen_sock { 98 98 u8 max_qlen_log; 99 - /* 3 bytes hole, try to use */ 99 + u8 synflood_warned; 100 + /* 2 bytes hole, try to use */ 100 101 int qlen; 101 102 int qlen_young; 102 103 int clock_hand;
+3
include/net/tcp.h
··· 460 460 extern void tcp_send_fin(struct sock *sk); 461 461 extern void tcp_send_active_reset(struct sock *sk, gfp_t priority); 462 462 extern int tcp_send_synack(struct sock *); 463 + extern int tcp_syn_flood_action(struct sock *sk, 464 + const struct sk_buff *skb, 465 + const char *proto); 463 466 extern void tcp_push_one(struct sock *, unsigned int mss_now); 464 467 extern void tcp_send_ack(struct sock *sk); 465 468 extern void tcp_send_delayed_ack(struct sock *sk);
+2
net/ipv4/proc.c
··· 254 254 SNMP_MIB_ITEM("TCPDeferAcceptDrop", LINUX_MIB_TCPDEFERACCEPTDROP), 255 255 SNMP_MIB_ITEM("IPReversePathFilter", LINUX_MIB_IPRPFILTER), 256 256 SNMP_MIB_ITEM("TCPTimeWaitOverflow", LINUX_MIB_TCPTIMEWAITOVERFLOW), 257 + SNMP_MIB_ITEM("TCPReqQFullDoCookies", LINUX_MIB_TCPREQQFULLDOCOOKIES), 258 + SNMP_MIB_ITEM("TCPReqQFullDrop", LINUX_MIB_TCPREQQFULLDROP), 257 259 SNMP_MIB_SENTINEL 258 260 }; 259 261
+28 -21
net/ipv4/tcp_ipv4.c
··· 808 808 kfree(inet_rsk(req)->opt); 809 809 } 810 810 811 - static void syn_flood_warning(const struct sk_buff *skb) 811 + /* 812 + * Return 1 if a syncookie should be sent 813 + */ 814 + int tcp_syn_flood_action(struct sock *sk, 815 + const struct sk_buff *skb, 816 + const char *proto) 812 817 { 813 - const char *msg; 818 + const char *msg = "Dropping request"; 819 + int want_cookie = 0; 820 + struct listen_sock *lopt; 821 + 822 + 814 823 815 824 #ifdef CONFIG_SYN_COOKIES 816 - if (sysctl_tcp_syncookies) 825 + if (sysctl_tcp_syncookies) { 817 826 msg = "Sending cookies"; 818 - else 827 + want_cookie = 1; 828 + NET_INC_STATS_BH(sock_net(sk), LINUX_MIB_TCPREQQFULLDOCOOKIES); 829 + } else 819 830 #endif 820 - msg = "Dropping request"; 831 + NET_INC_STATS_BH(sock_net(sk), LINUX_MIB_TCPREQQFULLDROP); 821 832 822 - pr_info("TCP: Possible SYN flooding on port %d. %s.\n", 823 - ntohs(tcp_hdr(skb)->dest), msg); 833 + lopt = inet_csk(sk)->icsk_accept_queue.listen_opt; 834 + if (!lopt->synflood_warned) { 835 + lopt->synflood_warned = 1; 836 + pr_info("%s: Possible SYN flooding on port %d. %s. " 837 + " Check SNMP counters.\n", 838 + proto, ntohs(tcp_hdr(skb)->dest), msg); 839 + } 840 + return want_cookie; 824 841 } 842 + EXPORT_SYMBOL(tcp_syn_flood_action); 825 843 826 844 /* 827 845 * Save and compile IPv4 options into the request_sock if needed. ··· 1253 1235 __be32 saddr = ip_hdr(skb)->saddr; 1254 1236 __be32 daddr = ip_hdr(skb)->daddr; 1255 1237 __u32 isn = TCP_SKB_CB(skb)->when; 1256 - #ifdef CONFIG_SYN_COOKIES 1257 1238 int want_cookie = 0; 1258 - #else 1259 - #define want_cookie 0 /* Argh, why doesn't gcc optimize this :( */ 1260 - #endif 1261 1239 1262 1240 /* Never answer to SYNs send to broadcast or multicast */ 1263 1241 if (skb_rtable(skb)->rt_flags & (RTCF_BROADCAST | RTCF_MULTICAST)) ··· 1264 1250 * evidently real one. 1265 1251 */ 1266 1252 if (inet_csk_reqsk_queue_is_full(sk) && !isn) { 1267 - if (net_ratelimit()) 1268 - syn_flood_warning(skb); 1269 - #ifdef CONFIG_SYN_COOKIES 1270 - if (sysctl_tcp_syncookies) { 1271 - want_cookie = 1; 1272 - } else 1273 - #endif 1274 - goto drop; 1253 + want_cookie = tcp_syn_flood_action(sk, skb, "TCP"); 1254 + if (!want_cookie) 1255 + goto drop; 1275 1256 } 1276 1257 1277 1258 /* Accept backlog is full. If we have already queued enough ··· 1312 1303 while (l-- > 0) 1313 1304 *c++ ^= *hash_location++; 1314 1305 1315 - #ifdef CONFIG_SYN_COOKIES 1316 1306 want_cookie = 0; /* not our kind of cookie */ 1317 - #endif 1318 1307 tmp_ext.cookie_out_never = 0; /* false */ 1319 1308 tmp_ext.cookie_plus = tmp_opt.cookie_plus; 1320 1309 } else if (!tp->rx_opt.cookie_in_always) {
+3 -28
net/ipv6/tcp_ipv6.c
··· 531 531 return tcp_v6_send_synack(sk, req, rvp); 532 532 } 533 533 534 - static inline void syn_flood_warning(struct sk_buff *skb) 535 - { 536 - #ifdef CONFIG_SYN_COOKIES 537 - if (sysctl_tcp_syncookies) 538 - printk(KERN_INFO 539 - "TCPv6: Possible SYN flooding on port %d. " 540 - "Sending cookies.\n", ntohs(tcp_hdr(skb)->dest)); 541 - else 542 - #endif 543 - printk(KERN_INFO 544 - "TCPv6: Possible SYN flooding on port %d. " 545 - "Dropping request.\n", ntohs(tcp_hdr(skb)->dest)); 546 - } 547 - 548 534 static void tcp_v6_reqsk_destructor(struct request_sock *req) 549 535 { 550 536 kfree_skb(inet6_rsk(req)->pktopts); ··· 1165 1179 struct tcp_sock *tp = tcp_sk(sk); 1166 1180 __u32 isn = TCP_SKB_CB(skb)->when; 1167 1181 struct dst_entry *dst = NULL; 1168 - #ifdef CONFIG_SYN_COOKIES 1169 1182 int want_cookie = 0; 1170 - #else 1171 - #define want_cookie 0 1172 - #endif 1173 1183 1174 1184 if (skb->protocol == htons(ETH_P_IP)) 1175 1185 return tcp_v4_conn_request(sk, skb); ··· 1174 1192 goto drop; 1175 1193 1176 1194 if (inet_csk_reqsk_queue_is_full(sk) && !isn) { 1177 - if (net_ratelimit()) 1178 - syn_flood_warning(skb); 1179 - #ifdef CONFIG_SYN_COOKIES 1180 - if (sysctl_tcp_syncookies) 1181 - want_cookie = 1; 1182 - else 1183 - #endif 1184 - goto drop; 1195 + want_cookie = tcp_syn_flood_action(sk, skb, "TCPv6"); 1196 + if (!want_cookie) 1197 + goto drop; 1185 1198 } 1186 1199 1187 1200 if (sk_acceptq_is_full(sk) && inet_csk_reqsk_queue_young(sk) > 1) ··· 1226 1249 while (l-- > 0) 1227 1250 *c++ ^= *hash_location++; 1228 1251 1229 - #ifdef CONFIG_SYN_COOKIES 1230 1252 want_cookie = 0; /* not our kind of cookie */ 1231 - #endif 1232 1253 tmp_ext.cookie_out_never = 0; /* false */ 1233 1254 tmp_ext.cookie_plus = tmp_opt.cookie_plus; 1234 1255 } else if (!tp->rx_opt.cookie_in_always) {