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 /net | |
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>
Diffstat (limited to 'net')
-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 |
3 files changed, 33 insertions, 49 deletions
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) { |