X-Git-Url: https://git.saurik.com/apple/xnu.git/blobdiff_plain/de355530ae67247cbd0da700edb3a2a1dae884c2..89b3af67bb32e691275bf6fa803d1834b2284115:/bsd/netinet/tcp_input.c diff --git a/bsd/netinet/tcp_input.c b/bsd/netinet/tcp_input.c index eb294a6a2..a6ea7bf86 100644 --- a/bsd/netinet/tcp_input.c +++ b/bsd/netinet/tcp_input.c @@ -1,23 +1,29 @@ /* * Copyright (c) 2000 Apple Computer, Inc. All rights reserved. * - * @APPLE_LICENSE_HEADER_START@ + * @APPLE_OSREFERENCE_LICENSE_HEADER_START@ * - * 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. The rights granted to you under the License + * may not be used to create, or enable the creation or redistribution of, + * unlawful or unlicensed copies of an Apple operating system, or to + * circumvent, violate, or enable the circumvention or violation of, any + * terms of an Apple operating system software license agreement. * - * This Original Code and all software distributed under the License are - * distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * 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 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT. Please see the - * License for the specific language governing rights and limitations - * under the License. + * 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. * - * @APPLE_LICENSE_HEADER_END@ + * @APPLE_OSREFERENCE_LICENSE_HEADER_END@ */ /* * Copyright (c) 1982, 1986, 1988, 1990, 1993, 1994, 1995 @@ -71,6 +77,7 @@ #include /* before tcp_seq.h, for tcp_random18() */ #include +#include #include #include @@ -128,11 +135,10 @@ extern int apple_hwcksum_rx; #if IPSEC extern int ipsec_bypass; +extern lck_mtx_t *sadb_mutex; #endif struct tcpstat tcpstat; -SYSCTL_STRUCT(_net_inet_tcp, TCPCTL_STATS, stats, CTLFLAG_RD, - &tcpstat , tcpstat, "TCP statistics (struct tcpstat, netinet/tcp_var.h)"); static int log_in_vain = 0; SYSCTL_INT(_net_inet_tcp, OID_AUTO, log_in_vain, CTLFLAG_RW, @@ -142,7 +148,7 @@ static int blackhole = 0; SYSCTL_INT(_net_inet_tcp, OID_AUTO, blackhole, CTLFLAG_RW, &blackhole, 0, "Do not send RST when dropping refused connections"); -int tcp_delack_enabled = 1; +int tcp_delack_enabled = 3; SYSCTL_INT(_net_inet_tcp, OID_AUTO, delayed_ack, CTLFLAG_RW, &tcp_delack_enabled, 0, "Delay ACK to try and piggyback it onto a data packet"); @@ -153,24 +159,47 @@ 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"); + + u_long tcp_now; struct inpcbhead tcb; #define tcb6 tcb /* for KAME src sync over BSD*'s */ struct inpcbinfo tcbinfo; -static void tcp_dooptions __P((struct tcpcb *, - u_char *, int, struct tcphdr *, struct tcpopt *)); -static void tcp_pulloutofband __P((struct socket *, - struct tcphdr *, struct mbuf *, int)); -static int tcp_reass __P((struct tcpcb *, struct tcphdr *, int *, - struct mbuf *)); -static void tcp_xmit_timer __P((struct tcpcb *, int)); -static int tcp_newreno __P((struct tcpcb *, struct tcphdr *)); +static void tcp_dooptions(struct tcpcb *, + u_char *, int, struct tcphdr *, struct tcpopt *); +static void tcp_pulloutofband(struct socket *, + struct tcphdr *, struct mbuf *, int); +static int tcp_reass(struct tcpcb *, struct tcphdr *, int *, + struct mbuf *); +static void tcp_xmit_timer(struct tcpcb *, int); /* Neighbor Discovery, Neighbor Unreachability Detection Upper layer hint. */ #if INET6 @@ -187,16 +216,44 @@ do { \ extern u_long *delack_bitmask; +extern void ipfwsyslog( int level, char *format,...); +extern int ChkAddressOK( __uint32_t dstaddr, __uint32_t srcaddr ); +extern int fw_verbose; + +#define log_in_vain_log( a ) { \ + if ( (log_in_vain == 3 ) && (fw_verbose == 2)) { /* Apple logging, log to ipfw.log */ \ + ipfwsyslog a ; \ + } \ + else log a ; \ +} + /* - * Indicate whether this ack should be delayed. We can delay the ack if - * - delayed acks are enabled and - * - there is no delayed ack timer in progress and + * Indicate whether this ack should be delayed. + * We can delay the ack if: + * - delayed acks are enabled (set to 1) and * - our last ack wasn't a 0-sized window. We never want to delay - * the ack that opens up a 0-sized window. + * the ack that opens up a 0-sized window. + * - delayed acks are enabled (set to 2, "more compatible") and + * - our last ack wasn't a 0-sized window. + * - if the peer hasn't sent us a TH_PUSH data packet (this solves 3649245) + * - the peer hasn't sent us a TH_PUSH data packet, if he did, take this as a clue that we + * need to ACK with no delay. This helps higher level protocols who won't send + * us more data even if the window is open because their last "segment" hasn't been ACKed + * - delayed acks are enabled (set to 3, "streaming detection") and + * - if we receive more than 4 full packets per second on this socket, we're streaming acts as "1". + * - if we don't meet that criteria, acts like "2". Allowing faster acking while browsing for example. + * */ #define DELAY_ACK(tp) \ - (tcp_delack_enabled && !callout_pending(tp->tt_delack) && \ - (tp->t_flags & TF_RXWIN0SENT) == 0) + (((tcp_delack_enabled == 1) && ((tp->t_flags & TF_RXWIN0SENT) == 0)) || \ + (((tcp_delack_enabled == 2) && (tp->t_flags & TF_RXWIN0SENT) == 0) && \ + ((thflags & TH_PUSH) == 0) && ((tp->t_flags & TF_DELACK) == 0)) || \ + (((tcp_delack_enabled == 3) && (tp->t_flags & TF_RXWIN0SENT) == 0) && \ + (((tp->t_rcvtime == 0) && (tp->rcv_byps > (4* tp->t_maxseg))) || (((thflags & TH_PUSH) == 0) && ((tp->t_flags & TF_DELACK) == 0))))) + + +static int tcpdropdropablreq(struct socket *head); +static void tcp_newreno_partial_ack(struct tcpcb *tp, struct tcphdr *th); static int @@ -209,17 +266,33 @@ tcp_reass(tp, th, tlenp, m) struct tseg_qent *q; struct tseg_qent *p = NULL; struct tseg_qent *nq; - struct tseg_qent *te; + struct tseg_qent *te = NULL; struct socket *so = tp->t_inpcb->inp_socket; int flags; + int dowakeup = 0; /* * Call with th==0 after become established to * force pre-ESTABLISHED data up to user socket. */ - if (th == 0) + if (th == NULL) 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); @@ -228,6 +301,7 @@ tcp_reass(tp, th, tlenp, m) m_freem(m); return (0); } + tcp_reass_qsize++; /* * Find a segment which begins after this one does. @@ -253,6 +327,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. @@ -288,6 +363,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; } @@ -319,9 +395,12 @@ present: LIST_REMOVE(q, tqe_q); if (so->so_state & SS_CANTRCVMORE) m_freem(q->tqe_m); - else - sbappend(&so->so_rcv, q->tqe_m); + else { + if (sbappend(&so->so_rcv, q->tqe_m)) + dowakeup = 1; + } FREE(q, M_TSEGQ); + tcp_reass_qsize--; q = nq; } while (q && q->tqe_th->th_seq == tp->rcv_nxt); ND6_HINT(tp); @@ -343,8 +422,9 @@ present: (((tp->t_inpcb->inp_laddr.s_addr & 0xffff) << 16) | (tp->t_inpcb->inp_faddr.s_addr & 0xffff)), 0,0,0); - } - sorwakeup(so); + } + if (dowakeup) + sorwakeup(so); /* done with socket lock held */ return (flags); } @@ -356,14 +436,14 @@ 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; - IP6_EXTHDR_CHECK(m, *offp, sizeof(struct tcphdr), IPPROTO_DONE); + IP6_EXTHDR_CHECK(m, *offp, sizeof(struct tcphdr), return IPPROTO_DONE); /* * draft-itojun-ipv6-tcp-to-anycast @@ -407,13 +487,26 @@ tcp_input(m, off0) #endif int dropsocket = 0; int iss = 0; + int nosock = 0; u_long tiwin; struct tcpopt to; /* options in this segment */ struct rmxp_tao *taop; /* pointer to our TAO cache entry */ struct rmxp_tao tao_noncached; /* in case there's no cached entry */ + struct sockaddr_in *next_hop = NULL; #if TCPDEBUG short ostate = 0; #endif + struct m_tag *fwd_tag; + + /* Grab info from PACKET_TAG_IPFORWARD tag prepended to the chain. */ + fwd_tag = m_tag_locate(m, KERNEL_MODULE_TAG_ID, KERNEL_TAG_TYPE_IPFORWARD, NULL); + if (fwd_tag != NULL) { + struct ip_fwd_tag *ipfwd_tag = (struct ip_fwd_tag *)(fwd_tag+1); + + next_hop = ipfwd_tag->next_hop; + m_tag_delete(m, fwd_tag); + } + #if INET6 struct ip6_hdr *ip6 = NULL; int isipv6; @@ -439,7 +532,7 @@ tcp_input(m, off0) tlen = sizeof(*ip6) + ntohs(ip6->ip6_plen) - off0; if (in6_cksum(m, IPPROTO_TCP, off0, tlen)) { tcpstat.tcps_rcvbadsum++; - goto drop; + goto dropnosock; } th = (struct tcphdr *)((caddr_t)ip6 + off0); @@ -456,11 +549,11 @@ tcp_input(m, off0) */ if (IN6_IS_ADDR_UNSPECIFIED(&ip6->ip6_src)) { /* XXX stat */ - goto drop; + goto dropnosock; } } else #endif /* INET6 */ - { + { /* * Get IP and TCP header together in first mbuf. * Note: IP leaves IP header in first mbuf. @@ -490,10 +583,20 @@ tcp_input(m, off0) if (m->m_pkthdr.csum_flags & CSUM_DATA_VALID) { if (apple_hwcksum_rx && (m->m_pkthdr.csum_flags & CSUM_TCP_SUM16)) { u_short pseudo; + char b[9]; + *(uint32_t*)&b[0] = *(uint32_t*)&ipov->ih_x1[0]; + *(uint32_t*)&b[4] = *(uint32_t*)&ipov->ih_x1[4]; + *(uint8_t*)&b[8] = *(uint8_t*)&ipov->ih_x1[8]; + bzero(ipov->ih_x1, sizeof(ipov->ih_x1)); ipov->ih_len = (u_short)tlen; HTONS(ipov->ih_len); pseudo = in_cksum(m, sizeof (struct ip)); + + *(uint32_t*)&ipov->ih_x1[0] = *(uint32_t*)&b[0]; + *(uint32_t*)&ipov->ih_x1[4] = *(uint32_t*)&b[4]; + *(uint8_t*)&ipov->ih_x1[8] = *(uint8_t*)&b[8]; + th->th_sum = in_addword(pseudo, (m->m_pkthdr.csum_data & 0xFFFF)); } else { if (m->m_pkthdr.csum_flags & CSUM_PSEUDO_HDR) @@ -505,24 +608,33 @@ tcp_input(m, off0) } th->th_sum ^= 0xffff; } else { + char b[9]; /* * Checksum extended TCP header and data. */ + *(uint32_t*)&b[0] = *(uint32_t*)&ipov->ih_x1[0]; + *(uint32_t*)&b[4] = *(uint32_t*)&ipov->ih_x1[4]; + *(uint8_t*)&b[8] = *(uint8_t*)&ipov->ih_x1[8]; + len = sizeof (struct ip) + tlen; bzero(ipov->ih_x1, sizeof(ipov->ih_x1)); ipov->ih_len = (u_short)tlen; HTONS(ipov->ih_len); th->th_sum = in_cksum(m, len); + + *(uint32_t*)&ipov->ih_x1[0] = *(uint32_t*)&b[0]; + *(uint32_t*)&ipov->ih_x1[4] = *(uint32_t*)&b[4]; + *(uint8_t*)&ipov->ih_x1[8] = *(uint8_t*)&b[8]; } if (th->th_sum) { tcpstat.tcps_rcvbadsum++; - goto drop; + goto dropnosock; } #if INET6 /* Re-initialization for later version check */ ip->ip_v = IPVERSION; #endif - } + } /* * Check that TCP offset makes sense, @@ -531,13 +643,13 @@ tcp_input(m, off0) off = th->th_off << 2; if (off < sizeof (struct tcphdr) || off > tlen) { tcpstat.tcps_rcvbadoff++; - goto drop; + goto dropnosock; } tlen -= off; /* tlen is used instead of ti->ti_len */ if (off > sizeof (struct tcphdr)) { #if INET6 if (isipv6) { - IP6_EXTHDR_CHECK(m, off0, off, ); + IP6_EXTHDR_CHECK(m, off0, off, return); ip6 = mtod(m, struct ip6_hdr *); th = (struct tcphdr *)((caddr_t)ip6 + off0); } else @@ -567,7 +679,7 @@ tcp_input(m, off0) optp[TCPOLEN_TSTAMP_APPA] == TCPOPT_EOL)) && *(u_int32_t *)optp == htonl(TCPOPT_TSTAMP_HDR) && (th->th_flags & TH_SYN) == 0) { - to.to_flag |= TOF_TS; + to.to_flags |= TOF_TS; to.to_tsval = ntohl(*(u_int32_t *)(optp + 4)); to.to_tsecr = ntohl(*(u_int32_t *)(optp + 8)); optp = NULL; /* we've parsed the options */ @@ -581,10 +693,10 @@ tcp_input(m, off0) * both the SYN and FIN bits set. This prevents e.g. nmap from * identifying the TCP/IP stack. * - * This is incompatible with RFC1644 extensions (T/TCP). + * This is a violation of the TCP specification. */ if (drop_synfin && (thflags & (TH_SYN|TH_FIN)) == (TH_SYN|TH_FIN)) - goto drop; + goto dropnosock; #endif /* @@ -596,7 +708,7 @@ tcp_input(m, off0) NTOHS(th->th_urp); /* - * Delay droping TCP, IP headers, IPv6 ext headers, and TCP options, + * Delay dropping TCP, IP headers, IPv6 ext headers, and TCP options, * until after ip6_savecontrol() is called and before other functions * which don't want those proto headers. * Because ip6_savecontrol() is going to parse the mbuf to @@ -610,7 +722,7 @@ tcp_input(m, off0) */ findpcb: #if IPFIREWALL_FORWARD - if (ip_fw_fwd_addr != NULL + if (next_hop != NULL #if INET6 && isipv6 == NULL /* IPv6 support is not yet */ #endif /* INET6 */ @@ -625,19 +737,18 @@ findpcb: /* * No, then it's new. Try find the ambushing socket */ - if (!ip_fw_fwd_addr->sin_port) { + if (!next_hop->sin_port) { inp = in_pcblookup_hash(&tcbinfo, ip->ip_src, - th->th_sport, ip_fw_fwd_addr->sin_addr, + th->th_sport, next_hop->sin_addr, th->th_dport, 1, m->m_pkthdr.rcvif); } else { inp = in_pcblookup_hash(&tcbinfo, ip->ip_src, th->th_sport, - ip_fw_fwd_addr->sin_addr, - ntohs(ip_fw_fwd_addr->sin_port), 1, + next_hop->sin_addr, + ntohs(next_hop->sin_port), 1, m->m_pkthdr.rcvif); } } - ip_fw_fwd_addr = NULL; } else #endif /* IPFIREWALL_FORWARD */ { @@ -653,17 +764,23 @@ findpcb: } #if IPSEC + if (ipsec_bypass == 0) { + lck_mtx_lock(sadb_mutex); #if INET6 - if (isipv6) { - if (ipsec_bypass == 0 && inp != NULL && ipsec6_in_reject_so(m, inp->inp_socket)) { - ipsec6stat.in_polvio++; - goto drop; - } - } else + if (isipv6) { + if (inp != NULL && ipsec6_in_reject_so(m, inp->inp_socket)) { + ipsec6stat.in_polvio++; + lck_mtx_unlock(sadb_mutex); + goto dropnosock; + } + } else #endif /* INET6 */ - if (ipsec_bypass == 0 && inp != NULL && ipsec4_in_reject_so(m, inp->inp_socket)) { - ipsecstat.in_polvio++; - goto drop; + if (inp != NULL && ipsec4_in_reject_so(m, inp->inp_socket)) { + ipsecstat.in_polvio++; + lck_mtx_unlock(sadb_mutex); + goto dropnosock; + } + lck_mtx_unlock(sadb_mutex); } #endif /*IPSEC*/ @@ -676,71 +793,105 @@ findpcb: if (inp == NULL) { if (log_in_vain) { #if INET6 - char dbuf[INET6_ADDRSTRLEN], sbuf[INET6_ADDRSTRLEN]; + char dbuf[MAX_IPv6_STR_LEN], sbuf[MAX_IPv6_STR_LEN]; #else /* INET6 */ - char dbuf[4*sizeof "123"], sbuf[4*sizeof "123"]; + char dbuf[MAX_IPv4_STR_LEN], sbuf[MAX_IPv4_STR_LEN]; #endif /* INET6 */ #if INET6 if (isipv6) { - strcpy(dbuf, ip6_sprintf(&ip6->ip6_dst)); - strcpy(sbuf, ip6_sprintf(&ip6->ip6_src)); + inet_ntop(AF_INET6, &ip6->ip6_dst, dbuf, sizeof(dbuf)); + inet_ntop(AF_INET6, &ip6->ip6_src, sbuf, sizeof(sbuf)); } else #endif - { - strcpy(dbuf, inet_ntoa(ip->ip_dst)); - strcpy(sbuf, inet_ntoa(ip->ip_src)); - } + { + inet_ntop(AF_INET, &ip->ip_dst, dbuf, sizeof(dbuf)); + inet_ntop(AF_INET, &ip->ip_src, sbuf, sizeof(sbuf)); + } switch (log_in_vain) { case 1: if(thflags & TH_SYN) log(LOG_INFO, - "Connection attempt to TCP %s:%d from %s:%d\n", - dbuf, ntohs(th->th_dport), - sbuf, - ntohs(th->th_sport)); + "Connection attempt to TCP %s:%d from %s:%d\n", + dbuf, ntohs(th->th_dport), + sbuf, + ntohs(th->th_sport)); break; case 2: log(LOG_INFO, - "Connection attempt to TCP %s:%d from %s:%d flags:0x%x\n", - dbuf, ntohs(th->th_dport), sbuf, - ntohs(th->th_sport), thflags); + "Connection attempt to TCP %s:%d from %s:%d flags:0x%x\n", + dbuf, ntohs(th->th_dport), sbuf, + ntohs(th->th_sport), thflags); + break; + case 3: + if ((thflags & TH_SYN) && + !(m->m_flags & (M_BCAST | M_MCAST)) && +#if INET6 + ((isipv6 && !IN6_ARE_ADDR_EQUAL(&ip6->ip6_dst, &ip6->ip6_src)) || + (!isipv6 && ip->ip_dst.s_addr != ip->ip_src.s_addr)) +#else + ip->ip_dst.s_addr != ip->ip_src.s_addr +#endif + ) + log_in_vain_log((LOG_INFO, + "Stealth Mode connection attempt to TCP %s:%d from %s:%d\n", + dbuf, ntohs(th->th_dport), + sbuf, + ntohs(th->th_sport))); break; default: break; } } if (blackhole) { - switch (blackhole) { - case 1: - if (thflags & TH_SYN) - goto drop; - break; - case 2: - goto drop; - default: - goto drop; - } + if (m->m_pkthdr.rcvif && m->m_pkthdr.rcvif->if_type != IFT_LOOP) + switch (blackhole) { + case 1: + if (thflags & TH_SYN) + goto dropnosock; + break; + case 2: + goto dropnosock; + default: + goto dropnosock; + } } rstreason = BANDLIM_RST_CLOSEDPORT; - goto dropwithreset; + goto dropwithresetnosock; } - tp = intotcpcb(inp); - if (tp == 0) { - rstreason = BANDLIM_RST_CLOSEDPORT; - goto dropwithreset; + so = inp->inp_socket; + if (so == NULL) { + if (in_pcb_checkstate(inp, WNT_RELEASE, 1) == WNT_STOPUSING) + inp = NULL; // pretend we didn't find it +#if TEMPDEBUG + printf("tcp_input: no more socket for inp=%x\n", inp); +#endif + goto dropnosock; } - if (tp->t_state == TCPS_CLOSED) - goto drop; #ifdef __APPLE__ /* * Bogus state when listening port owned by SharedIP with loopback as the * only configured interface: BlueBox does not filters loopback */ - if (tp->t_state == TCP_NSTATES) + if (so == &tcbinfo.nat_dummy_socket) goto drop; + #endif + tcp_lock(so, 1, 2); + if (in_pcb_checkstate(inp, WNT_RELEASE, 1) == WNT_STOPUSING) { + tcp_unlock(so, 1, 2); + inp = NULL; // pretend we didn't find it + goto dropnosock; + } + + tp = intotcpcb(inp); + if (tp == 0) { + rstreason = BANDLIM_RST_CLOSEDPORT; + goto dropwithreset; + } + if (tp->t_state == TCPS_CLOSED) + goto drop; /* Unscale the window into a 32-bit value. */ if ((thflags & TH_SYN) == 0) @@ -748,7 +899,6 @@ findpcb: else tiwin = th->th_win; - so = inp->inp_socket; if (so->so_options & (SO_DEBUG|SO_ACCEPTCONN)) { #if TCPDEBUG if (so->so_options & SO_DEBUG) { @@ -764,14 +914,14 @@ findpcb: } #endif if (so->so_options & SO_ACCEPTCONN) { - register struct tcpcb *tp0 = tp; + register struct tcpcb *tp0 = tp; struct socket *so2; -#if IPSEC struct socket *oso; -#endif + struct sockaddr_storage from; #if INET6 struct inpcb *oinp = sotoinpcb(so); #endif /* INET6 */ + int ogencnt = so->so_gencnt; #if !IPSEC /* @@ -836,25 +986,51 @@ findpcb: } } #endif - - so2 = sonewconn(so, 0); + if (so->so_filt) { + if (isipv6) { + struct sockaddr_in6 *sin6 = (struct sockaddr_in6*)&from; + + sin6->sin6_len = sizeof(*sin6); + sin6->sin6_family = AF_INET6; + sin6->sin6_port = th->th_sport; + sin6->sin6_flowinfo = 0; + sin6->sin6_addr = ip6->ip6_src; + sin6->sin6_scope_id = 0; + } else { + struct sockaddr_in *sin = (struct sockaddr_in*)&from; + + sin->sin_len = sizeof(*sin); + sin->sin_family = AF_INET; + sin->sin_port = th->th_sport; + sin->sin_addr = ip->ip_src; + } + so2 = sonewconn(so, 0, (struct sockaddr*)&from); + } else { + so2 = sonewconn(so, 0, NULL); + } if (so2 == 0) { tcpstat.tcps_listendrop++; - so2 = sodropablereq(so); - if (so2) { - if (tcp_lq_overflow) - sototcpcb(so2)->t_flags |= - TF_LQ_OVERFLOW; - tcp_drop(sototcpcb(so2), ETIMEDOUT); - so2 = sonewconn(so, 0); + if (tcpdropdropablreq(so)) { + if (so->so_filt) + so2 = sonewconn(so, 0, (struct sockaddr*)&from); + else + so2 = sonewconn(so, 0, NULL); } - if (!so2) + if (!so2) goto drop; } -#if IPSEC + /* + * 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; + oso = so; -#endif + tcp_unlock(so, 0, 0); /* Unlock but keep a reference on listener for now */ + so = so2; + tcp_lock(so, 1, 0); /* * This is ugly, but .... * @@ -880,7 +1056,7 @@ findpcb: } #endif /* INET6 */ inp->inp_lport = th->th_dport; - if (in_pcbinshash(inp) != 0) { + if (in_pcbinshash(inp, 0) != 0) { /* * Undo the assignments above if we failed to * put the PCB on the hash lists. @@ -892,6 +1068,8 @@ findpcb: #endif /* INET6 */ inp->inp_laddr.s_addr = INADDR_ANY; inp->inp_lport = 0; + tcp_lock(oso, 0, 0); /* release ref on parent */ + tcp_unlock(oso, 1, 0); goto drop; } #if IPSEC @@ -908,6 +1086,8 @@ findpcb: * Note: dropwithreset makes sure we don't * send a RST in response to a RST. */ + tcp_lock(oso, 0, 0); /* release ref on parent */ + tcp_unlock(oso, 1, 0); if (thflags & TH_ACK) { tcpstat.tcps_badsyn++; rstreason = BANDLIM_RST_OPENPORT; @@ -940,22 +1120,26 @@ findpcb: } else #endif /* INET6 */ inp->inp_options = ip_srcroute(); + tcp_lock(oso, 0, 0); #if IPSEC /* copy old policy into new socket's */ if (sotoinpcb(oso)->inp_sp) { int error = 0; + lck_mtx_lock(sadb_mutex); /* Is it a security hole here to silently fail to copy the policy? */ if (inp->inp_sp != NULL) error = ipsec_init_policy(so, &inp->inp_sp); if (error != 0 || ipsec_copy_policy(sotoinpcb(oso)->inp_sp, inp->inp_sp)) printf("tcp_input: could not copy policy\n"); + lck_mtx_unlock(sadb_mutex); } #endif + tcp_unlock(oso, 1, 0); /* now drop the reference on the listener */ tp = intotcpcb(inp); tp->t_state = TCPS_LISTEN; tp->t_flags |= tp0->t_flags & (TF_NOPUSH|TF_NOOPT|TF_NODELAY); - + tp->t_inpcb->inp_ip_ttl = tp0->t_inpcb->inp_ip_ttl; /* Compute proper scaling value from buffer space */ while (tp->request_r_scale < TCP_MAX_WINSHIFT && TCP_MAXWIN << tp->request_r_scale < @@ -966,13 +1150,80 @@ findpcb: } } +#if 1 + lck_mtx_assert(((struct inpcb *)so->so_pcb)->inpcb_mtx, LCK_MTX_ASSERT_OWNED); +#endif + /* + * Radar 3529618 + * This is the second part of the MSS DoS prevention code (after + * minmss on the sending side) and it deals with too many too small + * tcp packets in a too short timeframe (1 second). + * + * For every full second we count the number of received packets + * and bytes. If we get a lot of packets per second for this connection + * (tcp_minmssoverload) we take a closer look at it and compute the + * average packet size for the past second. If that is less than + * tcp_minmss we get too many packets with very small payload which + * is not good and burdens our system (and every packet generates + * a wakeup to the process connected to our socket). We can reasonable + * expect this to be small packet DoS attack to exhaust our CPU + * cycles. + * + * Care has to be taken for the minimum packet overload value. This + * value defines the minimum number of packets per second before we + * start to worry. This must not be too low to avoid killing for + * example interactive connections with many small packets like + * telnet or SSH. + * + * + * Account for packet if payload packet, skip over ACK, etc. + * + * The packet per second count is done all the time and is also used + * by "DELAY_ACK" to detect streaming situations. + * + */ + if (tp->t_state == TCPS_ESTABLISHED && tlen > 0) { + if (tp->rcv_reset > tcp_now) { + tp->rcv_pps++; + tp->rcv_byps += tlen + off; + /* + * Setting either tcp_minmssoverload or tcp_minmss to "0" disables + * the check. + */ + if (tcp_minmss && tcp_minmssoverload && tp->rcv_pps > tcp_minmssoverload) { + if ((tp->rcv_byps / tp->rcv_pps) < tcp_minmss) { + char ipstrbuf[MAX_IPv6_STR_LEN]; + printf("too many small tcp packets from " + "%s:%u, av. %lubyte/packet, " + "dropping connection\n", +#ifdef INET6 + isipv6 ? + inet_ntop(AF_INET6, &inp->in6p_faddr, ipstrbuf, + sizeof(ipstrbuf)) : +#endif + inet_ntop(AF_INET, &inp->inp_faddr, ipstrbuf, + sizeof(ipstrbuf)), + inp->inp_fport, + tp->rcv_byps / tp->rcv_pps); + tp = tcp_drop(tp, ECONNRESET); +/* tcpstat.tcps_minmssdrops++; */ + goto drop; + } + } + } else { + tp->rcv_reset = tcp_now + PR_SLOWHZ; + tp->rcv_pps = 1; + tp->rcv_byps = tlen + off; + } + } + /* * Segment received on connection. * Reset idle time and keep-alive timer. */ 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, @@ -981,6 +1232,27 @@ findpcb: if (tp->t_state != TCPS_LISTEN && optp) tcp_dooptions(tp, optp, optlen, th, &to); + + if (tp->t_state == TCPS_SYN_SENT && (thflags & TH_SYN)) { + if (to.to_flags & TOF_SCALE) { + tp->t_flags |= TF_RCVD_SCALE; + tp->requested_s_scale = to.to_requested_s_scale; + } + if (to.to_flags & TOF_TS) { + tp->t_flags |= TF_RCVD_TSTMP; + tp->ts_recent = to.to_tsval; + tp->ts_recent_age = tcp_now; + } + if (to.to_flags & TOF_MSS) + tcp_mss(tp, to.to_mss); + if (tp->sack_enable) { + if (!(to.to_flags & TOF_SACK)) + tp->sack_enable = 0; + else + tp->t_flags |= TF_SACK_PERMIT; + } + } + /* * Header prediction: check for the two common cases * of a uni-directional data xfer. If the packet has @@ -1001,15 +1273,8 @@ findpcb: if (tp->t_state == TCPS_ESTABLISHED && (thflags & (TH_SYN|TH_FIN|TH_RST|TH_URG|TH_ACK)) == TH_ACK && ((tp->t_flags & (TF_NEEDSYN|TF_NEEDFIN)) == 0) && - ((to.to_flag & TOF_TS) == 0 || + ((to.to_flags & TOF_TS) == 0 || TSTMP_GEQ(to.to_tsval, tp->ts_recent)) && - /* - * Using the CC option is compulsory if once started: - * the segment is OK if no T/TCP was negotiated or - * if the segment has a CC option equal to CCrecv - */ - ((tp->t_flags & (TF_REQ_CC|TF_RCVD_CC)) != (TF_REQ_CC|TF_RCVD_CC) || - ((to.to_flag & TOF_CC) != 0 && to.to_cc == tp->cc_recv)) && th->th_seq == tp->rcv_nxt && tiwin && tiwin == tp->snd_wnd && tp->snd_nxt == tp->snd_max) { @@ -1020,7 +1285,7 @@ findpcb: * NOTE that the test is modified according to the latest * proposal of the tcplw@cray.com list (Braden 1993/04/26). */ - if ((to.to_flag & TOF_TS) != 0 && + if ((to.to_flags & TOF_TS) != 0 && SEQ_LEQ(th->th_seq, tp->last_ack_sent)) { tp->ts_recent_age = tcp_now; tp->ts_recent = to.to_tsval; @@ -1030,7 +1295,11 @@ findpcb: if (SEQ_GT(th->th_ack, tp->snd_una) && SEQ_LEQ(th->th_ack, tp->snd_max) && tp->snd_cwnd >= tp->snd_wnd && - tp->t_dupacks < tcprexmtthresh) { + ((!tcp_do_newreno && !tp->sack_enable && + tp->t_dupacks < tcprexmtthresh) || + ((tcp_do_newreno || tp->sack_enable) && + !IN_FASTRECOVERY(tp) && to.to_nsacks == 0 && + TAILQ_EMPTY(&tp->snd_holes)))) { /* * this is a pure ack for outstanding data. */ @@ -1043,10 +1312,13 @@ findpcb: tp->snd_cwnd = tp->snd_cwnd_prev; tp->snd_ssthresh = tp->snd_ssthresh_prev; + tp->snd_recover = tp->snd_recover_prev; + if (tp->t_flags & TF_WASFRECOVERY) + ENTER_FASTRECOVERY(tp); tp->snd_nxt = tp->snd_max; tp->t_badrxtwin = 0; } - if (((to.to_flag & TOF_TS) != 0) && (to.to_tsecr != 0)) /* Makes sure we already have a TS */ + if (((to.to_flags & TOF_TS) != 0) && (to.to_tsecr != 0)) /* Makes sure we already have a TS */ tcp_xmit_timer(tp, tcp_now - to.to_tsecr + 1); else if (tp->t_rtttime && @@ -1056,7 +1328,16 @@ findpcb: tcpstat.tcps_rcvackpack++; tcpstat.tcps_rcvackbyte += acked; sbdrop(&so->so_snd, acked); + if (SEQ_GT(tp->snd_una, tp->snd_recover) && + SEQ_LEQ(th->th_ack, tp->snd_recover)) + tp->snd_recover = th->th_ack - 1; tp->snd_una = th->th_ack; + /* + * pull snd_wl2 up to prevent seq wrap relative + * to th_ack. + */ + tp->snd_wl2 = th->th_ack; + tp->t_dupacks = 0; m_freem(m); ND6_HINT(tp); /* some progress has been done */ @@ -1074,9 +1355,10 @@ findpcb: else if (tp->t_timer[TCPT_PERSIST] == 0) tp->t_timer[TCPT_REXMT] = tp->t_rxtcur; - if (so->so_snd.sb_cc) + sowwakeup(so); /* has to be done with socket lock held */ + if ((so->so_snd.sb_cc) || (tp->t_flags & TF_ACKNOW)) (void) tcp_output(tp); - sowwakeup(so); + tcp_unlock(so, 1, 0); KERNEL_DEBUG(DBG_FNC_TCP_INPUT | DBG_FUNC_END,0,0,0,0,0); return; } @@ -1088,8 +1370,21 @@ findpcb: * with nothing on the reassembly queue and * we have enough buffer space to take it. */ + /* Clean receiver SACK report if present */ + if (tp->sack_enable && tp->rcv_numsacks) + tcp_clean_sackreport(tp); ++tcpstat.tcps_preddat; tp->rcv_nxt += tlen; + /* + * Pull snd_wl1 up to prevent seq wrap relative to + * th_seq. + */ + tp->snd_wl1 = th->th_seq; + /* + * Pull rcv_up up to prevent seq wrap relative to + * rcv_nxt. + */ + tp->rcv_up = tp->rcv_nxt; tcpstat.tcps_rcvpack++; tcpstat.tcps_rcvbyte += tlen; ND6_HINT(tp); /* some progress has been done */ @@ -1097,7 +1392,8 @@ findpcb: * Add data to socket buffer. */ m_adj(m, drop_hdrlen); /* delayed header drop */ - sbappend(&so->so_rcv, m); + if (sbappend(&so->so_rcv, m)) + sorwakeup(so); #if INET6 if (isipv6) { KERNEL_DEBUG(DBG_LAYER_END, ((th->th_dport << 16) | th->th_sport), @@ -1111,14 +1407,13 @@ findpcb: (((ip->ip_src.s_addr & 0xffff) << 16) | (ip->ip_dst.s_addr & 0xffff)), th->th_seq, th->th_ack, th->th_win); } - if (tcp_delack_enabled) { - TCP_DELACK_BITSET(tp->t_inpcb->hash_element); + if (DELAY_ACK(tp)) { tp->t_flags |= TF_DELACK; } else { tp->t_flags |= TF_ACKNOW; tcp_output(tp); } - sorwakeup(so); + tcp_unlock(so, 1, 0); KERNEL_DEBUG(DBG_FNC_TCP_INPUT | DBG_FUNC_END,0,0,0,0,0); return; } @@ -1130,11 +1425,18 @@ findpcb: * Receive window is amount of space in rcv queue, * but not less than advertised window. */ +#if 1 + lck_mtx_assert(((struct inpcb *)so->so_pcb)->inpcb_mtx, LCK_MTX_ASSERT_OWNED); +#endif { int win; win = sbspace(&so->so_rcv); if (win < 0) win = 0; + else { /* clip rcv window to 4K for modems */ + if (tp->t_flags & TF_SLOWLINK && slowlink_wsize > 0) + win = min(win, slowlink_wsize); + } tp->rcv_wnd = imax(win, (int)(tp->rcv_adv - tp->rcv_nxt)); } @@ -1160,7 +1462,10 @@ findpcb: register struct sockaddr_in6 *sin6; #endif - if (thflags & TH_RST) +#if 1 + lck_mtx_assert(((struct inpcb *)so->so_pcb)->inpcb_mtx, LCK_MTX_ASSERT_OWNED); +#endif + if (thflags & TH_RST) goto drop; if (thflags & TH_ACK) { rstreason = BANDLIM_RST_OPENPORT; @@ -1225,6 +1530,9 @@ findpcb: } else #endif { +#if 1 + lck_mtx_assert(((struct inpcb *)so->so_pcb)->inpcb_mtx, LCK_MTX_ASSERT_OWNED); +#endif MALLOC(sin, struct sockaddr_in *, sizeof *sin, M_SONAME, M_NOWAIT); if (sin == NULL) @@ -1244,11 +1552,16 @@ findpcb: } FREE(sin, M_SONAME); } - if ((taop = tcp_gettaocache(inp)) == NULL) { - taop = &tao_noncached; - bzero(taop, sizeof(*taop)); - } + tcp_dooptions(tp, optp, optlen, th, &to); + + if (tp->sack_enable) { + if (!(to.to_flags & TOF_SACK)) + tp->sack_enable = 0; + else + tp->t_flags |= TF_SACK_PERMIT; + } + if (iss) tp->iss = iss; else { @@ -1264,79 +1577,6 @@ findpcb: * initialize CCsend and CCrecv. */ tp->snd_wnd = tiwin; /* initial send-window */ - tp->cc_send = CC_INC(tcp_ccgen); - tp->cc_recv = to.to_cc; - /* - * Perform TAO test on incoming CC (SEG.CC) option, if any. - * - compare SEG.CC against cached CC from the same host, - * if any. - * - if SEG.CC > chached value, SYN must be new and is accepted - * immediately: save new CC in the cache, mark the socket - * connected, enter ESTABLISHED state, turn on flag to - * send a SYN in the next segment. - * A virtual advertised window is set in rcv_adv to - * initialize SWS prevention. Then enter normal segment - * processing: drop SYN, process data and FIN. - * - otherwise do a normal 3-way handshake. - */ - if ((to.to_flag & TOF_CC) != 0) { - if (((tp->t_flags & TF_NOPUSH) != 0) && - taop->tao_cc != 0 && CC_GT(to.to_cc, taop->tao_cc)) { - - taop->tao_cc = to.to_cc; - - tp->t_state = TCPS_ESTABLISHED; - - /* - * If there is a FIN, or if there is data and the - * connection is local, then delay SYN,ACK(SYN) in - * the hope of piggy-backing it on a response - * segment. Otherwise must send ACK now in case - * the other side is slow starting. - */ - if (tcp_delack_enabled && ((thflags & TH_FIN) || - (tlen != 0 && -#if INET6 - (isipv6 && in6_localaddr(&inp->in6p_faddr)) - || - (!isipv6 && -#endif /* INET6 */ - in_localaddr(inp->inp_faddr) -#if INET6 - ) -#endif /* INET6 */ - ))) { - TCP_DELACK_BITSET(tp->t_inpcb->hash_element); - tp->t_flags |= (TF_DELACK | TF_NEEDSYN); - } - else - tp->t_flags |= (TF_ACKNOW | TF_NEEDSYN); - - /* - * Limit the `virtual advertised window' to TCP_MAXWIN - * here. Even if we requested window scaling, it will - * become effective only later when our SYN is acked. - */ - tp->rcv_adv += min(tp->rcv_wnd, TCP_MAXWIN); - tcpstat.tcps_connects++; - soisconnected(so); - tp->t_timer[TCPT_KEEP] = tcp_keepinit; - dropsocket = 0; /* committed to socket */ - tcpstat.tcps_accepts++; - goto trimthenstep6; - } - /* else do standard 3-way handshake */ - } else { - /* - * No CC option, but maybe CC.NEW: - * invalidate cached value. - */ - taop->tao_cc = 0; - } - /* - * TAO test failed or there was no CC option, - * do a standard 3-way handshake. - */ tp->t_flags |= TF_ACKNOW; tp->t_state = TCPS_SYN_RECEIVED; tp->t_timer[TCPT_KEEP] = tcp_keepinit; @@ -1371,28 +1611,11 @@ findpcb: * continue processing rest of data/controls, beginning with URG */ case TCPS_SYN_SENT: - if ((taop = tcp_gettaocache(inp)) == NULL) { - taop = &tao_noncached; - bzero(taop, sizeof(*taop)); - } - if ((thflags & TH_ACK) && (SEQ_LEQ(th->th_ack, tp->iss) || SEQ_GT(th->th_ack, tp->snd_max))) { - /* - * If we have a cached CCsent for the remote host, - * hence we haven't just crashed and restarted, - * do not send a RST. This may be a retransmission - * from the other side after our earlier ACK was lost. - * Our new SYN, when it arrives, will serve as the - * needed ACK. - */ - if (taop->tao_ccsent != 0) - goto drop; - else { - rstreason = BANDLIM_UNLIMITED; - goto dropwithreset; - } + rstreason = BANDLIM_UNLIMITED; + goto dropwithreset; } if (thflags & TH_RST) { if (thflags & TH_ACK) { @@ -1404,30 +1627,10 @@ findpcb: if ((thflags & TH_SYN) == 0) goto drop; tp->snd_wnd = th->th_win; /* initial send window */ - tp->cc_recv = to.to_cc; /* foreign CC */ tp->irs = th->th_seq; tcp_rcvseqinit(tp); if (thflags & TH_ACK) { - /* - * Our SYN was acked. If segment contains CC.ECHO - * option, check it to make sure this segment really - * matches our SYN. If not, just drop it as old - * duplicate, but send an RST if we're still playing - * by the old rules. If no CC.ECHO option, make sure - * we don't get fooled into using T/TCP. - */ - if (to.to_flag & TOF_CCECHO) { - if (tp->cc_send != to.to_ccecho) { - if (taop->tao_ccsent != 0) - goto drop; - else { - rstreason = BANDLIM_UNLIMITED; - goto dropwithreset; - } - } - } else - tp->t_flags &= ~TF_RCVD_CC; tcpstat.tcps_connects++; soisconnected(so); /* Do window scaling on this connection? */ @@ -1436,35 +1639,32 @@ findpcb: tp->snd_scale = tp->requested_s_scale; tp->rcv_scale = tp->request_r_scale; } - /* Segment is acceptable, update cache if undefined. */ - if (taop->tao_ccsent == 0) - taop->tao_ccsent = to.to_ccecho; - tp->rcv_adv += tp->rcv_wnd; tp->snd_una++; /* SYN is acked */ /* * If there's data, delay ACK; if there's also a FIN * ACKNOW will be turned on later. */ - if (tcp_delack_enabled && tlen != 0) { - TCP_DELACK_BITSET(tp->t_inpcb->hash_element); + if (DELAY_ACK(tp) && tlen != 0) { tp->t_flags |= TF_DELACK; } - else + else { tp->t_flags |= TF_ACKNOW; + } /* * Received in SYN_SENT[*] state. * Transitions: * SYN_SENT --> ESTABLISHED * SYN_SENT* --> FIN_WAIT_1 */ + tp->t_starttime = 0; if (tp->t_flags & TF_NEEDFIN) { tp->t_state = TCPS_FIN_WAIT_1; tp->t_flags &= ~TF_NEEDFIN; 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 { /* @@ -1474,34 +1674,11 @@ findpcb: * half-synchronized. Otherwise, do 3-way handshake: * SYN-SENT -> SYN-RECEIVED * SYN-SENT* -> SYN-RECEIVED* - * If there was no CC option, clear cached CC value. */ tp->t_flags |= TF_ACKNOW; tp->t_timer[TCPT_REXMT] = 0; - if (to.to_flag & TOF_CC) { - if (taop->tao_cc != 0 && - CC_GT(to.to_cc, taop->tao_cc)) { - /* - * update cache and make transition: - * SYN-SENT -> ESTABLISHED* - * SYN-SENT* -> FIN-WAIT-1* - */ - taop->tao_cc = to.to_cc; - if (tp->t_flags & TF_NEEDFIN) { - tp->t_state = TCPS_FIN_WAIT_1; - tp->t_flags &= ~TF_NEEDFIN; - } else { - tp->t_state = TCPS_ESTABLISHED; - tp->t_timer[TCPT_KEEP] = tcp_keepidle; - } - tp->t_flags |= TF_NEEDSYN; - } else - tp->t_state = TCPS_SYN_RECEIVED; - } else { - /* CC.NEW or no option => invalidate cache */ - taop->tao_cc = 0; - tp->t_state = TCPS_SYN_RECEIVED; - } + tp->t_state = TCPS_SYN_RECEIVED; + } trimthenstep6: @@ -1533,36 +1710,24 @@ trimthenstep6: goto step6; /* * If the state is LAST_ACK or CLOSING or TIME_WAIT: - * if segment contains a SYN and CC [not CC.NEW] option: - * if state == TIME_WAIT and connection duration > MSL, - * drop packet and send RST; - * - * if SEG.CC > CCrecv then is new SYN, and can implicitly - * ack the FIN (and data) in retransmission queue. - * Complete close and delete TCPCB. Then reprocess - * segment, hoping to find new TCPCB in LISTEN state; + * do normal processing. * - * else must be old SYN; drop it. - * else do normal processing. + * NB: Leftover from RFC1644 T/TCP. Cases to be reused later. */ case TCPS_LAST_ACK: case TCPS_CLOSING: case TCPS_TIME_WAIT: - if ((thflags & TH_SYN) && - (to.to_flag & TOF_CC) && tp->cc_recv != 0) { - if (tp->t_state == TCPS_TIME_WAIT && - tp->t_starttime > tcp_msl) { - rstreason = BANDLIM_UNLIMITED; - goto dropwithreset; - } - if (CC_GT(to.to_cc, tp->cc_recv)) { - tp = tcp_close(tp); - goto findpcb; - } - else - 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; } /* @@ -1593,6 +1758,12 @@ trimthenstep6: * echo of our outgoing acknowlegement numbers, but some hosts * send a reset with the sequence number at the rightmost edge * of our receive window, and we have to handle this case. + * Note 2: Paul Watson's paper "Slipping in the Window" has shown + * that brute force RST attacks are possible. To combat this, + * we use a much stricter check while in the ESTABLISHED state, + * only accepting RSTs where the sequence number is equal to + * last_ack_sent. In all other states (the states in which a + * RST is more likely), the more permissive check is used. * If we have multiple segments in flight, the intial reset * segment sequence numbers will be to the left of last_ack_sent, * but they will eventually catch up. @@ -1613,17 +1784,24 @@ trimthenstep6: * SYN_RECEIVED STATE: * If passive open, return to LISTEN state. * If active open, inform user that connection was refused. - * ESTABLISHED, FIN_WAIT_1, FIN_WAIT2, CLOSE_WAIT STATES: + * ESTABLISHED, FIN_WAIT_1, FIN_WAIT_2, CLOSE_WAIT STATES: * Inform user that connection was reset, and close tcb. * CLOSING, LAST_ACK STATES: * Close the tcb. * TIME_WAIT STATE: * Drop the segment - see Stevens, vol. 2, p. 964 and * RFC 1337. + * + * Radar 4776325: Allows for the case where we ACKed the FIN but + * there is already a RST in flight from the peer. + * In that case, accept the RST for non-established + * state if it's one off from last_ack_sent. */ if (thflags & TH_RST) { - if (SEQ_GEQ(th->th_seq, tp->last_ack_sent) && - SEQ_LT(th->th_seq, tp->last_ack_sent + tp->rcv_wnd)) { + if ((SEQ_GEQ(th->th_seq, tp->last_ack_sent) && + SEQ_LT(th->th_seq, tp->last_ack_sent + tp->rcv_wnd)) || + (tp->rcv_wnd == 0 && + (tp->last_ack_sent == th->th_seq)) || ((tp->last_ack_sent - 1) == th->th_seq)) { switch (tp->t_state) { case TCPS_SYN_RECEIVED: @@ -1631,6 +1809,9 @@ trimthenstep6: goto close; case TCPS_ESTABLISHED: + if (tp->last_ack_sent != th->th_seq) { + goto drop; + } case TCPS_FIN_WAIT_1: case TCPS_CLOSE_WAIT: /* @@ -1657,11 +1838,14 @@ trimthenstep6: goto drop; } +#if 1 + lck_mtx_assert(((struct inpcb *)so->so_pcb)->inpcb_mtx, LCK_MTX_ASSERT_OWNED); +#endif /* * RFC 1323 PAWS: If we have a timestamp reply on this segment * and it's less than ts_recent, drop it. */ - if ((to.to_flag & TOF_TS) != 0 && tp->ts_recent && + if ((to.to_flags & TOF_TS) != 0 && tp->ts_recent && TSTMP_LT(to.to_tsval, tp->ts_recent)) { /* Check to see if ts_recent is over 24 days old. */ @@ -1682,20 +1866,12 @@ trimthenstep6: tcpstat.tcps_rcvduppack++; tcpstat.tcps_rcvdupbyte += tlen; tcpstat.tcps_pawsdrop++; - goto dropafterack; + if (tlen) + goto dropafterack; + goto drop; } } - /* - * T/TCP mechanism - * If T/TCP was negotiated and the segment doesn't have CC, - * or if its CC is wrong then drop the segment. - * RST segments do not have to comply with this. - */ - if ((tp->t_flags & (TF_REQ_CC|TF_RCVD_CC)) == (TF_REQ_CC|TF_RCVD_CC) && - ((to.to_flag & TOF_CC) == 0 || tp->cc_recv != to.to_cc)) - goto dropafterack; - /* * In the SYN-RECEIVED state, validate that the packet belongs to * this connection before trimming the data to fit the receive @@ -1786,6 +1962,7 @@ trimthenstep6: SEQ_GT(th->th_seq, tp->rcv_nxt)) { iss = tcp_new_isn(tp); tp = tcp_close(tp); + tcp_unlock(so, 1, 0); goto findpcb; } /* @@ -1810,11 +1987,25 @@ trimthenstep6: /* * If last ACK falls within this segment's sequence numbers, * record its timestamp. - * NOTE that the test is modified according to the latest - * proposal of the tcplw@cray.com list (Braden 1993/04/26). + * NOTE: + * 1) That the test incorporates suggestions from the latest + * proposal of the tcplw@cray.com list (Braden 1993/04/26). + * 2) That updating only on newer timestamps interferes with + * our earlier PAWS tests, so this check should be solely + * predicated on the sequence space of this segment. + * 3) That we modify the segment boundary check to be + * Last.ACK.Sent <= SEG.SEQ + SEG.Len + * instead of RFC1323's + * Last.ACK.Sent < SEG.SEQ + SEG.Len, + * This modified check allows us to overcome RFC1323's + * limitations as described in Stevens TCP/IP Illustrated + * Vol. 2 p.869. In such cases, we can still calculate the + * RTT correctly when RCV.NXT == Last.ACK.Sent. */ - if ((to.to_flag & TOF_TS) != 0 && - SEQ_LEQ(th->th_seq, tp->last_ack_sent)) { + if ((to.to_flags & TOF_TS) != 0 && + SEQ_LEQ(th->th_seq, tp->last_ack_sent) && + SEQ_LEQ(tp->last_ack_sent, th->th_seq + tlen + + ((thflags & (TH_SYN|TH_FIN)) != 0))) { tp->ts_recent_age = tcp_now; tp->ts_recent = to.to_tsval; } @@ -1864,26 +2055,18 @@ trimthenstep6: tp->snd_scale = tp->requested_s_scale; tp->rcv_scale = tp->request_r_scale; } - /* - * Upon successful completion of 3-way handshake, - * update cache.CC if it was undefined, pass any queued - * data to the user, and advance state appropriately. - */ - if ((taop = tcp_gettaocache(inp)) != NULL && - taop->tao_cc == 0) - taop->tao_cc = tp->cc_recv; - /* * Make transitions: * SYN-RECEIVED -> ESTABLISHED * SYN-RECEIVED* -> FIN-WAIT-1 */ + tp->t_starttime = 0; if (tp->t_flags & TF_NEEDFIN) { tp->t_state = TCPS_FIN_WAIT_1; 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() @@ -1893,7 +2076,7 @@ trimthenstep6: (void) tcp_reass(tp, (struct tcphdr *)0, 0, (struct mbuf *)0); tp->snd_wl1 = th->th_seq - 1; - /* fall into ... */ + /* FALLTHROUGH */ /* * In ESTABLISHED state: drop duplicate ACKs; ACK out of range @@ -1910,7 +2093,13 @@ trimthenstep6: case TCPS_CLOSING: case TCPS_LAST_ACK: case TCPS_TIME_WAIT: - + if (SEQ_GT(th->th_ack, tp->snd_max)) { + tcpstat.tcps_rcvacktoomuch++; + goto dropafterack; + } + if (tp->sack_enable && + (to.to_nsacks > 0 || !TAILQ_EMPTY(&tp->snd_holes))) + tcp_sack_doack(tp, &to, th->th_ack); if (SEQ_LEQ(th->th_ack, tp->snd_una)) { if (tlen == 0 && tiwin == tp->snd_wnd) { tcpstat.tcps_rcvdupack++; @@ -1941,39 +2130,76 @@ trimthenstep6: if (tp->t_timer[TCPT_REXMT] == 0 || th->th_ack != tp->snd_una) tp->t_dupacks = 0; - else if (++tp->t_dupacks == tcprexmtthresh) { - tcp_seq onxt = tp->snd_nxt; - u_int win = - min(tp->snd_wnd, tp->snd_cwnd) / 2 / - tp->t_maxseg; - if (tcp_do_newreno && SEQ_LT(th->th_ack, - tp->snd_recover)) { - /* False retransmit, should not - * cut window + else if (++tp->t_dupacks > tcprexmtthresh || + ((tcp_do_newreno || tp->sack_enable) && + IN_FASTRECOVERY(tp))) { + if (tp->sack_enable && IN_FASTRECOVERY(tp)) { + int awnd; + + /* + * Compute the amount of data in flight first. + * We can inject new data into the pipe iff + * we have less than 1/2 the original window's + * worth of data in flight. */ + awnd = (tp->snd_nxt - tp->snd_fack) + + tp->sackhint.sack_bytes_rexmit; + if (awnd < tp->snd_ssthresh) { + tp->snd_cwnd += tp->t_maxseg; + if (tp->snd_cwnd > tp->snd_ssthresh) + tp->snd_cwnd = tp->snd_ssthresh; + } + } else tp->snd_cwnd += tp->t_maxseg; - tp->t_dupacks = 0; - (void) tcp_output(tp); - goto drop; + (void) tcp_output(tp); + goto drop; + } else if (tp->t_dupacks == tcprexmtthresh) { + tcp_seq onxt = tp->snd_nxt; + u_int win; + + /* + * If we're doing sack, check to + * see if we're already in sack + * recovery. If we're not doing sack, + * check to see if we're in newreno + * recovery. + */ + if (tp->sack_enable) { + if (IN_FASTRECOVERY(tp)) { + tp->t_dupacks = 0; + break; + } + } else if (tcp_do_newreno) { + if (SEQ_LEQ(th->th_ack, + tp->snd_recover)) { + tp->t_dupacks = 0; + break; + } } + win = min(tp->snd_wnd, tp->snd_cwnd) / + 2 / tp->t_maxseg; if (win < 2) win = 2; tp->snd_ssthresh = win * tp->t_maxseg; + ENTER_FASTRECOVERY(tp); tp->snd_recover = tp->snd_max; tp->t_timer[TCPT_REXMT] = 0; tp->t_rtttime = 0; + if (tp->sack_enable) { + tcpstat.tcps_sack_recovery_episode++; + tp->sack_newdata = tp->snd_nxt; + tp->snd_cwnd = tp->t_maxseg; + (void) tcp_output(tp); + goto drop; + } tp->snd_nxt = th->th_ack; tp->snd_cwnd = tp->t_maxseg; (void) tcp_output(tp); tp->snd_cwnd = tp->snd_ssthresh + - tp->t_maxseg * tp->t_dupacks; + tp->t_maxseg * tp->t_dupacks; if (SEQ_GT(onxt, tp->snd_nxt)) tp->snd_nxt = onxt; goto drop; - } else if (tp->t_dupacks > tcprexmtthresh) { - tp->snd_cwnd += tp->t_maxseg; - (void) tcp_output(tp); - goto drop; } } else tp->t_dupacks = 0; @@ -1983,36 +2209,41 @@ trimthenstep6: * If the congestion window was inflated to account * for the other side's cached packets, retract it. */ - if (tcp_do_newreno == 0) { - if (tp->t_dupacks >= tcprexmtthresh && - tp->snd_cwnd > tp->snd_ssthresh) - tp->snd_cwnd = tp->snd_ssthresh; - tp->t_dupacks = 0; - } else if (tp->t_dupacks >= tcprexmtthresh && - !tcp_newreno(tp, th)) { - /* - * Window inflation should have left us with approx. - * snd_ssthresh outstanding data. But in case we - * would be inclined to send a burst, better to do - * it via the slow start mechanism. - */ - if (SEQ_GT(th->th_ack + tp->snd_ssthresh, tp->snd_max)) - tp->snd_cwnd = - tp->snd_max - th->th_ack + tp->t_maxseg; - else - tp->snd_cwnd = tp->snd_ssthresh; - tp->t_dupacks = 0; - } - - if (tp->t_dupacks < tcprexmtthresh) - tp->t_dupacks = 0; - - if (SEQ_GT(th->th_ack, tp->snd_max)) { - tcpstat.tcps_rcvacktoomuch++; - goto dropafterack; + if (tcp_do_newreno || tp->sack_enable) { + if (IN_FASTRECOVERY(tp)) { + if (SEQ_LT(th->th_ack, tp->snd_recover)) { + if (tp->sack_enable) + tcp_sack_partialack(tp, th); + else + tcp_newreno_partial_ack(tp, th); + } else { + /* + * Out of fast recovery. + * Window inflation should have left us + * with approximately snd_ssthresh + * outstanding data. + * But in case we would be inclined to + * send a burst, better to do it via + * the slow start mechanism. + */ + if (SEQ_GT(th->th_ack + + tp->snd_ssthresh, + tp->snd_max)) + tp->snd_cwnd = tp->snd_max - + th->th_ack + + tp->t_maxseg; + else + tp->snd_cwnd = tp->snd_ssthresh; + } + } + } else { + if (tp->t_dupacks >= tcprexmtthresh && + tp->snd_cwnd > tp->snd_ssthresh) + tp->snd_cwnd = tp->snd_ssthresh; } + tp->t_dupacks = 0; /* - * If we reach this point, ACK is not a duplicate, + * If we reach this point, ACK is not a duplicate, * i.e., it ACKs something we sent. */ if (tp->t_flags & TF_NEEDSYN) { @@ -2048,6 +2279,9 @@ process_ACK: if (tp->t_rxtshift == 1 && tcp_now < tp->t_badrxtwin) { tp->snd_cwnd = tp->snd_cwnd_prev; tp->snd_ssthresh = tp->snd_ssthresh_prev; + tp->snd_recover = tp->snd_recover_prev; + if (tp->t_flags & TF_WASFRECOVERY) + ENTER_FASTRECOVERY(tp); tp->snd_nxt = tp->snd_max; tp->t_badrxtwin = 0; /* XXX probably not required */ } @@ -2062,7 +2296,7 @@ process_ACK: * Recompute the initial retransmit timer. * Also makes sure we have a valid time stamp in hand */ - if (((to.to_flag & TOF_TS) != 0) && (to.to_tsecr != 0)) + if (((to.to_flags & TOF_TS) != 0) && (to.to_tsecr != 0)) tcp_xmit_timer(tp, tcp_now - to.to_tsecr + 1); else if (tp->t_rtttime && SEQ_GT(th->th_ack, tp->t_rtseq)) tcp_xmit_timer(tp, tp->t_rtttime); @@ -2093,19 +2327,13 @@ process_ACK: * Otherwise open linearly: maxseg per window * (maxseg^2 / cwnd per packet). */ - { - register u_int cw = tp->snd_cwnd; - register u_int incr = tp->t_maxseg; - - if (cw > tp->snd_ssthresh) - incr = incr * incr / cw; - /* - * If t_dupacks != 0 here, it indicates that we are still - * in NewReno fast recovery mode, so we leave the congestion - * window alone. - */ - if (tcp_do_newreno == 0 || tp->t_dupacks == 0) - tp->snd_cwnd = min(cw + incr,TCP_MAXWIN<snd_scale); + if ((!tcp_do_newreno && !tp->sack_enable) || + !IN_FASTRECOVERY(tp)) { + register u_int cw = tp->snd_cwnd; + register u_int incr = tp->t_maxseg; + if (cw > tp->snd_ssthresh) + incr = incr * incr / cw; + tp->snd_cwnd = min(cw+incr, TCP_MAXWIN<snd_scale); } if (acked > so->so_snd.sb_cc) { tp->snd_wnd -= so->so_snd.sb_cc; @@ -2117,7 +2345,21 @@ process_ACK: ourfinisacked = 0; } sowwakeup(so); + /* detect una wraparound */ + if ((tcp_do_newreno || tp->sack_enable) && + !IN_FASTRECOVERY(tp) && + SEQ_GT(tp->snd_una, tp->snd_recover) && + SEQ_LEQ(th->th_ack, tp->snd_recover)) + tp->snd_recover = th->th_ack - 1; + if ((tcp_do_newreno || tp->sack_enable) && + IN_FASTRECOVERY(tp) && + SEQ_GEQ(th->th_ack, tp->snd_recover)) + EXIT_FASTRECOVERY(tp); tp->snd_una = th->th_ack; + if (tp->sack_enable) { + if (SEQ_GT(tp->snd_una, tp->snd_recover)) + tp->snd_recover = tp->snd_una; + } if (SEQ_LT(tp->snd_nxt, tp->snd_una)) tp->snd_nxt = tp->snd_una; @@ -2143,6 +2385,7 @@ process_ACK: } add_to_time_wait(tp); tp->t_state = TCPS_FIN_WAIT_2; + goto drop; } break; @@ -2288,44 +2531,45 @@ dodata: /* XXX */ */ if ((tlen || (thflags&TH_FIN)) && TCPS_HAVERCVDFIN(tp->t_state) == 0) { + tcp_seq save_start = th->th_seq; + tcp_seq save_end = th->th_seq + tlen; m_adj(m, drop_hdrlen); /* delayed header drop */ /* - * Insert segment which inludes th into reassembly queue of tcp with - * control block tp. Return TH_FIN if reassembly now includes - * a segment with FIN. This handle the common case inline (segment - * is the next to be received on an established connection, and the - * queue is empty), avoiding linkage into and removal from the queue - * and repetition of various conversions. - * Set DELACK for segments received in order, but ack immediately - * when segments are out of order (so fast retransmit can work). + * Insert segment which includes th into TCP reassembly queue + * with control block tp. Set thflags to whether reassembly now + * includes a segment with FIN. This handles the common case + * inline (segment is the next to be received on an established + * connection, and the queue is empty), avoiding linkage into + * and removal from the queue and repetition of various + * conversions. + * Set DELACK for segments received in order, but ack + * immediately when segments are out of order (so + * fast retransmit can work). */ if (th->th_seq == tp->rcv_nxt && LIST_EMPTY(&tp->t_segq) && TCPS_HAVEESTABLISHED(tp->t_state)) { -#ifdef __APPLE__ - if (tcp_delack_enabled) { - TCP_DELACK_BITSET(tp->t_inpcb->hash_element); + if (DELAY_ACK(tp) && ((tp->t_flags & TF_ACKNOW) == 0)) { tp->t_flags |= TF_DELACK; } -#else - if (DELAY_ACK(tp)) - callout_reset(tp->tt_delack, tcp_delacktime, - tcp_timer_delack, tp); -#endif - else + else { tp->t_flags |= TF_ACKNOW; + } tp->rcv_nxt += tlen; thflags = th->th_flags & TH_FIN; tcpstat.tcps_rcvpack++; tcpstat.tcps_rcvbyte += tlen; ND6_HINT(tp); - sbappend(&so->so_rcv, m); - sorwakeup(so); + if (sbappend(&so->so_rcv, m)) + sorwakeup(so); } else { thflags = tcp_reass(tp, th, &tlen, m); tp->t_flags |= TF_ACKNOW; } + if (tlen > 0 && tp->sack_enable) + tcp_update_sack_list(tp, save_start, save_end); + if (tp->t_flags & TF_DELACK) { #if INET6 @@ -2365,16 +2609,18 @@ dodata: /* XXX */ /* * If connection is half-synchronized * (ie NEEDSYN flag on) then delay ACK, - * so it may be piggybacked when SYN is sent. - * Otherwise, since we received a FIN then no - * more input can be expected, send ACK now. + * If connection is half-synchronized + * (ie NEEDSYN flag on) then delay ACK, + * so it may be piggybacked when SYN is sent. + * Otherwise, since we received a FIN then no + * more input can be expected, send ACK now. */ - if (tcp_delack_enabled && (tp->t_flags & TF_NEEDSYN)) { - TCP_DELACK_BITSET(tp->t_inpcb->hash_element); + if (DELAY_ACK(tp) && (tp->t_flags & TF_NEEDSYN)) { tp->t_flags |= TF_DELACK; } - else + else { tp->t_flags |= TF_ACKNOW; + } tp->rcv_nxt++; } switch (tp->t_state) { @@ -2384,7 +2630,7 @@ dodata: /* XXX */ * enter the CLOSE_WAIT state. */ case TCPS_SYN_RECEIVED: - /*FALLTHROUGH*/ + tp->t_starttime = 0; case TCPS_ESTABLISHED: tp->t_state = TCPS_CLOSE_WAIT; break; @@ -2440,6 +2686,7 @@ dodata: /* XXX */ */ if (needoutput || (tp->t_flags & TF_ACKNOW)) (void) tcp_output(tp); + tcp_unlock(so, 1, 0); KERNEL_DEBUG(DBG_FNC_TCP_INPUT | DBG_FUNC_END,0,0,0,0,0); return; @@ -2473,9 +2720,11 @@ dropafterack: m_freem(m); tp->t_flags |= TF_ACKNOW; (void) tcp_output(tp); + tcp_unlock(so, 1, 0); KERNEL_DEBUG(DBG_FNC_TCP_INPUT | DBG_FUNC_END,0,0,0,0,0); return; - +dropwithresetnosock: + nosock = 1; dropwithreset: /* * Generate a RST, dropping incoming segment. @@ -2523,11 +2772,17 @@ dropwithreset: (tcp_seq)0, TH_RST|TH_ACK); } /* destroy temporarily created socket */ - if (dropsocket) - (void) soabort(so); + if (dropsocket) { + (void) soabort(so); + tcp_unlock(so, 1, 0); + } + else + if ((inp != NULL) && (nosock == 0)) + tcp_unlock(so, 1, 0); KERNEL_DEBUG(DBG_FNC_TCP_INPUT | DBG_FUNC_END,0,0,0,0,0); return; - +dropnosock: + nosock = 1; drop: /* * Drop space held by incoming segment and return. @@ -2539,14 +2794,22 @@ drop: #endif m_freem(m); /* destroy temporarily created socket */ - if (dropsocket) - (void) soabort(so); + if (dropsocket) { + (void) soabort(so); + tcp_unlock(so, 1, 0); + } + else + if (nosock == 0) + tcp_unlock(so, 1, 0); KERNEL_DEBUG(DBG_FNC_TCP_INPUT | DBG_FUNC_END,0,0,0,0,0); return; } static void tcp_dooptions(tp, cp, cnt, th, to) +/* + * Parse TCP options and place in tcpopt. + */ struct tcpcb *tp; u_char *cp; int cnt; @@ -2595,7 +2858,7 @@ tcp_dooptions(tp, cp, cnt, th, to) case TCPOPT_TIMESTAMP: if (optlen != TCPOLEN_TIMESTAMP) continue; - to->to_flag |= TOF_TS; + to->to_flags |= TOF_TS; bcopy((char *)cp + 2, (char *)&to->to_tsval, sizeof(to->to_tsval)); NTOHL(to->to_tsval); @@ -2613,44 +2876,20 @@ tcp_dooptions(tp, cp, cnt, th, to) tp->ts_recent_age = tcp_now; } break; - case TCPOPT_CC: - if (optlen != TCPOLEN_CC) + case TCPOPT_SACK_PERMITTED: + if (!tcp_do_sack || + optlen != TCPOLEN_SACK_PERMITTED) continue; - to->to_flag |= TOF_CC; - bcopy((char *)cp + 2, - (char *)&to->to_cc, sizeof(to->to_cc)); - NTOHL(to->to_cc); - /* - * A CC or CC.new option received in a SYN makes - * it ok to send CC in subsequent segments. - */ if (th->th_flags & TH_SYN) - tp->t_flags |= TF_RCVD_CC; - break; - case TCPOPT_CCNEW: - if (optlen != TCPOLEN_CC) - continue; - if (!(th->th_flags & TH_SYN)) - continue; - to->to_flag |= TOF_CCNEW; - bcopy((char *)cp + 2, - (char *)&to->to_cc, sizeof(to->to_cc)); - NTOHL(to->to_cc); - /* - * A CC or CC.new option received in a SYN makes - * it ok to send CC in subsequent segments. - */ - tp->t_flags |= TF_RCVD_CC; + to->to_flags |= TOF_SACK; break; - case TCPOPT_CCECHO: - if (optlen != TCPOLEN_CC) - continue; - if (!(th->th_flags & TH_SYN)) + case TCPOPT_SACK: + if (optlen <= 2 || (optlen - 2) % TCPOLEN_SACK != 0) continue; - to->to_flag |= TOF_CCECHO; - bcopy((char *)cp + 2, - (char *)&to->to_ccecho, sizeof(to->to_ccecho)); - NTOHL(to->to_ccecho); + to->to_nsacks = (optlen - 2) / TCPOLEN_SACK; + to->to_sacks = cp + 2; + tcpstat.tcps_sack_rcv_blocks++; + break; } } @@ -2841,6 +3080,16 @@ tcp_mss(tp, offer) return; } ifp = rt->rt_ifp; + /* + * Slower link window correction: + * If a value is specificied for slowlink_wsize use it for PPP links + * believed to be on a serial modem (speed <128Kbps). Excludes 9600bps as + * it is the default value adversized by pseudo-devices over ppp. + */ + if (ifp->if_type == IFT_PPP && slowlink_wsize > 0 && + ifp->if_baudrate > 9600 && ifp->if_baudrate <= 128000) { + tp->t_flags |= TF_SLOWLINK; + } so = inp->inp_socket; taop = rmx_taop(rt->rt_rmx); @@ -2860,7 +3109,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 @@ -2868,6 +3122,7 @@ tcp_mss(tp, offer) * funny things may happen in tcp_output. */ offer = max(offer, 64); + } taop->tao_mssopt = offer; /* @@ -2934,34 +3189,25 @@ tcp_mss(tp, offer) tp->t_maxopd = mss; /* - * In case of T/TCP, origoffer==-1 indicates, that no segments - * were received yet. In this case we just guess, otherwise - * we do the same as before T/TCP. + * origoffer==-1 indicates, that no segments were received yet. + * In this case we just guess. */ - if ((tp->t_flags & (TF_REQ_TSTMP|TF_NOOPT)) == TF_REQ_TSTMP && + if ((tp->t_flags & (TF_REQ_TSTMP|TF_NOOPT)) == TF_REQ_TSTMP && (origoffer == -1 || (tp->t_flags & TF_RCVD_TSTMP) == TF_RCVD_TSTMP)) mss -= TCPOLEN_TSTAMP_APPA; - if ((tp->t_flags & (TF_REQ_CC|TF_NOOPT)) == TF_REQ_CC && - (origoffer == -1 || - (tp->t_flags & TF_RCVD_CC) == TF_RCVD_CC)) - mss -= TCPOLEN_CC_APPA; + tp->t_maxseg = mss; -#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) @@ -2975,7 +3221,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) { @@ -3023,6 +3270,7 @@ tcp_mssopt(tp) struct tcpcb *tp; { struct rtentry *rt; + int mss; #if INET6 int isipv6; int min_protoh; @@ -3047,30 +3295,46 @@ tcp_mssopt(tp) isipv6 ? tcp_v6mssdflt : #endif /* INET6 */ tcp_mssdflt; + /* + * Slower link window correction: + * If a value is specificied for slowlink_wsize use it for PPP links + * believed to be on a serial modem (speed <128Kbps). Excludes 9600bps as + * it is the default value adversized by pseudo-devices over ppp. + */ + if (rt->rt_ifp->if_type == IFT_PPP && slowlink_wsize > 0 && + rt->rt_ifp->if_baudrate > 9600 && rt->rt_ifp->if_baudrate <= 128000) { + tp->t_flags |= TF_SLOWLINK; + } - return rt->rt_ifp->if_mtu - min_protoh; + if (rt->rt_rmx.rmx_mtu) + mss = rt->rt_rmx.rmx_mtu; + else { + mss = +#if INET6 + (isipv6 ? nd_ifinfo[rt->rt_ifp->if_index].linkmtu : +#endif + rt->rt_ifp->if_mtu +#if INET6 + ); +#endif + } + return (mss - min_protoh); } - /* - * Checks for partial ack. If partial ack arrives, force the retransmission - * of the next unacknowledged segment, do not clear tp->t_dupacks, and return - * 1. By setting snd_nxt to ti_ack, this forces retransmission timer to - * be started again. If the ack advances at least to tp->snd_recover, return 0. + * On a partial ack arrives, force the retransmission of the + * next unacknowledged segment. Do not clear tp->t_dupacks. + * By setting snd_nxt to ti_ack, this forces retransmission timer to + * be started again. */ -static int -tcp_newreno(tp, th) +static void +tcp_newreno_partial_ack(tp, th) struct tcpcb *tp; struct tcphdr *th; { - if (SEQ_LT(th->th_ack, tp->snd_recover)) { tcp_seq onxt = tp->snd_nxt; u_long ocwnd = tp->snd_cwnd; -#ifdef __APPLE__ tp->t_timer[TCPT_REXMT] = 0; -#else - callout_stop(tp->tt_rexmt); -#endif tp->t_rtttime = 0; tp->snd_nxt = th->th_ack; /* @@ -3079,6 +3343,7 @@ tcp_newreno(tp, th) * is called) */ tp->snd_cwnd = tp->t_maxseg + (th->th_ack - tp->snd_una); + tp->t_flags |= TF_ACKNOW; (void) tcp_output(tp); tp->snd_cwnd = ocwnd; if (SEQ_GT(onxt, tp->snd_nxt)) @@ -3088,7 +3353,113 @@ tcp_newreno(tp, th) * not updated yet. */ tp->snd_cwnd -= (th->th_ack - tp->snd_una - tp->t_maxseg); - return (1); +} + +/* + * Drop a random TCP connection that hasn't been serviced yet and + * is eligible for discard. There is a one in qlen chance that + * we will return a null, saying that there are no dropable + * requests. In this case, the protocol specific code should drop + * the new request. This insures fairness. + * + * The listening TCP socket "head" must be locked + */ +static int +tcpdropdropablreq(struct socket *head) +{ + struct socket *so; + unsigned int i, j, qlen; + static int rnd; + static struct timeval old_runtime; + static unsigned int cur_cnt, old_cnt; + struct timeval tv; + struct inpcb *inp = NULL; + struct tcpcb *tp; + + microtime(&tv); + if ((i = (tv.tv_sec - old_runtime.tv_sec)) != 0) { + old_runtime = tv; + old_cnt = cur_cnt / i; + cur_cnt = 0; + } + + so = TAILQ_FIRST(&head->so_incomp); + if (!so) + return 0; + + qlen = head->so_incqlen; + if (++cur_cnt > qlen || old_cnt > qlen) { + rnd = (314159 * rnd + 66329) & 0xffff; + j = ((qlen + 1) * rnd) >> 16; + + while (j-- && so) + so = TAILQ_NEXT(so, so_list); + } + /* Find a connection that is not already closing */ + while (so) { + inp = (struct inpcb *)so->so_pcb; + + if (in_pcb_checkstate(inp, WNT_ACQUIRE, 0) != WNT_STOPUSING) + break; + + so = TAILQ_NEXT(so, so_list); + } + if (!so) + return 0; + + head->so_incqlen--; + head->so_qlen--; + TAILQ_REMOVE(&head->so_incomp, so, so_list); + tcp_unlock(head, 0, 0); + + /* Let's remove this connection from the incomplete list */ + tcp_lock(so, 1, 0); + + if (in_pcb_checkstate(inp, WNT_RELEASE, 1) == WNT_STOPUSING) { + tcp_unlock(so, 1, 0); + return 0; + } + + so->so_head = NULL; + so->so_usecount--; /* No more held by so_head */ + + /* + * We do not want to lose track of the PCB right away in case we receive + * more segments from the peer + */ + tp = sototcpcb(so); + tp->t_flags |= TF_LQ_OVERFLOW; + tp->t_state = TCPS_CLOSED; + (void) tcp_output(tp); + tcpstat.tcps_drops++; + soisdisconnected(so); + tcp_canceltimers(tp); + add_to_time_wait(tp); + + tcp_unlock(so, 1, 0); + tcp_lock(head, 0, 0); + + return 1; + +} + +static int +tcp_getstat SYSCTL_HANDLER_ARGS +{ + + int error; + + if (req->oldptr == 0) { + req->oldlen= (size_t)sizeof(struct tcpstat); } - return (0); + + error = SYSCTL_OUT(req, &tcpstat, (size_t) req->oldlen); + + return (error); + } + +SYSCTL_PROC(_net_inet_tcp, TCPCTL_STATS, stats, CTLFLAG_RD, 0, 0, + tcp_getstat, "S,tcpstat", "TCP statistics (struct tcpstat, netinet/tcp_var.h)"); + +