diff options
| author | Andre Oppermann <andre@FreeBSD.org> | 2004-01-08 17:40:07 +0000 |
|---|---|---|
| committer | Andre Oppermann <andre@FreeBSD.org> | 2004-01-08 17:40:07 +0000 |
| commit | 53369ac9bb59395106a6654481497e3b7368d79d (patch) | |
| tree | d8cee9c3cad3ec8e336b6e4409f27542451db5cc | |
| parent | 2aae6624793125670da0f312924d4e1a035caed4 (diff) | |
Notes
| -rw-r--r-- | sys/netinet/ip_icmp.c | 6 | ||||
| -rw-r--r-- | sys/netinet/tcp.h | 20 | ||||
| -rw-r--r-- | sys/netinet/tcp_input.c | 60 | ||||
| -rw-r--r-- | sys/netinet/tcp_reass.c | 60 | ||||
| -rw-r--r-- | sys/netinet/tcp_subr.c | 24 | ||||
| -rw-r--r-- | sys/netinet/tcp_timewait.c | 24 | ||||
| -rw-r--r-- | sys/netinet/tcp_usrreq.c | 3 | ||||
| -rw-r--r-- | sys/netinet/tcp_var.h | 7 |
8 files changed, 200 insertions, 4 deletions
diff --git a/sys/netinet/ip_icmp.c b/sys/netinet/ip_icmp.c index a4e0a9666b09..220c624646b2 100644 --- a/sys/netinet/ip_icmp.c +++ b/sys/netinet/ip_icmp.c @@ -409,7 +409,8 @@ icmp_input(m, off) * notice that the MTU has changed and adapt accordingly. * If no new MTU was suggested, then we guess a new one * less than the current value. If the new MTU is - * unreasonably small, then we don't update the MTU value. + * unreasonably small (defined by sysctl tcp_minmss), then + * we don't update the MTU value. * * XXX: All this should be done in tcp_mtudisc() because * the way we do it now, everyone can send us bogus ICMP @@ -431,7 +432,8 @@ icmp_input(m, off) if (!mtu) mtu = ip_next_mtu(mtu, 1); - if (mtu >= 256 + sizeof(struct tcpiphdr)) + if (mtu >= max(296, (tcp_minmss + + sizeof(struct tcpiphdr)))) tcp_hc_updatemtu(&inc, mtu); #ifdef DEBUG_MTUDISC diff --git a/sys/netinet/tcp.h b/sys/netinet/tcp.h index e189600fe4b6..0143bcabf384 100644 --- a/sys/netinet/tcp.h +++ b/sys/netinet/tcp.h @@ -105,11 +105,29 @@ struct tcphdr { /* * Default maximum segment size for TCP. - * With an IP MSS of 576, this is 536, + * With an IP MTU of 576, this is 536, * but 512 is probably more convenient. * This should be defined as MIN(512, IP_MSS - sizeof (struct tcpiphdr)). */ #define TCP_MSS 512 +/* + * TCP_MINMSS is defined to be 256 which is fine for the smallest + * link MTU (296 bytes, SLIP interface) in the Internet. + * However it is very unlikely to come across such low MTU interfaces + * these days (anno dato 2003). + * Probably it can be set to 512 without ill effects. But we play safe. + * See tcp_subr.c tcp_minmss SYSCTL declaration for more comments. + * Setting this to "0" disables the minmss check. + */ +#define TCP_MINMSS 256 +/* + * TCP_MINMSSOVERLOAD is defined to be 1000 which should cover any type + * of interactive TCP session. + * See tcp_subr.c tcp_minmssoverload SYSCTL declaration and tcp_input.c + * for more comments. + * Setting this to "0" disables the minmssoverload check. + */ +#define TCP_MINMSSOVERLOAD 1000 /* * Default maximum segment size for TCP6. diff --git a/sys/netinet/tcp_input.c b/sys/netinet/tcp_input.c index 36f044484028..263dfd83f163 100644 --- a/sys/netinet/tcp_input.c +++ b/sys/netinet/tcp_input.c @@ -919,6 +919,61 @@ after_listen: panic("tcp_input: TCPS_LISTEN"); /* + * This is the second part of the MSS DoS prevention code (after + * minmss on the sending side) and it deals with too many too small + * tcp packets in a too short timeframe (1 second). + * + * For every full second we count the number of received packets + * and bytes. If we get a lot of packets per second for this connection + * (tcp_minmssoverload) we take a closer look at it and compute the + * average packet size for the past second. If that is less than + * tcp_minmss we get too many packets with very small payload which + * is not good and burdens our system (and every packet generates + * a wakeup to the process connected to our socket). We can reasonable + * expect this to be small packet DoS attack to exhaust our CPU + * cycles. + * + * Care has to be taken for the minimum packet overload value. This + * value defines the minimum number of packets per second before we + * start to worry. This must not be too low to avoid killing for + * example interactive connections with many small packets like + * telnet or SSH. + * + * Setting either tcp_minmssoverload or tcp_minmss to "0" disables + * this check. + * + * Account for packet if payload packet, skip over ACK, etc. + */ + if (tcp_minmss && tcp_minmssoverload && + tp->t_state == TCPS_ESTABLISHED && tlen > 0) { + if (tp->rcv_second > ticks) { + tp->rcv_pps++; + tp->rcv_byps += tlen + off; + if (tp->rcv_pps > tcp_minmssoverload) { + if ((tp->rcv_byps / tp->rcv_pps) < tcp_minmss) { + printf("too many small tcp packets from " + "%s:%u, av. %lubyte/packet, " + "dropping connection\n", +#ifdef INET6 + isipv6 ? + ip6_sprintf(&inp->inp_inc.inc6_faddr) : +#endif + inet_ntoa(inp->inp_inc.inc_faddr), + inp->inp_inc.inc_fport, + tp->rcv_byps / tp->rcv_pps); + tp = tcp_drop(tp, ECONNRESET); + tcpstat.tcps_minmssdrops++; + goto drop; + } + } + } else { + tp->rcv_second = ticks + hz; + tp->rcv_pps = 1; + tp->rcv_byps = tlen + off; + } + } + + /* * Segment received on connection. * Reset idle time and keep-alive timer. */ @@ -2691,6 +2746,11 @@ tcp_mss(tp, offer) default: /* + * Prevent DoS attack with too small MSS. Round up + * to at least minmss. + */ + offer = max(offer, tcp_minmss); + /* * Sanity check: make sure that maxopd will be large * enough to allow some data on segments even if the * all the option space is used (40bytes). Otherwise diff --git a/sys/netinet/tcp_reass.c b/sys/netinet/tcp_reass.c index 36f044484028..263dfd83f163 100644 --- a/sys/netinet/tcp_reass.c +++ b/sys/netinet/tcp_reass.c @@ -919,6 +919,61 @@ after_listen: panic("tcp_input: TCPS_LISTEN"); /* + * This is the second part of the MSS DoS prevention code (after + * minmss on the sending side) and it deals with too many too small + * tcp packets in a too short timeframe (1 second). + * + * For every full second we count the number of received packets + * and bytes. If we get a lot of packets per second for this connection + * (tcp_minmssoverload) we take a closer look at it and compute the + * average packet size for the past second. If that is less than + * tcp_minmss we get too many packets with very small payload which + * is not good and burdens our system (and every packet generates + * a wakeup to the process connected to our socket). We can reasonable + * expect this to be small packet DoS attack to exhaust our CPU + * cycles. + * + * Care has to be taken for the minimum packet overload value. This + * value defines the minimum number of packets per second before we + * start to worry. This must not be too low to avoid killing for + * example interactive connections with many small packets like + * telnet or SSH. + * + * Setting either tcp_minmssoverload or tcp_minmss to "0" disables + * this check. + * + * Account for packet if payload packet, skip over ACK, etc. + */ + if (tcp_minmss && tcp_minmssoverload && + tp->t_state == TCPS_ESTABLISHED && tlen > 0) { + if (tp->rcv_second > ticks) { + tp->rcv_pps++; + tp->rcv_byps += tlen + off; + if (tp->rcv_pps > tcp_minmssoverload) { + if ((tp->rcv_byps / tp->rcv_pps) < tcp_minmss) { + printf("too many small tcp packets from " + "%s:%u, av. %lubyte/packet, " + "dropping connection\n", +#ifdef INET6 + isipv6 ? + ip6_sprintf(&inp->inp_inc.inc6_faddr) : +#endif + inet_ntoa(inp->inp_inc.inc_faddr), + inp->inp_inc.inc_fport, + tp->rcv_byps / tp->rcv_pps); + tp = tcp_drop(tp, ECONNRESET); + tcpstat.tcps_minmssdrops++; + goto drop; + } + } + } else { + tp->rcv_second = ticks + hz; + tp->rcv_pps = 1; + tp->rcv_byps = tlen + off; + } + } + + /* * Segment received on connection. * Reset idle time and keep-alive timer. */ @@ -2691,6 +2746,11 @@ tcp_mss(tp, offer) default: /* + * Prevent DoS attack with too small MSS. Round up + * to at least minmss. + */ + offer = max(offer, tcp_minmss); + /* * Sanity check: make sure that maxopd will be large * enough to allow some data on segments even if the * all the option space is used (40bytes). Otherwise diff --git a/sys/netinet/tcp_subr.c b/sys/netinet/tcp_subr.c index 60709fe51647..4b56295f4bc4 100644 --- a/sys/netinet/tcp_subr.c +++ b/sys/netinet/tcp_subr.c @@ -121,6 +121,30 @@ SYSCTL_INT(_net_inet_tcp, TCPCTL_V6MSSDFLT, v6mssdflt, "Default TCP Maximum Segment Size for IPv6"); #endif +/* + * Minimum MSS we accept and use. This prevents DoS attacks where + * we are forced to a ridiculous low MSS like 20 and send hundreds + * of packets instead of one. The effect scales with the available + * bandwidth and quickly saturates the CPU and network interface + * with packet generation and sending. Set to zero to disable MINMSS + * checking. This setting prevents us from sending too small packets. + */ +int tcp_minmss = TCP_MINMSS; +SYSCTL_INT(_net_inet_tcp, OID_AUTO, minmss, CTLFLAG_RW, + &tcp_minmss , 0, "Minmum TCP Maximum Segment Size"); +/* + * Number of TCP segments per second we accept from remote host + * before we start to calculate average segment size. If average + * segment size drops below the minimum TCP MSS we assume a DoS + * attack and reset+drop the connection. Care has to be taken not to + * set this value too small to not kill interactive type connections + * (telnet, SSH) which send many small packets. + */ +int tcp_minmssoverload = TCP_MINMSSOVERLOAD; +SYSCTL_INT(_net_inet_tcp, OID_AUTO, minmssoverload, CTLFLAG_RW, + &tcp_minmssoverload , 0, "Number of TCP Segments per Second allowed to" + "be under the MINMSS Size"); + #if 0 static int tcp_rttdflt = TCPTV_SRTTDFLT / PR_SLOWHZ; SYSCTL_INT(_net_inet_tcp, TCPCTL_RTTDFLT, rttdflt, CTLFLAG_RW, diff --git a/sys/netinet/tcp_timewait.c b/sys/netinet/tcp_timewait.c index 60709fe51647..4b56295f4bc4 100644 --- a/sys/netinet/tcp_timewait.c +++ b/sys/netinet/tcp_timewait.c @@ -121,6 +121,30 @@ SYSCTL_INT(_net_inet_tcp, TCPCTL_V6MSSDFLT, v6mssdflt, "Default TCP Maximum Segment Size for IPv6"); #endif +/* + * Minimum MSS we accept and use. This prevents DoS attacks where + * we are forced to a ridiculous low MSS like 20 and send hundreds + * of packets instead of one. The effect scales with the available + * bandwidth and quickly saturates the CPU and network interface + * with packet generation and sending. Set to zero to disable MINMSS + * checking. This setting prevents us from sending too small packets. + */ +int tcp_minmss = TCP_MINMSS; +SYSCTL_INT(_net_inet_tcp, OID_AUTO, minmss, CTLFLAG_RW, + &tcp_minmss , 0, "Minmum TCP Maximum Segment Size"); +/* + * Number of TCP segments per second we accept from remote host + * before we start to calculate average segment size. If average + * segment size drops below the minimum TCP MSS we assume a DoS + * attack and reset+drop the connection. Care has to be taken not to + * set this value too small to not kill interactive type connections + * (telnet, SSH) which send many small packets. + */ +int tcp_minmssoverload = TCP_MINMSSOVERLOAD; +SYSCTL_INT(_net_inet_tcp, OID_AUTO, minmssoverload, CTLFLAG_RW, + &tcp_minmssoverload , 0, "Number of TCP Segments per Second allowed to" + "be under the MINMSS Size"); + #if 0 static int tcp_rttdflt = TCPTV_SRTTDFLT / PR_SLOWHZ; SYSCTL_INT(_net_inet_tcp, TCPCTL_RTTDFLT, rttdflt, CTLFLAG_RW, diff --git a/sys/netinet/tcp_usrreq.c b/sys/netinet/tcp_usrreq.c index 985d3b5de139..3d161ab90581 100644 --- a/sys/netinet/tcp_usrreq.c +++ b/sys/netinet/tcp_usrreq.c @@ -1102,7 +1102,8 @@ tcp_ctloutput(so, sopt) if (error) break; - if (optval > 0 && optval <= tp->t_maxseg) + if (optval > 0 && optval <= tp->t_maxseg && + optval + 40 >= tcp_minmss) tp->t_maxseg = optval; else error = EINVAL; diff --git a/sys/netinet/tcp_var.h b/sys/netinet/tcp_var.h index ddcfd3cb9f0c..33353b47c2f9 100644 --- a/sys/netinet/tcp_var.h +++ b/sys/netinet/tcp_var.h @@ -179,6 +179,10 @@ struct tcpcb { tcp_seq snd_recover_prev; /* snd_recover prior to retransmit */ u_long t_badrxtwin; /* window for retransmit recovery */ u_char snd_limited; /* segments limited transmitted */ +/* anti DoS counters */ + u_long rcv_second; /* start of interval second */ + u_long rcv_pps; /* received packets per second */ + u_long rcv_byps; /* received bytes per second */ }; #define IN_FASTRECOVERY(tp) (tp->t_flags & TF_FASTRECOVERY) @@ -332,6 +336,7 @@ struct tcpstat { u_long tcps_connects; /* connections established */ u_long tcps_drops; /* connections dropped */ u_long tcps_conndrops; /* embryonic connections dropped */ + u_long tcps_minmssdrops; /* average minmss too low drops */ u_long tcps_closed; /* conn. closed (includes drops) */ u_long tcps_segstimed; /* segs where we tried to get rtt */ u_long tcps_rttupdated; /* times we succeeded */ @@ -473,6 +478,8 @@ extern struct inpcbhead tcb; /* head of queue of active tcpcb's */ extern struct inpcbinfo tcbinfo; extern struct tcpstat tcpstat; /* tcp statistics */ extern int tcp_mssdflt; /* XXX */ +extern int tcp_minmss; +extern int tcp_minmssoverload; extern int tcp_delack_enabled; extern int tcp_do_newreno; extern int path_mtu_discovery; |
