Author: pkelsey
Date: Wed Jul 29 17:59:13 2015
New Revision: 286027
URL: https://svnweb.freebsd.org/changeset/base/286027
Log:
Revert r265338, r271089 and r271123 as those changes do not handle
non-inline urgent data and introduce an mbuf exhaustion attack vector
similar to FreeBSD-SA-15:15.tcp, but not requiring VNETs.
Address the issue described in FreeBSD-SA-15:15.tcp.
Reviewed by: glebius
Approved by: so
Approved by: jmallett (mentor)
Security: FreeBSD-SA-15:15.tcp
Sponsored by: Norse Corp, Inc.
Modified:
head/sys/netinet/tcp_input.c
head/sys/netinet/tcp_reass.c
head/sys/netinet/tcp_subr.c
head/sys/netinet/tcp_usrreq.c
head/sys/netinet/tcp_var.h
head/sys/sys/mbuf.h
Modified: head/sys/netinet/tcp_input.c
==
--- head/sys/netinet/tcp_input.cWed Jul 29 17:50:14 2015
(r286026)
+++ head/sys/netinet/tcp_input.cWed Jul 29 17:59:13 2015
(r286027)
@@ -1665,7 +1665,8 @@ tcp_do_segment(struct mbuf *m, struct tc
tp-snd_nxt == tp-snd_max
tiwin tiwin == tp-snd_wnd
((tp-t_flags (TF_NEEDSYN|TF_NEEDFIN)) == 0)
- tp-t_segq == NULL ((to.to_flags TOF_TS) == 0 ||
+ LIST_EMPTY(tp-t_segq)
+ ((to.to_flags TOF_TS) == 0 ||
TSTMP_GEQ(to.to_tsval, tp-ts_recent)) ) {
/*
@@ -2903,7 +2904,8 @@ dodata:
/* XXX */
* immediately when segments are out of order (so
* fast retransmit can work).
*/
- if (th-th_seq == tp-rcv_nxt tp-t_segq == NULL
+ if (th-th_seq == tp-rcv_nxt
+ LIST_EMPTY(tp-t_segq)
TCPS_HAVEESTABLISHED(tp-t_state)) {
if (DELAY_ACK(tp, tlen))
tp-t_flags |= TF_DELACK;
Modified: head/sys/netinet/tcp_reass.c
==
--- head/sys/netinet/tcp_reass.cWed Jul 29 17:50:14 2015
(r286026)
+++ head/sys/netinet/tcp_reass.cWed Jul 29 17:59:13 2015
(r286027)
@@ -71,33 +71,80 @@ __FBSDID($FreeBSD$);
#include netinet/tcp_var.h
#include netinet6/tcp6_var.h
#include netinet/tcpip.h
+#ifdef TCPDEBUG
+#include netinet/tcp_debug.h
+#endif /* TCPDEBUG */
+
+static SYSCTL_NODE(_net_inet_tcp, OID_AUTO, reass, CTLFLAG_RW, 0,
+TCP Segment Reassembly Queue);
+
+static int tcp_reass_maxseg = 0;
+SYSCTL_INT(_net_inet_tcp_reass, OID_AUTO, maxsegments, CTLFLAG_RDTUN,
+tcp_reass_maxseg, 0,
+Global maximum number of TCP Segments in Reassembly Queue);
+
+static uma_zone_t tcp_reass_zone;
+SYSCTL_UMA_CUR(_net_inet_tcp_reass, OID_AUTO, cursegments, CTLFLAG_VNET,
+tcp_reass_zone,
+Global number of TCP Segments currently in Reassembly Queue);
+
+/* Initialize TCP reassembly queue */
+static void
+tcp_reass_zone_change(void *tag)
+{
+
+ /* Set the zone limit and read back the effective value. */
+ tcp_reass_maxseg = nmbclusters / 16;
+ tcp_reass_maxseg = uma_zone_set_max(tcp_reass_zone,
+ tcp_reass_maxseg);
+}
+
+void
+tcp_reass_global_init(void)
+{
+
+ tcp_reass_maxseg = nmbclusters / 16;
+ TUNABLE_INT_FETCH(net.inet.tcp.reass.maxsegments,
+ tcp_reass_maxseg);
+ tcp_reass_zone = uma_zcreate(tcpreass, sizeof (struct tseg_qent),
+ NULL, NULL, NULL, NULL, UMA_ALIGN_PTR, UMA_ZONE_NOFREE);
+ /* Set the zone limit and read back the effective value. */
+ tcp_reass_maxseg = uma_zone_set_max(tcp_reass_zone,
+ tcp_reass_maxseg);
+ EVENTHANDLER_REGISTER(nmbclusters_change,
+ tcp_reass_zone_change, NULL, EVENTHANDLER_PRI_ANY);
+}
void
tcp_reass_flush(struct tcpcb *tp)
{
- struct mbuf *m;
+ struct tseg_qent *qe;
INP_WLOCK_ASSERT(tp-t_inpcb);
- while ((m = tp-t_segq) != NULL) {
- tp-t_segq = m-m_nextpkt;
- tp-t_segqlen -= m-m_pkthdr.len;
- m_freem(m);
+ while ((qe = LIST_FIRST(tp-t_segq)) != NULL) {
+ LIST_REMOVE(qe, tqe_q);
+ m_freem(qe-tqe_m);
+ uma_zfree(tcp_reass_zone, qe);
+ tp-t_segqlen--;
}
KASSERT((tp-t_segqlen == 0),
- (TCP reass queue %p length is %d instead of 0 after flush.,
+ (TCP reass queue %p segment count is %d instead of 0 after flush.,
tp, tp-t_segqlen));
}
-#defineM_TCPHDR(m) ((struct tcphdr *)((m)-m_pkthdr.pkt_tcphdr))
-
int
tcp_reass(struct tcpcb *tp, struct tcphdr *th, int *tlenp, struct mbuf *m)
{
+ struct tseg_qent *q;
+ struct tseg_qent *p = NULL;
+ struct tseg_qent *nq;
+ struct tseg_qent *te = NULL;
struct socket *so = tp-t_inpcb-inp_socket;
- struct mbuf *mq, *mp;
- int flags, wakeup;
+