udp6_usrreq.c revision 185370
154359Sroberto/*-
254359Sroberto * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project.
354359Sroberto * All rights reserved.
454359Sroberto *
554359Sroberto * Redistribution and use in source and binary forms, with or without
654359Sroberto * modification, are permitted provided that the following conditions
754359Sroberto * are met:
854359Sroberto * 1. Redistributions of source code must retain the above copyright
954359Sroberto *    notice, this list of conditions and the following disclaimer.
1082498Sroberto * 2. Redistributions in binary form must reproduce the above copyright
1182498Sroberto *    notice, this list of conditions and the following disclaimer in the
1282498Sroberto *    documentation and/or other materials provided with the distribution.
1382498Sroberto * 3. Neither the name of the project nor the names of its contributors
1482498Sroberto *    may be used to endorse or promote products derived from this software
1582498Sroberto *    without specific prior written permission.
1654359Sroberto *
1754359Sroberto * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND
1854359Sroberto * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
1954359Sroberto * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
2054359Sroberto * ARE DISCLAIMED.  IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE
2154359Sroberto * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
2254359Sroberto * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
2356746Sroberto * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
2454359Sroberto * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
2554359Sroberto * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
2654359Sroberto * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
2754359Sroberto * SUCH DAMAGE.
28290000Sglebius *
29290000Sglebius *	$KAME: udp6_usrreq.c,v 1.27 2001/05/21 05:45:10 jinmei Exp $
30290000Sglebius *	$KAME: udp6_output.c,v 1.31 2001/05/21 16:39:15 jinmei Exp $
31290000Sglebius */
32290000Sglebius
33290000Sglebius/*-
34290000Sglebius * Copyright (c) 1982, 1986, 1988, 1990, 1993, 1995
3554359Sroberto *	The Regents of the University of California.
36290000Sglebius * All rights reserved.
37290000Sglebius *
38290000Sglebius * Redistribution and use in source and binary forms, with or without
39290000Sglebius * modification, are permitted provided that the following conditions
40290000Sglebius * are met:
41290000Sglebius * 1. Redistributions of source code must retain the above copyright
42290000Sglebius *    notice, this list of conditions and the following disclaimer.
4354359Sroberto * 2. Redistributions in binary form must reproduce the above copyright
4454359Sroberto *    notice, this list of conditions and the following disclaimer in the
4554359Sroberto *    documentation and/or other materials provided with the distribution.
4654359Sroberto * 4. Neither the name of the University nor the names of its contributors
47290000Sglebius *    may be used to endorse or promote products derived from this software
4854359Sroberto *    without specific prior written permission.
49290000Sglebius *
50290000Sglebius * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
51290000Sglebius * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
52290000Sglebius * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
53290000Sglebius * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
54290000Sglebius * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
55290000Sglebius * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
56290000Sglebius * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
57290000Sglebius * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
58290000Sglebius * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
5954359Sroberto * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
6054359Sroberto * SUCH DAMAGE.
6154359Sroberto *
6254359Sroberto *	@(#)udp_usrreq.c	8.6 (Berkeley) 5/23/95
6354359Sroberto */
6454359Sroberto
6554359Sroberto#include <sys/cdefs.h>
6654359Sroberto__FBSDID("$FreeBSD: head/sys/netinet6/udp6_usrreq.c 185370 2008-11-27 12:04:35Z bz $");
6754359Sroberto
6854359Sroberto#include "opt_inet.h"
6954359Sroberto#include "opt_inet6.h"
7054359Sroberto#include "opt_ipsec.h"
7154359Sroberto#include "opt_mac.h"
72290000Sglebius
7354359Sroberto#include <sys/param.h>
74290000Sglebius#include <sys/kernel.h>
75290000Sglebius#include <sys/lock.h>
76290000Sglebius#include <sys/mbuf.h>
77290000Sglebius#include <sys/priv.h>
7854359Sroberto#include <sys/proc.h>
79290000Sglebius#include <sys/protosw.h>
8054359Sroberto#include <sys/signalvar.h>
81290000Sglebius#include <sys/socket.h>
82290000Sglebius#include <sys/socketvar.h>
83290000Sglebius#include <sys/sx.h>
8454359Sroberto#include <sys/sysctl.h>
85290000Sglebius#include <sys/syslog.h>
86290000Sglebius#include <sys/systm.h>
87290000Sglebius#include <sys/vimage.h>
88290000Sglebius
89290000Sglebius#include <net/if.h>
90290000Sglebius#include <net/if_types.h>
91290000Sglebius#include <net/route.h>
92290000Sglebius
93290000Sglebius#include <netinet/in.h>
9454359Sroberto#include <netinet/in_pcb.h>
95290000Sglebius#include <netinet/in_systm.h>
9654359Sroberto#include <netinet/in_var.h>
97290000Sglebius#include <netinet/ip.h>
98290000Sglebius#include <netinet/ip_icmp.h>
99290000Sglebius#include <netinet/ip6.h>
100290000Sglebius#include <netinet/icmp_var.h>
101290000Sglebius#include <netinet/icmp6.h>
102290000Sglebius#include <netinet/ip_var.h>
103290000Sglebius#include <netinet/udp.h>
104290000Sglebius#include <netinet/udp_var.h>
105290000Sglebius#include <netinet6/ip6protosw.h>
106290000Sglebius#include <netinet6/ip6_var.h>
107290000Sglebius#include <netinet6/in6_pcb.h>
108290000Sglebius#include <netinet6/udp6_var.h>
109290000Sglebius#include <netinet6/scope6_var.h>
110290000Sglebius
111290000Sglebius#ifdef IPSEC
112290000Sglebius#include <netipsec/ipsec.h>
113290000Sglebius#include <netipsec/ipsec6.h>
114290000Sglebius#endif /* IPSEC */
115290000Sglebius
116290000Sglebius#include <security/mac/mac_framework.h>
117290000Sglebius
118290000Sglebius/*
11954359Sroberto * UDP protocol implementation.
12054359Sroberto * Per RFC 768, August, 1980.
121290000Sglebius */
122290000Sglebius
123290000Sglebiusextern struct protosw	inetsw[];
124290000Sglebiusstatic void		udp6_detach(struct socket *so);
125290000Sglebius
12654359Srobertostatic void
12754359Srobertoudp6_append(struct inpcb *inp, struct mbuf *n, int off,
12854359Sroberto    struct sockaddr_in6 *fromsa)
12954359Sroberto{
13054359Sroberto	INIT_VNET_INET(inp->inp_vnet);
13154359Sroberto	struct socket *so;
13254359Sroberto	struct mbuf *opts;
13354359Sroberto
134132451Sroberto	INP_LOCK_ASSERT(inp);
135182007Sroberto
136182007Sroberto#ifdef IPSEC
137182007Sroberto	/* Check AH/ESP integrity. */
138182007Sroberto	if (ipsec6_in_reject(n, inp)) {
139290000Sglebius		INIT_VNET_IPSEC(inp->inp_vnet);
140182007Sroberto		m_freem(n);
141290000Sglebius		V_ipsec6stat.in_polvio++;
142290000Sglebius		return;
143290000Sglebius	}
144290000Sglebius#endif /* IPSEC */
145290000Sglebius#ifdef MAC
14654359Sroberto	if (mac_inpcb_check_deliver(inp, n) != 0) {
14754359Sroberto		m_freem(n);
14854359Sroberto		return;
14954359Sroberto	}
15082498Sroberto#endif
15154359Sroberto	opts = NULL;
15254359Sroberto	if (inp->in6p_flags & IN6P_CONTROLOPTS ||
15354359Sroberto	    inp->inp_socket->so_options & SO_TIMESTAMP)
154132451Sroberto		ip6_savecontrol(inp, n, &opts);
15556746Sroberto	m_adj(n, off + sizeof(struct udphdr));
15654359Sroberto
15754359Sroberto	so = inp->inp_socket;
15854359Sroberto	SOCKBUF_LOCK(&so->so_rcv);
159290000Sglebius	if (sbappendaddr_locked(&so->so_rcv, (struct sockaddr *)fromsa, n,
160290000Sglebius	    opts) == 0) {
161290000Sglebius		SOCKBUF_UNLOCK(&so->so_rcv);
16254359Sroberto		m_freem(n);
163290000Sglebius		if (opts)
164290000Sglebius			m_freem(opts);
165290000Sglebius		V_udpstat.udps_fullsock++;
166290000Sglebius	} else
16754359Sroberto		sorwakeup_locked(so);
16856746Sroberto}
16954359Sroberto
17054359Srobertoint
171290000Sglebiusudp6_input(struct mbuf **mp, int *offp, int proto)
172290000Sglebius{
173290000Sglebius	INIT_VNET_INET(curvnet);
174290000Sglebius	INIT_VNET_INET6(curvnet);
175290000Sglebius	struct mbuf *m = *mp;
176290000Sglebius	struct ip6_hdr *ip6;
177290000Sglebius	struct udphdr *uh;
178290000Sglebius	struct inpcb *inp;
179290000Sglebius	int off = *offp;
18054359Sroberto	int plen, ulen;
181290000Sglebius	struct sockaddr_in6 fromsa;
182290000Sglebius
18354359Sroberto	ip6 = mtod(m, struct ip6_hdr *);
18454359Sroberto
18554359Sroberto	if (faithprefix_p != NULL && (*faithprefix_p)(&ip6->ip6_dst)) {
18654359Sroberto		/* XXX send icmp6 host/port unreach? */
18754359Sroberto		m_freem(m);
18854359Sroberto		return (IPPROTO_DONE);
18954359Sroberto	}
19054359Sroberto
19154359Sroberto#ifndef PULLDOWN_TEST
192290000Sglebius	IP6_EXTHDR_CHECK(m, off, sizeof(struct udphdr), IPPROTO_DONE);
19354359Sroberto	ip6 = mtod(m, struct ip6_hdr *);
19454359Sroberto	uh = (struct udphdr *)((caddr_t)ip6 + off);
19554359Sroberto#else
19654359Sroberto	IP6_EXTHDR_GET(uh, struct udphdr *, m, off, sizeof(*uh));
19754359Sroberto	if (!uh)
19854359Sroberto		return (IPPROTO_DONE);
19954359Sroberto#endif
20054359Sroberto
201132451Sroberto	V_udpstat.udps_ipackets++;
20254359Sroberto
203290000Sglebius	/*
204290000Sglebius	 * Destination port of 0 is illegal, based on RFC768.
20554359Sroberto	 */
20654359Sroberto	if (uh->uh_dport == 0)
20754359Sroberto		goto badunlocked;
20854359Sroberto
209290000Sglebius	plen = ntohs(ip6->ip6_plen) - off + sizeof(*ip6);
21054359Sroberto	ulen = ntohs((u_short)uh->uh_ulen);
21154359Sroberto
212290000Sglebius	if (plen != ulen) {
213290000Sglebius		V_udpstat.udps_badlen++;
214290000Sglebius		goto badunlocked;
21554359Sroberto	}
21654359Sroberto
21754359Sroberto	/*
218132451Sroberto	 * Checksum extended UDP header and data.
219132451Sroberto	 */
22054359Sroberto	if (uh->uh_sum == 0) {
22154359Sroberto		V_udpstat.udps_nosum++;
22254359Sroberto		goto badunlocked;
22354359Sroberto	}
224132451Sroberto	if (in6_cksum(m, IPPROTO_UDP, off, ulen) != 0) {
225132451Sroberto		V_udpstat.udps_badsum++;
226132451Sroberto		goto badunlocked;
227132451Sroberto	}
228290000Sglebius
22954359Sroberto	/*
23054359Sroberto	 * Construct sockaddr format source address.
231132451Sroberto	 */
23254359Sroberto	init_sin6(&fromsa, m);
23354359Sroberto	fromsa.sin6_port = uh->uh_sport;
23454359Sroberto
23554359Sroberto	INP_INFO_RLOCK(&V_udbinfo);
23654359Sroberto	if (IN6_IS_ADDR_MULTICAST(&ip6->ip6_dst)) {
237290000Sglebius		struct inpcb *last;
23854359Sroberto
239290000Sglebius		/*
240290000Sglebius		 * In the event that laddr should be set to the link-local
24154359Sroberto		 * address (this happens in RIPng), the multicast address
24254359Sroberto		 * specified in the received packet will not match laddr.  To
24354359Sroberto		 * handle this situation, matching is relaxed if the
24454359Sroberto		 * receiving interface is the same as one specified in the
24554359Sroberto		 * socket and if the destination multicast address matches
24654359Sroberto		 * one of the multicast groups specified in the socket.
24754359Sroberto		 */
248132451Sroberto
24954359Sroberto		/*
25054359Sroberto		 * KAME note: traditionally we dropped udpiphdr from mbuf
25154359Sroberto		 * here.  We need udphdr for IPsec processing so we do that
25254359Sroberto		 * later.
25354359Sroberto		 */
25454359Sroberto		last = NULL;
25554359Sroberto		LIST_FOREACH(inp, &V_udb, inp_list) {
25654359Sroberto			if ((inp->inp_vflag & INP_IPV6) == 0)
25754359Sroberto				continue;
25854359Sroberto			if (inp->in6p_lport != uh->uh_dport)
25954359Sroberto				continue;
26054359Sroberto			/*
26154359Sroberto			 * XXX: Do not check source port of incoming datagram
26254359Sroberto			 * unless inp_connect() has been called to bind the
263290000Sglebius			 * fport part of the 4-tuple; the source could be
26454359Sroberto			 * trying to talk to us with an ephemeral port.
265290000Sglebius			 */
266290000Sglebius			if (inp->inp_fport != 0 &&
267290000Sglebius			    inp->inp_fport != uh->uh_sport)
26854359Sroberto				continue;
26954359Sroberto			if (!IN6_IS_ADDR_UNSPECIFIED(&inp->in6p_laddr)) {
27054359Sroberto				if (!IN6_ARE_ADDR_EQUAL(&inp->in6p_laddr,
27154359Sroberto							&ip6->ip6_dst))
27254359Sroberto					continue;
27354359Sroberto			}
274290000Sglebius			if (!IN6_IS_ADDR_UNSPECIFIED(&inp->in6p_faddr)) {
275290000Sglebius				if (!IN6_ARE_ADDR_EQUAL(&inp->in6p_faddr,
276290000Sglebius							&ip6->ip6_src) ||
277290000Sglebius				    inp->in6p_fport != uh->uh_sport)
27854359Sroberto					continue;
27954359Sroberto			}
28054359Sroberto
28154359Sroberto			if (last != NULL) {
282290000Sglebius				struct mbuf *n;
283290000Sglebius
284290000Sglebius				if ((n = m_copy(m, 0, M_COPYALL)) != NULL) {
285290000Sglebius					INP_RLOCK(last);
286290000Sglebius					udp6_append(last, n, off, &fromsa);
28754359Sroberto					INP_RUNLOCK(last);
28854359Sroberto				}
28954359Sroberto			}
29054359Sroberto			last = inp;
29154359Sroberto			/*
29254359Sroberto			 * Don't look for additional matches if this one does
29354359Sroberto			 * not have either the SO_REUSEPORT or SO_REUSEADDR
29454359Sroberto			 * socket options set.  This heuristic avoids
29554359Sroberto			 * searching through all pcbs in the common case of a
29654359Sroberto			 * non-shared port.  It assumes that an application
29754359Sroberto			 * will never clear these options after setting them.
29854359Sroberto			 */
29954359Sroberto			if ((last->inp_socket->so_options &
30054359Sroberto			     (SO_REUSEPORT|SO_REUSEADDR)) == 0)
30154359Sroberto				break;
30254359Sroberto		}
30354359Sroberto
30454359Sroberto		if (last == NULL) {
30554359Sroberto			/*
30654359Sroberto			 * No matching pcb found; discard datagram.  (No need
307132451Sroberto			 * to send an ICMP Port Unreachable for a broadcast
30854359Sroberto			 * or multicast datgram.)
30954359Sroberto			 */
31054359Sroberto			V_udpstat.udps_noport++;
31154359Sroberto			V_udpstat.udps_noportmcast++;
31254359Sroberto			goto badheadlocked;
31354359Sroberto		}
31454359Sroberto		INP_RLOCK(last);
31554359Sroberto		INP_INFO_RUNLOCK(&V_udbinfo);
31654359Sroberto		udp6_append(last, m, off, &fromsa);
31754359Sroberto		INP_RUNLOCK(last);
31854359Sroberto		return (IPPROTO_DONE);
31954359Sroberto	}
32054359Sroberto	/*
32154359Sroberto	 * Locate pcb for datagram.
32254359Sroberto	 */
32354359Sroberto	inp = in6_pcblookup_hash(&V_udbinfo, &ip6->ip6_src, uh->uh_sport,
324132451Sroberto	    &ip6->ip6_dst, uh->uh_dport, 1, m->m_pkthdr.rcvif);
32556746Sroberto	if (inp == NULL) {
32654359Sroberto		if (udp_log_in_vain) {
32756746Sroberto			char ip6bufs[INET6_ADDRSTRLEN];
32856746Sroberto			char ip6bufd[INET6_ADDRSTRLEN];
32956746Sroberto
33056746Sroberto			log(LOG_INFO,
33154359Sroberto			    "Connection attempt to UDP [%s]:%d from [%s]:%d\n",
33254359Sroberto			    ip6_sprintf(ip6bufd, &ip6->ip6_dst),
33354359Sroberto			    ntohs(uh->uh_dport),
33454359Sroberto			    ip6_sprintf(ip6bufs, &ip6->ip6_src),
335290000Sglebius			    ntohs(uh->uh_sport));
33654359Sroberto		}
33754359Sroberto		V_udpstat.udps_noport++;
338290000Sglebius		if (m->m_flags & M_MCAST) {
33954359Sroberto			printf("UDP6: M_MCAST is set in a unicast packet.\n");
34054359Sroberto			V_udpstat.udps_noportmcast++;
34154359Sroberto			goto badheadlocked;
342290000Sglebius		}
343290000Sglebius		INP_INFO_RUNLOCK(&V_udbinfo);
34454359Sroberto		if (V_udp_blackhole)
34554359Sroberto			goto badunlocked;
34654359Sroberto		if (badport_bandlim(BANDLIM_ICMP6_UNREACH) < 0)
347290000Sglebius			goto badunlocked;
34854359Sroberto		icmp6_error(m, ICMP6_DST_UNREACH, ICMP6_DST_UNREACH_NOPORT, 0);
34954359Sroberto		return (IPPROTO_DONE);
35054359Sroberto	}
35154359Sroberto	INP_RLOCK(inp);
35254359Sroberto	INP_INFO_RUNLOCK(&V_udbinfo);
35354359Sroberto	udp6_append(inp, m, off, &fromsa);
35454359Sroberto	INP_RUNLOCK(inp);
35554359Sroberto	return (IPPROTO_DONE);
35654359Sroberto
35756746Srobertobadheadlocked:
35854359Sroberto	INP_INFO_RUNLOCK(&V_udbinfo);
35954359Srobertobadunlocked:
36054359Sroberto	if (m)
36154359Sroberto		m_freem(m);
36254359Sroberto	return (IPPROTO_DONE);
36354359Sroberto}
36454359Sroberto
36554359Srobertovoid
36654359Srobertoudp6_ctlinput(int cmd, struct sockaddr *sa, void *d)
36754359Sroberto{
36854359Sroberto	INIT_VNET_INET(curvnet);
36954359Sroberto	struct udphdr uh;
370290000Sglebius	struct ip6_hdr *ip6;
371132451Sroberto	struct mbuf *m;
37254359Sroberto	int off = 0;
37356746Sroberto	struct ip6ctlparam *ip6cp = NULL;
37454359Sroberto	const struct sockaddr_in6 *sa6_src = NULL;
37554359Sroberto	void *cmdarg;
37654359Sroberto	struct inpcb *(*notify)(struct inpcb *, int) = udp_notify;
37754359Sroberto	struct udp_portonly {
37854359Sroberto		u_int16_t uh_sport;
37954359Sroberto		u_int16_t uh_dport;
38054359Sroberto	} *uhp;
38154359Sroberto
38254359Sroberto	if (sa->sa_family != AF_INET6 ||
38354359Sroberto	    sa->sa_len != sizeof(struct sockaddr_in6))
38454359Sroberto		return;
38554359Sroberto
38654359Sroberto	if ((unsigned)cmd >= PRC_NCMDS)
38754359Sroberto		return;
38854359Sroberto	if (PRC_IS_REDIRECT(cmd))
38954359Sroberto		notify = in6_rtchange, d = NULL;
39054359Sroberto	else if (cmd == PRC_HOSTDEAD)
391290000Sglebius		d = NULL;
392290000Sglebius	else if (inet6ctlerrmap[cmd] == 0)
393290000Sglebius		return;
394290000Sglebius
395290000Sglebius	/* if the parameter is from icmp6, decode it. */
39654359Sroberto	if (d != NULL) {
39754359Sroberto		ip6cp = (struct ip6ctlparam *)d;
39854359Sroberto		m = ip6cp->ip6c_m;
39954359Sroberto		ip6 = ip6cp->ip6c_ip6;
40054359Sroberto		off = ip6cp->ip6c_off;
40154359Sroberto		cmdarg = ip6cp->ip6c_cmdarg;
40254359Sroberto		sa6_src = ip6cp->ip6c_src;
40354359Sroberto	} else {
40454359Sroberto		m = NULL;
40554359Sroberto		ip6 = NULL;
40654359Sroberto		cmdarg = NULL;
40754359Sroberto		sa6_src = &sa6_any;
40854359Sroberto	}
40954359Sroberto
41054359Sroberto	if (ip6) {
41154359Sroberto		/*
41254359Sroberto		 * XXX: We assume that when IPV6 is non NULL,
41354359Sroberto		 * M and OFF are valid.
41454359Sroberto		 */
41554359Sroberto
41654359Sroberto		/* Check if we can safely examine src and dst ports. */
41754359Sroberto		if (m->m_pkthdr.len < off + sizeof(*uhp))
41854359Sroberto			return;
419132451Sroberto
42054359Sroberto		bzero(&uh, sizeof(uh));
42154359Sroberto		m_copydata(m, off, sizeof(*uhp), (caddr_t)&uh);
422290000Sglebius
42354359Sroberto		(void) in6_pcbnotify(&V_udbinfo, sa, uh.uh_dport,
424290000Sglebius		    (struct sockaddr *)ip6cp->ip6c_src, uh.uh_sport, cmd,
42554359Sroberto		    cmdarg, notify);
42654359Sroberto	} else
42754359Sroberto		(void) in6_pcbnotify(&V_udbinfo, sa, 0,
42854359Sroberto		    (const struct sockaddr *)sa6_src, 0, cmd, cmdarg, notify);
42954359Sroberto}
430132451Sroberto
431132451Srobertostatic int
43254359Srobertoudp6_getcred(SYSCTL_HANDLER_ARGS)
43354359Sroberto{
434132451Sroberto	INIT_VNET_INET(curvnet);
43554359Sroberto	INIT_VNET_INET6(curvnet);
43654359Sroberto	struct xucred xuc;
43754359Sroberto	struct sockaddr_in6 addrs[2];
438132451Sroberto	struct inpcb *inp;
439132451Sroberto	int error;
440132451Sroberto
441132451Sroberto	error = priv_check(req->td, PRIV_NETINET_GETCRED);
442132451Sroberto	if (error)
443132451Sroberto		return (error);
44454359Sroberto
445290000Sglebius	if (req->newlen != sizeof(addrs))
446132451Sroberto		return (EINVAL);
44754359Sroberto	if (req->oldlen != sizeof(struct xucred))
44854359Sroberto		return (EINVAL);
44954359Sroberto	error = SYSCTL_IN(req, addrs, sizeof(addrs));
45054359Sroberto	if (error)
45154359Sroberto		return (error);
45254359Sroberto	if ((error = sa6_embedscope(&addrs[0], V_ip6_use_defzone)) != 0 ||
45354359Sroberto	    (error = sa6_embedscope(&addrs[1], V_ip6_use_defzone)) != 0) {
45454359Sroberto		return (error);
45554359Sroberto	}
45654359Sroberto	INP_INFO_RLOCK(&V_udbinfo);
457290000Sglebius	inp = in6_pcblookup_hash(&V_udbinfo, &addrs[1].sin6_addr,
458290000Sglebius	    addrs[1].sin6_port, &addrs[0].sin6_addr, addrs[0].sin6_port, 1,
459290000Sglebius	    NULL);
460290000Sglebius	if (inp != NULL) {
461290000Sglebius		INP_RLOCK(inp);
46254359Sroberto		INP_INFO_RUNLOCK(&V_udbinfo);
46354359Sroberto		if (inp->inp_socket == NULL)
464132451Sroberto			error = ENOENT;
46554359Sroberto		if (error == 0)
46656746Sroberto			error = cr_canseesocket(req->td->td_ucred,
46754359Sroberto			    inp->inp_socket);
46854359Sroberto		if (error == 0)
46954359Sroberto			cru2x(inp->inp_cred, &xuc);
47054359Sroberto		INP_RUNLOCK(inp);
47154359Sroberto	} else {
47254359Sroberto		INP_INFO_RUNLOCK(&V_udbinfo);
47354359Sroberto		error = ENOENT;
47454359Sroberto	}
475290000Sglebius	if (error == 0)
47654359Sroberto		error = SYSCTL_OUT(req, &xuc, sizeof(struct xucred));
477290000Sglebius	return (error);
47854359Sroberto}
47954359Sroberto
48054359SrobertoSYSCTL_PROC(_net_inet6_udp6, OID_AUTO, getcred, CTLTYPE_OPAQUE|CTLFLAG_RW, 0,
48154359Sroberto    0, udp6_getcred, "S,xucred", "Get the xucred of a UDP6 connection");
482132451Sroberto
48354359Srobertostatic int
484132451Srobertoudp6_output(struct inpcb *inp, struct mbuf *m, struct sockaddr *addr6,
485132451Sroberto    struct mbuf *control, struct thread *td)
486132451Sroberto{
487132451Sroberto	INIT_VNET_INET(curvnet);
48854359Sroberto	INIT_VNET_INET6(curvnet);
489132451Sroberto	u_int32_t ulen = m->m_pkthdr.len;
490132451Sroberto	u_int32_t plen = sizeof(struct udphdr) + ulen;
491132451Sroberto	struct ip6_hdr *ip6;
49254359Sroberto	struct udphdr *udp6;
49354359Sroberto	struct in6_addr *laddr, *faddr;
494290000Sglebius	struct sockaddr_in6 *sin6 = NULL;
49554359Sroberto	struct ifnet *oifp = NULL;
49654359Sroberto	int scope_ambiguous = 0;
49754359Sroberto	u_short fport;
498290000Sglebius	int error = 0;
49954359Sroberto	struct ip6_pktopts *optp, opt;
500290000Sglebius	int af = AF_INET6, hlen = sizeof(struct ip6_hdr);
501290000Sglebius	int flags;
50254359Sroberto	struct sockaddr_in6 tmp;
50354359Sroberto
50454359Sroberto	INP_WLOCK_ASSERT(inp);
50554359Sroberto
50654359Sroberto	if (addr6) {
50754359Sroberto		/* addr6 has been validated in udp6_send(). */
50854359Sroberto		sin6 = (struct sockaddr_in6 *)addr6;
50954359Sroberto
51054359Sroberto		/* protect *sin6 from overwrites */
51154359Sroberto		tmp = *sin6;
51254359Sroberto		sin6 = &tmp;
51354359Sroberto
51454359Sroberto		/*
51554359Sroberto		 * Application should provide a proper zone ID or the use of
51654359Sroberto		 * default zone IDs should be enabled.  Unfortunately, some
51754359Sroberto		 * applications do not behave as it should, so we need a
51854359Sroberto		 * workaround.  Even if an appropriate ID is not determined,
51954359Sroberto		 * we'll see if we can determine the outgoing interface.  If we
520290000Sglebius		 * can, determine the zone ID based on the interface below.
52154359Sroberto		 */
52254359Sroberto		if (sin6->sin6_scope_id == 0 && !V_ip6_use_defzone)
523290000Sglebius			scope_ambiguous = 1;
524290000Sglebius		if ((error = sa6_embedscope(sin6, V_ip6_use_defzone)) != 0)
525290000Sglebius			return (error);
52654359Sroberto	}
527290000Sglebius
528290000Sglebius	if (control) {
529290000Sglebius		if ((error = ip6_setpktopts(control, &opt,
530290000Sglebius		    inp->in6p_outputopts, td->td_ucred, IPPROTO_UDP)) != 0)
531290000Sglebius			goto release;
532290000Sglebius		optp = &opt;
533290000Sglebius	} else
534290000Sglebius		optp = inp->in6p_outputopts;
535290000Sglebius
536290000Sglebius	if (sin6) {
537290000Sglebius		faddr = &sin6->sin6_addr;
538290000Sglebius
539290000Sglebius		/*
540290000Sglebius		 * IPv4 version of udp_output calls in_pcbconnect in this case,
541290000Sglebius		 * which needs splnet and affects performance.
542290000Sglebius		 * Since we saw no essential reason for calling in_pcbconnect,
543290000Sglebius		 * we get rid of such kind of logic, and call in6_selectsrc
544290000Sglebius		 * and in6_pcbsetport in order to fill in the local address
54554359Sroberto		 * and the local port.
54654359Sroberto		 */
54754359Sroberto		if (sin6->sin6_port == 0) {
548290000Sglebius			error = EADDRNOTAVAIL;
549290000Sglebius			goto release;
550290000Sglebius		}
55154359Sroberto
552290000Sglebius		if (!IN6_IS_ADDR_UNSPECIFIED(&inp->in6p_faddr)) {
553290000Sglebius			/* how about ::ffff:0.0.0.0 case? */
554290000Sglebius			error = EISCONN;
555290000Sglebius			goto release;
55654359Sroberto		}
557290000Sglebius
558290000Sglebius		fport = sin6->sin6_port; /* allow 0 port */
559290000Sglebius
560290000Sglebius		if (IN6_IS_ADDR_V4MAPPED(faddr)) {
561290000Sglebius			if ((inp->in6p_flags & IN6P_IPV6_V6ONLY)) {
56254359Sroberto				/*
56354359Sroberto				 * I believe we should explicitly discard the
56454359Sroberto				 * packet when mapped addresses are disabled,
56554359Sroberto				 * rather than send the packet as an IPv6 one.
56654359Sroberto				 * If we chose the latter approach, the packet
56754359Sroberto				 * might be sent out on the wire based on the
56854359Sroberto				 * default route, the situation which we'd
56954359Sroberto				 * probably want to avoid.
570132451Sroberto				 * (20010421 jinmei@kame.net)
57154359Sroberto				 */
572132451Sroberto				error = EINVAL;
57354359Sroberto				goto release;
57454359Sroberto			}
57554359Sroberto			if (!IN6_IS_ADDR_UNSPECIFIED(&inp->in6p_laddr) &&
57654359Sroberto			    !IN6_IS_ADDR_V4MAPPED(&inp->in6p_laddr)) {
57754359Sroberto				/*
57854359Sroberto				 * when remote addr is an IPv4-mapped address,
57954359Sroberto				 * local addr should not be an IPv6 address,
58054359Sroberto				 * since you cannot determine how to map IPv6
581290000Sglebius				 * source address to IPv4.
582290000Sglebius				 */
58354359Sroberto				error = EINVAL;
58454359Sroberto				goto release;
58554359Sroberto			}
58654359Sroberto
58754359Sroberto			af = AF_INET;
58854359Sroberto		}
58954359Sroberto
59054359Sroberto		if (!IN6_IS_ADDR_V4MAPPED(faddr)) {
59154359Sroberto			laddr = in6_selectsrc(sin6, optp, inp, NULL,
59254359Sroberto			    td->td_ucred, &oifp, &error);
59354359Sroberto			if (oifp && scope_ambiguous &&
59454359Sroberto			    (error = in6_setscope(&sin6->sin6_addr,
59554359Sroberto			    oifp, NULL))) {
59654359Sroberto				goto release;
59754359Sroberto			}
598290000Sglebius		} else
599290000Sglebius			laddr = &inp->in6p_laddr;	/* XXX */
60054359Sroberto		if (laddr == NULL) {
60154359Sroberto			if (error == 0)
602290000Sglebius				error = EADDRNOTAVAIL;
60354359Sroberto			goto release;
60454359Sroberto		}
60554359Sroberto		if (inp->in6p_lport == 0 &&
60654359Sroberto		    (error = in6_pcbsetport(laddr, inp, td->td_ucred)) != 0)
607290000Sglebius			goto release;
608290000Sglebius	} else {
60954359Sroberto		if (IN6_IS_ADDR_UNSPECIFIED(&inp->in6p_faddr)) {
61054359Sroberto			error = ENOTCONN;
61154359Sroberto			goto release;
61254359Sroberto		}
61354359Sroberto		if (IN6_IS_ADDR_V4MAPPED(&inp->in6p_faddr)) {
614290000Sglebius			if ((inp->in6p_flags & IN6P_IPV6_V6ONLY)) {
615290000Sglebius				/*
616290000Sglebius				 * XXX: this case would happen when the
61754359Sroberto				 * application sets the V6ONLY flag after
61854359Sroberto				 * connecting the foreign address.
619290000Sglebius				 * Such applications should be fixed,
620290000Sglebius				 * so we bark here.
621290000Sglebius				 */
62254359Sroberto				log(LOG_INFO, "udp6_output: IPV6_V6ONLY "
623290000Sglebius				    "option was set for a connected socket\n");
624290000Sglebius				error = EINVAL;
62554359Sroberto				goto release;
62654359Sroberto			} else
62754359Sroberto				af = AF_INET;
628290000Sglebius		}
629290000Sglebius		laddr = &inp->in6p_laddr;
63054359Sroberto		faddr = &inp->in6p_faddr;
63154359Sroberto		fport = inp->in6p_fport;
632290000Sglebius	}
633290000Sglebius
63454359Sroberto	if (af == AF_INET)
635132451Sroberto		hlen = sizeof(struct ip);
636132451Sroberto
637290000Sglebius	/*
638290000Sglebius	 * Calculate data length and get a mbuf
63954359Sroberto	 * for UDP and IP6 headers.
640132451Sroberto	 */
64154359Sroberto	M_PREPEND(m, hlen + sizeof(struct udphdr), M_DONTWAIT);
642132451Sroberto	if (m == 0) {
64354359Sroberto		error = ENOBUFS;
644132451Sroberto		goto release;
64554359Sroberto	}
64654359Sroberto
64754359Sroberto	/*
64854359Sroberto	 * Stuff checksum and output datagram.
64954359Sroberto	 */
65054359Sroberto	udp6 = (struct udphdr *)(mtod(m, caddr_t) + hlen);
65154359Sroberto	udp6->uh_sport = inp->in6p_lport; /* lport is always set in the PCB */
65254359Sroberto	udp6->uh_dport = fport;
65354359Sroberto	if (plen <= 0xffff)
65454359Sroberto		udp6->uh_ulen = htons((u_short)plen);
65554359Sroberto	else
65654359Sroberto		udp6->uh_ulen = 0;
65754359Sroberto	udp6->uh_sum = 0;
65854359Sroberto
65954359Sroberto	switch (af) {
66054359Sroberto	case AF_INET6:
66154359Sroberto		ip6 = mtod(m, struct ip6_hdr *);
66254359Sroberto		ip6->ip6_flow	= inp->in6p_flowinfo & IPV6_FLOWINFO_MASK;
66354359Sroberto		ip6->ip6_vfc	&= ~IPV6_VERSION_MASK;
66454359Sroberto		ip6->ip6_vfc	|= IPV6_VERSION;
66554359Sroberto#if 0				/* ip6_plen will be filled in ip6_output. */
66654359Sroberto		ip6->ip6_plen	= htons((u_short)plen);
66754359Sroberto#endif
66854359Sroberto		ip6->ip6_nxt	= IPPROTO_UDP;
66954359Sroberto		ip6->ip6_hlim	= in6_selecthlim(inp, NULL);
67054359Sroberto		ip6->ip6_src	= *laddr;
67154359Sroberto		ip6->ip6_dst	= *faddr;
672290000Sglebius
673290000Sglebius		if ((udp6->uh_sum = in6_cksum(m, IPPROTO_UDP,
674290000Sglebius				sizeof(struct ip6_hdr), plen)) == 0) {
67554359Sroberto			udp6->uh_sum = 0xffff;
67654359Sroberto		}
67754359Sroberto
67854359Sroberto		flags = 0;
67954359Sroberto
680290000Sglebius		V_udpstat.udps_opackets++;
681132451Sroberto		error = ip6_output(m, optp, NULL, flags, inp->in6p_moptions,
682182007Sroberto		    NULL, inp);
68354359Sroberto		break;
68454359Sroberto	case AF_INET:
685132451Sroberto		error = EAFNOSUPPORT;
68654359Sroberto		goto release;
687132451Sroberto	}
68854359Sroberto	goto releaseopt;
68954359Sroberto
69054359Srobertorelease:
69154359Sroberto	m_freem(m);
69254359Sroberto
69354359Srobertoreleaseopt:
69454359Sroberto	if (control) {
69554359Sroberto		ip6_clearpktopts(&opt, -1);
69654359Sroberto		m_freem(control);
69754359Sroberto	}
698132451Sroberto	return (error);
69954359Sroberto}
700290000Sglebius
701290000Sglebiusstatic void
702290000Sglebiusudp6_abort(struct socket *so)
703290000Sglebius{
70454359Sroberto	INIT_VNET_INET(so->so_vnet);
705290000Sglebius	struct inpcb *inp;
706290000Sglebius
707290000Sglebius	inp = sotoinpcb(so);
708290000Sglebius	KASSERT(inp != NULL, ("udp6_abort: inp == NULL"));
709290000Sglebius
710290000Sglebius#ifdef INET
711290000Sglebius	if (inp->inp_vflag & INP_IPV4) {
712290000Sglebius		struct pr_usrreqs *pru;
713290000Sglebius
714290000Sglebius		pru = inetsw[ip_protox[IPPROTO_UDP]].pr_usrreqs;
715290000Sglebius		(*pru->pru_abort)(so);
716290000Sglebius		return;
717290000Sglebius	}
718290000Sglebius#endif
719290000Sglebius
720290000Sglebius	INP_INFO_WLOCK(&V_udbinfo);
721290000Sglebius	INP_WLOCK(inp);
722290000Sglebius	if (!IN6_IS_ADDR_UNSPECIFIED(&inp->in6p_faddr)) {
723290000Sglebius		in6_pcbdisconnect(inp);
724290000Sglebius		inp->in6p_laddr = in6addr_any;
725290000Sglebius		soisdisconnected(so);
726290000Sglebius	}
727290000Sglebius	INP_WUNLOCK(inp);
728290000Sglebius	INP_INFO_WUNLOCK(&V_udbinfo);
729290000Sglebius}
730290000Sglebius
731290000Sglebiusstatic int
732290000Sglebiusudp6_attach(struct socket *so, int proto, struct thread *td)
733290000Sglebius{
734290000Sglebius	INIT_VNET_INET(so->so_vnet);
735290000Sglebius	struct inpcb *inp;
736290000Sglebius	int error;
737290000Sglebius
738290000Sglebius	inp = sotoinpcb(so);
739290000Sglebius	KASSERT(inp == NULL, ("udp6_attach: inp != NULL"));
740290000Sglebius
741290000Sglebius	if (so->so_snd.sb_hiwat == 0 || so->so_rcv.sb_hiwat == 0) {
742290000Sglebius		error = soreserve(so, udp_sendspace, udp_recvspace);
743290000Sglebius		if (error)
744290000Sglebius			return (error);
745290000Sglebius	}
746290000Sglebius	INP_INFO_WLOCK(&V_udbinfo);
747290000Sglebius	error = in_pcballoc(so, &V_udbinfo);
748290000Sglebius	if (error) {
749290000Sglebius		INP_INFO_WUNLOCK(&V_udbinfo);
750290000Sglebius		return (error);
751290000Sglebius	}
752290000Sglebius	inp = (struct inpcb *)so->so_pcb;
753290000Sglebius	INP_INFO_WUNLOCK(&V_udbinfo);
754290000Sglebius	inp->inp_vflag |= INP_IPV6;
755290000Sglebius	if ((inp->inp_flags & IN6P_IPV6_V6ONLY) == 0)
756290000Sglebius		inp->inp_vflag |= INP_IPV4;
757290000Sglebius	inp->in6p_hops = -1;	/* use kernel default */
758290000Sglebius	inp->in6p_cksum = -1;	/* just to be sure */
759290000Sglebius	/*
760290000Sglebius	 * XXX: ugly!!
761290000Sglebius	 * IPv4 TTL initialization is necessary for an IPv6 socket as well,
762290000Sglebius	 * because the socket may be bound to an IPv6 wildcard address,
763290000Sglebius	 * which may match an IPv4-mapped IPv6 address.
764290000Sglebius	 */
765290000Sglebius	inp->inp_ip_ttl = V_ip_defttl;
766290000Sglebius	INP_WUNLOCK(inp);
767290000Sglebius	return (0);
768290000Sglebius}
769290000Sglebius
770290000Sglebiusstatic int
77154359Srobertoudp6_bind(struct socket *so, struct sockaddr *nam, struct thread *td)
77254359Sroberto{
773290000Sglebius	INIT_VNET_INET(so->so_vnet);
774290000Sglebius	struct inpcb *inp;
77554359Sroberto	int error;
776290000Sglebius
777290000Sglebius	inp = sotoinpcb(so);
778290000Sglebius	KASSERT(inp != NULL, ("udp6_bind: inp == NULL"));
779290000Sglebius
78054359Sroberto	INP_INFO_WLOCK(&V_udbinfo);
781290000Sglebius	INP_WLOCK(inp);
782290000Sglebius	inp->inp_vflag &= ~INP_IPV4;
783290000Sglebius	inp->inp_vflag |= INP_IPV6;
784290000Sglebius	if ((inp->inp_flags & IN6P_IPV6_V6ONLY) == 0) {
785290000Sglebius		struct sockaddr_in6 *sin6_p;
786290000Sglebius
787290000Sglebius		sin6_p = (struct sockaddr_in6 *)nam;
788290000Sglebius
789290000Sglebius		if (IN6_IS_ADDR_UNSPECIFIED(&sin6_p->sin6_addr))
790290000Sglebius			inp->inp_vflag |= INP_IPV4;
79154359Sroberto		else if (IN6_IS_ADDR_V4MAPPED(&sin6_p->sin6_addr)) {
792290000Sglebius			struct sockaddr_in sin;
793290000Sglebius
794290000Sglebius			in6_sin6_2_sin(&sin, sin6_p);
795290000Sglebius			inp->inp_vflag |= INP_IPV4;
796290000Sglebius			inp->inp_vflag &= ~INP_IPV6;
797290000Sglebius			error = in_pcbbind(inp, (struct sockaddr *)&sin,
798290000Sglebius			    td->td_ucred);
79954359Sroberto			goto out;
800290000Sglebius		}
801290000Sglebius	}
802290000Sglebius
803290000Sglebius	error = in6_pcbbind(inp, nam, td->td_ucred);
80454359Srobertoout:
805290000Sglebius	INP_WUNLOCK(inp);
806290000Sglebius	INP_INFO_WUNLOCK(&V_udbinfo);
807290000Sglebius	return (error);
808290000Sglebius}
809290000Sglebius
81054359Srobertostatic void
811290000Sglebiusudp6_close(struct socket *so)
812290000Sglebius{
813290000Sglebius	INIT_VNET_INET(so->so_vnet);
814290000Sglebius	struct inpcb *inp;
815290000Sglebius
81654359Sroberto	inp = sotoinpcb(so);
817290000Sglebius	KASSERT(inp != NULL, ("udp6_close: inp == NULL"));
818290000Sglebius
819290000Sglebius#ifdef INET
82054359Sroberto	if (inp->inp_vflag & INP_IPV4) {
82154359Sroberto		struct pr_usrreqs *pru;
82254359Sroberto
82354359Sroberto		pru = inetsw[ip_protox[IPPROTO_UDP]].pr_usrreqs;
82454359Sroberto		(*pru->pru_disconnect)(so);
82554359Sroberto		return;
82654359Sroberto	}
827290000Sglebius#endif
828290000Sglebius	INP_INFO_WLOCK(&V_udbinfo);
829290000Sglebius	INP_WLOCK(inp);
830290000Sglebius	if (!IN6_IS_ADDR_UNSPECIFIED(&inp->in6p_faddr)) {
83154359Sroberto		in6_pcbdisconnect(inp);
83254359Sroberto		inp->in6p_laddr = in6addr_any;
83354359Sroberto		soisdisconnected(so);
83454359Sroberto	}
83554359Sroberto	INP_WUNLOCK(inp);
83654359Sroberto	INP_INFO_WUNLOCK(&V_udbinfo);
83754359Sroberto}
83854359Sroberto
83954359Srobertostatic int
84054359Srobertoudp6_connect(struct socket *so, struct sockaddr *nam, struct thread *td)
84154359Sroberto{
84254359Sroberto	INIT_VNET_INET(so->so_vnet);
84354359Sroberto	struct inpcb *inp;
844290000Sglebius	int error;
845290000Sglebius
846290000Sglebius	inp = sotoinpcb(so);
847290000Sglebius	KASSERT(inp != NULL, ("udp6_connect: inp == NULL"));
84854359Sroberto
849290000Sglebius	INP_INFO_WLOCK(&V_udbinfo);
850290000Sglebius	INP_WLOCK(inp);
851290000Sglebius	if ((inp->inp_flags & IN6P_IPV6_V6ONLY) == 0) {
85254359Sroberto		struct sockaddr_in6 *sin6_p;
85354359Sroberto
854290000Sglebius		sin6_p = (struct sockaddr_in6 *)nam;
85554359Sroberto		if (IN6_IS_ADDR_V4MAPPED(&sin6_p->sin6_addr)) {
856290000Sglebius			struct sockaddr_in sin;
85754359Sroberto
858290000Sglebius			if (inp->inp_faddr.s_addr != INADDR_ANY) {
85954359Sroberto				error = EISCONN;
86054359Sroberto				goto out;
86154359Sroberto			}
862290000Sglebius			in6_sin6_2_sin(&sin, sin6_p);
863290000Sglebius			error = in_pcbconnect(inp, (struct sockaddr *)&sin,
864290000Sglebius			    td->td_ucred);
865290000Sglebius			if (error == 0) {
866290000Sglebius				inp->inp_vflag |= INP_IPV4;
86754359Sroberto				inp->inp_vflag &= ~INP_IPV6;
868290000Sglebius				soisconnected(so);
869290000Sglebius			}
870290000Sglebius			goto out;
871290000Sglebius		}
87254359Sroberto	}
873290000Sglebius	if (!IN6_IS_ADDR_UNSPECIFIED(&inp->in6p_faddr)) {
874290000Sglebius		error = EISCONN;
87554359Sroberto		goto out;
87654359Sroberto	}
877290000Sglebius	error = in6_pcbconnect(inp, nam, td->td_ucred);
878290000Sglebius	if (error == 0) {
879290000Sglebius		if ((inp->inp_flags & IN6P_IPV6_V6ONLY) == 0) {
88054359Sroberto			/* should be non mapped addr */
881290000Sglebius			inp->inp_vflag &= ~INP_IPV4;
882290000Sglebius			inp->inp_vflag |= INP_IPV6;
883290000Sglebius		}
884290000Sglebius		soisconnected(so);
885290000Sglebius	}
886290000Sglebiusout:
887290000Sglebius	INP_WUNLOCK(inp);
888290000Sglebius	INP_INFO_WUNLOCK(&V_udbinfo);
889290000Sglebius	return (error);
89054359Sroberto}
89154359Sroberto
892290000Sglebiusstatic void
893132451Srobertoudp6_detach(struct socket *so)
894132451Sroberto{
895132451Sroberto	INIT_VNET_INET(so->so_vnet);
896132451Sroberto	struct inpcb *inp;
897132451Sroberto
898132451Sroberto	inp = sotoinpcb(so);
899132451Sroberto	KASSERT(inp != NULL, ("udp6_detach: inp == NULL"));
900132451Sroberto
901132451Sroberto	INP_INFO_WLOCK(&V_udbinfo);
902132451Sroberto	INP_WLOCK(inp);
90354359Sroberto	in_pcbdetach(inp);
904290000Sglebius	in_pcbfree(inp);
90554359Sroberto	INP_INFO_WUNLOCK(&V_udbinfo);
906290000Sglebius}
907290000Sglebius
908132451Srobertostatic int
909132451Srobertoudp6_disconnect(struct socket *so)
91054359Sroberto{
911132451Sroberto	INIT_VNET_INET(so->so_vnet);
91254359Sroberto	struct inpcb *inp;
913290000Sglebius	int error;
914290000Sglebius
915290000Sglebius	inp = sotoinpcb(so);
916290000Sglebius	KASSERT(inp != NULL, ("udp6_disconnect: inp == NULL"));
917290000Sglebius
918290000Sglebius	INP_INFO_WLOCK(&V_udbinfo);
919290000Sglebius	INP_WLOCK(inp);
920290000Sglebius
921290000Sglebius#ifdef INET
922290000Sglebius	if (inp->inp_vflag & INP_IPV4) {
923290000Sglebius		struct pr_usrreqs *pru;
924290000Sglebius
925290000Sglebius		pru = inetsw[ip_protox[IPPROTO_UDP]].pr_usrreqs;
926290000Sglebius		error = (*pru->pru_disconnect)(so);
927290000Sglebius		goto out;
928290000Sglebius	}
92954359Sroberto#endif
930132451Sroberto
93154359Sroberto	if (IN6_IS_ADDR_UNSPECIFIED(&inp->in6p_faddr)) {
932290000Sglebius		error = ENOTCONN;
933290000Sglebius		goto out;
934290000Sglebius	}
935290000Sglebius
936290000Sglebius	in6_pcbdisconnect(inp);
937290000Sglebius	inp->in6p_laddr = in6addr_any;
938290000Sglebius	SOCK_LOCK(so);
939290000Sglebius	so->so_state &= ~SS_ISCONNECTED;		/* XXX */
940290000Sglebius	SOCK_UNLOCK(so);
941290000Sglebiusout:
942290000Sglebius	INP_WUNLOCK(inp);
943290000Sglebius	INP_INFO_WUNLOCK(&V_udbinfo);
944290000Sglebius	return (0);
945290000Sglebius}
946290000Sglebius
947290000Sglebiusstatic int
948132451Srobertoudp6_send(struct socket *so, int flags, struct mbuf *m,
949132451Sroberto    struct sockaddr *addr, struct mbuf *control, struct thread *td)
950132451Sroberto{
951290000Sglebius	INIT_VNET_INET(so->so_vnet);
95254359Sroberto	struct inpcb *inp;
953290000Sglebius	int error = 0;
954132451Sroberto
955132451Sroberto	inp = sotoinpcb(so);
956132451Sroberto	KASSERT(inp != NULL, ("udp6_send: inp == NULL"));
95754359Sroberto
958132451Sroberto	INP_INFO_WLOCK(&V_udbinfo);
959290000Sglebius	INP_WLOCK(inp);
960132451Sroberto	if (addr) {
96154359Sroberto		if (addr->sa_len != sizeof(struct sockaddr_in6)) {
962132451Sroberto			error = EINVAL;
96354359Sroberto			goto bad;
96454359Sroberto		}
965290000Sglebius		if (addr->sa_family != AF_INET6) {
96654359Sroberto			error = EAFNOSUPPORT;
96754359Sroberto			goto bad;
96854359Sroberto		}
96954359Sroberto	}
97054359Sroberto
97154359Sroberto#ifdef INET
972132451Sroberto	if ((inp->inp_flags & IN6P_IPV6_V6ONLY) == 0) {
973132451Sroberto		int hasv4addr;
974290000Sglebius		struct sockaddr_in6 *sin6 = 0;
97554359Sroberto
97654359Sroberto		if (addr == 0)
97754359Sroberto			hasv4addr = (inp->inp_vflag & INP_IPV4);
97854359Sroberto		else {
97954359Sroberto			sin6 = (struct sockaddr_in6 *)addr;
98054359Sroberto			hasv4addr = IN6_IS_ADDR_V4MAPPED(&sin6->sin6_addr)
98154359Sroberto			    ? 1 : 0;
98254359Sroberto		}
98354359Sroberto		if (hasv4addr) {
98454359Sroberto			struct pr_usrreqs *pru;
98554359Sroberto
986132451Sroberto			if (!IN6_IS_ADDR_UNSPECIFIED(&inp->in6p_laddr) &&
98754359Sroberto			    !IN6_IS_ADDR_V4MAPPED(&inp->in6p_laddr)) {
98854359Sroberto				/*
989182007Sroberto				 * When remote addr is IPv4-mapped address,
990290000Sglebius				 * local addr should not be an IPv6 address;
991290000Sglebius				 * since you cannot determine how to map IPv6
992290000Sglebius				 * source address to IPv4.
993132451Sroberto				 */
994132451Sroberto				error = EINVAL;
995132451Sroberto				goto out;
996290000Sglebius			}
997132451Sroberto
99854359Sroberto			/*
99954359Sroberto			 * XXXRW: We release UDP-layer locks before calling
100054359Sroberto			 * udp_send() in order to avoid recursion.  However,
100154359Sroberto			 * this does mean there is a short window where inp's
100254359Sroberto			 * fields are unstable.  Could this lead to a
100354359Sroberto			 * potential race in which the factors causing us to
100454359Sroberto			 * select the UDPv4 output routine are invalidated?
100554359Sroberto			 */
100654359Sroberto			INP_WUNLOCK(inp);
1007290000Sglebius			INP_INFO_WUNLOCK(&V_udbinfo);
1008290000Sglebius			if (sin6)
1009290000Sglebius				in6_sin6_2_sin_in_sock(addr);
1010290000Sglebius			pru = inetsw[ip_protox[IPPROTO_UDP]].pr_usrreqs;
101154359Sroberto			/* addr will just be freed in sendit(). */
101254359Sroberto			return ((*pru->pru_send)(so, flags, m, addr, control,
101354359Sroberto			    td));
101454359Sroberto		}
101554359Sroberto	}
101654359Sroberto#endif
101754359Sroberto#ifdef MAC
101854359Sroberto	mac_inpcb_create_mbuf(inp, m);
101954359Sroberto#endif
102054359Sroberto	error = udp6_output(inp, m, addr, control, td);
1021290000Sglebiusout:
102254359Sroberto	INP_WUNLOCK(inp);
102354359Sroberto	INP_INFO_WUNLOCK(&V_udbinfo);
102454359Sroberto	return (error);
102554359Sroberto
102656746Srobertobad:
102754359Sroberto	INP_WUNLOCK(inp);
1028290000Sglebius	INP_INFO_WUNLOCK(&V_udbinfo);
102954359Sroberto	m_freem(m);
1030132451Sroberto	return (error);
1031132451Sroberto}
1032290000Sglebius
103354359Srobertostruct pr_usrreqs udp6_usrreqs = {
103456746Sroberto	.pru_abort =		udp6_abort,
103556746Sroberto	.pru_attach =		udp6_attach,
103654359Sroberto	.pru_bind =		udp6_bind,
1037132451Sroberto	.pru_connect =		udp6_connect,
103854359Sroberto	.pru_control =		in6_control,
103954359Sroberto	.pru_detach =		udp6_detach,
1040290000Sglebius	.pru_disconnect =	udp6_disconnect,
104154359Sroberto	.pru_peeraddr =		in6_mapped_peeraddr,
104254359Sroberto	.pru_send =		udp6_send,
104354359Sroberto	.pru_shutdown =		udp_shutdown,
1044	.pru_sockaddr =		in6_mapped_sockaddr,
1045	.pru_soreceive =	soreceive_dgram,
1046	.pru_sosend =		sosend_dgram,
1047	.pru_sosetlabel =	in_pcbsosetlabel,
1048	.pru_close =		udp6_close
1049};
1050