diff options
author | Eric Dumazet <eric.dumazet@gmail.com> | 2011-08-29 23:21:44 -0400 |
---|---|---|
committer | David S. Miller <davem@davemloft.net> | 2011-09-15 14:49:43 -0400 |
commit | 946cedccbd7387488d2cee5da92cdfeb28d2e670 (patch) | |
tree | fbb0d9c8dc11d6efee64e2a077a4951831932058 | |
parent | 27e95a8c670e0c587990ec5b9a87a7ea17873d28 (diff) |
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>
-rw-r--r-- | include/linux/snmp.h | 2 | ||||
-rw-r--r-- | include/net/request_sock.h | 3 | ||||
-rw-r--r-- | include/net/tcp.h | 3 | ||||
-rw-r--r-- | net/ipv4/proc.c | 2 | ||||
-rw-r--r-- | net/ipv4/tcp_ipv4.c | 49 | ||||
-rw-r--r-- | net/ipv6/tcp_ipv6.c | 31 |
6 files changed, 40 insertions, 50 deletions
diff --git a/include/linux/snmp.h b/include/linux/snmp.h index 12b2b18e50c1..e16557a357e5 100644 --- a/include/linux/snmp.h +++ b/include/linux/snmp.h | |||
@@ -231,6 +231,8 @@ enum | |||
231 | LINUX_MIB_TCPDEFERACCEPTDROP, | 231 | LINUX_MIB_TCPDEFERACCEPTDROP, |
232 | LINUX_MIB_IPRPFILTER, /* IP Reverse Path Filter (rp_filter) */ | 232 | LINUX_MIB_IPRPFILTER, /* IP Reverse Path Filter (rp_filter) */ |
233 | LINUX_MIB_TCPTIMEWAITOVERFLOW, /* TCPTimeWaitOverflow */ | 233 | LINUX_MIB_TCPTIMEWAITOVERFLOW, /* TCPTimeWaitOverflow */ |
234 | LINUX_MIB_TCPREQQFULLDOCOOKIES, /* TCPReqQFullDoCookies */ | ||
235 | LINUX_MIB_TCPREQQFULLDROP, /* TCPReqQFullDrop */ | ||
234 | __LINUX_MIB_MAX | 236 | __LINUX_MIB_MAX |
235 | }; | 237 | }; |
236 | 238 | ||
diff --git a/include/net/request_sock.h b/include/net/request_sock.h index 99e6e19b57c2..4c0766e201e3 100644 --- a/include/net/request_sock.h +++ b/include/net/request_sock.h | |||
@@ -96,7 +96,8 @@ extern int sysctl_max_syn_backlog; | |||
96 | */ | 96 | */ |
97 | struct listen_sock { | 97 | struct listen_sock { |
98 | u8 max_qlen_log; | 98 | u8 max_qlen_log; |
99 | /* 3 bytes hole, try to use */ | 99 | u8 synflood_warned; |
100 | /* 2 bytes hole, try to use */ | ||
100 | int qlen; | 101 | int qlen; |
101 | int qlen_young; | 102 | int qlen_young; |
102 | int clock_hand; | 103 | int clock_hand; |
diff --git a/include/net/tcp.h b/include/net/tcp.h index 149a415d1e0a..e9b48b094683 100644 --- a/include/net/tcp.h +++ b/include/net/tcp.h | |||
@@ -460,6 +460,9 @@ extern int tcp_write_wakeup(struct sock *); | |||
460 | extern void tcp_send_fin(struct sock *sk); | 460 | extern void tcp_send_fin(struct sock *sk); |
461 | extern void tcp_send_active_reset(struct sock *sk, gfp_t priority); | 461 | extern void tcp_send_active_reset(struct sock *sk, gfp_t priority); |
462 | extern int tcp_send_synack(struct sock *); | 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 | extern void tcp_push_one(struct sock *, unsigned int mss_now); | 466 | extern void tcp_push_one(struct sock *, unsigned int mss_now); |
464 | extern void tcp_send_ack(struct sock *sk); | 467 | extern void tcp_send_ack(struct sock *sk); |
465 | extern void tcp_send_delayed_ack(struct sock *sk); | 468 | extern void tcp_send_delayed_ack(struct sock *sk); |
diff --git a/net/ipv4/proc.c b/net/ipv4/proc.c index b14ec7d03b6e..4bfad5da94f4 100644 --- a/net/ipv4/proc.c +++ b/net/ipv4/proc.c | |||
@@ -254,6 +254,8 @@ static const struct snmp_mib snmp4_net_list[] = { | |||
254 | SNMP_MIB_ITEM("TCPDeferAcceptDrop", LINUX_MIB_TCPDEFERACCEPTDROP), | 254 | SNMP_MIB_ITEM("TCPDeferAcceptDrop", LINUX_MIB_TCPDEFERACCEPTDROP), |
255 | SNMP_MIB_ITEM("IPReversePathFilter", LINUX_MIB_IPRPFILTER), | 255 | SNMP_MIB_ITEM("IPReversePathFilter", LINUX_MIB_IPRPFILTER), |
256 | SNMP_MIB_ITEM("TCPTimeWaitOverflow", LINUX_MIB_TCPTIMEWAITOVERFLOW), | 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 | SNMP_MIB_SENTINEL | 259 | SNMP_MIB_SENTINEL |
258 | }; | 260 | }; |
259 | 261 | ||
diff --git a/net/ipv4/tcp_ipv4.c b/net/ipv4/tcp_ipv4.c index 1c12b8ec849d..c34f01513945 100644 --- a/net/ipv4/tcp_ipv4.c +++ b/net/ipv4/tcp_ipv4.c | |||
@@ -808,20 +808,38 @@ static void tcp_v4_reqsk_destructor(struct request_sock *req) | |||
808 | kfree(inet_rsk(req)->opt); | 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 | #ifdef CONFIG_SYN_COOKIES | 824 | #ifdef CONFIG_SYN_COOKIES |
816 | if (sysctl_tcp_syncookies) | 825 | if (sysctl_tcp_syncookies) { |
817 | msg = "Sending cookies"; | 826 | msg = "Sending cookies"; |
818 | else | 827 | want_cookie = 1; |
828 | NET_INC_STATS_BH(sock_net(sk), LINUX_MIB_TCPREQQFULLDOCOOKIES); | ||
829 | } else | ||
819 | #endif | 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", | 833 | lopt = inet_csk(sk)->icsk_accept_queue.listen_opt; |
823 | ntohs(tcp_hdr(skb)->dest), msg); | 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 | * Save and compile IPv4 options into the request_sock if needed. | 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) | |||
1235 | __be32 saddr = ip_hdr(skb)->saddr; | 1253 | __be32 saddr = ip_hdr(skb)->saddr; |
1236 | __be32 daddr = ip_hdr(skb)->daddr; | 1254 | __be32 daddr = ip_hdr(skb)->daddr; |
1237 | __u32 isn = TCP_SKB_CB(skb)->when; | 1255 | __u32 isn = TCP_SKB_CB(skb)->when; |
1238 | #ifdef CONFIG_SYN_COOKIES | ||
1239 | int want_cookie = 0; | 1256 | int want_cookie = 0; |
1240 | #else | ||
1241 | #define want_cookie 0 /* Argh, why doesn't gcc optimize this :( */ | ||
1242 | #endif | ||
1243 | 1257 | ||
1244 | /* Never answer to SYNs send to broadcast or multicast */ | 1258 | /* Never answer to SYNs send to broadcast or multicast */ |
1245 | if (skb_rtable(skb)->rt_flags & (RTCF_BROADCAST | RTCF_MULTICAST)) | 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) | |||
1250 | * evidently real one. | 1264 | * evidently real one. |
1251 | */ | 1265 | */ |
1252 | if (inet_csk_reqsk_queue_is_full(sk) && !isn) { | 1266 | if (inet_csk_reqsk_queue_is_full(sk) && !isn) { |
1253 | if (net_ratelimit()) | 1267 | want_cookie = tcp_syn_flood_action(sk, skb, "TCP"); |
1254 | syn_flood_warning(skb); | 1268 | if (!want_cookie) |
1255 | #ifdef CONFIG_SYN_COOKIES | 1269 | goto drop; |
1256 | if (sysctl_tcp_syncookies) { | ||
1257 | want_cookie = 1; | ||
1258 | } else | ||
1259 | #endif | ||
1260 | goto drop; | ||
1261 | } | 1270 | } |
1262 | 1271 | ||
1263 | /* Accept backlog is full. If we have already queued enough | 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) | |||
1303 | while (l-- > 0) | 1312 | while (l-- > 0) |
1304 | *c++ ^= *hash_location++; | 1313 | *c++ ^= *hash_location++; |
1305 | 1314 | ||
1306 | #ifdef CONFIG_SYN_COOKIES | ||
1307 | want_cookie = 0; /* not our kind of cookie */ | 1315 | want_cookie = 0; /* not our kind of cookie */ |
1308 | #endif | ||
1309 | tmp_ext.cookie_out_never = 0; /* false */ | 1316 | tmp_ext.cookie_out_never = 0; /* false */ |
1310 | tmp_ext.cookie_plus = tmp_opt.cookie_plus; | 1317 | tmp_ext.cookie_plus = tmp_opt.cookie_plus; |
1311 | } else if (!tp->rx_opt.cookie_in_always) { | 1318 | } else if (!tp->rx_opt.cookie_in_always) { |
diff --git a/net/ipv6/tcp_ipv6.c b/net/ipv6/tcp_ipv6.c index d1fb63f4aeb7..3c9fa618b69d 100644 --- a/net/ipv6/tcp_ipv6.c +++ b/net/ipv6/tcp_ipv6.c | |||
@@ -531,20 +531,6 @@ static int tcp_v6_rtx_synack(struct sock *sk, struct request_sock *req, | |||
531 | return tcp_v6_send_synack(sk, req, rvp); | 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 | static void tcp_v6_reqsk_destructor(struct request_sock *req) | 534 | static void tcp_v6_reqsk_destructor(struct request_sock *req) |
549 | { | 535 | { |
550 | kfree_skb(inet6_rsk(req)->pktopts); | 536 | kfree_skb(inet6_rsk(req)->pktopts); |
@@ -1179,11 +1165,7 @@ static int tcp_v6_conn_request(struct sock *sk, struct sk_buff *skb) | |||
1179 | struct tcp_sock *tp = tcp_sk(sk); | 1165 | struct tcp_sock *tp = tcp_sk(sk); |
1180 | __u32 isn = TCP_SKB_CB(skb)->when; | 1166 | __u32 isn = TCP_SKB_CB(skb)->when; |
1181 | struct dst_entry *dst = NULL; | 1167 | struct dst_entry *dst = NULL; |
1182 | #ifdef CONFIG_SYN_COOKIES | ||
1183 | int want_cookie = 0; | 1168 | int want_cookie = 0; |
1184 | #else | ||
1185 | #define want_cookie 0 | ||
1186 | #endif | ||
1187 | 1169 | ||
1188 | if (skb->protocol == htons(ETH_P_IP)) | 1170 | if (skb->protocol == htons(ETH_P_IP)) |
1189 | return tcp_v4_conn_request(sk, skb); | 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) | |||
1192 | goto drop; | 1174 | goto drop; |
1193 | 1175 | ||
1194 | if (inet_csk_reqsk_queue_is_full(sk) && !isn) { | 1176 | if (inet_csk_reqsk_queue_is_full(sk) && !isn) { |
1195 | if (net_ratelimit()) | 1177 | want_cookie = tcp_syn_flood_action(sk, skb, "TCPv6"); |
1196 | syn_flood_warning(skb); | 1178 | if (!want_cookie) |
1197 | #ifdef CONFIG_SYN_COOKIES | 1179 | goto drop; |
1198 | if (sysctl_tcp_syncookies) | ||
1199 | want_cookie = 1; | ||
1200 | else | ||
1201 | #endif | ||
1202 | goto drop; | ||
1203 | } | 1180 | } |
1204 | 1181 | ||
1205 | if (sk_acceptq_is_full(sk) && inet_csk_reqsk_queue_young(sk) > 1) | 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) | |||
1249 | while (l-- > 0) | 1226 | while (l-- > 0) |
1250 | *c++ ^= *hash_location++; | 1227 | *c++ ^= *hash_location++; |
1251 | 1228 | ||
1252 | #ifdef CONFIG_SYN_COOKIES | ||
1253 | want_cookie = 0; /* not our kind of cookie */ | 1229 | want_cookie = 0; /* not our kind of cookie */ |
1254 | #endif | ||
1255 | tmp_ext.cookie_out_never = 0; /* false */ | 1230 | tmp_ext.cookie_out_never = 0; /* false */ |
1256 | tmp_ext.cookie_plus = tmp_opt.cookie_plus; | 1231 | tmp_ext.cookie_plus = tmp_opt.cookie_plus; |
1257 | } else if (!tp->rx_opt.cookie_in_always) { | 1232 | } else if (!tp->rx_opt.cookie_in_always) { |