X-Git-Url: https://git.saurik.com/apple/xnu.git/blobdiff_plain/1c79356b52d46aa6b508fb032f5ae709b1f2897b..bb59bff194111743b33cc36712410b5656329d3c:/bsd/netinet6/udp6_usrreq.c diff --git a/bsd/netinet6/udp6_usrreq.c b/bsd/netinet6/udp6_usrreq.c index c6bfcc685..29d037b58 100644 --- a/bsd/netinet6/udp6_usrreq.c +++ b/bsd/netinet6/udp6_usrreq.c @@ -1,4 +1,33 @@ -/* $KAME: udp6_usrreq.c,v 1.25 2000/04/04 11:18:10 itojun Exp $ */ +/* + * Copyright (c) 2000-2014 Apple Inc. All rights reserved. + * + * @APPLE_OSREFERENCE_LICENSE_HEADER_START@ + * + * 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. + * + * 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, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_OSREFERENCE_LICENSE_HEADER_END@ + */ + +/* $FreeBSD: src/sys/netinet6/udp6_usrreq.c,v 1.6.2.6 2001/07/29 19:32:40 ume Exp $ */ +/* $KAME: udp6_usrreq.c,v 1.27 2001/05/21 05:45:10 jinmei Exp $ */ /* * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project. @@ -63,9 +92,6 @@ * * @(#)udp_var.h 8.1 (Berkeley) 6/10/93 */ -#if BSD310 -#include "opt_inet.h" -#endif #include #include @@ -80,10 +106,13 @@ #include #include #include +#include #include #include #include +#include +#include #include #include @@ -102,92 +131,174 @@ #if IPSEC #include -#endif /*IPSEC*/ +#include +#endif /* IPSEC */ -#include "faith.h" +#if NECP +#include +#endif /* NECP */ /* * UDP protocol inplementation. * Per RFC 768, August, 1980. */ -extern struct protosw inetsw[]; -static int in6_mcmatch __P((struct inpcb *, struct in6_addr *, struct ifnet *)); -static int udp6_detach __P((struct socket *so)); +static int udp6_abort(struct socket *); +static int udp6_attach(struct socket *, int, struct proc *); +static int udp6_bind(struct socket *, struct sockaddr *, struct proc *); +static int udp6_connectx(struct socket *, struct sockaddr_list **, + struct sockaddr_list **, struct proc *, uint32_t, associd_t, connid_t *, + uint32_t, void *, uint32_t); +static int udp6_detach(struct socket *); +static int udp6_disconnect(struct socket *); +static int udp6_disconnectx(struct socket *, associd_t, connid_t); +static int udp6_send(struct socket *, int, struct mbuf *, struct sockaddr *, + struct mbuf *, struct proc *); +static void udp6_append(struct inpcb *, struct ip6_hdr *, + struct sockaddr_in6 *, struct mbuf *, int, struct ifnet *); +static int udp6_input_checksum(struct mbuf *, struct udphdr *, int, int); + +#if IPFIREWALL +extern int fw_verbose; +extern void ipfwsyslog( int level, const char *format,...); +extern void ipfw_stealth_stats_incr_udpv6(void); + +/* Apple logging, log to ipfw.log */ +#define log_in_vain_log(a) { \ + if ((udp_log_in_vain == 3) && (fw_verbose == 2)) { \ + ipfwsyslog a; \ + } else if ((udp_log_in_vain == 4) && (fw_verbose == 2)) { \ + ipfw_stealth_stats_incr_udpv6(); \ + } else { \ + log a; \ + } \ +} +#else /* !IPFIREWALL */ +#define log_in_vain_log( a ) { log a; } +#endif /* !IPFIREWALL */ -static int -in6_mcmatch(in6p, ia6, ifp) - struct inpcb *in6p; - register struct in6_addr *ia6; - struct ifnet *ifp; +struct pr_usrreqs udp6_usrreqs = { + .pru_abort = udp6_abort, + .pru_attach = udp6_attach, + .pru_bind = udp6_bind, + .pru_connect = udp6_connect, + .pru_connectx = udp6_connectx, + .pru_control = in6_control, + .pru_detach = udp6_detach, + .pru_disconnect = udp6_disconnect, + .pru_disconnectx = udp6_disconnectx, + .pru_peeraddr = in6_mapped_peeraddr, + .pru_send = udp6_send, + .pru_shutdown = udp_shutdown, + .pru_sockaddr = in6_mapped_sockaddr, + .pru_sosend = sosend, + .pru_soreceive = soreceive, +}; + +/* + * subroutine of udp6_input(), mainly for source code readability. + */ +static void +udp6_append(struct inpcb *last, struct ip6_hdr *ip6, + struct sockaddr_in6 *udp_in6, struct mbuf *n, int off, struct ifnet *ifp) { - struct ip6_moptions *im6o = in6p->in6p_moptions; - struct in6_multi_mship *imm; - - if (im6o == NULL) - return 0; - - for (imm = im6o->im6o_memberships.lh_first; imm != NULL; - imm = imm->i6mm_chain.le_next) { - if ((ifp == NULL || - imm->i6mm_maddr->in6m_ifp == ifp) && - IN6_ARE_ADDR_EQUAL(&imm->i6mm_maddr->in6m_addr, - ia6)) - return 1; +#pragma unused(ip6) + struct mbuf *opts = NULL; + int ret = 0; + boolean_t cell = IFNET_IS_CELLULAR(ifp); + boolean_t wifi = (!cell && IFNET_IS_WIFI(ifp)); + boolean_t wired = (!wifi && IFNET_IS_WIRED(ifp)); + +#if CONFIG_MACF_NET + if (mac_inpcb_check_deliver(last, n, AF_INET6, SOCK_DGRAM) != 0) { + m_freem(n); + return; } - return 0; +#endif /* CONFIG_MACF_NET */ + if ((last->in6p_flags & INP_CONTROLOPTS) != 0 || + (last->in6p_socket->so_options & SO_TIMESTAMP) != 0 || + (last->in6p_socket->so_options & SO_TIMESTAMP_MONOTONIC) != 0) { + ret = ip6_savecontrol(last, n, &opts); + if (ret != 0) { + m_freem(n); + m_freem(opts); + return; + } + } + m_adj(n, off); + if (nstat_collect) { + INP_ADD_STAT(last, cell, wifi, wired, rxpackets, 1); + INP_ADD_STAT(last, cell, wifi, wired, rxbytes, n->m_pkthdr.len); + } + so_recv_data_stat(last->in6p_socket, n, 0); + if (sbappendaddr(&last->in6p_socket->so_rcv, + (struct sockaddr *)udp_in6, n, opts, NULL) == 0) + udpstat.udps_fullsock++; + else + sorwakeup(last->in6p_socket); } int -udp6_input(mp, offp, proto) - struct mbuf **mp; - int *offp, proto; +udp6_input(struct mbuf **mp, int *offp, int proto) { +#pragma unused(proto) struct mbuf *m = *mp; - register struct ip6_hdr *ip6; - register struct udphdr *uh; - register struct inpcb *in6p; - struct ip6_recvpktopts opts; + struct ifnet *ifp; + struct ip6_hdr *ip6; + struct udphdr *uh; + struct inpcb *in6p; + struct mbuf *opts = NULL; int off = *offp; - int plen, ulen; + int plen, ulen, ret = 0; + boolean_t cell, wifi, wired; struct sockaddr_in6 udp_in6; + struct inpcbinfo *pcbinfo = &udbinfo; + struct sockaddr_in6 fromsa; -#if defined(NFAITH) && 0 < NFAITH - if (m->m_pkthdr.rcvif) { - if (m->m_pkthdr.rcvif->if_type == IFT_FAITH) { - /* XXX send icmp6 host/port unreach? */ - m_freem(m); - return IPPROTO_DONE; - } - } -#endif - udpstat.udps_ipackets++; - bzero(&opts, sizeof(opts)); + IP6_EXTHDR_CHECK(m, off, sizeof (struct udphdr), return IPPROTO_DONE); - IP6_EXTHDR_CHECK(m, off, sizeof(struct udphdr), IPPROTO_DONE); + /* Expect 32-bit aligned data pointer on strict-align platforms */ + MBUF_STRICT_DATA_ALIGNMENT_CHECK_32(m); + ifp = m->m_pkthdr.rcvif; ip6 = mtod(m, struct ip6_hdr *); - plen = ntohs(ip6->ip6_plen) - off + sizeof(*ip6); - uh = (struct udphdr *)((caddr_t)ip6 + off); + cell = IFNET_IS_CELLULAR(ifp); + wifi = (!cell && IFNET_IS_WIFI(ifp)); + wired = (!wifi && IFNET_IS_WIRED(ifp)); + + udpstat.udps_ipackets++; + + plen = ntohs(ip6->ip6_plen) - off + sizeof (*ip6); + uh = (struct udphdr *)(void *)((caddr_t)ip6 + off); ulen = ntohs((u_short)uh->uh_ulen); if (plen != ulen) { udpstat.udps_badlen++; + IF_UDP_STATINC(ifp, badlength); + goto bad; + } + + /* destination port of 0 is illegal, based on RFC768. */ + if (uh->uh_dport == 0) { + IF_UDP_STATINC(ifp, port0); goto bad; } /* * Checksum extended UDP header and data. */ -// if (uh->uh_sum == 0) -// udpstat.udps_nosum++; - else if (in6_cksum(m, IPPROTO_UDP, off, ulen) != 0) { - udpstat.udps_badsum++; + if (udp6_input_checksum(m, uh, off, ulen)) goto bad; - } + + /* + * Construct sockaddr format source address. + */ + init_sin6(&fromsa, m); + fromsa.sin6_port = uh->uh_sport; if (IN6_IS_ADDR_MULTICAST(&ip6->ip6_dst)) { - struct inpcb *last; + int reuse_sock = 0, mcast_delivered = 0; + struct ip6_moptions *imo; /* * Deliver a multicast datagram to all sockets @@ -229,65 +340,101 @@ udp6_input(mp, offp, proto) * Locate pcb(s) for datagram. * (Algorithm copied from raw_intr().) */ - last = NULL; + lck_rw_lock_shared(pcbinfo->ipi_lock); + LIST_FOREACH(in6p, &udb, inp_list) { +#if IPSEC + int skipit; +#endif /* IPSEC */ + if ((in6p->inp_vflag & INP_IPV6) == 0) continue; - if (in6p->in6p_lport != uh->uh_dport) + + if (inp_restricted_recv(in6p, ifp)) + continue; + + if (in_pcb_checkstate(in6p, WNT_ACQUIRE, 0) == + WNT_STOPUSING) + continue; + + udp_lock(in6p->in6p_socket, 1, 0); + + if (in_pcb_checkstate(in6p, WNT_RELEASE, 1) == + WNT_STOPUSING) { + udp_unlock(in6p->in6p_socket, 1, 0); + continue; + } + if (in6p->in6p_lport != uh->uh_dport) { + udp_unlock(in6p->in6p_socket, 1, 0); continue; - if (!IN6_IS_ADDR_UNSPECIFIED(&in6p->in6p_laddr)) { - if (!IN6_ARE_ADDR_EQUAL(&in6p->in6p_laddr, - &ip6->ip6_dst) && - !in6_mcmatch(in6p, &ip6->ip6_dst, - m->m_pkthdr.rcvif)) - continue; } - if (!IN6_IS_ADDR_UNSPECIFIED(&in6p->in6p_faddr)) { - if (!IN6_ARE_ADDR_EQUAL(&in6p->in6p_faddr, - &ip6->ip6_src) || - in6p->in6p_fport != uh->uh_sport) + + /* + * Handle socket delivery policy for any-source + * and source-specific multicast. [RFC3678] + */ + imo = in6p->in6p_moptions; + if (imo && IN6_IS_ADDR_MULTICAST(&ip6->ip6_dst)) { + struct sockaddr_in6 mcaddr; + int blocked; + + IM6O_LOCK(imo); + bzero(&mcaddr, sizeof (struct sockaddr_in6)); + mcaddr.sin6_len = sizeof (struct sockaddr_in6); + mcaddr.sin6_family = AF_INET6; + mcaddr.sin6_addr = ip6->ip6_dst; + + blocked = im6o_mc_filter(imo, ifp, + (struct sockaddr *)&mcaddr, + (struct sockaddr *)&fromsa); + IM6O_UNLOCK(imo); + if (blocked != MCAST_PASS) { + udp_unlock(in6p->in6p_socket, 1, 0); + if (blocked == MCAST_NOTSMEMBER || + blocked == MCAST_MUTED) + udpstat.udps_filtermcast++; continue; + } + } + if (!IN6_IS_ADDR_UNSPECIFIED(&in6p->in6p_faddr) && + (!IN6_ARE_ADDR_EQUAL(&in6p->in6p_faddr, + &ip6->ip6_src) || + in6p->in6p_fport != uh->uh_sport)) { + udp_unlock(in6p->in6p_socket, 1, 0); + continue; } - if (last != NULL) { - struct mbuf *n; + reuse_sock = in6p->inp_socket->so_options & + (SO_REUSEPORT | SO_REUSEADDR); -#if IPSEC +#if NECP + skipit = 0; + if (!necp_socket_is_allowed_to_send_recv_v6(in6p, + uh->uh_dport, uh->uh_sport, &ip6->ip6_dst, + &ip6->ip6_src, ifp, NULL)) { + /* do not inject data to pcb */ + skipit = 1; + } + if (skipit == 0) +#endif /* NECP */ + { + struct mbuf *n = NULL; /* - * Check AH/ESP integrity. + * KAME NOTE: do not + * m_copy(m, offset, ...) below. + * sbappendaddr() expects M_PKTHDR, + * and m_copy() will copy M_PKTHDR + * only if offset is 0. */ - if (ipsec6_in_reject_so(m, last->inp_socket)) - ipsec6stat.in_polvio++; - /* do not inject data into pcb */ - else -#endif /*IPSEC*/ - if ((n = m_copy(m, 0, M_COPYALL)) != NULL) { - /* - * KAME NOTE: do not - * m_copy(m, offset, ...) above. - * sbappendaddr() expects M_PKTHDR, - * and m_copy() will copy M_PKTHDR - * only if offset is 0. - */ - if (last->in6p_flags & IN6P_CONTROLOPTS - || last->in6p_socket->so_options & SO_TIMESTAMP) - ip6_savecontrol(last, ip6, n, - &opts, NULL); - - m_adj(n, off + sizeof(struct udphdr)); - if (sbappendaddr(&last->in6p_socket->so_rcv, - (struct sockaddr *)&udp_in6, - n, opts.head) == 0) { - m_freem(n); - if (opts.head) - m_freem(opts.head); - udpstat.udps_fullsock++; - } else - sorwakeup(last->in6p_socket); - bzero(&opts, sizeof(opts)); - } + if (reuse_sock) + n = m_copy(m, 0, M_COPYALL); + udp6_append(in6p, ip6, &udp_in6, m, + off + sizeof (struct udphdr), ifp); + mcast_delivered++; + m = n; } - last = in6p; + udp_unlock(in6p->in6p_socket, 1, 0); + /* * Don't look for additional matches if this one does * not have either the SO_REUSEPORT or SO_REUSEADDR @@ -296,121 +443,151 @@ udp6_input(mp, offp, proto) * port. It assumes that an application will never * clear these options after setting them. */ - if ((last->in6p_socket->so_options & - (SO_REUSEPORT|SO_REUSEADDR)) == 0) + if (reuse_sock == 0 || m == NULL) break; + + /* + * Expect 32-bit aligned data pointer on strict-align + * platforms. + */ + MBUF_STRICT_DATA_ALIGNMENT_CHECK_32(m); + + /* + * Recompute IP and UDP header pointers for new mbuf + */ + ip6 = mtod(m, struct ip6_hdr *); + uh = (struct udphdr *)(void *)((caddr_t)ip6 + off); } + lck_rw_done(pcbinfo->ipi_lock); - if (last == NULL) { + if (mcast_delivered == 0) { /* * No matching pcb found; discard datagram. * (No need to send an ICMP Port Unreachable * for a broadcast or multicast datgram.) */ udpstat.udps_noport++; -// udpstat.udps_noportmcast++; + udpstat.udps_noportmcast++; + IF_UDP_STATINC(ifp, port_unreach); goto bad; } -#if IPSEC - /* - * Check AH/ESP integrity. - */ - if (ipsec6_in_reject_so(m, last->inp_socket)) { - ipsec6stat.in_polvio++; - goto bad; - } -#endif /*IPSEC*/ - if (last->in6p_flags & IN6P_CONTROLOPTS - || last->in6p_socket->so_options & SO_TIMESTAMP) - ip6_savecontrol(last, ip6, m, &opts, NULL); - - m_adj(m, off + sizeof(struct udphdr)); - if (sbappendaddr(&last->in6p_socket->so_rcv, - (struct sockaddr *)&udp_in6, - m, opts.head) == 0) { - udpstat.udps_fullsock++; - goto bad; - } - sorwakeup(last->in6p_socket); - return IPPROTO_DONE; + + /* free the extra copy of mbuf or skipped by NECP */ + if (m != NULL) + m_freem(m); + return (IPPROTO_DONE); } /* * Locate pcb for datagram. */ in6p = in6_pcblookup_hash(&udbinfo, &ip6->ip6_src, uh->uh_sport, - &ip6->ip6_dst, uh->uh_dport, 1, - m->m_pkthdr.rcvif); - if (in6p == 0) { - if (log_in_vain) { + &ip6->ip6_dst, uh->uh_dport, 1, m->m_pkthdr.rcvif); + if (in6p == NULL) { + IF_UDP_STATINC(ifp, port_unreach); + + if (udp_log_in_vain) { char buf[INET6_ADDRSTRLEN]; - strcpy(buf, ip6_sprintf(&ip6->ip6_dst)); - log(LOG_INFO, - "Connection attempt to UDP %s:%d from %s:%d\n", - buf, ntohs(uh->uh_dport), - ip6_sprintf(&ip6->ip6_src), ntohs(uh->uh_sport)); + strlcpy(buf, ip6_sprintf(&ip6->ip6_dst), sizeof (buf)); + if (udp_log_in_vain < 3) { + log(LOG_INFO, "Connection attempt to UDP " + "%s:%d from %s:%d\n", buf, + ntohs(uh->uh_dport), + ip6_sprintf(&ip6->ip6_src), + ntohs(uh->uh_sport)); + } else if (!(m->m_flags & (M_BCAST | M_MCAST)) && + !IN6_ARE_ADDR_EQUAL(&ip6->ip6_dst, &ip6->ip6_src)) { + log_in_vain_log((LOG_INFO, "Connection attempt " + "to UDP %s:%d from %s:%d\n", buf, + ntohs(uh->uh_dport), + ip6_sprintf(&ip6->ip6_src), + ntohs(uh->uh_sport))); + } } udpstat.udps_noport++; if (m->m_flags & M_MCAST) { printf("UDP6: M_MCAST is set in a unicast packet.\n"); -// udpstat.udps_noportmcast++; + udpstat.udps_noportmcast++; + IF_UDP_STATINC(ifp, badmcast); goto bad; } icmp6_error(m, ICMP6_DST_UNREACH, ICMP6_DST_UNREACH_NOPORT, 0); - return IPPROTO_DONE; + return (IPPROTO_DONE); } -#if IPSEC - /* - * Check AH/ESP integrity. - */ - if (ipsec6_in_reject_so(m, in6p->in6p_socket)) { - ipsec6stat.in_polvio++; +#if NECP + if (!necp_socket_is_allowed_to_send_recv_v6(in6p, uh->uh_dport, + uh->uh_sport, &ip6->ip6_dst, &ip6->ip6_src, ifp, NULL)) { + in_pcb_checkstate(in6p, WNT_RELEASE, 0); + IF_UDP_STATINC(ifp, badipsec); goto bad; } -#endif /*IPSEC*/ +#endif /* NECP */ /* * Construct sockaddr format source address. * Stuff source address and datagram in user buffer. */ + udp_lock(in6p->in6p_socket, 1, 0); + + if (in_pcb_checkstate(in6p, WNT_RELEASE, 1) == WNT_STOPUSING) { + udp_unlock(in6p->in6p_socket, 1, 0); + IF_UDP_STATINC(ifp, cleanup); + goto bad; + } + init_sin6(&udp_in6, m); /* general init */ udp_in6.sin6_port = uh->uh_sport; - if (in6p->in6p_flags & IN6P_CONTROLOPTS - || in6p->in6p_socket->so_options & SO_TIMESTAMP) - ip6_savecontrol(in6p, ip6, m, &opts, NULL); - m_adj(m, off + sizeof(struct udphdr)); + if ((in6p->in6p_flags & INP_CONTROLOPTS) != 0 || + (in6p->in6p_socket->so_options & SO_TIMESTAMP) != 0 || + (in6p->in6p_socket->so_options & SO_TIMESTAMP_MONOTONIC) != 0) { + ret = ip6_savecontrol(in6p, m, &opts); + if (ret != 0) { + udp_unlock(in6p->in6p_socket, 1, 0); + goto bad; + } + } + m_adj(m, off + sizeof (struct udphdr)); + if (nstat_collect) { + INP_ADD_STAT(in6p, cell, wifi, wired, rxpackets, 1); + INP_ADD_STAT(in6p, cell, wifi, wired, rxbytes, m->m_pkthdr.len); + } + so_recv_data_stat(in6p->in6p_socket, m, 0); if (sbappendaddr(&in6p->in6p_socket->so_rcv, - (struct sockaddr *)&udp_in6, - m, opts.head) == 0) { + (struct sockaddr *)&udp_in6, m, opts, NULL) == 0) { + m = NULL; + opts = NULL; udpstat.udps_fullsock++; + udp_unlock(in6p->in6p_socket, 1, 0); goto bad; } sorwakeup(in6p->in6p_socket); - return IPPROTO_DONE; + udp_unlock(in6p->in6p_socket, 1, 0); + return (IPPROTO_DONE); bad: - if (m) + if (m != NULL) m_freem(m); - if (opts.head) - m_freem(opts.head); - return IPPROTO_DONE; + if (opts != NULL) + m_freem(opts); + return (IPPROTO_DONE); } void -udp6_ctlinput(cmd, sa, d) - int cmd; - struct sockaddr *sa; - void *d; +udp6_ctlinput(int cmd, struct sockaddr *sa, void *d) { - register struct udphdr *uhp; struct udphdr uh; - struct sockaddr_in6 sa6; struct ip6_hdr *ip6; struct mbuf *m; int off = 0; - void (*notify) __P((struct inpcb *, int)) = udp_notify; + struct ip6ctlparam *ip6cp = NULL; + const struct sockaddr_in6 *sa6_src = NULL; + void (*notify)(struct inpcb *, int) = udp_notify; + struct udp_portonly { + u_int16_t uh_sport; + u_int16_t uh_dport; + } *uhp; if (sa->sa_family != AF_INET6 || - sa->sa_len != sizeof(struct sockaddr_in6)) + sa->sa_len != sizeof (struct sockaddr_in6)) return; if ((unsigned)cmd >= PRC_NCMDS) @@ -424,375 +601,223 @@ udp6_ctlinput(cmd, sa, d) /* if the parameter is from icmp6, decode it. */ if (d != NULL) { - struct ip6ctlparam *ip6cp = (struct ip6ctlparam *)d; + ip6cp = (struct ip6ctlparam *)d; m = ip6cp->ip6c_m; ip6 = ip6cp->ip6c_ip6; off = ip6cp->ip6c_off; + sa6_src = ip6cp->ip6c_src; } else { m = NULL; ip6 = NULL; + sa6_src = &sa6_any; } - /* translate addresses into internal form */ - sa6 = *(struct sockaddr_in6 *)sa; - if (IN6_IS_ADDR_LINKLOCAL(&sa6.sin6_addr) && m && m->m_pkthdr.rcvif) - sa6.sin6_addr.s6_addr16[1] = htons(m->m_pkthdr.rcvif->if_index); - - if (ip6) { + if (ip6 != NULL) { /* * XXX: We assume that when IPV6 is non NULL, * M and OFF are valid. */ - struct in6_addr s; - - /* translate addresses into internal form */ - memcpy(&s, &ip6->ip6_src, sizeof(s)); - if (IN6_IS_ADDR_LINKLOCAL(&s)) - s.s6_addr16[1] = htons(m->m_pkthdr.rcvif->if_index); - - if (m->m_len < off + sizeof(uh)) { - /* - * this should be rare case, - * so we compromise on this copy... - */ - m_copydata(m, off, sizeof(uh), (caddr_t)&uh); - uhp = &uh; - } else - uhp = (struct udphdr *)(mtod(m, caddr_t) + off); - (void) in6_pcbnotify(&udb, (struct sockaddr *)&sa6, - uhp->uh_dport, &s, - uhp->uh_sport, cmd, notify); - } else - (void) in6_pcbnotify(&udb, (struct sockaddr *)&sa6, 0, - &zeroin6_addr, 0, cmd, notify); -} -#if 0 -static int -udp6_getcred SYSCTL_HANDLER_ARGS -{ - struct sockaddr_in6 addrs[2]; - struct inpcb *inp; - int error, s; - - error = suser(req->p->p_ucred, &req->p->p_acflag); - if (error) - return (error); - - if (req->newlen != sizeof(addrs)) - return (EINVAL); - if (req->oldlen != sizeof(struct ucred)) - return (EINVAL); - error = SYSCTL_IN(req, addrs, sizeof(addrs)); - if (error) - return (error); - s = splnet(); - inp = in6_pcblookup_hash(&udbinfo, &addrs[1].sin6_addr, - addrs[1].sin6_port, - &addrs[0].sin6_addr, addrs[0].sin6_port, - 1, NULL); - if (!inp || !inp->inp_socket || !inp->inp_socket->so_cred) { - error = ENOENT; - goto out; - } - error = SYSCTL_OUT(req, inp->inp_socket->so_cred->pc_ucred, - sizeof(struct ucred)); -out: - splx(s); - return (error); -} + /* check if we can safely examine src and dst ports */ + if (m->m_pkthdr.len < off + sizeof (*uhp)) + return; -SYSCTL_PROC(_net_inet6_udp6, OID_AUTO, getcred, CTLTYPE_OPAQUE|CTLFLAG_RW, - 0, 0, - udp6_getcred, "S,ucred", "Get the ucred of a UDP6 connection"); -#endif -int -udp6_output(in6p, m, addr6, control, p) - register struct inpcb *in6p; - register struct mbuf *m; - struct sockaddr *addr6; - struct mbuf *control; - struct proc *p; -{ - register int ulen = m->m_pkthdr.len; - int plen = sizeof(struct udphdr) + ulen; - struct ip6_hdr *ip6; - struct udphdr *udp6; - struct in6_addr laddr6; - int s = 0, error = 0; - struct ip6_pktopts opt, *stickyopt = in6p->in6p_outputopts; - int flags; - - if (control) { - if (error = ip6_setpktoptions(control, &opt, - p && - suser(p->p_ucred, &p->p_acflag), 0)) - goto release; - in6p->in6p_outputopts = &opt; - } + bzero(&uh, sizeof (uh)); + m_copydata(m, off, sizeof (*uhp), (caddr_t)&uh); - if (addr6) { - laddr6 = in6p->in6p_laddr; - if (!IN6_IS_ADDR_UNSPECIFIED(&in6p->in6p_faddr)) { - error = EISCONN; - goto release; - } - /* - * Must block input while temporarily connected. - */ - s = splnet(); - /* - * XXX: the user might want to overwrite the local address - * via an ancillary data. - */ - bzero(&in6p->in6p_laddr, sizeof(struct in6_addr)); - error = in6_pcbconnect(in6p, addr6, p); - if (error) { - splx(s); - goto release; - } + (void) in6_pcbnotify(&udbinfo, sa, uh.uh_dport, + (struct sockaddr*)ip6cp->ip6c_src, uh.uh_sport, + cmd, NULL, notify); } else { - if (IN6_IS_ADDR_UNSPECIFIED(&in6p->in6p_faddr)) { - error = ENOTCONN; - goto release; - } - } - /* - * Calculate data length and get a mbuf - * for UDP and IP6 headers. - */ - M_PREPEND(m, sizeof(struct ip6_hdr) + sizeof(struct udphdr), - M_DONTWAIT); - if (m == 0) { - error = ENOBUFS; - if (addr6) - splx(s); - goto release; + (void) in6_pcbnotify(&udbinfo, sa, 0, + (struct sockaddr *)&sa6_src, 0, cmd, NULL, notify); } - - /* - * Stuff checksum and output datagram. - */ - ip6 = mtod(m, struct ip6_hdr *); - ip6->ip6_flow = in6p->in6p_flowinfo & IPV6_FLOWINFO_MASK; - ip6->ip6_vfc |= IPV6_VERSION; -#if 0 /* ip6_plen will be filled in ip6_output. */ - ip6->ip6_plen = htons((u_short)plen); -#endif - ip6->ip6_nxt = IPPROTO_UDP; - ip6->ip6_hlim = in6_selecthlim(in6p, - in6p->in6p_route.ro_rt ? - in6p->in6p_route.ro_rt->rt_ifp : - NULL); - ip6->ip6_src = in6p->in6p_laddr; - ip6->ip6_dst = in6p->in6p_faddr; - - udp6 = (struct udphdr *)(ip6 + 1); - udp6->uh_sport = in6p->in6p_lport; - udp6->uh_dport = in6p->in6p_fport; - udp6->uh_ulen = htons((u_short)plen); - udp6->uh_sum = 0; - - if ((udp6->uh_sum = in6_cksum(m, IPPROTO_UDP, - sizeof(struct ip6_hdr), plen)) == 0) { - udp6->uh_sum = 0xffff; - } - - flags = 0; - if (in6p->in6p_flags & IN6P_MINMTU) - flags |= IPV6_MINMTU; - - udpstat.udps_opackets++; - -#if IPSEC - ipsec_setsocket(m, in6p->in6p_socket); -#endif /*IPSEC*/ - error = ip6_output(m, in6p->in6p_outputopts, &in6p->in6p_route, - flags, in6p->in6p_moptions, NULL); - - if (addr6) { - in6_pcbdisconnect(in6p); - in6p->in6p_laddr = laddr6; - splx(s); - } - goto releaseopt; - -release: - m_freem(m); - -releaseopt: - if (control) { - ip6_clearpktopts(in6p->in6p_outputopts, 0, -1); - in6p->in6p_outputopts = stickyopt; - m_freem(control); - } - return(error); } static int udp6_abort(struct socket *so) { struct inpcb *inp; - int s; inp = sotoinpcb(so); - if (inp == 0) - return EINVAL; /* ??? possible? panic instead? */ + if (inp == NULL) { + panic("%s: so=%p null inp\n", __func__, so); + /* NOTREACHED */ + } soisdisconnected(so); - s = splnet(); in6_pcbdetach(inp); - splx(s); - return 0; + return (0); } static int udp6_attach(struct socket *so, int proto, struct proc *p) { +#pragma unused(proto) struct inpcb *inp; - int s, error; + int error; inp = sotoinpcb(so); - if (inp != 0) - return EINVAL; + if (inp != NULL) + return (EINVAL); + + error = in_pcballoc(so, &udbinfo, p); + if (error) + return (error); if (so->so_snd.sb_hiwat == 0 || so->so_rcv.sb_hiwat == 0) { error = soreserve(so, udp_sendspace, udp_recvspace); if (error) - return error; + return (error); } - s = splnet(); - error = in_pcballoc(so, &udbinfo, p); - splx(s); - if (error) - return error; inp = (struct inpcb *)so->so_pcb; inp->inp_vflag |= INP_IPV6; + if (ip6_mapped_addr_on) + inp->inp_vflag |= INP_IPV4; inp->in6p_hops = -1; /* use kernel default */ inp->in6p_cksum = -1; /* just to be sure */ -#if IPSEC - error = ipsec_init_policy(so, &inp->in6p_sp); - if (error != 0) { - in6_pcbdetach(inp); - return (error); - } -#endif /*IPSEC*/ - return 0; + /* + * XXX: ugly!! + * IPv4 TTL initialization is necessary for an IPv6 socket as well, + * because the socket may be bound to an IPv6 wildcard address, + * which may match an IPv4-mapped IPv6 address. + */ + inp->inp_ip_ttl = ip_defttl; + if (nstat_collect) + nstat_udp_new_pcb(inp); + return (0); } static int udp6_bind(struct socket *so, struct sockaddr *nam, struct proc *p) { struct inpcb *inp; - int s, error; + int error; inp = sotoinpcb(so); - if (inp == 0) - return EINVAL; + if (inp == NULL +#if NECP + || (necp_socket_should_use_flow_divert(inp)) +#endif /* NECP */ + ) + return (inp == NULL ? EINVAL : EPROTOTYPE); inp->inp_vflag &= ~INP_IPV4; inp->inp_vflag |= INP_IPV6; - if (ip6_mapped_addr_on && (inp->inp_flags & IN6P_BINDV6ONLY) == 0) { + if ((inp->inp_flags & IN6P_IPV6_V6ONLY) == 0) { struct sockaddr_in6 *sin6_p; - sin6_p = (struct sockaddr_in6 *)nam; + sin6_p = (struct sockaddr_in6 *)(void *)nam; - if (IN6_IS_ADDR_UNSPECIFIED(&sin6_p->sin6_addr)) + if (IN6_IS_ADDR_UNSPECIFIED(&sin6_p->sin6_addr)) { inp->inp_vflag |= INP_IPV4; - else if (IN6_IS_ADDR_V4MAPPED(&sin6_p->sin6_addr)) { + } else if (IN6_IS_ADDR_V4MAPPED(&sin6_p->sin6_addr)) { struct sockaddr_in sin; in6_sin6_2_sin(&sin, sin6_p); inp->inp_vflag |= INP_IPV4; inp->inp_vflag &= ~INP_IPV6; - s = splnet(); error = in_pcbbind(inp, (struct sockaddr *)&sin, p); - splx(s); - return error; + return (error); } } - s = splnet(); error = in6_pcbbind(inp, nam, p); - splx(s); - return error; + return (error); } -static int +int udp6_connect(struct socket *so, struct sockaddr *nam, struct proc *p) { struct inpcb *inp; - int s, error; + int error; inp = sotoinpcb(so); - if (inp == 0) - return EINVAL; - - if (ip6_mapped_addr_on) { + if (inp == NULL +#if NECP + || (necp_socket_should_use_flow_divert(inp)) +#endif /* NECP */ + ) + return (inp == NULL ? EINVAL : EPROTOTYPE); + + if ((inp->inp_flags & IN6P_IPV6_V6ONLY) == 0) { struct sockaddr_in6 *sin6_p; - sin6_p = (struct sockaddr_in6 *)nam; + sin6_p = (struct sockaddr_in6 *)(void *)nam; if (IN6_IS_ADDR_V4MAPPED(&sin6_p->sin6_addr)) { struct sockaddr_in sin; if (inp->inp_faddr.s_addr != INADDR_ANY) - return EISCONN; + return (EISCONN); in6_sin6_2_sin(&sin, sin6_p); - s = splnet(); - error = in_pcbconnect(inp, (struct sockaddr *)&sin, p); - splx(s); + error = in_pcbconnect(inp, (struct sockaddr *)&sin, + p, IFSCOPE_NONE, NULL); if (error == 0) { inp->inp_vflag |= INP_IPV4; inp->inp_vflag &= ~INP_IPV6; soisconnected(so); } - return error; + return (error); } } if (!IN6_IS_ADDR_UNSPECIFIED(&inp->in6p_faddr)) - return EISCONN; - s = splnet(); + return (EISCONN); error = in6_pcbconnect(inp, nam, p); - if (ip6_auto_flowlabel) { - inp->in6p_flowinfo &= ~IPV6_FLOWLABEL_MASK; - inp->in6p_flowinfo |= - (htonl(ip6_flow_seq++) & IPV6_FLOWLABEL_MASK); - } - splx(s); if (error == 0) { - if (ip6_mapped_addr_on) { /* should be non mapped addr */ + /* should be non mapped addr */ + if (ip6_mapped_addr_on || + (inp->inp_flags & IN6P_IPV6_V6ONLY) == 0) { inp->inp_vflag &= ~INP_IPV4; inp->inp_vflag |= INP_IPV6; } soisconnected(so); + 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); + } } - return error; + return (error); +} + +static int +udp6_connectx(struct socket *so, struct sockaddr_list **src_sl, + struct sockaddr_list **dst_sl, struct proc *p, uint32_t ifscope, + associd_t aid, connid_t *pcid, uint32_t flags, void *arg, + uint32_t arglen) +{ + return (udp_connectx_common(so, AF_INET6, src_sl, dst_sl, + p, ifscope, aid, pcid, flags, arg, arglen)); } static int udp6_detach(struct socket *so) { struct inpcb *inp; - int s; inp = sotoinpcb(so); - if (inp == 0) - return EINVAL; - s = splnet(); + if (inp == NULL) + return (EINVAL); in6_pcbdetach(inp); - splx(s); - return 0; + return (0); } static int udp6_disconnect(struct socket *so) { struct inpcb *inp; - int s; inp = sotoinpcb(so); - if (inp == 0) - return EINVAL; + if (inp == NULL +#if NECP + || (necp_socket_should_use_flow_divert(inp)) +#endif /* NECP */ + ) + return (inp == NULL ? EINVAL : EPROTOTYPE); if (inp->inp_vflag & INP_IPV4) { struct pr_usrreqs *pru; @@ -802,60 +827,162 @@ udp6_disconnect(struct socket *so) } if (IN6_IS_ADDR_UNSPECIFIED(&inp->in6p_faddr)) - return ENOTCONN; + return (ENOTCONN); - s = splnet(); in6_pcbdisconnect(inp); + + /* reset flow-controlled state, just in case */ + inp_reset_fc_state(inp); + inp->in6p_laddr = in6addr_any; - splx(s); + inp->in6p_last_outifp = NULL; so->so_state &= ~SS_ISCONNECTED; /* XXX */ - return 0; + return (0); +} + +static int +udp6_disconnectx(struct socket *so, associd_t aid, connid_t cid) +{ +#pragma unused(cid) + if (aid != ASSOCID_ANY && aid != ASSOCID_ALL) + return (EINVAL); + + return (udp6_disconnect(so)); } static int udp6_send(struct socket *so, int flags, struct mbuf *m, struct sockaddr *addr, - struct mbuf *control, struct proc *p) + struct mbuf *control, struct proc *p) { struct inpcb *inp; + int error = 0; inp = sotoinpcb(so); - if (inp == 0) { - m_freem(m); - return EINVAL; + if (inp == NULL +#if NECP + || (necp_socket_should_use_flow_divert(inp)) +#endif /* NECP */ + ) { + if (inp == NULL) + error = EINVAL; + else + error = EPROTOTYPE; + goto bad; } - if (ip6_mapped_addr_on) { + if (addr != NULL) { + if (addr->sa_len != sizeof (struct sockaddr_in6)) { + error = EINVAL; + goto bad; + } + if (addr->sa_family != AF_INET6) { + error = EAFNOSUPPORT; + goto bad; + } + } + + if (ip6_mapped_addr_on || (inp->inp_flags & IN6P_IPV6_V6ONLY) == 0) { int hasv4addr; - struct sockaddr_in6 *sin6 = 0; + struct sockaddr_in6 *sin6 = NULL; - if (addr == 0) + if (addr == NULL) { hasv4addr = (inp->inp_vflag & INP_IPV4); - else { - sin6 = (struct sockaddr_in6 *)addr; - hasv4addr = IN6_IS_ADDR_V4MAPPED(&sin6->sin6_addr) - ? 1 : 0; + } else { + sin6 = (struct sockaddr_in6 *)(void *)addr; + hasv4addr = + IN6_IS_ADDR_V4MAPPED(&sin6->sin6_addr) ? 1 : 0; } if (hasv4addr) { struct pr_usrreqs *pru; - int error; - if (sin6) + if (sin6 != NULL) in6_sin6_2_sin_in_sock(addr); pru = ip_protox[IPPROTO_UDP]->pr_usrreqs; - error = ((*pru->pru_send)(so, flags, m, addr, control, - p)); + error = ((*pru->pru_send)(so, flags, m, addr, + control, p)); /* addr will just be freed in sendit(). */ - return error; + return (error); } } + return (udp6_output(inp, m, addr, control, p)); - return udp6_output(inp, m, addr, control, p); +bad: + VERIFY(error != 0); + + if (m != NULL) + m_freem(m); + if (control != NULL) + m_freem(control); + + return (error); } -struct pr_usrreqs udp6_usrreqs = { - udp6_abort, pru_accept_notsupp, udp6_attach, udp6_bind, udp6_connect, - pru_connect2_notsupp, in6_control, udp6_detach, udp6_disconnect, - pru_listen_notsupp, in6_mapped_peeraddr, pru_rcvd_notsupp, - pru_rcvoob_notsupp, udp6_send, pru_sense_null, udp_shutdown, - in6_mapped_sockaddr, sosend, soreceive, sopoll -}; +/* + * Checksum extended UDP header and data. + */ +static int +udp6_input_checksum(struct mbuf *m, struct udphdr *uh, int off, int ulen) +{ + struct ifnet *ifp = m->m_pkthdr.rcvif; + struct ip6_hdr *ip6 = mtod(m, struct ip6_hdr *); + + if (uh->uh_sum == 0) { + /* UDP/IPv6 checksum is mandatory (RFC2460) */ + udpstat.udps_nosum++; + goto badsum; + } + + if ((hwcksum_rx || (ifp->if_flags & IFF_LOOPBACK) || + (m->m_pkthdr.pkt_flags & PKTF_LOOP)) && + (m->m_pkthdr.csum_flags & CSUM_DATA_VALID)) { + if (m->m_pkthdr.csum_flags & CSUM_PSEUDO_HDR) { + uh->uh_sum = m->m_pkthdr.csum_rx_val; + } else { + uint16_t sum = m->m_pkthdr.csum_rx_val; + uint16_t start = m->m_pkthdr.csum_rx_start; + + /* + * Perform 1's complement adjustment of octets + * that got included/excluded in the hardware- + * calculated checksum value. + */ + if ((m->m_pkthdr.csum_flags & CSUM_PARTIAL) && + start != off) { + uint16_t s, d; + + if (IN6_IS_SCOPE_EMBED(&ip6->ip6_src)) { + s = ip6->ip6_src.s6_addr16[1]; + ip6->ip6_src.s6_addr16[1] = 0 ; + } + if (IN6_IS_SCOPE_EMBED(&ip6->ip6_dst)) { + d = ip6->ip6_dst.s6_addr16[1]; + ip6->ip6_dst.s6_addr16[1] = 0; + } + + /* callee folds in sum */ + sum = m_adj_sum16(m, start, off, sum); + + if (IN6_IS_SCOPE_EMBED(&ip6->ip6_src)) + ip6->ip6_src.s6_addr16[1] = s; + if (IN6_IS_SCOPE_EMBED(&ip6->ip6_dst)) + ip6->ip6_dst.s6_addr16[1] = d; + } + + uh->uh_sum = in6_pseudo(&ip6->ip6_src, &ip6->ip6_dst, + sum + htonl(ulen + IPPROTO_UDP)); + } + uh->uh_sum ^= 0xffff; + } else { + udp_in6_cksum_stats(ulen); + uh->uh_sum = in6_cksum(m, IPPROTO_UDP, off, ulen); + } + + if (uh->uh_sum != 0) { +badsum: + udpstat.udps_badsum++; + IF_UDP_STATINC(ifp, badchksum); + return (-1); + } + + return (0); +}