X-Git-Url: https://git.saurik.com/apple/xnu.git/blobdiff_plain/d9a64523371fa019c4575bb400cbbc3a50ac9903..HEAD:/bsd/netinet/tcp_usrreq.c diff --git a/bsd/netinet/tcp_usrreq.c b/bsd/netinet/tcp_usrreq.c index bea1e4c0d..861c9f71d 100644 --- a/bsd/netinet/tcp_usrreq.c +++ b/bsd/netinet/tcp_usrreq.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2000-2017 Apple Inc. All rights reserved. + * Copyright (c) 2000-2020 Apple Inc. All rights reserved. * * @APPLE_OSREFERENCE_LICENSE_HEADER_START@ * @@ -67,12 +67,11 @@ #include #include #include -#if INET6 #include -#endif /* INET6 */ -#if !CONFIG_EMBEDDED +#if XNU_TARGET_OS_OSX #include -#endif +#endif /* XNU_TARGET_OS_OSX */ +#include #include #include #include @@ -82,21 +81,16 @@ #include #include #include +#include #include #include -#if INET6 #include -#endif #include -#if INET6 #include -#endif #include #include -#if INET6 #include -#endif #include #include #include @@ -104,6 +98,7 @@ #include #include #include +#include #include #if TCPDEBUG #include @@ -122,40 +117,37 @@ errno_t tcp_fill_info_for_info_tuple(struct info_tuple *, struct tcp_info *); -int tcp_sysctl_info(struct sysctl_oid *, void *, int , struct sysctl_req *); +int tcp_sysctl_info(struct sysctl_oid *, void *, int, struct sysctl_req *); static void tcp_connection_fill_info(struct tcpcb *tp, struct tcp_connection_info *tci); +static int tcp_get_mpkl_send_info(struct mbuf *, struct so_mpkl_send_info *); /* * TCP protocol interface to socket abstraction. */ -extern char *tcpstates[]; /* XXX ??? */ - -static int tcp_attach(struct socket *, struct proc *); -static int tcp_connect(struct tcpcb *, struct sockaddr *, struct proc *); -#if INET6 -static int tcp6_connect(struct tcpcb *, struct sockaddr *, struct proc *); -static int tcp6_usr_connect(struct socket *, struct sockaddr *, - struct proc *); -#endif /* INET6 */ +static int tcp_attach(struct socket *, struct proc *); +static int tcp_connect(struct tcpcb *, struct sockaddr *, struct proc *); +static int tcp6_connect(struct tcpcb *, struct sockaddr *, struct proc *); +static int tcp6_usr_connect(struct socket *, struct sockaddr *, + struct proc *); static struct tcpcb *tcp_disconnect(struct tcpcb *); static struct tcpcb *tcp_usrclosed(struct tcpcb *); extern void tcp_sbrcv_trim(struct tcpcb *tp, struct sockbuf *sb); #if TCPDEBUG -#define TCPDEBUG0 int ostate = 0 -#define TCPDEBUG1() ostate = tp ? tp->t_state : 0 -#define TCPDEBUG2(req) if (tp && (so->so_options & SO_DEBUG)) \ - tcp_trace(TA_USER, ostate, tp, 0, 0, req) +#define TCPDEBUG0 int ostate = 0 +#define TCPDEBUG1() ostate = tp ? tp->t_state : 0 +#define TCPDEBUG2(req) if (tp && (so->so_options & SO_DEBUG)) \ + tcp_trace(TA_USER, ostate, tp, 0, 0, req) #else -#define TCPDEBUG0 -#define TCPDEBUG1() -#define TCPDEBUG2(req) +#define TCPDEBUG0 +#define TCPDEBUG1() +#define TCPDEBUG2(req) #endif SYSCTL_PROC(_net_inet_tcp, OID_AUTO, info, CTLFLAG_RW | CTLFLAG_LOCKED | CTLFLAG_ANYBODY | CTLFLAG_KERN, - 0 , 0, tcp_sysctl_info, "S", "TCP info per tuple"); + 0, 0, tcp_sysctl_info, "S", "TCP info per tuple"); /* * TCP attaches to socket via pru_attach(), reserving space, @@ -182,11 +174,13 @@ tcp_usr_attach(struct socket *so, __unused int proto, struct proc *p) } error = tcp_attach(so, p); - if (error) + if (error) { goto out; + } - if ((so->so_options & SO_LINGER) && so->so_linger == 0) - so->so_linger = TCP_LINGERTIME * hz; + if ((so->so_options & SO_LINGER) && so->so_linger == 0) { + so->so_linger = (short)(TCP_LINGERTIME * hz); + } tp = sototcpcb(so); out: TCPDEBUG2(PRU_ATTACH); @@ -209,13 +203,14 @@ tcp_usr_detach(struct socket *so) TCPDEBUG0; if (inp == 0 || (inp->inp_state == INPCB_STATE_DEAD)) { - return EINVAL; /* XXX */ + return EINVAL; /* XXX */ } socket_lock_assert_owned(so); tp = intotcpcb(inp); /* In case we got disconnected from the peer */ - if (tp == NULL) + if (tp == NULL) { goto out; + } TCPDEBUG1(); calculate_tcp_clock(); @@ -227,28 +222,29 @@ out: } #if NECP -#define COMMON_START() TCPDEBUG0; \ -do { \ - if (inp == NULL || inp->inp_state == INPCB_STATE_DEAD) \ - return (EINVAL); \ - if (necp_socket_should_use_flow_divert(inp)) \ - return (EPROTOTYPE); \ - tp = intotcpcb(inp); \ - TCPDEBUG1(); \ - calculate_tcp_clock(); \ +#define COMMON_START_ALLOW_FLOW_DIVERT(allow) TCPDEBUG0; \ +do { \ + if (inp == NULL || inp->inp_state == INPCB_STATE_DEAD) \ + return (EINVAL); \ + if (!(allow) && necp_socket_should_use_flow_divert(inp)) \ + return (EPROTOTYPE); \ + tp = intotcpcb(inp); \ + TCPDEBUG1(); \ + calculate_tcp_clock(); \ } while (0) #else /* NECP */ -#define COMMON_START() TCPDEBUG0; \ -do { \ - if (inp == NULL || inp->inp_state == INPCB_STATE_DEAD) \ - return (EINVAL); \ - tp = intotcpcb(inp); \ - TCPDEBUG1(); \ - calculate_tcp_clock(); \ +#define COMMON_START_ALLOW_FLOW_DIVERT(allow) TCPDEBUG0; \ +do { \ + if (inp == NULL || inp->inp_state == INPCB_STATE_DEAD) \ + return (EINVAL); \ + tp = intotcpcb(inp); \ + TCPDEBUG1(); \ + calculate_tcp_clock(); \ } while (0) #endif /* !NECP */ -#define COMMON_END(req) out: TCPDEBUG2(req); return error; goto out +#define COMMON_START() COMMON_START_ALLOW_FLOW_DIVERT(false) +#define COMMON_END(req) out: TCPDEBUG2(req); return error; goto out /* @@ -273,7 +269,7 @@ tcp_usr_bind(struct socket *so, struct sockaddr *nam, struct proc *p) struct tcpcb *tp; struct sockaddr_in *sinp; - COMMON_START(); + COMMON_START_ALLOW_FLOW_DIVERT(true); if (nam->sa_family != 0 && nam->sa_family != AF_INET) { error = EAFNOSUPPORT; @@ -291,13 +287,14 @@ tcp_usr_bind(struct socket *so, struct sockaddr *nam, struct proc *p) goto out; } error = in_pcbbind(inp, nam, p); - if (error) + if (error) { goto out; + } #if NECP /* Update NECP client with bind result if not in middle of connect */ if ((inp->inp_flags2 & INP2_CONNECT_IN_PROGRESS) && - !uuid_is_null(inp->necp_client_uuid)) { + !uuid_is_null(inp->necp_client_uuid)) { socket_unlock(so, 0); necp_client_assign_from_socket(so->last_pid, inp->necp_client_uuid, inp); socket_lock(so, 0); @@ -305,10 +302,8 @@ tcp_usr_bind(struct socket *so, struct sockaddr *nam, struct proc *p) #endif /* NECP */ COMMON_END(PRU_BIND); - } -#if INET6 static int tcp6_usr_bind(struct socket *so, struct sockaddr *nam, struct proc *p) { @@ -317,7 +312,7 @@ tcp6_usr_bind(struct socket *so, struct sockaddr *nam, struct proc *p) struct tcpcb *tp; struct sockaddr_in6 *sin6p; - COMMON_START(); + COMMON_START_ALLOW_FLOW_DIVERT(true); if (nam->sa_family != 0 && nam->sa_family != AF_INET6) { error = EAFNOSUPPORT; @@ -337,9 +332,9 @@ tcp6_usr_bind(struct socket *so, struct sockaddr *nam, struct proc *p) inp->inp_vflag &= ~INP_IPV4; inp->inp_vflag |= INP_IPV6; if ((inp->inp_flags & IN6P_IPV6_V6ONLY) == 0) { - if (IN6_IS_ADDR_UNSPECIFIED(&sin6p->sin6_addr)) + if (IN6_IS_ADDR_UNSPECIFIED(&sin6p->sin6_addr)) { inp->inp_vflag |= INP_IPV4; - else if (IN6_IS_ADDR_V4MAPPED(&sin6p->sin6_addr)) { + } else if (IN6_IS_ADDR_V4MAPPED(&sin6p->sin6_addr)) { struct sockaddr_in sin; in6_sin6_2_sin(&sin, sin6p); @@ -350,11 +345,11 @@ tcp6_usr_bind(struct socket *so, struct sockaddr *nam, struct proc *p) } } error = in6_pcbbind(inp, nam, p); - if (error) + if (error) { goto out; + } COMMON_END(PRU_BIND); } -#endif /* INET6 */ /* * Prepare to accept connections. @@ -376,15 +371,17 @@ tcp_usr_listen(struct socket *so, struct proc *p) struct inpcb *inp = sotoinpcb(so); struct tcpcb *tp; - COMMON_START(); - if (inp->inp_lport == 0) + COMMON_START_ALLOW_FLOW_DIVERT(true); + if (inp->inp_lport == 0) { error = in_pcbbind(inp, NULL, p); - if (error == 0) + } + if (error == 0) { tp->t_state = TCPS_LISTEN; + } + TCP_LOG_LISTEN(tp, error); COMMON_END(PRU_LISTEN); } -#if INET6 static int tcp6_usr_listen(struct socket *so, struct proc *p) { @@ -392,18 +389,20 @@ tcp6_usr_listen(struct socket *so, struct proc *p) struct inpcb *inp = sotoinpcb(so); struct tcpcb *tp; - COMMON_START(); + COMMON_START_ALLOW_FLOW_DIVERT(true); if (inp->inp_lport == 0) { inp->inp_vflag &= ~INP_IPV4; - if ((inp->inp_flags & IN6P_IPV6_V6ONLY) == 0) + if ((inp->inp_flags & IN6P_IPV6_V6ONLY) == 0) { inp->inp_vflag |= INP_IPV4; + } error = in6_pcbbind(inp, NULL, p); } - if (error == 0) + if (error == 0) { tp->t_state = TCPS_LISTEN; + } + TCP_LOG_LISTEN(tp, error); COMMON_END(PRU_LISTEN); } -#endif /* INET6 */ static int tcp_connect_complete(struct socket *so) @@ -414,8 +413,10 @@ tcp_connect_complete(struct socket *so) /* TFO delays the tcp_output until later, when the app calls write() */ if (so->so_flags1 & SOF1_PRECONNECT_DATA) { - if (!necp_socket_is_allowed_to_send_recv(sotoinpcb(so), NULL, NULL, NULL)) - return (EHOSTUNREACH); + if (!necp_socket_is_allowed_to_send_recv(sotoinpcb(so), NULL, 0, NULL, NULL, NULL, NULL)) { + TCP_LOG_DROP_NECP(NULL, NULL, tp, true); + return EHOSTUNREACH; + } /* Initialize enough state so that we can actually send data */ tcp_mss(tp, -1, IFSCOPE_NONE); @@ -434,7 +435,7 @@ tcp_connect_complete(struct socket *so) } #endif /* NECP */ - return (error); + return error; } /* @@ -460,30 +461,26 @@ tcp_usr_connect(struct socket *so, struct sockaddr *nam, struct proc *p) error = so->so_error; so->so_error = 0; return error; - } else + } else { return EINVAL; + } } #if NECP +#if CONTENT_FILTER + error = cfil_sock_attach(so, NULL, nam, CFS_CONNECTION_DIR_OUT); + if (error != 0) { + return error; + } +#endif /* CONTENT_FILTER */ #if FLOW_DIVERT - else if (necp_socket_should_use_flow_divert(inp)) { - uint32_t fd_ctl_unit = necp_socket_get_flow_divert_control_unit(inp); - if (fd_ctl_unit > 0) { - error = flow_divert_pcb_init(so, fd_ctl_unit); - if (error == 0) { - error = flow_divert_connect_out(so, nam, p); - } - } else { - error = ENETDOWN; + if (necp_socket_should_use_flow_divert(inp)) { + error = flow_divert_pcb_init(so); + if (error == 0) { + error = flow_divert_connect_out(so, nam, p); } - return error; } #endif /* FLOW_DIVERT */ -#if CONTENT_FILTER - error = cfil_sock_attach(so); - if (error != 0) - return error; -#endif /* CONTENT_FILTER */ #endif /* NECP */ tp = intotcpcb(inp); TCPDEBUG1(); @@ -504,11 +501,15 @@ tcp_usr_connect(struct socket *so, struct sockaddr *nam, struct proc *p) goto out; } - if ((error = tcp_connect(tp, nam, p)) != 0) + if ((error = tcp_connect(tp, nam, p)) != 0) { + TCP_LOG_CONNECT(tp, true, error); goto out; + } error = tcp_connect_complete(so); + TCP_LOG_CONNECT(tp, true, error); + COMMON_END(PRU_CONNECT); } @@ -524,8 +525,9 @@ tcp_usr_connectx_common(struct socket *so, int af, int error = 0; user_ssize_t datalen = 0; - if (inp == NULL) - return (EINVAL); + if (inp == NULL) { + return EINVAL; + } VERIFY(dst != NULL); @@ -537,18 +539,19 @@ tcp_usr_connectx_common(struct socket *so, int af, #endif /* NECP */ if ((so->so_flags1 & SOF1_DATA_IDEMPOTENT) && - (tcp_fastopen & TCP_FASTOPEN_CLIENT)) + (tcp_fastopen & TCP_FASTOPEN_CLIENT)) { sototcpcb(so)->t_flagsext |= TF_FASTOPEN; + } /* bind socket to the specified interface, if requested */ if (ifscope != IFSCOPE_NONE && - (error = inp_bindif(inp, ifscope, NULL)) != 0) { + (error = inp_bindif(inp, ifscope, NULL)) != 0) { goto done; } /* if source address and/or port is specified, bind to it */ if (src != NULL) { - error = sobindlock(so, src, 0); /* already locked */ + error = sobindlock(so, src, 0); /* already locked */ if (error != 0) { goto done; } @@ -558,11 +561,9 @@ tcp_usr_connectx_common(struct socket *so, int af, case AF_INET: error = tcp_usr_connect(so, dst, p); break; -#if INET6 case AF_INET6: error = tcp6_usr_connect(so, dst, p); break; -#endif /* INET6 */ default: VERIFY(0); /* NOTREACHED */ @@ -580,11 +581,12 @@ tcp_usr_connectx_common(struct socket *so, int af, datalen = uio_resid(auio); error = so->so_proto->pr_usrreqs->pru_sosend(so, NULL, - (uio_t)auio, NULL, NULL, 0); + (uio_t)auio, NULL, NULL, 0); socket_lock(so, 0); - if (error == 0 || error == EWOULDBLOCK) + if (error == 0 || error == EWOULDBLOCK) { *bytes_written = datalen - uio_resid(auio); + } /* * sosend returns EWOULDBLOCK if it's a non-blocking @@ -594,19 +596,21 @@ tcp_usr_connectx_common(struct socket *so, int af, * However, connectx() returns EINPROGRESS in case of a * blocking socket. So we change the return value here. */ - if (error == EWOULDBLOCK) + if (error == EWOULDBLOCK) { error = EINPROGRESS; + } } - if (error == 0 && pcid != NULL) + if (error == 0 && pcid != NULL) { *pcid = 1; /* there is only one connection in regular TCP */ - + } done: - if (error && error != EINPROGRESS) + if (error && error != EINPROGRESS) { so->so_flags1 &= ~SOF1_PRECONNECT_DATA; + } inp->inp_flags2 &= ~INP2_CONNECT_IN_PROGRESS; - return (error); + return error; } static int @@ -615,11 +619,10 @@ tcp_usr_connectx(struct socket *so, struct sockaddr *src, sae_associd_t aid, sae_connid_t *pcid, uint32_t flags, void *arg, uint32_t arglen, struct uio *uio, user_ssize_t *bytes_written) { - return (tcp_usr_connectx_common(so, AF_INET, src, dst, p, ifscope, aid, - pcid, flags, arg, arglen, uio, bytes_written)); + return tcp_usr_connectx_common(so, AF_INET, src, dst, p, ifscope, aid, + pcid, flags, arg, arglen, uio, bytes_written); } -#if INET6 static int tcp6_usr_connect(struct socket *so, struct sockaddr *nam, struct proc *p) { @@ -636,30 +639,26 @@ tcp6_usr_connect(struct socket *so, struct sockaddr *nam, struct proc *p) error = so->so_error; so->so_error = 0; return error; - } else + } else { return EINVAL; + } } #if NECP +#if CONTENT_FILTER + error = cfil_sock_attach(so, NULL, nam, CFS_CONNECTION_DIR_OUT); + if (error != 0) { + return error; + } +#endif /* CONTENT_FILTER */ #if FLOW_DIVERT - else if (necp_socket_should_use_flow_divert(inp)) { - uint32_t fd_ctl_unit = necp_socket_get_flow_divert_control_unit(inp); - if (fd_ctl_unit > 0) { - error = flow_divert_pcb_init(so, fd_ctl_unit); - if (error == 0) { - error = flow_divert_connect_out(so, nam, p); - } - } else { - error = ENETDOWN; + if (necp_socket_should_use_flow_divert(inp)) { + error = flow_divert_pcb_init(so); + if (error == 0) { + error = flow_divert_connect_out(so, nam, p); } - return error; } #endif /* FLOW_DIVERT */ -#if CONTENT_FILTER - error = cfil_sock_attach(so); - if (error != 0) - return error; -#endif /* CONTENT_FILTER */ #endif /* NECP */ tp = intotcpcb(inp); @@ -685,24 +684,40 @@ tcp6_usr_connect(struct socket *so, struct sockaddr *nam, struct proc *p) if (IN6_IS_ADDR_V4MAPPED(&sin6p->sin6_addr)) { struct sockaddr_in sin; - if ((inp->inp_flags & IN6P_IPV6_V6ONLY) != 0) - return (EINVAL); + if ((inp->inp_flags & IN6P_IPV6_V6ONLY) != 0) { + error = EINVAL; + goto out; + } in6_sin6_2_sin(&sin, sin6p); + /* + * Must disallow TCP ``connections'' to multicast addresses. + */ + if (IN_MULTICAST(ntohl(sin.sin_addr.s_addr))) { + error = EAFNOSUPPORT; + goto out; + } inp->inp_vflag |= INP_IPV4; inp->inp_vflag &= ~INP_IPV6; - if ((error = tcp_connect(tp, (struct sockaddr *)&sin, p)) != 0) + if ((error = tcp_connect(tp, (struct sockaddr *)&sin, p)) != 0) { + TCP_LOG_CONNECT(tp, true, error); goto out; + } error = tcp_connect_complete(so); goto out; } inp->inp_vflag &= ~INP_IPV4; inp->inp_vflag |= INP_IPV6; - if ((error = tcp6_connect(tp, nam, p)) != 0) + if ((error = tcp6_connect(tp, nam, p)) != 0) { + TCP_LOG_CONNECT(tp, true, error); goto out; + } error = tcp_connect_complete(so); + + TCP_LOG_CONNECT(tp, true, error); + COMMON_END(PRU_CONNECT); } @@ -712,10 +727,9 @@ tcp6_usr_connectx(struct socket *so, struct sockaddr*src, sae_associd_t aid, sae_connid_t *pcid, uint32_t flags, void *arg, uint32_t arglen, struct uio *uio, user_ssize_t *bytes_written) { - return (tcp_usr_connectx_common(so, AF_INET6, src, dst, p, ifscope, aid, - pcid, flags, arg, arglen, uio, bytes_written)); + return tcp_usr_connectx_common(so, AF_INET6, src, dst, p, ifscope, aid, + pcid, flags, arg, arglen, uio, bytes_written); } -#endif /* INET6 */ /* * Initiate disconnect from peer. @@ -737,9 +751,10 @@ tcp_usr_disconnect(struct socket *so) socket_lock_assert_owned(so); COMMON_START(); - /* In case we got disconnected from the peer */ - if (tp == NULL) + /* In case we got disconnected from the peer */ + if (tp == NULL) { goto out; + } tp = tcp_disconnect(tp); COMMON_END(PRU_DISCONNECT); } @@ -751,10 +766,11 @@ static int tcp_usr_disconnectx(struct socket *so, sae_associd_t aid, sae_connid_t cid) { #pragma unused(cid) - if (aid != SAE_ASSOCID_ANY && aid != SAE_ASSOCID_ALL) - return (EINVAL); + if (aid != SAE_ASSOCID_ANY && aid != SAE_ASSOCID_ALL) { + return EINVAL; + } - return (tcp_usr_disconnect(so)); + return tcp_usr_disconnect(so); } /* @@ -776,27 +792,26 @@ tcp_usr_accept(struct socket *so, struct sockaddr **nam) error = ECONNABORTED; goto out; } - if (inp == NULL || inp->inp_state == INPCB_STATE_DEAD) - return (EINVAL); + if (inp == NULL || inp->inp_state == INPCB_STATE_DEAD) { + return EINVAL; + } #if NECP - else if (necp_socket_should_use_flow_divert(inp)) - return (EPROTOTYPE); -#if CONTENT_FILTER - error = cfil_sock_attach(so); - if (error != 0) - return (error); -#endif /* CONTENT_FILTER */ + else if (necp_socket_should_use_flow_divert(inp)) { + return EPROTOTYPE; + } + #endif /* NECP */ tp = intotcpcb(inp); TCPDEBUG1(); + TCP_LOG_ACCEPT(tp, 0); + calculate_tcp_clock(); COMMON_END(PRU_ACCEPT); } -#if INET6 static int tcp6_usr_accept(struct socket *so, struct sockaddr **nam) { @@ -809,27 +824,26 @@ tcp6_usr_accept(struct socket *so, struct sockaddr **nam) error = ECONNABORTED; goto out; } - if (inp == NULL || inp->inp_state == INPCB_STATE_DEAD) - return (EINVAL); + if (inp == NULL || inp->inp_state == INPCB_STATE_DEAD) { + return EINVAL; + } #if NECP - else if (necp_socket_should_use_flow_divert(inp)) - return (EPROTOTYPE); -#if CONTENT_FILTER - error = cfil_sock_attach(so); - if (error != 0) - return (error); -#endif /* CONTENT_FILTER */ + else if (necp_socket_should_use_flow_divert(inp)) { + return EPROTOTYPE; + } + #endif /* NECP */ tp = intotcpcb(inp); TCPDEBUG1(); + TCP_LOG_ACCEPT(tp, 0); + calculate_tcp_clock(); in6_mapped_peeraddr(so, nam); COMMON_END(PRU_ACCEPT); } -#endif /* INET6 */ /* * Mark the connection as being incapable of further output. @@ -856,12 +870,13 @@ tcp_usr_shutdown(struct socket *so) struct tcpcb *tp; TCPDEBUG0; - if (inp == NULL || inp->inp_state == INPCB_STATE_DEAD) - return (EINVAL); + if (inp == NULL || inp->inp_state == INPCB_STATE_DEAD) { + return EINVAL; + } socantsendmore(so); - /* + /* * In case we got disconnected from the peer, or if this is * a socket that is to be flow-diverted (but not yet). */ @@ -870,11 +885,12 @@ tcp_usr_shutdown(struct socket *so) if (tp == NULL #if NECP - || (necp_socket_should_use_flow_divert(inp)) + || (necp_socket_should_use_flow_divert(inp)) #endif /* NECP */ - ) { - if (tp != NULL) + ) { + if (tp != NULL) { error = EPROTOTYPE; + } goto out; } @@ -891,11 +907,13 @@ tcp_usr_shutdown(struct socket *so) #if CONTENT_FILTER /* Don't send a FIN yet */ if (tp && !(so->so_state & SS_ISDISCONNECTED) && - cfil_sock_data_pending(&so->so_snd)) + cfil_sock_data_pending(&so->so_snd)) { goto out; + } #endif /* CONTENT_FILTER */ - if (tp) + if (tp) { error = tcp_output(tp); + } COMMON_END(PRU_SHUTDOWN); } @@ -903,25 +921,31 @@ tcp_usr_shutdown(struct socket *so) * After a receive, possibly send window update to peer. */ static int -tcp_usr_rcvd(struct socket *so, __unused int flags) +tcp_usr_rcvd(struct socket *so, int flags) { int error = 0; struct inpcb *inp = sotoinpcb(so); struct tcpcb *tp; COMMON_START(); - /* In case we got disconnected from the peer */ - if (tp == NULL) + /* In case we got disconnected from the peer */ + if (tp == NULL) { goto out; + } tcp_sbrcv_trim(tp, &so->so_rcv); + if (flags & MSG_WAITALL) { + tp->t_flags |= TF_ACKNOW; + } + /* * This tcp_output is solely there to trigger window-updates. * However, we really do not want these window-updates while we * are still in SYN_SENT or SYN_RECEIVED. */ - if (TCPS_HAVEESTABLISHED(tp->t_state)) + if (TCPS_HAVEESTABLISHED(tp->t_state)) { tcp_output(tp); + } #if CONTENT_FILTER cfil_sock_buf_update(&so->so_rcv); @@ -963,114 +987,119 @@ tcp_usr_rcvd(struct socket *so, __unused int flags) */ static int tcp_usr_send(struct socket *so, int flags, struct mbuf *m, - struct sockaddr *nam, struct mbuf *control, struct proc *p) + struct sockaddr *nam, struct mbuf *control, struct proc *p) { int error = 0; struct inpcb *inp = sotoinpcb(so); struct tcpcb *tp; - uint32_t msgpri = MSG_PRI_DEFAULT; -#if INET6 + uint32_t mpkl_len = 0; /* length of mbuf chain */ + uint32_t mpkl_seq; /* sequence number where new data is added */ + struct so_mpkl_send_info mpkl_send_info = {}; + int isipv6; -#endif TCPDEBUG0; if (inp == NULL || inp->inp_state == INPCB_STATE_DEAD #if NECP - || (necp_socket_should_use_flow_divert(inp)) + || (necp_socket_should_use_flow_divert(inp)) #endif /* NECP */ - ) { + ) { /* * OOPS! we lost a race, the TCP session got reset after * we checked SS_CANTSENDMORE, eg: while doing uiomove or a * network interrupt in the non-splnet() section of sosend(). */ - if (m != NULL) + if (m != NULL) { m_freem(m); + } if (control != NULL) { m_freem(control); control = NULL; } - if (inp == NULL) - error = ECONNRESET; /* XXX EPIPE? */ - else + if (inp == NULL) { + error = ECONNRESET; /* XXX EPIPE? */ + } else { error = EPROTOTYPE; + } tp = NULL; TCPDEBUG1(); goto out; } -#if INET6 isipv6 = nam && nam->sa_family == AF_INET6; -#endif /* INET6 */ tp = intotcpcb(inp); TCPDEBUG1(); calculate_tcp_clock(); + if (net_mpklog_enabled) { + mpkl_seq = tp->snd_una + so->so_snd.sb_cc; + if (m) { + mpkl_len = m_length(m); + } + if (so->so_flags1 & SOF1_MPKL_SEND_INFO) { + uuid_copy(mpkl_send_info.mpkl_uuid, so->so_mpkl_send_uuid); + mpkl_send_info.mpkl_proto = so->so_mpkl_send_proto; + } + } + if (control != NULL) { - if (so->so_flags & SOF_ENABLE_MSGS) { - /* Get the msg priority from control mbufs */ - error = tcp_get_msg_priority(control, &msgpri); - if (error) { + if (control->m_len > 0 && net_mpklog_enabled) { + error = tcp_get_mpkl_send_info(control, &mpkl_send_info); + /* + * Intepretation of the returned code: + * 0: client wants us to use value passed in SCM_MPKL_SEND_INFO + * 1: SCM_MPKL_SEND_INFO was not present + * other: failure + */ + if (error != 0 && error != ENOMSG) { m_freem(control); - if (m != NULL) + if (m != NULL) { m_freem(m); + } control = NULL; m = NULL; goto out; } - m_freem(control); - control = NULL; - } else if (control->m_len) { - /* - * if not unordered, TCP should not have - * control mbufs - */ - m_freem(control); - if (m != NULL) - m_freem(m); - control = NULL; - m = NULL; - error = EINVAL; - goto out; } - } - - if (so->so_flags & SOF_ENABLE_MSGS) { - VERIFY(m->m_flags & M_PKTHDR); - m->m_pkthdr.msg_pri = msgpri; + /* + * Silently drop unsupported ancillary data messages + */ + m_freem(control); + control = NULL; } /* MPTCP sublow socket buffers must not be compressed */ VERIFY(!(so->so_flags & SOF_MP_SUBFLOW) || (so->so_snd.sb_flags & SB_NOCOMPRESS)); - if(!(flags & PRUS_OOB) || (so->so_flags1 & SOF1_PRECONNECT_DATA)) { - /* Call msg send if message delivery is enabled */ - if (so->so_flags & SOF_ENABLE_MSGS) - sbappendmsg_snd(&so->so_snd, m); - else - sbappendstream(&so->so_snd, m); + if (!(flags & PRUS_OOB) || (so->so_flags1 & SOF1_PRECONNECT_DATA)) { + sbappendstream(&so->so_snd, m); if (nam && tp->t_state < TCPS_SYN_SENT) { - /* * Do implied connect if not yet connected, * initialize window to default value, and * initialize maxseg/maxopd using peer's cached * MSS. */ -#if INET6 - if (isipv6) + if (isipv6) { error = tcp6_connect(tp, nam, p); - else -#endif /* INET6 */ + } else { error = tcp_connect(tp, nam, p); - if (error) + } + if (error) { + TCP_LOG_CONNECT(tp, true, error); goto out; + } tp->snd_wnd = TTCP_CLIENT_SND_WND; tp->max_sndwnd = tp->snd_wnd; tcp_mss(tp, -1, IFSCOPE_NONE); + + TCP_LOG_CONNECT(tp, true, error); + + /* The sequence number of the data is past the SYN */ + mpkl_seq = tp->iss + 1; } if (flags & PRUS_EOF) { @@ -1082,11 +1111,13 @@ tcp_usr_send(struct socket *so, int flags, struct mbuf *m, tp = tcp_usrclosed(tp); } if (tp != NULL) { - if (flags & PRUS_MORETOCOME) + if (flags & PRUS_MORETOCOME) { tp->t_flags |= TF_MORETOCOME; + } error = tcp_output(tp); - if (flags & PRUS_MORETOCOME) + if (flags & PRUS_MORETOCOME) { tp->t_flags &= ~TF_MORETOCOME; + } } } else { if (sbspace(&so->so_snd) == 0) { @@ -1112,17 +1143,20 @@ tcp_usr_send(struct socket *so, int flags, struct mbuf *m, * initialize maxseg/maxopd using peer's cached * MSS. */ -#if INET6 - if (isipv6) + if (isipv6) { error = tcp6_connect(tp, nam, p); - else -#endif /* INET6 */ - error = tcp_connect(tp, nam, p); - if (error) + } else { + error = tcp_connect(tp, nam, p); + } + if (error) { + TCP_LOG_CONNECT(tp, true, error); goto out; + } tp->snd_wnd = TTCP_CLIENT_SND_WND; tp->max_sndwnd = tp->snd_wnd; tcp_mss(tp, -1, IFSCOPE_NONE); + + TCP_LOG_CONNECT(tp, true, error); } tp->snd_up = tp->snd_una + so->so_snd.sb_cc; tp->t_flagsext |= TF_FORCE; @@ -1130,20 +1164,32 @@ tcp_usr_send(struct socket *so, int flags, struct mbuf *m, tp->t_flagsext &= ~TF_FORCE; } + if (net_mpklog_enabled && (inp = tp->t_inpcb) != NULL && + ((inp->inp_last_outifp != NULL && + (inp->inp_last_outifp->if_xflags & IFXF_MPK_LOG)) || + (inp->inp_boundifp != NULL && + (inp->inp_boundifp->if_xflags & IFXF_MPK_LOG)))) { + MPKL_TCP_SEND(tcp_mpkl_log_object, + mpkl_send_info.mpkl_proto, mpkl_send_info.mpkl_uuid, + ntohs(inp->inp_lport), ntohs(inp->inp_fport), + mpkl_seq, mpkl_len, + so->last_pid, so->so_log_seqn++); + } /* * We wait for the socket to successfully connect before returning. * This allows us to signal a timeout to the application. */ if (so->so_state & SS_ISCONNECTING) { - if (so->so_state & SS_NBIO) + if (so->so_state & SS_NBIO) { error = EWOULDBLOCK; - else + } else { error = sbwait(&so->so_snd); + } } COMMON_END((flags & PRUS_OOB) ? PRU_SENDOOB : - ((flags & PRUS_EOF) ? PRU_SEND_EOF : PRU_SEND)); + ((flags & PRUS_EOF) ? PRU_SEND_EOF : PRU_SEND)); } /* @@ -1157,9 +1203,10 @@ tcp_usr_abort(struct socket *so) struct tcpcb *tp; COMMON_START(); - /* In case we got disconnected from the peer */ - if (tp == NULL) + /* In case we got disconnected from the peer */ + if (tp == NULL) { goto out; + } tp = tcp_drop(tp, ECONNABORTED); VERIFY(so->so_usecount > 0); so->so_usecount--; @@ -1183,7 +1230,7 @@ tcp_usr_rcvoob(struct socket *so, struct mbuf *m, int flags) COMMON_START(); if ((so->so_oobmark == 0 && - (so->so_state & SS_RCVATMARK) == 0) || + (so->so_state & SS_RCVATMARK) == 0) || so->so_options & SO_OOBINLINE || tp->t_oobflags & TCPOOB_HADDATA) { error = EINVAL; @@ -1196,8 +1243,9 @@ tcp_usr_rcvoob(struct socket *so, struct mbuf *m, int flags) m->m_len = 1; *mtod(m, caddr_t) = tp->t_iobc; so->so_state &= ~SS_RCVATMARK; - if ((flags & MSG_PEEK) == 0) + if ((flags & MSG_PEEK) == 0) { tp->t_oobflags ^= (TCPOOB_HAVEDATA | TCPOOB_HADDATA); + } COMMON_END(PRU_RCVOOB); } @@ -1226,52 +1274,50 @@ tcp_usr_preconnect(struct socket *so) /* xxx - should be const */ struct pr_usrreqs tcp_usrreqs = { - .pru_abort = tcp_usr_abort, - .pru_accept = tcp_usr_accept, - .pru_attach = tcp_usr_attach, - .pru_bind = tcp_usr_bind, - .pru_connect = tcp_usr_connect, - .pru_connectx = tcp_usr_connectx, - .pru_control = in_control, - .pru_detach = tcp_usr_detach, - .pru_disconnect = tcp_usr_disconnect, - .pru_disconnectx = tcp_usr_disconnectx, - .pru_listen = tcp_usr_listen, - .pru_peeraddr = in_getpeeraddr, - .pru_rcvd = tcp_usr_rcvd, - .pru_rcvoob = tcp_usr_rcvoob, - .pru_send = tcp_usr_send, - .pru_shutdown = tcp_usr_shutdown, - .pru_sockaddr = in_getsockaddr, - .pru_sosend = sosend, - .pru_soreceive = soreceive, - .pru_preconnect = tcp_usr_preconnect, + .pru_abort = tcp_usr_abort, + .pru_accept = tcp_usr_accept, + .pru_attach = tcp_usr_attach, + .pru_bind = tcp_usr_bind, + .pru_connect = tcp_usr_connect, + .pru_connectx = tcp_usr_connectx, + .pru_control = in_control, + .pru_detach = tcp_usr_detach, + .pru_disconnect = tcp_usr_disconnect, + .pru_disconnectx = tcp_usr_disconnectx, + .pru_listen = tcp_usr_listen, + .pru_peeraddr = in_getpeeraddr, + .pru_rcvd = tcp_usr_rcvd, + .pru_rcvoob = tcp_usr_rcvoob, + .pru_send = tcp_usr_send, + .pru_shutdown = tcp_usr_shutdown, + .pru_sockaddr = in_getsockaddr, + .pru_sosend = sosend, + .pru_soreceive = soreceive, + .pru_preconnect = tcp_usr_preconnect, }; -#if INET6 struct pr_usrreqs tcp6_usrreqs = { - .pru_abort = tcp_usr_abort, - .pru_accept = tcp6_usr_accept, - .pru_attach = tcp_usr_attach, - .pru_bind = tcp6_usr_bind, - .pru_connect = tcp6_usr_connect, - .pru_connectx = tcp6_usr_connectx, - .pru_control = in6_control, - .pru_detach = tcp_usr_detach, - .pru_disconnect = tcp_usr_disconnect, - .pru_disconnectx = tcp_usr_disconnectx, - .pru_listen = tcp6_usr_listen, - .pru_peeraddr = in6_mapped_peeraddr, - .pru_rcvd = tcp_usr_rcvd, - .pru_rcvoob = tcp_usr_rcvoob, - .pru_send = tcp_usr_send, - .pru_shutdown = tcp_usr_shutdown, - .pru_sockaddr = in6_mapped_sockaddr, - .pru_sosend = sosend, - .pru_soreceive = soreceive, - .pru_preconnect = tcp_usr_preconnect, + .pru_abort = tcp_usr_abort, + .pru_accept = tcp6_usr_accept, + .pru_attach = tcp_usr_attach, + .pru_bind = tcp6_usr_bind, + .pru_connect = tcp6_usr_connect, + .pru_connectx = tcp6_usr_connectx, + .pru_control = in6_control, + .pru_detach = tcp_usr_detach, + .pru_disconnect = tcp_usr_disconnect, + .pru_disconnectx = tcp_usr_disconnectx, + .pru_listen = tcp6_usr_listen, + .pru_peeraddr = in6_mapped_peeraddr, + .pru_rcvd = tcp_usr_rcvd, + .pru_rcvoob = tcp_usr_rcvoob, + .pru_send = tcp_usr_send, + .pru_shutdown = tcp_usr_shutdown, + .pru_sockaddr = in6_mapped_sockaddr, + .pru_sosend = sosend, + .pru_soreceive = soreceive, + .pru_preconnect = tcp_usr_preconnect, }; -#endif /* INET6 */ /* * Common subroutine to open a TCP connection to remote host specified @@ -1310,8 +1356,9 @@ tcp_connect(struct tcpcb *tp, struct sockaddr *nam, struct proc *p) if (inp->inp_lport == 0) { error = in_pcbbind(inp, NULL, p); - if (error) + if (error) { goto done; + } } /* @@ -1320,22 +1367,25 @@ tcp_connect(struct tcpcb *tp, struct sockaddr *nam, struct proc *p) * TIME_WAIT state, creating an ADDRINUSE error. */ error = in_pcbladdr(inp, nam, &laddr, IFSCOPE_NONE, &outif, 0); - if (error) + if (error) { goto done; + } socket_unlock(inp->inp_socket, 0); oinp = in_pcblookup_hash(inp->inp_pcbinfo, sin->sin_addr, sin->sin_port, inp->inp_laddr.s_addr != INADDR_ANY ? inp->inp_laddr : laddr, - inp->inp_lport, 0, NULL); + inp->inp_lport, 0, NULL); socket_lock(inp->inp_socket, 0); if (oinp) { - if (oinp != inp) /* 4143933: avoid deadlock if inp == oinp */ + if (oinp != inp) { /* 4143933: avoid deadlock if inp == oinp */ socket_lock(oinp->inp_socket, 1); + } if (in_pcb_checkstate(oinp, WNT_RELEASE, 1) == WNT_STOPUSING) { - if (oinp != inp) + if (oinp != inp) { socket_unlock(oinp->inp_socket, 1); + } goto skip_oinp; } @@ -1347,13 +1397,15 @@ tcp_connect(struct tcpcb *tp, struct sockaddr *nam, struct proc *p) } else { printf("tcp_connect: inp=0x%llx err=EADDRINUSE\n", (uint64_t)VM_KERNEL_ADDRPERM(inp)); - if (oinp != inp) + if (oinp != inp) { socket_unlock(oinp->inp_socket, 1); + } error = EADDRINUSE; goto done; } - if (oinp != inp) + if (oinp != inp) { socket_unlock(oinp->inp_socket, 1); + } } skip_oinp: if ((inp->inp_laddr.s_addr == INADDR_ANY ? laddr.s_addr : @@ -1380,10 +1432,11 @@ skip_oinp: in_pcbrehash(inp); lck_rw_done(inp->inp_pcbinfo->ipi_lock); - if (inp->inp_flowhash == 0) + if (inp->inp_flowhash == 0) { inp->inp_flowhash = inp_calc_flowhash(inp); + } - tcp_set_max_rwinscale(tp, so, outif); + tcp_set_max_rwinscale(tp, so); soisconnecting(so); tcpstat.tcps_connattempt++; @@ -1391,17 +1444,21 @@ skip_oinp: tp->t_timer[TCPT_KEEP] = OFFSET_FROM_START(tp, TCP_CONN_KEEPINIT(tp)); tp->iss = tcp_new_isn(tp); tcp_sendseqinit(tp); - if (nstat_collect) + tp->t_connect_time = tcp_now; + if (nstat_collect) { nstat_route_connect_attempt(inp->inp_route.ro_rt); + } + + tcp_add_fsw_flow(tp, outif); done: - if (outif != NULL) + if (outif != NULL) { ifnet_release(outif); + } - return (error); + return error; } -#if INET6 static int tcp6_connect(struct tcpcb *tp, struct sockaddr *nam, struct proc *p) { @@ -1415,8 +1472,9 @@ tcp6_connect(struct tcpcb *tp, struct sockaddr *nam, struct proc *p) if (inp->inp_lport == 0) { error = in6_pcbbind(inp, NULL, p); - if (error) + if (error) { goto done; + } } /* @@ -1429,15 +1487,16 @@ tcp6_connect(struct tcpcb *tp, struct sockaddr *nam, struct proc *p) * whenever it's non-NULL. */ error = in6_pcbladdr(inp, nam, &addr6, &outif); - if (error) + if (error) { goto done; + } socket_unlock(inp->inp_socket, 0); oinp = in6_pcblookup_hash(inp->inp_pcbinfo, - &sin6->sin6_addr, sin6->sin6_port, - IN6_IS_ADDR_UNSPECIFIED(&inp->in6p_laddr) - ? &addr6 - : &inp->in6p_laddr, - inp->inp_lport, 0, NULL); + &sin6->sin6_addr, sin6->sin6_port, + IN6_IS_ADDR_UNSPECIFIED(&inp->in6p_laddr) + ? &addr6 + : &inp->in6p_laddr, + inp->inp_lport, 0, NULL); socket_lock(inp->inp_socket, 0); if (oinp) { if (oinp != inp && (otp = intotcpcb(oinp)) != NULL && @@ -1458,44 +1517,50 @@ tcp6_connect(struct tcpcb *tp, struct sockaddr *nam, struct proc *p) } if (IN6_IS_ADDR_UNSPECIFIED(&inp->in6p_laddr)) { inp->in6p_laddr = addr6; - inp->in6p_last_outifp = outif; /* no reference needed */ + inp->in6p_last_outifp = outif; /* no reference needed */ inp->in6p_flags |= INP_IN6ADDR_ANY; } inp->in6p_faddr = sin6->sin6_addr; inp->inp_fport = sin6->sin6_port; - if ((sin6->sin6_flowinfo & IPV6_FLOWINFO_MASK) != 0) + if ((sin6->sin6_flowinfo & IPV6_FLOWINFO_MASK) != 0) { inp->inp_flow = sin6->sin6_flowinfo; + } in_pcbrehash(inp); lck_rw_done(inp->inp_pcbinfo->ipi_lock); - if (inp->inp_flowhash == 0) + if (inp->inp_flowhash == 0) { inp->inp_flowhash = inp_calc_flowhash(inp); + } /* update flowinfo - RFC 6437 */ if (inp->inp_flow == 0 && inp->in6p_flags & IN6P_AUTOFLOWLABEL) { inp->inp_flow &= ~IPV6_FLOWLABEL_MASK; inp->inp_flow |= - (htonl(inp->inp_flowhash) & IPV6_FLOWLABEL_MASK); + (htonl(ip6_randomflowlabel()) & IPV6_FLOWLABEL_MASK); } - tcp_set_max_rwinscale(tp, so, outif); + tcp_set_max_rwinscale(tp, so); soisconnecting(so); tcpstat.tcps_connattempt++; tp->t_state = TCPS_SYN_SENT; tp->t_timer[TCPT_KEEP] = OFFSET_FROM_START(tp, - TCP_CONN_KEEPINIT(tp)); + TCP_CONN_KEEPINIT(tp)); tp->iss = tcp_new_isn(tp); tcp_sendseqinit(tp); - if (nstat_collect) + tp->t_connect_time = tcp_now; + if (nstat_collect) { nstat_route_connect_attempt(inp->inp_route.ro_rt); + } + + tcp_add_fsw_flow(tp, outif); done: - if (outif != NULL) + if (outif != NULL) { ifnet_release(outif); + } - return (error); + return error; } -#endif /* INET6 */ /* * Export TCP internal state information via a struct tcp_info @@ -1507,28 +1572,33 @@ tcp_fill_info(struct tcpcb *tp, struct tcp_info *ti) bzero(ti, sizeof(*ti)); - ti->tcpi_state = tp->t_state; + ti->tcpi_state = (uint8_t)tp->t_state; ti->tcpi_flowhash = inp->inp_flowhash; if (tp->t_state > TCPS_LISTEN) { - if (TSTMP_SUPPORTED(tp)) + if (TSTMP_SUPPORTED(tp)) { ti->tcpi_options |= TCPI_OPT_TIMESTAMPS; - if (SACK_ENABLED(tp)) + } + if (SACK_ENABLED(tp)) { ti->tcpi_options |= TCPI_OPT_SACK; + } if (TCP_WINDOW_SCALE_ENABLED(tp)) { ti->tcpi_options |= TCPI_OPT_WSCALE; ti->tcpi_snd_wscale = tp->snd_scale; ti->tcpi_rcv_wscale = tp->rcv_scale; } - if (TCP_ECN_ENABLED(tp)) + if (TCP_ECN_ENABLED(tp)) { ti->tcpi_options |= TCPI_OPT_ECN; + } /* Are we in retranmission episode */ - if (IN_FASTRECOVERY(tp) || tp->t_rxtshift > 0) + if (IN_FASTRECOVERY(tp) || tp->t_rxtshift > 0) { ti->tcpi_flags |= TCPI_FLAG_LOSSRECOVERY; + } - if (tp->t_flags & TF_STREAMING_ON) + if (tp->t_flags & TF_STREAMING_ON) { ti->tcpi_flags |= TCPI_FLAG_STREAMING_ON; + } ti->tcpi_rto = tp->t_timer[TCPT_REXMT] ? tp->t_rxtcur : 0; ti->tcpi_snd_mss = tp->t_maxseg; @@ -1551,8 +1621,8 @@ tcp_fill_info(struct tcpcb *tp, struct tcp_info *ti) /* convert bytes/msec to bits/sec */ if ((tp->t_flagsext & TF_MEASURESNDBW) != 0 && - tp->t_bwmeas != NULL) { - ti->tcpi_snd_bw = (tp->t_bwmeas->bw_sndbw * 8000); + tp->t_bwmeas != NULL) { + ti->tcpi_snd_bw = (tp->t_bwmeas->bw_sndbw * 8000); } ti->tcpi_last_outif = (tp->t_inpcb->inp_last_outifp == NULL) ? 0 : @@ -1572,7 +1642,7 @@ tcp_fill_info(struct tcpcb *tp, struct tcp_info *ti) ti->tcpi_rxoutoforderbytes = tp->t_stat.rxoutoforderbytes; if (tp->t_state > TCPS_LISTEN) { - ti->tcpi_synrexmits = tp->t_stat.synrxtshift; + ti->tcpi_synrexmits = (uint8_t)tp->t_stat.rxmitsyns; } ti->tcpi_cell_rxpackets = inp->inp_cstat->rxpackets; ti->tcpi_cell_rxbytes = inp->inp_cstat->rxbytes; @@ -1616,22 +1686,29 @@ tcp_fill_info(struct tcpcb *tp, struct tcp_info *ti) ti->tcpi_local_peer = !!(tp->t_flags & TF_LOCAL); if (tp->t_inpcb->inp_last_outifp != NULL) { - if (IFNET_IS_CELLULAR(tp->t_inpcb->inp_last_outifp)) + if (IFNET_IS_CELLULAR(tp->t_inpcb->inp_last_outifp)) { ti->tcpi_if_cell = 1; - if (IFNET_IS_WIFI(tp->t_inpcb->inp_last_outifp)) + } + if (IFNET_IS_WIFI(tp->t_inpcb->inp_last_outifp)) { ti->tcpi_if_wifi = 1; - if (IFNET_IS_WIRED(tp->t_inpcb->inp_last_outifp)) + } + if (IFNET_IS_WIRED(tp->t_inpcb->inp_last_outifp)) { ti->tcpi_if_wired = 1; - if (IFNET_IS_WIFI_INFRA(tp->t_inpcb->inp_last_outifp)) + } + if (IFNET_IS_WIFI_INFRA(tp->t_inpcb->inp_last_outifp)) { ti->tcpi_if_wifi_infra = 1; - if (tp->t_inpcb->inp_last_outifp->if_eflags & IFEF_AWDL) + } + if (tp->t_inpcb->inp_last_outifp->if_eflags & IFEF_AWDL) { ti->tcpi_if_wifi_awdl = 1; + } } - if (tp->tcp_cc_index == TCP_CC_ALGO_BACKGROUND_INDEX) + if (tp->tcp_cc_index == TCP_CC_ALGO_BACKGROUND_INDEX) { ti->tcpi_snd_background = 1; + } if (tcp_recv_bg == 1 || - IS_TCP_RECV_BG(tp->t_inpcb->inp_socket)) + IS_TCP_RECV_BG(tp->t_inpcb->inp_socket)) { ti->tcpi_rcv_background = 1; + } ti->tcpi_ecn_recv_ce = tp->t_ecn_recv_ce; ti->tcpi_ecn_recv_cwr = tp->t_ecn_recv_cwr; @@ -1653,45 +1730,49 @@ tcp_fill_info_for_info_tuple(struct info_tuple *itpl, struct tcp_info *ti) struct socket *so; struct tcpcb *tp; - if (itpl->itpl_proto == IPPROTO_TCP) + if (itpl->itpl_proto == IPPROTO_TCP) { pcbinfo = &tcbinfo; - else + } else { return EINVAL; + } if (itpl->itpl_local_sa.sa_family == AF_INET && - itpl->itpl_remote_sa.sa_family == AF_INET) { + itpl->itpl_remote_sa.sa_family == AF_INET) { inp = in_pcblookup_hash(pcbinfo, - itpl->itpl_remote_sin.sin_addr, - itpl->itpl_remote_sin.sin_port, - itpl->itpl_local_sin.sin_addr, - itpl->itpl_local_sin.sin_port, - 0, NULL); + itpl->itpl_remote_sin.sin_addr, + itpl->itpl_remote_sin.sin_port, + itpl->itpl_local_sin.sin_addr, + itpl->itpl_local_sin.sin_port, + 0, NULL); } else if (itpl->itpl_local_sa.sa_family == AF_INET6 && - itpl->itpl_remote_sa.sa_family == AF_INET6) { + itpl->itpl_remote_sa.sa_family == AF_INET6) { struct in6_addr ina6_local; struct in6_addr ina6_remote; ina6_local = itpl->itpl_local_sin6.sin6_addr; if (IN6_IS_SCOPE_LINKLOCAL(&ina6_local) && - itpl->itpl_local_sin6.sin6_scope_id) - ina6_local.s6_addr16[1] = htons(itpl->itpl_local_sin6.sin6_scope_id); + itpl->itpl_local_sin6.sin6_scope_id) { + ina6_local.s6_addr16[1] = htons((uint16_t)itpl->itpl_local_sin6.sin6_scope_id); + } ina6_remote = itpl->itpl_remote_sin6.sin6_addr; if (IN6_IS_SCOPE_LINKLOCAL(&ina6_remote) && - itpl->itpl_remote_sin6.sin6_scope_id) - ina6_remote.s6_addr16[1] = htons(itpl->itpl_remote_sin6.sin6_scope_id); + itpl->itpl_remote_sin6.sin6_scope_id) { + ina6_remote.s6_addr16[1] = htons((uint16_t)itpl->itpl_remote_sin6.sin6_scope_id); + } inp = in6_pcblookup_hash(pcbinfo, - &ina6_remote, - itpl->itpl_remote_sin6.sin6_port, - &ina6_local, - itpl->itpl_local_sin6.sin6_port, - 0, NULL); + &ina6_remote, + itpl->itpl_remote_sin6.sin6_port, + &ina6_local, + itpl->itpl_local_sin6.sin6_port, + 0, NULL); } else { return EINVAL; } - if (inp == NULL || (so = inp->inp_socket) == NULL) + if (inp == NULL || (so = inp->inp_socket) == NULL) { return ENOENT; + } socket_lock(so, 0); if (in_pcb_checkstate(inp, WNT_RELEASE, 1) == WNT_STOPUSING) { @@ -1712,25 +1793,30 @@ tcp_connection_fill_info(struct tcpcb *tp, struct tcp_connection_info *tci) struct inpcb *inp = tp->t_inpcb; bzero(tci, sizeof(*tci)); - tci->tcpi_state = tp->t_state; + tci->tcpi_state = (uint8_t)tp->t_state; if (tp->t_state > TCPS_LISTEN) { - if (TSTMP_SUPPORTED(tp)) + if (TSTMP_SUPPORTED(tp)) { tci->tcpi_options |= TCPCI_OPT_TIMESTAMPS; - if (SACK_ENABLED(tp)) + } + if (SACK_ENABLED(tp)) { tci->tcpi_options |= TCPCI_OPT_SACK; + } if (TCP_WINDOW_SCALE_ENABLED(tp)) { tci->tcpi_options |= TCPCI_OPT_WSCALE; tci->tcpi_snd_wscale = tp->snd_scale; tci->tcpi_rcv_wscale = tp->rcv_scale; } - if (TCP_ECN_ENABLED(tp)) + if (TCP_ECN_ENABLED(tp)) { tci->tcpi_options |= TCPCI_OPT_ECN; - if (IN_FASTRECOVERY(tp) || tp->t_rxtshift > 0) + } + if (IN_FASTRECOVERY(tp) || tp->t_rxtshift > 0) { tci->tcpi_flags |= TCPCI_FLAG_LOSSRECOVERY; - if (tp->t_flagsext & TF_PKTS_REORDERED) + } + if (tp->t_flagsext & TF_PKTS_REORDERED) { tci->tcpi_flags |= TCPCI_FLAG_REORDERING_DETECTED; + } tci->tcpi_rto = (tp->t_timer[TCPT_REXMT] > 0) ? - tp->t_rxtcur : 0; + tp->t_rxtcur : 0; tci->tcpi_maxseg = tp->t_maxseg; tci->tcpi_snd_ssthresh = tp->snd_ssthresh; tci->tcpi_snd_cwnd = tp->snd_cwnd; @@ -1773,43 +1859,6 @@ tcp_sysctl_info(__unused struct sysctl_oid *oidp, __unused void *arg1, __unused int error; struct tcp_info ti = {}; struct info_tuple itpl; -#if !CONFIG_EMBEDDED - proc_t caller = PROC_NULL; - proc_t caller_parent = PROC_NULL; - char command_name[MAXCOMLEN + 1] = ""; - char parent_name[MAXCOMLEN + 1] = ""; - - if ((caller = proc_self()) != PROC_NULL) { - /* get process name */ - strlcpy(command_name, caller->p_comm, sizeof(command_name)); - - /* get parent process name if possible */ - if ((caller_parent = proc_find(caller->p_ppid)) != PROC_NULL) { - strlcpy(parent_name, caller_parent->p_comm, - sizeof(parent_name)); - proc_rele(caller_parent); - } - - if ((escape_str(command_name, strlen(command_name) + 1, - sizeof(command_name)) == 0) && - (escape_str(parent_name, strlen(parent_name) + 1, - sizeof(parent_name)) == 0)) { - kern_asl_msg(LOG_DEBUG, "messagetracer", - 5, - "com.apple.message.domain", - "com.apple.kernel.tcpstat", /* 1 */ - "com.apple.message.signature", - "tcpinfo", /* 2 */ - "com.apple.message.signature2", command_name, /* 3 */ - "com.apple.message.signature3", parent_name, /* 4 */ - "com.apple.message.summarize", "YES", /* 5 */ - NULL); - } - } - - if (caller != PROC_NULL) - proc_rele(caller); -#endif /* !CONFIG_EMBEDDED */ if (req->newptr == USER_ADDR_NULL) { return EINVAL; @@ -1838,12 +1887,14 @@ tcp_lookup_peer_pid_locked(struct socket *so, pid_t *out_pid) { int error = EHOSTUNREACH; *out_pid = -1; - if ((so->so_state & SS_ISCONNECTED) == 0) return ENOTCONN; + if ((so->so_state & SS_ISCONNECTED) == 0) { + return ENOTCONN; + } - struct inpcb *inp = (struct inpcb*)so->so_pcb; - uint16_t lport = inp->inp_lport; - uint16_t fport = inp->inp_fport; - struct inpcb *finp = NULL; + struct inpcb *inp = (struct inpcb*)so->so_pcb; + uint16_t lport = inp->inp_lport; + uint16_t fport = inp->inp_fport; + struct inpcb *finp = NULL; struct in6_addr laddr6, faddr6; struct in_addr laddr4, faddr4; @@ -1879,6 +1930,90 @@ tcp_getconninfo(struct socket *so, struct conninfo_tcp *tcp_ci) tcp_fill_info(sototcpcb(so), &tcp_ci->tcpci_tcp_info); } +void +tcp_clear_keep_alive_offload(struct socket *so) +{ + struct inpcb *inp; + struct ifnet *ifp; + + inp = sotoinpcb(so); + if (inp == NULL) { + return; + } + + if ((inp->inp_flags2 & INP2_KEEPALIVE_OFFLOAD) == 0) { + return; + } + + ifp = inp->inp_boundifp != NULL ? inp->inp_boundifp : + inp->inp_last_outifp; + if (ifp == NULL) { + panic("%s: so %p inp %p ifp NULL", + __func__, so, inp); + } + + ifnet_lock_exclusive(ifp); + + if (ifp->if_tcp_kao_cnt == 0) { + panic("%s: so %p inp %p ifp %p if_tcp_kao_cnt == 0", + __func__, so, inp, ifp); + } + ifp->if_tcp_kao_cnt--; + inp->inp_flags2 &= ~INP2_KEEPALIVE_OFFLOAD; + + ifnet_lock_done(ifp); +} + +static int +tcp_set_keep_alive_offload(struct socket *so, struct proc *proc) +{ + int error = 0; + struct inpcb *inp; + struct ifnet *ifp; + + inp = sotoinpcb(so); + if (inp == NULL) { + return ECONNRESET; + } + if ((inp->inp_flags2 & INP2_KEEPALIVE_OFFLOAD) != 0) { + return 0; + } + + ifp = inp->inp_boundifp != NULL ? inp->inp_boundifp : + inp->inp_last_outifp; + if (ifp == NULL) { + error = ENXIO; + os_log_info(OS_LOG_DEFAULT, + "%s: error %d for proc %s[%u] out ifp is not set\n", + __func__, error, + proc != NULL ? proc->p_comm : "kernel", + proc != NULL ? proc->p_pid : 0); + return ENXIO; + } + + error = if_get_tcp_kao_max(ifp); + if (error != 0) { + return error; + } + + ifnet_lock_exclusive(ifp); + if (ifp->if_tcp_kao_cnt < ifp->if_tcp_kao_max) { + ifp->if_tcp_kao_cnt++; + inp->inp_flags2 |= INP2_KEEPALIVE_OFFLOAD; + } else { + error = ETOOMANYREFS; + os_log_info(OS_LOG_DEFAULT, + "%s: error %d for proc %s[%u] if_tcp_kao_max %u\n", + __func__, error, + proc != NULL ? proc->p_comm : "kernel", + proc != NULL ? proc->p_pid : 0, + ifp->if_tcp_kao_max); + } + ifnet_lock_done(ifp); + + return error; +} + /* * The new sockopt interface makes it possible for us to block in the * copyin/out step (if we take a page fault). Taking a page fault at @@ -1889,29 +2024,28 @@ tcp_getconninfo(struct socket *so, struct conninfo_tcp *tcp_ci) int tcp_ctloutput(struct socket *so, struct sockopt *sopt) { - int error = 0, opt = 0, optval = 0; - struct inpcb *inp; - struct tcpcb *tp; + int error = 0, opt = 0, optval = 0; + struct inpcb *inp; + struct tcpcb *tp; inp = sotoinpcb(so); if (inp == NULL) { - return (ECONNRESET); + return ECONNRESET; } /* Allow at this level */ if (sopt->sopt_level != IPPROTO_TCP && !(sopt->sopt_level == SOL_SOCKET && (sopt->sopt_name == SO_FLUSH || sopt->sopt_name == SO_TRAFFIC_MGT_BACKGROUND))) { -#if INET6 - if (SOCK_CHECK_DOM(so, PF_INET6)) + if (SOCK_CHECK_DOM(so, PF_INET6)) { error = ip6_ctloutput(so, sopt); - else -#endif /* INET6 */ - error = ip_ctloutput(so, sopt); - return (error); + } else { + error = ip_ctloutput(so, sopt); + } + return error; } tp = intotcpcb(inp); if (tp == NULL) { - return (ECONNRESET); + return ECONNRESET; } calculate_tcp_clock(); @@ -1923,9 +2057,10 @@ tcp_ctloutput(struct socket *so, struct sockopt *sopt) case TCP_NOOPT: case TCP_NOPUSH: error = sooptcopyin(sopt, &optval, sizeof optval, - sizeof optval); - if (error) + sizeof optval); + if (error) { break; + } switch (sopt->sopt_name) { case TCP_NODELAY: @@ -1942,17 +2077,19 @@ tcp_ctloutput(struct socket *so, struct sockopt *sopt) break; } - if (optval) + if (optval) { tp->t_flags |= opt; - else + } else { tp->t_flags &= ~opt; + } break; case TCP_RXT_FINDROP: case TCP_NOTIMEWAIT: error = sooptcopyin(sopt, &optval, sizeof optval, - sizeof optval); - if (error) + sizeof optval); + if (error) { break; + } switch (sopt->sopt_name) { case TCP_RXT_FINDROP: opt = TF_RXTFINDROP; @@ -1964,16 +2101,18 @@ tcp_ctloutput(struct socket *so, struct sockopt *sopt) opt = 0; break; } - if (optval) + if (optval) { tp->t_flagsext |= opt; - else + } else { tp->t_flagsext &= ~opt; + } break; case TCP_MEASURE_SND_BW: error = sooptcopyin(sopt, &optval, sizeof optval, - sizeof optval); - if (error) + sizeof optval); + if (error) { break; + } opt = TF_MEASURESNDBW; if (optval) { if (tp->t_bwmeas == NULL) { @@ -1999,18 +2138,19 @@ tcp_ctloutput(struct socket *so, struct sockopt *sopt) bzero(&in, sizeof(in)); error = sooptcopyin(sopt, &in, sizeof(in), - sizeof(in)); - if (error) + sizeof(in)); + if (error) { break; + } if ((tp->t_flagsext & TF_MEASURESNDBW) == 0 || - tp->t_bwmeas == NULL) { + tp->t_bwmeas == NULL) { error = EINVAL; break; } minpkts = (in.min_burst_size != 0) ? in.min_burst_size : - tp->t_bwmeas->bw_minsizepkts; + tp->t_bwmeas->bw_minsizepkts; maxpkts = (in.max_burst_size != 0) ? in.max_burst_size : - tp->t_bwmeas->bw_maxsizepkts; + tp->t_bwmeas->bw_maxsizepkts; if (minpkts > maxpkts) { error = EINVAL; break; @@ -2023,46 +2163,50 @@ tcp_ctloutput(struct socket *so, struct sockopt *sopt) } case TCP_MAXSEG: error = sooptcopyin(sopt, &optval, sizeof optval, - sizeof optval); - if (error) + sizeof optval); + if (error) { break; + } if (optval > 0 && optval <= tp->t_maxseg && - optval + 40 >= tcp_minmss) + optval + 40 >= tcp_minmss) { tp->t_maxseg = optval; - else + } else { error = EINVAL; + } break; case TCP_KEEPALIVE: error = sooptcopyin(sopt, &optval, sizeof optval, - sizeof optval); - if (error) + sizeof optval); + if (error) { break; - if (optval < 0 || optval > UINT32_MAX/TCP_RETRANSHZ) { + } + if (optval < 0 || optval > UINT32_MAX / TCP_RETRANSHZ) { error = EINVAL; } else { tp->t_keepidle = optval * TCP_RETRANSHZ; /* reset the timer to new value */ tp->t_timer[TCPT_KEEP] = OFFSET_FROM_START(tp, - TCP_CONN_KEEPIDLE(tp)); + TCP_CONN_KEEPIDLE(tp)); tcp_check_timer_state(tp); } - break; + break; case TCP_CONNECTIONTIMEOUT: error = sooptcopyin(sopt, &optval, sizeof optval, - sizeof optval); - if (error) + sizeof optval); + if (error) { break; - if (optval < 0 || optval > UINT32_MAX/TCP_RETRANSHZ) { + } + if (optval < 0 || optval > UINT32_MAX / TCP_RETRANSHZ) { error = EINVAL; } else { tp->t_keepinit = optval * TCP_RETRANSHZ; if (tp->t_state == TCPS_SYN_RECEIVED || - tp->t_state == TCPS_SYN_SENT) { + tp->t_state == TCPS_SYN_SENT) { tp->t_timer[TCPT_KEEP] = OFFSET_FROM_START(tp, - TCP_CONN_KEEPINIT(tp)); + TCP_CONN_KEEPINIT(tp)); tcp_check_timer_state(tp); } } @@ -2070,17 +2214,18 @@ tcp_ctloutput(struct socket *so, struct sockopt *sopt) case TCP_KEEPINTVL: error = sooptcopyin(sopt, &optval, sizeof(optval), - sizeof(optval)); - if (error) + sizeof(optval)); + if (error) { break; - if (optval < 0 || optval > UINT32_MAX/TCP_RETRANSHZ) { + } + if (optval < 0 || optval > UINT32_MAX / TCP_RETRANSHZ) { error = EINVAL; } else { tp->t_keepintvl = optval * TCP_RETRANSHZ; if (tp->t_state == TCPS_FIN_WAIT_2 && - TCP_CONN_MAXIDLE(tp) > 0) { + TCP_CONN_MAXIDLE(tp) > 0) { tp->t_timer[TCPT_2MSL] = OFFSET_FROM_START(tp, - TCP_CONN_MAXIDLE(tp)); + TCP_CONN_MAXIDLE(tp)); tcp_check_timer_state(tp); } } @@ -2088,62 +2233,75 @@ tcp_ctloutput(struct socket *so, struct sockopt *sopt) case TCP_KEEPCNT: error = sooptcopyin(sopt, &optval, sizeof(optval), - sizeof(optval)); - if (error) + sizeof(optval)); + if (error) { break; + } if (optval < 0 || optval > INT32_MAX) { error = EINVAL; } else { tp->t_keepcnt = optval; if (tp->t_state == TCPS_FIN_WAIT_2 && - TCP_CONN_MAXIDLE(tp) > 0) { + TCP_CONN_MAXIDLE(tp) > 0) { tp->t_timer[TCPT_2MSL] = OFFSET_FROM_START(tp, - TCP_CONN_MAXIDLE(tp)); + TCP_CONN_MAXIDLE(tp)); tcp_check_timer_state(tp); } } break; case TCP_KEEPALIVE_OFFLOAD: + if ((error = priv_check_cred(kauth_cred_get(), + PRIV_NETINET_TCP_KA_OFFLOAD, 0)) != 0) { + break; + } error = sooptcopyin(sopt, &optval, sizeof(optval), - sizeof(optval)); - if (error) + sizeof(optval)); + if (error) { break; + } if (optval < 0 || optval > INT32_MAX) { error = EINVAL; break; } - if (optval != 0) - inp->inp_flags2 |= INP2_KEEPALIVE_OFFLOAD; - else - inp->inp_flags2 &= ~INP2_KEEPALIVE_OFFLOAD; + if (optval != 0) { + error = tcp_set_keep_alive_offload(so, + sopt->sopt_p); + } else { + tcp_clear_keep_alive_offload(so); + } break; case PERSIST_TIMEOUT: error = sooptcopyin(sopt, &optval, sizeof optval, - sizeof optval); - if (error) + sizeof optval); + if (error) { break; - if (optval < 0) + } + if (optval < 0) { error = EINVAL; - else + } else { tp->t_persist_timeout = optval * TCP_RETRANSHZ; + } break; case TCP_RXT_CONNDROPTIME: error = sooptcopyin(sopt, &optval, sizeof(optval), - sizeof(optval)); - if (error) + sizeof(optval)); + if (error) { break; - if (optval < 0) + } + if (optval < 0) { error = EINVAL; - else + } else { tp->t_rxt_conndroptime = optval * TCP_RETRANSHZ; + } break; case TCP_NOTSENT_LOWAT: error = sooptcopyin(sopt, &optval, sizeof(optval), - sizeof(optval)); - if (error) + sizeof(optval)); + if (error) { break; + } if (optval < 0) { error = EINVAL; break; @@ -2158,10 +2316,11 @@ tcp_ctloutput(struct socket *so, struct sockopt *sopt) } break; case TCP_ADAPTIVE_READ_TIMEOUT: - error = sooptcopyin(sopt, &optval, sizeof (optval), + error = sooptcopyin(sopt, &optval, sizeof(optval), sizeof(optval)); - if (error) + if (error) { break; + } if (optval < 0 || optval > TCP_ADAPTIVE_TIMEOUT_MAX) { error = EINVAL; @@ -2170,72 +2329,33 @@ tcp_ctloutput(struct socket *so, struct sockopt *sopt) tp->t_adaptive_rtimo = 0; tcp_keepalive_reset(tp); - if (tp->t_mpsub) + if (tp->t_mpsub) { mptcp_reset_keepalive(tp); + } } else { - tp->t_adaptive_rtimo = optval; + tp->t_adaptive_rtimo = (uint8_t)optval; } break; case TCP_ADAPTIVE_WRITE_TIMEOUT: - error = sooptcopyin(sopt, &optval, sizeof (optval), - sizeof (optval)); - if (error) + error = sooptcopyin(sopt, &optval, sizeof(optval), + sizeof(optval)); + if (error) { break; + } if (optval < 0 || optval > TCP_ADAPTIVE_TIMEOUT_MAX) { error = EINVAL; break; } else { - tp->t_adaptive_wtimo = optval; - } - break; - case TCP_ENABLE_MSGS: - error = sooptcopyin(sopt, &optval, sizeof(optval), - sizeof(optval)); - if (error) - break; - if (optval < 0 || optval > 1) { - error = EINVAL; - } else if (optval == 1) { - /* - * Check if messages option is already - * enabled, if so return. - */ - if (so->so_flags & SOF_ENABLE_MSGS) { - VERIFY(so->so_msg_state != NULL); - break; - } - - /* - * allocate memory for storing message - * related state - */ - VERIFY(so->so_msg_state == NULL); - MALLOC(so->so_msg_state, - struct msg_state *, - sizeof(struct msg_state), - M_TEMP, M_WAITOK | M_ZERO); - if (so->so_msg_state == NULL) { - error = ENOMEM; - break; - } - - /* Enable message delivery */ - so->so_flags |= SOF_ENABLE_MSGS; - } else { - /* - * Can't disable message delivery on socket - * because of restrictions imposed by - * encoding/decoding - */ - error = EINVAL; + tp->t_adaptive_wtimo = (uint8_t)optval; } break; case TCP_SENDMOREACKS: error = sooptcopyin(sopt, &optval, sizeof(optval), - sizeof(optval)); - if (error) + sizeof(optval)); + if (error) { break; + } if (optval < 0 || optval > 1) { error = EINVAL; } else if (optval == 0) { @@ -2246,9 +2366,10 @@ tcp_ctloutput(struct socket *so, struct sockopt *sopt) break; case TCP_DISABLE_BLACKHOLE_DETECTION: error = sooptcopyin(sopt, &optval, sizeof(optval), - sizeof(optval)); - if (error) + sizeof(optval)); + if (error) { break; + } if (optval < 0 || optval > 1) { error = EINVAL; } else if (optval == 0) { @@ -2256,8 +2377,9 @@ tcp_ctloutput(struct socket *so, struct sockopt *sopt) } else { tp->t_flagsext |= TF_NOBLACKHOLE_DETECTION; if ((tp->t_flags & TF_BLACKHOLE) && - tp->t_pmtud_saved_maxopd > 0) + tp->t_pmtud_saved_maxopd > 0) { tcp_pmtud_revert_segment_size(tp); + } } break; case TCP_FASTOPEN: @@ -2267,9 +2389,10 @@ tcp_ctloutput(struct socket *so, struct sockopt *sopt) } error = sooptcopyin(sopt, &optval, sizeof(optval), - sizeof(optval)); - if (error) + sizeof(optval)); + if (error) { break; + } if (optval < 0 || optval > 1) { error = EINVAL; break; @@ -2278,17 +2401,22 @@ tcp_ctloutput(struct socket *so, struct sockopt *sopt) error = EINVAL; break; } - if (optval) + if (optval) { tp->t_flagsext |= TF_FASTOPEN; - else + } else { tcp_disable_tfo(tp); + } break; case TCP_FASTOPEN_FORCE_HEURISTICS: + + break; + case TCP_FASTOPEN_FORCE_ENABLE: error = sooptcopyin(sopt, &optval, sizeof(optval), - sizeof(optval)); + sizeof(optval)); - if (error) + if (error) { break; + } if (optval < 0 || optval > 1) { error = EINVAL; break; @@ -2298,17 +2426,19 @@ tcp_ctloutput(struct socket *so, struct sockopt *sopt) error = EINVAL; break; } - if (optval) - tp->t_flagsext |= TF_FASTOPEN_HEUR; - else - tp->t_flagsext &= ~TF_FASTOPEN_HEUR; + if (optval) { + tp->t_flagsext |= TF_FASTOPEN_FORCE_ENABLE; + } else { + tp->t_flagsext &= ~TF_FASTOPEN_FORCE_ENABLE; + } break; case TCP_ENABLE_ECN: error = sooptcopyin(sopt, &optval, sizeof optval, - sizeof optval); - if (error) + sizeof optval); + if (error) { break; + } if (optval) { tp->ecn_flags |= TE_ECN_MODE_ENABLE; tp->ecn_flags &= ~TE_ECN_MODE_DISABLE; @@ -2319,9 +2449,10 @@ tcp_ctloutput(struct socket *so, struct sockopt *sopt) break; case TCP_ECN_MODE: error = sooptcopyin(sopt, &optval, sizeof optval, - sizeof optval); - if (error) + sizeof optval); + if (error) { break; + } if (optval == ECN_MODE_DEFAULT) { tp->ecn_flags &= ~TE_ECN_MODE_ENABLE; tp->ecn_flags &= ~TE_ECN_MODE_DISABLE; @@ -2338,8 +2469,9 @@ tcp_ctloutput(struct socket *so, struct sockopt *sopt) case TCP_NOTIFY_ACKNOWLEDGEMENT: error = sooptcopyin(sopt, &optval, sizeof(optval), sizeof(optval)); - if (error) + if (error) { break; + } if (optval <= 0) { error = EINVAL; break; @@ -2360,17 +2492,19 @@ tcp_ctloutput(struct socket *so, struct sockopt *sopt) error = tcp_add_notify_ack_marker(tp, optval); break; case SO_FLUSH: - if ((error = sooptcopyin(sopt, &optval, sizeof (optval), - sizeof (optval))) != 0) + if ((error = sooptcopyin(sopt, &optval, sizeof(optval), + sizeof(optval))) != 0) { break; + } error = inp_flush(inp, optval); break; case SO_TRAFFIC_MGT_BACKGROUND: - if ((error = sooptcopyin(sopt, &optval, sizeof (optval), - sizeof (optval))) != 0) + if ((error = sooptcopyin(sopt, &optval, sizeof(optval), + sizeof(optval))) != 0) { break; + } if (optval) { socket_set_traffic_mgt_flags_locked(so, @@ -2383,8 +2517,9 @@ tcp_ctloutput(struct socket *so, struct sockopt *sopt) case TCP_RXT_MINIMUM_TIMEOUT: error = sooptcopyin(sopt, &optval, sizeof(optval), sizeof(optval)); - if (error) + if (error) { break; + } if (optval < 0) { error = EINVAL; break; @@ -2413,22 +2548,25 @@ tcp_ctloutput(struct socket *so, struct sockopt *sopt) optval = tp->t_maxseg; break; case TCP_KEEPALIVE: - if (tp->t_keepidle > 0) + if (tp->t_keepidle > 0) { optval = tp->t_keepidle / TCP_RETRANSHZ; - else + } else { optval = tcp_keepidle / TCP_RETRANSHZ; + } break; case TCP_KEEPINTVL: - if (tp->t_keepintvl > 0) + if (tp->t_keepintvl > 0) { optval = tp->t_keepintvl / TCP_RETRANSHZ; - else + } else { optval = tcp_keepintvl / TCP_RETRANSHZ; + } break; case TCP_KEEPCNT: - if (tp->t_keepcnt > 0) + if (tp->t_keepcnt > 0) { optval = tp->t_keepcnt; - else + } else { optval = tcp_keepcnt; + } break; case TCP_KEEPALIVE_OFFLOAD: optval = !!(inp->inp_flags2 & INP2_KEEPALIVE_OFFLOAD); @@ -2443,12 +2581,13 @@ tcp_ctloutput(struct socket *so, struct sockopt *sopt) optval = (tp->ecn_flags & TE_ECN_MODE_ENABLE) ? 1 : 0; break; case TCP_ECN_MODE: - if (tp->ecn_flags & TE_ECN_MODE_ENABLE) + if (tp->ecn_flags & TE_ECN_MODE_ENABLE) { optval = ECN_MODE_ENABLE; - else if (tp->ecn_flags & TE_ECN_MODE_DISABLE) + } else if (tp->ecn_flags & TE_ECN_MODE_DISABLE) { optval = ECN_MODE_DISABLE; - else + } else { optval = ECN_MODE_DEFAULT; + } break; case TCP_CONNECTIONTIMEOUT: optval = tp->t_keepinit / TCP_RETRANSHZ; @@ -2474,7 +2613,10 @@ tcp_ctloutput(struct socket *so, struct sockopt *sopt) optval = tfo_enabled(tp); break; case TCP_FASTOPEN_FORCE_HEURISTICS: - optval = (tp->t_flagsext & TF_FASTOPEN_HEUR) ? 1 : 0; + optval = 0; + break; + case TCP_FASTOPEN_FORCE_ENABLE: + optval = (tp->t_flagsext & TF_FASTOPEN_FORCE_ENABLE) ? 1 : 0; break; case TCP_MEASURE_SND_BW: optval = tp->t_flagsext & TF_MEASURESNDBW; @@ -2497,7 +2639,7 @@ tcp_ctloutput(struct socket *so, struct sockopt *sopt) case TCP_MEASURE_BW_BURST: { struct tcp_measure_bw_burst out = {}; if ((tp->t_flagsext & TF_MEASURESNDBW) == 0 || - tp->t_bwmeas == NULL) { + tp->t_bwmeas == NULL) { error = EINVAL; break; } @@ -2513,31 +2655,26 @@ tcp_ctloutput(struct socket *so, struct sockopt *sopt) optval = 0; } break; - - case TCP_ENABLE_MSGS: - if (so->so_flags & SOF_ENABLE_MSGS) { + case TCP_SENDMOREACKS: + if (tp->t_flagsext & TF_NOSTRETCHACK) { optval = 1; } else { optval = 0; } break; - case TCP_SENDMOREACKS: - if (tp->t_flagsext & TF_NOSTRETCHACK) - optval = 1; - else - optval = 0; - break; case TCP_DISABLE_BLACKHOLE_DETECTION: - if (tp->t_flagsext & TF_NOBLACKHOLE_DETECTION) + if (tp->t_flagsext & TF_NOBLACKHOLE_DETECTION) { optval = 1; - else + } else { optval = 0; + } break; case TCP_PEER_PID: { - pid_t pid; + pid_t pid; error = tcp_lookup_peer_pid_locked(so, &pid); - if (error == 0) + if (error == 0) { error = sooptcopyout(sopt, &pid, sizeof(pid)); + } goto done; } case TCP_ADAPTIVE_READ_TIMEOUT: @@ -2553,16 +2690,17 @@ tcp_ctloutput(struct socket *so, struct sockopt *sopt) case TCP_NOTIFY_ACKNOWLEDGEMENT: { struct tcp_notify_ack_complete retid; - if (sopt->sopt_valsize != sizeof (retid)) { + if (sopt->sopt_valsize != sizeof(retid)) { error = EINVAL; break; } - bzero(&retid, sizeof (retid)); + bzero(&retid, sizeof(retid)); tcp_get_notify_ack_count(tp, &retid); - if (retid.notify_complete_count > 0) + if (retid.notify_complete_count > 0) { tcp_get_notify_ack_ids(tp, &retid); + } - error = sooptcopyout(sopt, &retid, sizeof (retid)); + error = sooptcopyout(sopt, &retid, sizeof(retid)); goto done; } case TCP_RXT_MINIMUM_TIMEOUT: @@ -2572,12 +2710,13 @@ tcp_ctloutput(struct socket *so, struct sockopt *sopt) error = ENOPROTOOPT; break; } - if (error == 0) + if (error == 0) { error = sooptcopyout(sopt, &optval, sizeof optval); + } break; } done: - return (error); + return error; } /* @@ -2585,8 +2724,8 @@ done: * sizes, respectively. These are obsolescent (this information should * be set by the route). */ -u_int32_t tcp_sendspace = 1448*256; -u_int32_t tcp_recvspace = 1448*384; +u_int32_t tcp_sendspace = 1448 * 256; +u_int32_t tcp_recvspace = 1448 * 384; /* During attach, the size of socket buffer allocated is limited to * sb_max in sbreserve. Disallow setting the tcp send and recv space @@ -2595,25 +2734,25 @@ u_int32_t tcp_recvspace = 1448*384; */ static int sysctl_tcp_sospace(struct sysctl_oid *oidp, __unused void *arg1, - int arg2, struct sysctl_req *req) + int arg2, struct sysctl_req *req) { #pragma unused(arg2) u_int32_t new_value = 0, *space_p = NULL; int changed = 0, error = 0; - u_quad_t sb_effective_max = (sb_max / (MSIZE+MCLBYTES)) * MCLBYTES; + u_quad_t sb_effective_max = (sb_max / (MSIZE + MCLBYTES)) * MCLBYTES; switch (oidp->oid_number) { - case TCPCTL_SENDSPACE: - space_p = &tcp_sendspace; - break; - case TCPCTL_RECVSPACE: - space_p = &tcp_recvspace; - break; - default: - return EINVAL; + case TCPCTL_SENDSPACE: + space_p = &tcp_sendspace; + break; + case TCPCTL_RECVSPACE: + space_p = &tcp_recvspace; + break; + default: + return EINVAL; } error = sysctl_io_number(req, *space_p, sizeof(u_int32_t), - &new_value, &changed); + &new_value, &changed); if (changed) { if (new_value > 0 && new_value <= sb_effective_max) { *space_p = new_value; @@ -2627,18 +2766,18 @@ sysctl_tcp_sospace(struct sysctl_oid *oidp, __unused void *arg1, #if SYSCTL_SKMEM SYSCTL_PROC(_net_inet_tcp, TCPCTL_SENDSPACE, sendspace, - CTLTYPE_INT | CTLFLAG_RW | CTLFLAG_LOCKED, &tcp_sendspace, - offsetof(skmem_sysctl, tcp.sendspace), sysctl_tcp_sospace, - "IU", "Maximum outgoing TCP datagram size"); + CTLTYPE_INT | CTLFLAG_RW | CTLFLAG_LOCKED, &tcp_sendspace, + offsetof(skmem_sysctl, tcp.sendspace), sysctl_tcp_sospace, + "IU", "Maximum outgoing TCP datagram size"); SYSCTL_PROC(_net_inet_tcp, TCPCTL_RECVSPACE, recvspace, - CTLTYPE_INT | CTLFLAG_RW | CTLFLAG_LOCKED, &tcp_recvspace, - offsetof(skmem_sysctl, tcp.recvspace), sysctl_tcp_sospace, - "IU", "Maximum incoming TCP datagram size"); + CTLTYPE_INT | CTLFLAG_RW | CTLFLAG_LOCKED, &tcp_recvspace, + offsetof(skmem_sysctl, tcp.recvspace), sysctl_tcp_sospace, + "IU", "Maximum incoming TCP datagram size"); #else /* SYSCTL_SKMEM */ SYSCTL_PROC(_net_inet_tcp, TCPCTL_SENDSPACE, sendspace, CTLTYPE_INT | CTLFLAG_RW | CTLFLAG_LOCKED, - &tcp_sendspace , 0, &sysctl_tcp_sospace, "IU", "Maximum outgoing TCP datagram size"); + &tcp_sendspace, 0, &sysctl_tcp_sospace, "IU", "Maximum outgoing TCP datagram size"); SYSCTL_PROC(_net_inet_tcp, TCPCTL_RECVSPACE, recvspace, CTLTYPE_INT | CTLFLAG_RW | CTLFLAG_LOCKED, - &tcp_recvspace , 0, &sysctl_tcp_sospace, "IU", "Maximum incoming TCP datagram size"); + &tcp_recvspace, 0, &sysctl_tcp_sospace, "IU", "Maximum incoming TCP datagram size"); #endif /* SYSCTL_SKMEM */ /* @@ -2658,57 +2797,57 @@ tcp_attach(struct socket *so, struct proc *p) struct tcpcb *tp; struct inpcb *inp; int error; -#if INET6 int isipv6 = SOCK_CHECK_DOM(so, PF_INET6) != 0; -#endif error = in_pcballoc(so, &tcbinfo, p); - if (error) - return (error); + if (error) { + return error; + } inp = sotoinpcb(so); if (so->so_snd.sb_hiwat == 0 || so->so_rcv.sb_hiwat == 0) { error = soreserve(so, tcp_sendspace, tcp_recvspace); - if (error) - return (error); + if (error) { + return error; + } } if (so->so_snd.sb_preconn_hiwat == 0) { soreserve_preconnect(so, 2048); } - if ((so->so_rcv.sb_flags & SB_USRSIZE) == 0) + if ((so->so_rcv.sb_flags & SB_USRSIZE) == 0) { so->so_rcv.sb_flags |= SB_AUTOSIZE; - if ((so->so_snd.sb_flags & SB_USRSIZE) == 0) + } + if ((so->so_snd.sb_flags & SB_USRSIZE) == 0) { so->so_snd.sb_flags |= SB_AUTOSIZE; + } -#if INET6 if (isipv6) { inp->inp_vflag |= INP_IPV6; - inp->in6p_hops = -1; /* use kernel default */ + inp->in6p_hops = -1; /* use kernel default */ + } else { + inp->inp_vflag |= INP_IPV4; } - else -#endif /* INET6 */ - inp->inp_vflag |= INP_IPV4; tp = tcp_newtcpcb(inp); if (tp == NULL) { - int nofd = so->so_state & SS_NOFDREF; /* XXX */ + int nofd = so->so_state & SS_NOFDREF; /* XXX */ - so->so_state &= ~SS_NOFDREF; /* don't free the socket yet */ -#if INET6 - if (isipv6) + so->so_state &= ~SS_NOFDREF; /* don't free the socket yet */ + if (isipv6) { in6_pcbdetach(inp); - else -#endif /* INET6 */ - in_pcbdetach(inp); + } else { + in_pcbdetach(inp); + } so->so_state |= nofd; - return (ENOBUFS); + return ENOBUFS; } - if (nstat_collect) + if (nstat_collect) { nstat_tcp_new_pcb(inp); + } tp->t_state = TCPS_CLOSED; - return (0); + return 0; } /* @@ -2724,27 +2863,30 @@ tcp_disconnect(struct tcpcb *tp) { struct socket *so = tp->t_inpcb->inp_socket; - if (so->so_rcv.sb_cc != 0 || tp->t_reassqlen != 0) + if (so->so_rcv.sb_cc != 0 || tp->t_reassqlen != 0) { return tcp_drop(tp, 0); + } - if (tp->t_state < TCPS_ESTABLISHED) + if (tp->t_state < TCPS_ESTABLISHED) { tp = tcp_close(tp); - else if ((so->so_options & SO_LINGER) && so->so_linger == 0) + } else if ((so->so_options & SO_LINGER) && so->so_linger == 0) { tp = tcp_drop(tp, 0); - else { + } else { soisdisconnecting(so); sbflush(&so->so_rcv); tp = tcp_usrclosed(tp); #if MPTCP /* A reset has been sent but socket exists, do not send FIN */ if ((so->so_flags & SOF_MP_SUBFLOW) && - (tp) && (tp->t_mpflags & TMPF_RESET)) - return (tp); + (tp) && (tp->t_mpflags & TMPF_RESET)) { + return tp; + } #endif - if (tp) + if (tp) { (void) tcp_output(tp); + } } - return (tp); + return tp; } /* @@ -2761,7 +2903,6 @@ static struct tcpcb * tcp_usrclosed(struct tcpcb *tp) { switch (tp->t_state) { - case TCPS_CLOSED: case TCPS_LISTEN: case TCPS_SYN_SENT: @@ -2774,28 +2915,31 @@ tcp_usrclosed(struct tcpcb *tp) case TCPS_ESTABLISHED: DTRACE_TCP4(state__change, void, NULL, - struct inpcb *, tp->t_inpcb, - struct tcpcb *, tp, - int32_t, TCPS_FIN_WAIT_1); + struct inpcb *, tp->t_inpcb, + struct tcpcb *, tp, + int32_t, TCPS_FIN_WAIT_1); tp->t_state = TCPS_FIN_WAIT_1; + TCP_LOG_CONNECTION_SUMMARY(tp); break; case TCPS_CLOSE_WAIT: DTRACE_TCP4(state__change, void, NULL, - struct inpcb *, tp->t_inpcb, - struct tcpcb *, tp, - int32_t, TCPS_LAST_ACK); + struct inpcb *, tp->t_inpcb, + struct tcpcb *, tp, + int32_t, TCPS_LAST_ACK); tp->t_state = TCPS_LAST_ACK; + TCP_LOG_CONNECTION_SUMMARY(tp); break; } if (tp && tp->t_state >= TCPS_FIN_WAIT_2) { soisdisconnected(tp->t_inpcb->inp_socket); /* To prevent the connection hanging in FIN_WAIT_2 forever. */ - if (tp->t_state == TCPS_FIN_WAIT_2) + if (tp->t_state == TCPS_FIN_WAIT_2) { tp->t_timer[TCPT_2MSL] = OFFSET_FROM_START(tp, - TCP_CONN_MAXIDLE(tp)); + TCP_CONN_MAXIDLE(tp)); + } } - return (tp); + return tp; } void @@ -2812,7 +2956,6 @@ tcp_out_cksum_stats(u_int32_t len) tcpstat.tcps_snd_swcsum_bytes += len; } -#if INET6 void tcp_in6_cksum_stats(u_int32_t len) { @@ -2827,31 +2970,32 @@ tcp_out6_cksum_stats(u_int32_t len) tcpstat.tcps_snd6_swcsum_bytes += len; } -/* - * When messages are enabled on a TCP socket, the message priority - * is sent as a control message. This function will extract it. - */ int -tcp_get_msg_priority(struct mbuf *control, uint32_t *msgpri) +tcp_get_mpkl_send_info(struct mbuf *control, + struct so_mpkl_send_info *mpkl_send_info) { struct cmsghdr *cm; - if (control == NULL) - return(EINVAL); + + if (control == NULL || mpkl_send_info == NULL) { + return EINVAL; + } for (cm = M_FIRST_CMSGHDR(control); cm; - cm = M_NXT_CMSGHDR(control, cm)) { + cm = M_NXT_CMSGHDR(control, cm)) { if (cm->cmsg_len < sizeof(struct cmsghdr) || - cm->cmsg_len > control->m_len) { - return (EINVAL); + cm->cmsg_len > control->m_len) { + return EINVAL; } - if (cm->cmsg_level == SOL_SOCKET && - cm->cmsg_type == SCM_MSG_PRIORITY) { - *msgpri = *(unsigned int *)(void *)CMSG_DATA(cm); - break; + if (cm->cmsg_level != SOL_SOCKET || + cm->cmsg_type != SCM_MPKL_SEND_INFO) { + continue; } + if (cm->cmsg_len != CMSG_LEN(sizeof(struct so_mpkl_send_info))) { + return EINVAL; + } + memcpy(mpkl_send_info, CMSG_DATA(cm), + sizeof(struct so_mpkl_send_info)); + return 0; } - - VERIFY(*msgpri >= MSG_PRI_MIN && *msgpri <= MSG_PRI_MAX); - return (0); + return ENOMSG; } -#endif /* INET6 */