From 3edf7c371e7f1c1332a219dc2ffc0c50ac28a6bc Mon Sep 17 00:00:00 2001 From: Robert Garrett Date: Tue, 2 Mar 2004 20:41:13 +0000 Subject: [PATCH] Patch forr FreeBSD-SA-04:04.tcp limits out of sequence reassembly queue size, to make sure we don't run out of mbufs, resulting in a DOS attack. This is the same as tcp47.patch checked by Robert Garrett & Joerg Sonnenberger --- sys/netinet/tcp_input.c | 39 ++++++++++++++++++++++++++++++++++++++- sys/netinet/tcp_subr.c | 9 ++++++++- sys/netinet/tcp_var.h | 4 +++- 3 files changed, 49 insertions(+), 3 deletions(-) diff --git a/sys/netinet/tcp_input.c b/sys/netinet/tcp_input.c index d8260b2b46..b1b6c2557a 100644 --- a/sys/netinet/tcp_input.c +++ b/sys/netinet/tcp_input.c @@ -33,7 +33,7 @@ * * @(#)tcp_input.c 8.12 (Berkeley) 5/24/95 * $FreeBSD: src/sys/netinet/tcp_input.c,v 1.107.2.38 2003/05/21 04:46:41 cjc Exp $ - * $DragonFly: src/sys/netinet/tcp_input.c,v 1.14 2004/02/25 08:46:28 hsu Exp $ + * $DragonFly: src/sys/netinet/tcp_input.c,v 1.15 2004/03/02 20:41:13 rob Exp $ */ #include "opt_ipfw.h" /* for ipfw_fwd */ @@ -139,6 +139,24 @@ static int tcp_do_eifel_detect = 1; SYSCTL_INT(_net_inet_tcp, OID_AUTO, eifel, CTLFLAG_RW, &tcp_do_eifel_detect, 0, "Eifel detection algorithm (RFC 3522)"); +SYSCTL_NODE(_net_inet_tcp, OID_AUTO, reass, CTLFLAG_RW, 0, + "TCP Segment Reassembly Queue"); + +int tcp_reass_maxseg = 0; +SYSCTL_INT(_net_inet_tcp_reass, OID_AUTO, maxsegments, CTLFLAG_RD, + &tcp_reass_maxseg, 0, + "Global maximum number of TCP Segments in Reassembly Queue"); + +int tcp_reass_qsize = 0; +SYSCTL_INT(_net_inet_tcp_reass, OID_AUTO, cursegments, CTLFLAG_RD, + &tcp_reass_qsize, 0, + "Global number of TCP Segments currently in Reassembly Queue"); + +static int tcp_reass_overflows = 0; +SYSCTL_INT(_net_inet_tcp_reass, OID_AUTO, overflows, CTLFLAG_RD, + &tcp_reass_overflows, 0, + "Global number of TCP Segment Reassembly Queue Overflows"); + struct inpcbhead tcb; #define tcb6 tcb /* for KAME src sync over BSD*'s */ struct inpcbinfo tcbinfo; @@ -196,6 +214,21 @@ tcp_reass(tp, th, tlenp, m) if (th == 0) goto present; + /* + * Limit the number of segments in the reassembly queue to prevent + * holding on to too many segments (and thus running out of mbufs). + * Make sure to let the missing segment through which caused this + * queue. Always keep one global queue entry spare to be able to + * process the missing segment. + */ + if (th->th_seq != tp->rcv_nxt && + tcp_reass_qsize + 1 >= tcp_reass_maxseg) { + tcp_reass_overflows++; + tcpstat.tcps_rcvmemdrop++; + m_freem(m); + return (0); + } + /* Allocate a new queue entry. If we can't, just drop the pkt. XXX */ MALLOC(te, struct tseg_qent *, sizeof(struct tseg_qent), M_TSEGQ, M_NOWAIT); @@ -204,6 +237,7 @@ tcp_reass(tp, th, tlenp, m) m_freem(m); return (0); } + tcp_reass_qsize++; /* * Find a segment which begins after this one does. @@ -229,6 +263,7 @@ tcp_reass(tp, th, tlenp, m) tcpstat.tcps_rcvdupbyte += *tlenp; m_freem(m); free(te, M_TSEGQ); + tcp_reass_qsize--; /* * Try to present any queued data * at the left window edge to the user. @@ -264,6 +299,7 @@ tcp_reass(tp, th, tlenp, m) LIST_REMOVE(q, tqe_q); m_freem(q->tqe_m); free(q, M_TSEGQ); + tcp_reass_qsize--; q = nq; } @@ -298,6 +334,7 @@ present: else sbappend(&so->so_rcv, q->tqe_m); free(q, M_TSEGQ); + tcp_reass_qsize--; q = nq; } while (q && q->tqe_th->th_seq == tp->rcv_nxt); ND6_HINT(tp); diff --git a/sys/netinet/tcp_subr.c b/sys/netinet/tcp_subr.c index 8854333062..94db77efbe 100644 --- a/sys/netinet/tcp_subr.c +++ b/sys/netinet/tcp_subr.c @@ -32,7 +32,7 @@ * * @(#)tcp_subr.c 8.2 (Berkeley) 5/24/95 * $FreeBSD: src/sys/netinet/tcp_subr.c,v 1.73.2.31 2003/01/24 05:11:34 sam Exp $ - * $DragonFly: src/sys/netinet/tcp_subr.c,v 1.10 2004/02/14 21:12:39 dillon Exp $ + * $DragonFly: src/sys/netinet/tcp_subr.c,v 1.11 2004/03/02 20:41:13 rob Exp $ */ #include "opt_compat.h" @@ -246,6 +246,11 @@ tcp_init() &tcbinfo.porthashmask); tcbinfo.ipi_zone = zinit("tcpcb", sizeof(struct inp_tp), maxsockets, ZONE_INTERRUPT, 0); + + tcp_reass_maxseg = nmbclusters / 16; + TUNABLE_INT_FETCH("net.inet.tcp.reass.maxsegments", + &tcp_reass_maxseg); + #ifdef INET6 #define TCP_MINPROTOHDR (sizeof(struct ip6_hdr) + sizeof(struct tcphdr)) #else /* INET6 */ @@ -751,6 +756,7 @@ tcp_close(tp) LIST_REMOVE(q, tqe_q); m_freem(q->tqe_m); FREE(q, M_TSEGQ); + tcp_reass_qsize--; } inp->inp_ppcb = NULL; soisdisconnected(so); @@ -788,6 +794,7 @@ tcp_drain() LIST_REMOVE(te, tqe_q); m_freem(te->tqe_m); FREE(te, M_TSEGQ); + tcp_reass_qsize--; } } } diff --git a/sys/netinet/tcp_var.h b/sys/netinet/tcp_var.h index 53238f31b1..99b1a95559 100644 --- a/sys/netinet/tcp_var.h +++ b/sys/netinet/tcp_var.h @@ -32,7 +32,7 @@ * * @(#)tcp_var.h 8.4 (Berkeley) 5/24/95 * $FreeBSD: src/sys/netinet/tcp_var.h,v 1.56.2.13 2003/02/03 02:34:07 hsu Exp $ - * $DragonFly: src/sys/netinet/tcp_var.h,v 1.9 2003/11/08 07:57:51 dillon Exp $ + * $DragonFly: src/sys/netinet/tcp_var.h,v 1.10 2004/03/02 20:41:13 rob Exp $ */ #ifndef _NETINET_TCP_VAR_H_ @@ -54,6 +54,8 @@ struct tseg_qent { struct mbuf *tqe_m; /* mbuf contains packet */ }; LIST_HEAD(tsegqe_head, tseg_qent); +extern int tcp_reass_maxseg; +extern int tcp_reass_qsize; #ifdef MALLOC_DECLARE MALLOC_DECLARE(M_TSEGQ); #endif -- 2.41.0