]> git.saurik.com Git - apple/xnu.git/blobdiff - bsd/netinet/tcp_input.c
xnu-517.9.5.tar.gz
[apple/xnu.git] / bsd / netinet / tcp_input.c
index 5ecafc97c2f1a191fe0451c6e1f848ab623100ff..46fe20ed1b63578a78ac653abd696e54d9daabd5 100644 (file)
@@ -3,22 +3,19 @@
  *
  * @APPLE_LICENSE_HEADER_START@
  * 
  *
  * @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,
  * 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@
  */
  * 
  * @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
     "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_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");
 __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;
 
        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);
        /* 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);
        }
                m_freem(m);
                return (0);
        }
+       tcp_reass_qsize++;
 
        /*
         * Find a segment which begins after this one does.
 
        /*
         * 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);
                                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.
                                /*
                                 * 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);
                LIST_REMOVE(q, tqe_q);
                m_freem(q->tqe_m);
                FREE(q, M_TSEGQ);
+               tcp_reass_qsize--;
                q = nq;
        }
 
                q = nq;
        }
 
@@ -331,6 +365,7 @@ present:
                else
                        sbappend(&so->so_rcv, q->tqe_m);
                FREE(q, M_TSEGQ);
                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);
                q = nq;
        } while (q && q->tqe_th->th_seq == tp->rcv_nxt);
        ND6_HINT(tp);
@@ -365,9 +400,9 @@ present:
  */
 #if INET6
 int
  */
 #if INET6
 int
-tcp6_input(mp, offp, proto)
+tcp6_input(mp, offp)
        struct mbuf **mp;
        struct mbuf **mp;
-       int *offp, proto;
+       int *offp;
 {
        register struct mbuf *m = *mp;
        struct in6_ifaddr *ia6;
 {
        register struct mbuf *m = *mp;
        struct in6_ifaddr *ia6;
@@ -800,6 +835,7 @@ findpcb:
 #if INET6
                        struct inpcb *oinp = sotoinpcb(so);
 #endif /* INET6 */
 #if INET6
                        struct inpcb *oinp = sotoinpcb(so);
 #endif /* INET6 */
+                       int ogencnt = so->so_gencnt;
 
 #if !IPSEC
                        /*
 
 #if !IPSEC
                        /*
@@ -879,6 +915,12 @@ findpcb:
                                if (!so2)
                                        goto drop;
                        }
                                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
 #if IPSEC
                        oso = so;
 #endif
@@ -1000,7 +1042,7 @@ findpcb:
         */
        tp->t_rcvtime = 0;
        if (TCPS_HAVEESTABLISHED(tp->t_state))
         */
        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,
 
        /*
         * Process options if not in LISTEN state,
@@ -1499,7 +1541,7 @@ findpcb:
                                thflags &= ~TH_SYN;
                        } else {
                                tp->t_state = TCPS_ESTABLISHED;
                                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 {
                /*
                        }
                } else {
                /*
@@ -1527,7 +1569,7 @@ findpcb:
                                                tp->t_flags &= ~TF_NEEDFIN;
                                        } else {
                                                tp->t_state = TCPS_ESTABLISHED;
                                                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
                                        }
                                        tp->t_flags |= TF_NEEDSYN;
                                } else
@@ -1598,6 +1640,16 @@ trimthenstep6:
                                goto drop;
                }
                break;  /* continue normal processing */
                                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_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()
                }
                /*
                 * 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;
                        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
                /*
                 * 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);
                 * funny things may happen in tcp_output.
                 */
                offer = max(offer, 64);
+       }
        taop->tao_mssopt = offer;
 
        /*
        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;
 
             (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
         * 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)
 #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
        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) {
 #endif
                bufsize = so->so_rcv.sb_hiwat;
        if (bufsize > mss) {