X-Git-Url: https://git.saurik.com/apple/xnu.git/blobdiff_plain/43866e378188c25dd1e2208016ab3cbeb086ae6c..5eebf7385fedb1517b66b53c28e5aa6bb0a2be50:/bsd/netinet/tcp_input.c diff --git a/bsd/netinet/tcp_input.c b/bsd/netinet/tcp_input.c index 5ecafc97c..46fe20ed1 100644 --- a/bsd/netinet/tcp_input.c +++ b/bsd/netinet/tcp_input.c @@ -3,22 +3,19 @@ * * @APPLE_LICENSE_HEADER_START@ * - * Copyright (c) 1999-2003 Apple Computer, Inc. All Rights Reserved. + * The contents of this file constitute Original Code as defined in and + * are subject to the Apple Public Source License Version 1.1 (the + * "License"). You may not use this file except in compliance with the + * License. Please obtain a copy of the License at + * http://www.apple.com/publicsource and read it before using this file. * - * This file contains Original Code and/or Modifications of Original Code - * as defined in and that are subject to the Apple Public Source License - * Version 2.0 (the 'License'). You may not use this file except in - * compliance with the License. Please obtain a copy of the License at - * http://www.opensource.apple.com/apsl/ and read it before using this - * file. - * - * The Original Code and all software distributed under the License are - * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * This Original Code and all software distributed under the License are + * distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, EITHER * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. - * Please see the License for the specific language governing rights and - * limitations under the License. + * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT. Please see the + * License for the specific language governing rights and limitations + * under the License. * * @APPLE_LICENSE_HEADER_END@ */ @@ -157,11 +154,30 @@ SYSCTL_INT(_net_inet_tcp, OID_AUTO, tcp_lq_overflow, CTLFLAG_RW, "Listen Queue Overflow"); #if TCP_DROP_SYNFIN -static int drop_synfin = 0; +static int drop_synfin = 1; 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"); + +__private_extern__ int tcp_reass_maxseg = 0; +SYSCTL_INT(_net_inet_tcp_reass, OID_AUTO, maxsegments, CTLFLAG_RW, + &tcp_reass_maxseg, 0, + "Global maximum number of TCP Segments in Reassembly Queue"); + +__private_extern__ 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"); + + __private_extern__ int slowlink_wsize = 8192; SYSCTL_INT(_net_inet_tcp, OID_AUTO, slowlink_wsize, CTLFLAG_RW, &slowlink_wsize, 0, "Maximum advertised window size for slowlink"); @@ -229,6 +245,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); @@ -237,6 +268,7 @@ tcp_reass(tp, th, tlenp, m) m_freem(m); return (0); } + tcp_reass_qsize++; /* * Find a segment which begins after this one does. @@ -262,6 +294,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. @@ -297,6 +330,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; } @@ -331,6 +365,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); @@ -365,9 +400,9 @@ present: */ #if INET6 int -tcp6_input(mp, offp, proto) +tcp6_input(mp, offp) struct mbuf **mp; - int *offp, proto; + int *offp; { register struct mbuf *m = *mp; struct in6_ifaddr *ia6; @@ -800,6 +835,7 @@ findpcb: #if INET6 struct inpcb *oinp = sotoinpcb(so); #endif /* INET6 */ + int ogencnt = so->so_gencnt; #if !IPSEC /* @@ -879,6 +915,12 @@ findpcb: if (!so2) goto drop; } + /* + * Make sure listening socket did not get closed during socket allocation, + * not only this is incorrect but it is know to cause panic + */ + if (so->so_gencnt != ogencnt) + goto drop; #if IPSEC oso = so; #endif @@ -1000,7 +1042,7 @@ findpcb: */ tp->t_rcvtime = 0; if (TCPS_HAVEESTABLISHED(tp->t_state)) - tp->t_timer[TCPT_KEEP] = tcp_keepidle; + tp->t_timer[TCPT_KEEP] = TCP_KEEPIDLE(tp); /* * Process options if not in LISTEN state, @@ -1499,7 +1541,7 @@ findpcb: thflags &= ~TH_SYN; } else { tp->t_state = TCPS_ESTABLISHED; - tp->t_timer[TCPT_KEEP] = tcp_keepidle; + tp->t_timer[TCPT_KEEP] = TCP_KEEPIDLE(tp); } } else { /* @@ -1527,7 +1569,7 @@ findpcb: tp->t_flags &= ~TF_NEEDFIN; } else { tp->t_state = TCPS_ESTABLISHED; - tp->t_timer[TCPT_KEEP] = tcp_keepidle; + tp->t_timer[TCPT_KEEP] = TCP_KEEPIDLE(tp); } tp->t_flags |= TF_NEEDSYN; } else @@ -1598,6 +1640,16 @@ trimthenstep6: goto drop; } break; /* continue normal processing */ + + /* Received a SYN while connection is already established. + * This is a "half open connection and other anomalies" described + * in RFC793 page 34, send an ACK so the remote reset the connection + * or recovers by adjusting its sequence numberering + */ + case TCPS_ESTABLISHED: + if (thflags & TH_SYN) + goto dropafterack; + break; } /* @@ -1918,7 +1970,7 @@ trimthenstep6: tp->t_flags &= ~TF_NEEDFIN; } else { tp->t_state = TCPS_ESTABLISHED; - tp->t_timer[TCPT_KEEP] = tcp_keepidle; + tp->t_timer[TCPT_KEEP] = TCP_KEEPIDLE(tp); } /* * If segment contains data or ACK, will call tcp_reass() @@ -2905,7 +2957,12 @@ tcp_mss(tp, offer) isipv6 ? tcp_v6mssdflt : #endif /* INET6 */ tcp_mssdflt; - else + else { + /* + * 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 is the @@ -2913,6 +2970,7 @@ tcp_mss(tp, offer) * funny things may happen in tcp_output. */ offer = max(offer, 64); + } taop->tao_mssopt = offer; /* @@ -2992,21 +3050,16 @@ tcp_mss(tp, offer) (tp->t_flags & TF_RCVD_CC) == TF_RCVD_CC)) mss -= TCPOLEN_CC_APPA; -#if (MCLBYTES & (MCLBYTES - 1)) == 0 - if (mss > MCLBYTES) - mss &= ~(MCLBYTES-1); -#else - if (mss > MCLBYTES) - mss = mss / MCLBYTES * MCLBYTES; -#endif /* - * If there's a pipesize, change the socket buffer - * to that size. Make the socket buffers an integral + * If there's a pipesize (ie loopback), change the socket + * buffer to that size only if it's bigger than the current + * sockbuf size. Make the socket buffers an integral * number of mss units; if the mss is larger than * the socket buffer, decrease the mss. */ #if RTV_SPIPE - if ((bufsize = rt->rt_rmx.rmx_sendpipe) == 0) + bufsize = rt->rt_rmx.rmx_sendpipe; + if (bufsize < so->so_snd.sb_hiwat) #endif bufsize = so->so_snd.sb_hiwat; if (bufsize < mss) @@ -3020,7 +3073,8 @@ tcp_mss(tp, offer) tp->t_maxseg = mss; #if RTV_RPIPE - if ((bufsize = rt->rt_rmx.rmx_recvpipe) == 0) + bufsize = rt->rt_rmx.rmx_recvpipe; + if (bufsize < so->so_rcv.sb_hiwat) #endif bufsize = so->so_rcv.sb_hiwat; if (bufsize > mss) {