"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.
Unified · Split
| @@ -231,6 +231,8 @@ enum · 230 unmodified lines | ||
| 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 | |
| @@ -96,7 +96,8 @@ extern int sysctl_max_syn_backlog; · 95 unmodified lines | ||
| 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; |
| @@ -460,6 +460,9 @@ extern int tcp_write_wakeup(struct sock *); · 459 unmodified lines | ||
| 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); |
| @@ -254,6 +254,8 @@ static const struct snmp_mib snmp4_net_list[] = { · 253 unmodified lines | ||
| 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 | |
| @@ -808,20 +808,38 @@ static void tcp_v4_reqsk_destructor(struct request_sock *req) · 807 unmodified lines | ||
| 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. |
| @@ -1235,11 +1253,7 @@ int tcp_v4_conn_request(struct sock *sk, struct sk_buff *skb) · 407 unmodified lines | ||
| 1235 | 1253 | __be32 saddr = ip_hdr(skb)->saddr; |
| 1236 | 1254 | __be32 daddr = ip_hdr(skb)->daddr; |
| 1237 | 1255 | __u32 isn = TCP_SKB_CB(skb)->when; |
| 1238 | −#ifdef CONFIG_SYN_COOKIES | |
| 1239 | 1256 | int want_cookie = 0; |
| 1240 | −#else | |
| 1241 | −#define want_cookie 0 /* Argh, why doesn't gcc optimize this :( */ | |
| 1242 | −#endif | |
| 1243 | 1257 | |
| 1244 | 1258 | /* Never answer to SYNs send to broadcast or multicast */ |
| 1245 | 1259 | 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) · 4 unmodified lines | ||
| 1250 | 1264 | * evidently real one. |
| 1251 | 1265 | */ |
| 1252 | 1266 | if (inet_csk_reqsk_queue_is_full(sk) && !isn) { |
| 1253 | − if (net_ratelimit()) | |
| 1254 | − syn_flood_warning(skb); | |
| 1255 | −#ifdef CONFIG_SYN_COOKIES | |
| 1256 | − if (sysctl_tcp_syncookies) { | |
| 1257 | − want_cookie = 1; | |
| 1258 | − } else | |
| 1259 | −#endif | |
| 1260 | − goto drop; | |
| 1267 | + want_cookie = tcp_syn_flood_action(sk, skb, "TCP"); | |
| 1268 | + if (!want_cookie) | |
| 1269 | + goto drop; | |
| 1261 | 1270 | } |
| 1262 | 1271 | |
| 1263 | 1272 | /* 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) · 39 unmodified lines | ||
| 1303 | 1312 | while (l-- > 0) |
| 1304 | 1313 | *c++ ^= *hash_location++; |
| 1305 | 1314 | |
| 1306 | −#ifdef CONFIG_SYN_COOKIES | |
| 1307 | 1315 | want_cookie = 0; /* not our kind of cookie */ |
| 1308 | −#endif | |
| 1309 | 1316 | tmp_ext.cookie_out_never = 0; /* false */ |
| 1310 | 1317 | tmp_ext.cookie_plus = tmp_opt.cookie_plus; |
| 1311 | 1318 | } 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, · 530 unmodified lines | ||
| 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); |
| @@ -1179,11 +1165,7 @@ static int tcp_v6_conn_request(struct sock *sk, struct sk_buff *skb) · 628 unmodified lines | ||
| 1179 | 1165 | struct tcp_sock *tp = tcp_sk(sk); |
| 1180 | 1166 | __u32 isn = TCP_SKB_CB(skb)->when; |
| 1181 | 1167 | struct dst_entry *dst = NULL; |
| 1182 | −#ifdef CONFIG_SYN_COOKIES | |
| 1183 | 1168 | int want_cookie = 0; |
| 1184 | −#else | |
| 1185 | −#define want_cookie 0 | |
| 1186 | −#endif | |
| 1187 | 1169 | |
| 1188 | 1170 | if (skb->protocol == htons(ETH_P_IP)) |
| 1189 | 1171 | return tcp_v4_conn_request(sk, skb); |
| @@ -1192,14 +1174,9 @@ static int tcp_v6_conn_request(struct sock *sk, struct sk_buff *skb) · 2 unmodified lines | ||
| 1192 | 1174 | goto drop; |
| 1193 | 1175 | |
| 1194 | 1176 | if (inet_csk_reqsk_queue_is_full(sk) && !isn) { |
| 1195 | − if (net_ratelimit()) | |
| 1196 | − syn_flood_warning(skb); | |
| 1197 | −#ifdef CONFIG_SYN_COOKIES | |
| 1198 | − if (sysctl_tcp_syncookies) | |
| 1199 | − want_cookie = 1; | |
| 1200 | − else | |
| 1201 | −#endif | |
| 1202 | − goto drop; | |
| 1177 | + want_cookie = tcp_syn_flood_action(sk, skb, "TCPv6"); | |
| 1178 | + if (!want_cookie) | |
| 1179 | + goto drop; | |
| 1203 | 1180 | } |
| 1204 | 1181 | |
| 1205 | 1182 | 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) · 43 unmodified lines | ||
| 1249 | 1226 | while (l-- > 0) |
| 1250 | 1227 | *c++ ^= *hash_location++; |
| 1251 | 1228 | |
| 1252 | −#ifdef CONFIG_SYN_COOKIES | |
| 1253 | 1229 | want_cookie = 0; /* not our kind of cookie */ |
| 1254 | −#endif | |
| 1255 | 1230 | tmp_ext.cookie_out_never = 0; /* false */ |
| 1256 | 1231 | tmp_ext.cookie_plus = tmp_opt.cookie_plus; |
| 1257 | 1232 | } else if (!tp->rx_opt.cookie_in_always) { |