X-Git-Url: https://git.saurik.com/apple/xnu.git/blobdiff_plain/1c79356b52d46aa6b508fb032f5ae709b1f2897b..13f56ec4e58bf8687e2a68032c093c0213dd519b:/bsd/netinet6/udp6_usrreq.c?ds=sidebyside diff --git a/bsd/netinet6/udp6_usrreq.c b/bsd/netinet6/udp6_usrreq.c index c6bfcc685..c88c0d169 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-2010 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,12 @@ #include #include #include +#include #include #include #include +#include #include #include @@ -102,71 +130,106 @@ #if IPSEC #include +#include +extern int ipsec_bypass; #endif /*IPSEC*/ -#include "faith.h" - /* * 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_detach(struct socket *so); +static void udp6_append(struct inpcb *, struct ip6_hdr *, + struct sockaddr_in6 *, struct mbuf *, int); + +extern void ipfwsyslog( int level, const char *format,...); +extern int fw_verbose; + +#if IPFIREWALL +#define log_in_vain_log( a ) { \ + if ( (log_in_vain == 3 ) && (fw_verbose == 2)) { /* Apple logging, log to ipfw.log */ \ + ipfwsyslog a ; \ + } \ + else log a ; \ +} +#else +#define log_in_vain_log( a ) { log a; } +#endif -static int -in6_mcmatch(in6p, ia6, ifp) - struct inpcb *in6p; - register struct in6_addr *ia6; - struct ifnet *ifp; +/* + * subroutine of udp6_input(), mainly for source code readability. + */ +static void +udp6_append(struct inpcb *last, __unused struct ip6_hdr *ip6, + struct sockaddr_in6 *udp_in6, struct mbuf *n, int off) { - 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; + struct mbuf *opts = NULL; + int ret = 0; +#if CONFIG_MACF_NET + if (mac_inpcb_check_deliver(last, n, AF_INET6, SOCK_DGRAM) != 0) { + m_freem(n); + return; } - return 0; +#endif + if ((last->in6p_flags & IN6P_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) { + locked_add_64(&last->inp_stat->rxpackets, 1); + locked_add_64(&last->inp_stat->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; + struct ifnet *ifp; register struct ip6_hdr *ip6; register struct udphdr *uh; register struct inpcb *in6p; - struct ip6_recvpktopts opts; + struct mbuf *opts = NULL; int off = *offp; - int plen, ulen; + int plen, ulen, ret = 0; struct sockaddr_in6 udp_in6; + struct inpcbinfo *pcbinfo = &udbinfo; + struct sockaddr_in6 fromsa; + + IP6_EXTHDR_CHECK(m, off, sizeof(struct udphdr), return IPPROTO_DONE); + + ifp = m->m_pkthdr.rcvif; + ip6 = mtod(m, struct ip6_hdr *); #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; - } + if (faithprefix(&ip6->ip6_dst)) { + /* 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), IPPROTO_DONE); + udpstat.udps_ipackets++; - ip6 = mtod(m, struct ip6_hdr *); plen = ntohs(ip6->ip6_plen) - off + sizeof(*ip6); uh = (struct udphdr *)((caddr_t)ip6 + off); ulen = ntohs((u_short)uh->uh_ulen); @@ -176,18 +239,41 @@ udp6_input(mp, offp, proto) goto bad; } + /* destination port of 0 is illegal, based on RFC768. */ + if (uh->uh_dport == 0) + 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++; - goto bad; + if (uh->uh_sum) { + if ((apple_hwcksum_rx != 0) && (m->m_pkthdr.csum_flags & CSUM_DATA_VALID)) { + uh->uh_sum = m->m_pkthdr.csum_data; + uh->uh_sum ^= 0xffff; + } + else { + if (in6_cksum(m, IPPROTO_UDP, off, ulen) != 0) { + udpstat.udps_badsum++; + goto bad; + } + } } +#ifndef __APPLE__ + else + udpstat.udps_nosum++; +#endif + + /* + * 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; + struct mbuf *n = NULL; /* * Deliver a multicast datagram to all sockets @@ -229,65 +315,92 @@ udp6_input(mp, offp, proto) * Locate pcb(s) for datagram. * (Algorithm copied from raw_intr().) */ - last = NULL; + lck_rw_lock_shared(pcbinfo->mtx); + LIST_FOREACH(in6p, &udb, inp_list) { + if ((in6p->inp_vflag & INP_IPV6) == 0) continue; - if (in6p->in6p_lport != uh->uh_dport) + + if (in_pcb_checkstate(in6p, WNT_ACQUIRE, 0) == WNT_STOPUSING) 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)) + + 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; + } + + /* + * 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); 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) + 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 - /* - * Check AH/ESP integrity. - */ - if (ipsec6_in_reject_so(m, last->inp_socket)) - ipsec6stat.in_polvio++; - /* do not inject data into pcb */ - else + int skipit = 0; + /* Check AH/ESP integrity. */ + if (ipsec_bypass == 0) { + if (ipsec6_in_reject_so(m, in6p->inp_socket)) { + IPSEC_STAT_INCREMENT(ipsec6stat.in_polvio); + /* do not inject data to pcb */ + skipit = 1; + } + } + if (skipit == 0) #endif /*IPSEC*/ - if ((n = m_copy(m, 0, M_COPYALL)) != NULL) { + { /* * KAME NOTE: do not - * m_copy(m, offset, ...) above. + * m_copy(m, offset, ...) below. * 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)); + mcast_delivered++; } + udp_unlock(in6p->in6p_socket, 1, 0); } - last = in6p; /* * Don't look for additional matches if this one does * not have either the SO_REUSEPORT or SO_REUSEADDR @@ -296,42 +409,31 @@ 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 = n) == NULL)) break; + /* + * Recompute IP and UDP header pointers for new mbuf + */ + ip6 = mtod(m, struct ip6_hdr *); + uh = (struct udphdr *)((caddr_t)ip6 + off); } + lck_rw_done(pcbinfo->mtx); - 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++; - 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++; +#ifndef __APPLE__ + udpstat.udps_noportmcast++; +#endif goto bad; } - sorwakeup(last->in6p_socket); + + if (reuse_sock != 0) /* free the extra copy of mbuf */ + m_freem(m); return IPPROTO_DONE; } /* @@ -344,16 +446,25 @@ udp6_input(mp, offp, proto) if (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 (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++; +#ifndef __APPLE__ + udpstat.udps_noportmcast++; +#endif goto bad; } icmp6_error(m, ICMP6_DST_UNREACH, ICMP6_DST_UNREACH_NOPORT, 0); @@ -363,9 +474,12 @@ udp6_input(mp, offp, proto) /* * Check AH/ESP integrity. */ - if (ipsec6_in_reject_so(m, in6p->in6p_socket)) { - ipsec6stat.in_polvio++; - goto bad; + if (ipsec_bypass == 0) { + if (ipsec6_in_reject_so(m, in6p->in6p_socket)) { + IPSEC_STAT_INCREMENT(ipsec6stat.in_polvio); + in_pcb_checkstate(in6p, WNT_RELEASE, 0); + goto bad; + } } #endif /*IPSEC*/ @@ -373,41 +487,67 @@ udp6_input(mp, offp, proto) * 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); + 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); + if ((in6p->in6p_flags & IN6P_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) { + locked_add_64(&in6p->inp_stat->rxpackets, 1); + locked_add_64(&in6p->inp_stat->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) { + m, opts, NULL) == 0) { + m = NULL; + opts = NULL; udpstat.udps_fullsock++; + udp_unlock(in6p->in6p_socket, 1, 0); goto bad; } sorwakeup(in6p->in6p_socket); + udp_unlock(in6p->in6p_socket, 1, 0); return IPPROTO_DONE; bad: if (m) m_freem(m); - if (opts.head) - m_freem(opts.head); + if (opts) + 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)) @@ -424,49 +564,39 @@ 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) { /* * 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); + /* check if we can safely examine src and dst ports */ + if (m->m_pkthdr.len < off + sizeof(*uhp)) + return; - 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); + bzero(&uh, sizeof(uh)); + m_copydata(m, off, sizeof(*uhp), (caddr_t)&uh); + + (void) in6_pcbnotify(&udbinfo, sa, uh.uh_dport, + (struct sockaddr*)ip6cp->ip6c_src, + uh.uh_sport, cmd, NULL, notify); } else - (void) in6_pcbnotify(&udb, (struct sockaddr *)&sa6, 0, - &zeroin6_addr, 0, cmd, notify); + (void) in6_pcbnotify(&udbinfo, sa, 0, (struct sockaddr *)&sa6_src, + 0, cmd, NULL, notify); } -#if 0 + +#ifndef __APPLE__ static int udp6_getcred SYSCTL_HANDLER_ARGS { @@ -474,13 +604,13 @@ udp6_getcred SYSCTL_HANDLER_ARGS struct inpcb *inp; int error, s; - error = suser(req->p->p_ucred, &req->p->p_acflag); + error = proc_suser(req->p); if (error) return (error); if (req->newlen != sizeof(addrs)) return (EINVAL); - if (req->oldlen != sizeof(struct ucred)) + if (req->oldlen != sizeof(*(kauth_cred_t)0)) return (EINVAL); error = SYSCTL_IN(req, addrs, sizeof(addrs)); if (error) @@ -494,8 +624,14 @@ udp6_getcred SYSCTL_HANDLER_ARGS error = ENOENT; goto out; } + /* + * XXX This should not be copying out a credential!!!! This + * XXX is an opaque type, and is not intended to be introspected, + * XXX and the size of this structure *WILL* change as planned MACF + * XXX and kauth changes go forward. + */ error = SYSCTL_OUT(req, inp->inp_socket->so_cred->pc_ucred, - sizeof(struct ucred)); + sizeof(*(kauth_cred_t)0)); out: splx(s); @@ -506,176 +642,53 @@ 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; - } - - 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; - } - } 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; - } - - /* - * 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? */ soisdisconnected(so); - s = splnet(); in6_pcbdetach(inp); - splx(s); return 0; } static int -udp6_attach(struct socket *so, int proto, struct proc *p) +udp6_attach(struct socket *so, __unused int proto, struct proc *p) { struct inpcb *inp; - int s, error; + int error; inp = sotoinpcb(so); if (inp != 0) 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; } - 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*/ + /* + * 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; + nstat_udp_new_pcb(inp); return 0; } @@ -683,7 +696,7 @@ 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) @@ -691,7 +704,7 @@ udp6_bind(struct socket *so, struct sockaddr *nam, struct proc *p) 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; @@ -704,16 +717,12 @@ udp6_bind(struct socket *so, struct sockaddr *nam, struct proc *p) 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; } } - s = splnet(); error = in6_pcbbind(inp, nam, p); - splx(s); return error; } @@ -721,13 +730,13 @@ static 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->inp_flags & IN6P_IPV6_V6ONLY) == 0) { struct sockaddr_in6 *sin6_p; sin6_p = (struct sockaddr_in6 *)nam; @@ -737,9 +746,7 @@ udp6_connect(struct socket *so, struct sockaddr *nam, struct proc *p) if (inp->inp_faddr.s_addr != INADDR_ANY) 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, NULL); if (error == 0) { inp->inp_vflag |= INP_IPV4; inp->inp_vflag &= ~INP_IPV6; @@ -751,16 +758,9 @@ udp6_connect(struct socket *so, struct sockaddr *nam, struct proc *p) if (!IN6_IS_ADDR_UNSPECIFIED(&inp->in6p_faddr)) return EISCONN; - s = splnet(); 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 */ + if (ip6_mapped_addr_on || (inp->inp_flags & IN6P_IPV6_V6ONLY) == 0) { /* should be non mapped addr */ inp->inp_vflag &= ~INP_IPV4; inp->inp_vflag |= INP_IPV6; } @@ -773,14 +773,11 @@ static int udp6_detach(struct socket *so) { struct inpcb *inp; - int s; inp = sotoinpcb(so); if (inp == 0) return EINVAL; - s = splnet(); in6_pcbdetach(inp); - splx(s); return 0; } @@ -788,7 +785,6 @@ static int udp6_disconnect(struct socket *so) { struct inpcb *inp; - int s; inp = sotoinpcb(so); if (inp == 0) @@ -804,10 +800,9 @@ udp6_disconnect(struct socket *so) if (IN6_IS_ADDR_UNSPECIFIED(&inp->in6p_faddr)) return ENOTCONN; - s = splnet(); in6_pcbdisconnect(inp); inp->in6p_laddr = in6addr_any; - splx(s); + inp->in6p_last_outif = 0; so->so_state &= ~SS_ISCONNECTED; /* XXX */ return 0; } @@ -817,14 +812,26 @@ udp6_send(struct socket *so, int flags, struct mbuf *m, struct sockaddr *addr, struct mbuf *control, struct proc *p) { struct inpcb *inp; + int error = 0; inp = sotoinpcb(so); if (inp == 0) { - m_freem(m); - return EINVAL; + error = EINVAL; + goto bad; + } + + if (addr) { + 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) { + if (ip6_mapped_addr_on || (inp->inp_flags & IN6P_IPV6_V6ONLY) == 0) { int hasv4addr; struct sockaddr_in6 *sin6 = 0; @@ -837,7 +844,6 @@ udp6_send(struct socket *so, int flags, struct mbuf *m, struct sockaddr *addr, } if (hasv4addr) { struct pr_usrreqs *pru; - int error; if (sin6) in6_sin6_2_sin_in_sock(addr); @@ -850,6 +856,10 @@ udp6_send(struct socket *so, int flags, struct mbuf *m, struct sockaddr *addr, } return udp6_output(inp, m, addr, control, p); + + bad: + m_freem(m); + return(error); } struct pr_usrreqs udp6_usrreqs = { @@ -857,5 +867,5 @@ struct pr_usrreqs udp6_usrreqs = { 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 + in6_mapped_sockaddr, sosend, soreceive, pru_sopoll_notsupp };