diff packages/net/bsd_tcpip/current/src/sys/netinet/tcp_input.c @ 3013:286a2ab55044

* src/sys/netinet/if_ether.c: Merge fix from FreeBSD CVS r1.108. Prevents creation of reciprocal ARP cache entries for hosts not on local subnet. * cdl/freebsd_net.cdl: * include/netinet/tcp_var.c: * src/sys/netinet/tcp_subr.c: * src/sys/netinet/tcp_input.c: Merge fix from FreeBSD CVS. Introduces a maximum size for the TCP reassembly queue to limit network buffer usage associated with reassembly. Enable CYGNUM_NET_TCP_REASS_DIVISOR and set it to configure the maximum number of buffers usable for TCP reassembly.
author jlarmour
date Tue, 22 Feb 2011 04:36:20 +0000
parents 74dbf4c3f2e1
children 9c050c918d4a
line wrap: on
line diff
--- a/packages/net/bsd_tcpip/current/src/sys/netinet/tcp_input.c
+++ b/packages/net/bsd_tcpip/current/src/sys/netinet/tcp_input.c
@@ -132,6 +132,26 @@ SYSCTL_INT(_net_inet_tcp, OID_AUTO, drop
     &drop_synfin, 0, "Drop TCP packets with SYN+FIN set");
 #endif
 
+#ifdef CYGNUM_NET_TCP_REASS_DIVISOR
+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");
+#endif
+
 struct inpcbhead tcb;
 #define	tcb6	tcb  /* for KAME src sync over BSD*'s */
 struct inpcbinfo tcbinfo;
@@ -185,6 +205,23 @@ 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.
+	 */
+#ifdef CYGNUM_NET_TCP_REASS_DIVISOR
+	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);
+	}
+#endif
+	
 	/* 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);
@@ -193,6 +230,9 @@ tcp_reass(tp, th, tlenp, m)
 		m_freem(m);
 		return (0);
 	}
+#ifdef CYGNUM_NET_TCP_REASS_DIVISOR
+	tcp_reass_qsize++;
+#endif
 
 	/*
 	 * Find a segment which begins after this one does.
@@ -218,6 +258,9 @@ tcp_reass(tp, th, tlenp, m)
 				tcpstat.tcps_rcvdupbyte += *tlenp;
 				m_freem(m);
 				FREE(te, M_TSEGQ);
+#ifdef CYGNUM_NET_TCP_REASS_DIVISOR
+				tcp_reass_qsize--;
+#endif
 				/*
 				 * Try to present any queued data
 				 * at the left window edge to the user.
@@ -253,6 +296,9 @@ tcp_reass(tp, th, tlenp, m)
 		LIST_REMOVE(q, tqe_q);
 		m_freem(q->tqe_m);
 		FREE(q, M_TSEGQ);
+#ifdef CYGNUM_NET_TCP_REASS_DIVISOR
+		tcp_reass_qsize--;
+#endif
 		q = nq;
 	}
 
@@ -287,6 +333,9 @@ present:
 		else
 			sbappend(&so->so_rcv, q->tqe_m);
 		FREE(q, M_TSEGQ);
+#ifdef CYGNUM_NET_TCP_REASS_DIVISOR
+		tcp_reass_qsize--;
+#endif
 		q = nq;
 	} while (q && q->tqe_th->th_seq == tp->rcv_nxt);
 	ND6_HINT(tp);