aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJacques Vidrine <nectar@FreeBSD.org>2004-03-02 17:27:47 +0000
committerJacques Vidrine <nectar@FreeBSD.org>2004-03-02 17:27:47 +0000
commit1540e0028186a7a1040eb9936cad01b035ca7f91 (patch)
treec4fa4fe8628a0d56fed171f1df3ba0f902a62ff1
parent5087fc241c22e7289028c239b614abadaf20853b (diff)
downloadsrc-1540e0028186a7a1040eb9936cad01b035ca7f91.tar.gz
src-1540e0028186a7a1040eb9936cad01b035ca7f91.zip
MFC in part tcp_input.c 1.228, tcp_subr.c 1.182, tcp_var.h 1.98:
Limit TCP segment reassembly queue size.
Notes
Notes: svn path=/releng/4.9/; revision=126497
-rw-r--r--UPDATING3
-rw-r--r--sys/conf/newvers.sh2
-rw-r--r--sys/netinet/tcp_input.c37
-rw-r--r--sys/netinet/tcp_subr.c7
-rw-r--r--sys/netinet/tcp_var.h2
5 files changed, 50 insertions, 1 deletions
diff --git a/UPDATING b/UPDATING
index 9c8471111194..ea979b1f074d 100644
--- a/UPDATING
+++ b/UPDATING
@@ -17,6 +17,9 @@ minimal number of processes, if possible, for that patch. For those
updates that don't have an advisory, or to be safe, you can do a full
build and install as described in the COMMON ITEMS section.
+20040302: p3 FreeBSD-SA-04:04.tcp
+ Limit TCP segment reassembly queue size.
+
20040205: p2 FreeBSD-SA-04:02.shmat
Correct a reference counting bug in shmat(2).
diff --git a/sys/conf/newvers.sh b/sys/conf/newvers.sh
index 44a1f1b79ed0..d314bc0a1b46 100644
--- a/sys/conf/newvers.sh
+++ b/sys/conf/newvers.sh
@@ -36,7 +36,7 @@
TYPE="FreeBSD"
REVISION="4.9"
-BRANCH="RELEASE-p2"
+BRANCH="RELEASE-p3"
RELEASE="${REVISION}-${BRANCH}"
VERSION="${TYPE} ${RELEASE}"
diff --git a/sys/netinet/tcp_input.c b/sys/netinet/tcp_input.c
index e20be8774824..39772282d292 100644
--- a/sys/netinet/tcp_input.c
+++ b/sys/netinet/tcp_input.c
@@ -125,6 +125,24 @@ SYSCTL_INT(_net_inet_tcp, OID_AUTO, drop_synfin, CTLFLAG_RW,
&drop_synfin, 0, "Drop TCP packets with SYN+FIN set");
#endif
+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;
@@ -182,6 +200,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);
@@ -190,6 +223,7 @@ tcp_reass(tp, th, tlenp, m)
m_freem(m);
return (0);
}
+ tcp_reass_qsize++;
/*
* Find a segment which begins after this one does.
@@ -215,6 +249,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.
@@ -250,6 +285,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;
}
@@ -284,6 +320,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 1bc891c096ac..dc6300467666 100644
--- a/sys/netinet/tcp_subr.c
+++ b/sys/netinet/tcp_subr.c
@@ -245,6 +245,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 */
@@ -749,6 +754,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);
@@ -786,6 +792,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 6236764e5cdb..6521455a3a89 100644
--- a/sys/netinet/tcp_var.h
+++ b/sys/netinet/tcp_var.h
@@ -53,6 +53,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