udp6_usrreq.c revision 127505
1/*	$FreeBSD: head/sys/netinet6/udp6_usrreq.c 127505 2004-03-27 21:05:46Z pjd $	*/
2/*	$KAME: udp6_usrreq.c,v 1.27 2001/05/21 05:45:10 jinmei Exp $	*/
3
4/*
5 * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project.
6 * All rights reserved.
7 *
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions
10 * are met:
11 * 1. Redistributions of source code must retain the above copyright
12 *    notice, this list of conditions and the following disclaimer.
13 * 2. Redistributions in binary form must reproduce the above copyright
14 *    notice, this list of conditions and the following disclaimer in the
15 *    documentation and/or other materials provided with the distribution.
16 * 3. Neither the name of the project nor the names of its contributors
17 *    may be used to endorse or promote products derived from this software
18 *    without specific prior written permission.
19 *
20 * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND
21 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
23 * ARE DISCLAIMED.  IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE
24 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
25 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
26 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
27 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
28 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
29 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
30 * SUCH DAMAGE.
31 */
32
33/*
34 * Copyright (c) 1982, 1986, 1989, 1993
35 *	The Regents of the University of California.  All rights reserved.
36 *
37 * Redistribution and use in source and binary forms, with or without
38 * modification, are permitted provided that the following conditions
39 * are met:
40 * 1. Redistributions of source code must retain the above copyright
41 *    notice, this list of conditions and the following disclaimer.
42 * 2. Redistributions in binary form must reproduce the above copyright
43 *    notice, this list of conditions and the following disclaimer in the
44 *    documentation and/or other materials provided with the distribution.
45 * 3. All advertising materials mentioning features or use of this software
46 *    must display the following acknowledgement:
47 *	This product includes software developed by the University of
48 *	California, Berkeley and its contributors.
49 * 4. Neither the name of the University nor the names of its contributors
50 *    may be used to endorse or promote products derived from this software
51 *    without specific prior written permission.
52 *
53 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
54 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
55 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
56 * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
57 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
58 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
59 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
60 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
61 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
62 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
63 * SUCH DAMAGE.
64 *
65 *	@(#)udp_var.h	8.1 (Berkeley) 6/10/93
66 */
67
68#include "opt_inet.h"
69#include "opt_inet6.h"
70#include "opt_ipsec.h"
71
72#include <sys/param.h>
73#include <sys/errno.h>
74#include <sys/kernel.h>
75#include <sys/lock.h>
76#include <sys/mbuf.h>
77#include <sys/proc.h>
78#include <sys/protosw.h>
79#include <sys/signalvar.h>
80#include <sys/socket.h>
81#include <sys/socketvar.h>
82#include <sys/stat.h>
83#include <sys/sx.h>
84#include <sys/sysctl.h>
85#include <sys/syslog.h>
86#include <sys/systm.h>
87
88#include <net/if.h>
89#include <net/if_types.h>
90#include <net/route.h>
91
92#include <netinet/in.h>
93#include <netinet/in_pcb.h>
94#include <netinet/in_systm.h>
95#include <netinet/in_var.h>
96#include <netinet/ip.h>
97#include <netinet/ip6.h>
98#include <netinet/icmp6.h>
99#include <netinet/ip_var.h>
100#include <netinet/udp.h>
101#include <netinet/udp_var.h>
102#include <netinet6/ip6protosw.h>
103#include <netinet6/ip6_var.h>
104#include <netinet6/in6_pcb.h>
105#include <netinet6/udp6_var.h>
106
107#ifdef IPSEC
108#include <netinet6/ipsec.h>
109#include <netinet6/ipsec6.h>
110#endif /* IPSEC */
111
112#ifdef FAST_IPSEC
113#include <netipsec/ipsec.h>
114#include <netipsec/ipsec6.h>
115#endif /* FAST_IPSEC */
116
117/*
118 * UDP protocol inplementation.
119 * Per RFC 768, August, 1980.
120 */
121
122extern	struct protosw inetsw[];
123static	int udp6_detach __P((struct socket *so));
124
125int
126udp6_input(mp, offp, proto)
127	struct mbuf **mp;
128	int *offp, proto;
129{
130	struct mbuf *m = *mp, *opts;
131	register struct ip6_hdr *ip6;
132	register struct udphdr *uh;
133	register struct inpcb *in6p;
134	int off = *offp;
135	int plen, ulen;
136	struct sockaddr_in6 fromsa;
137
138	opts = NULL;
139
140	ip6 = mtod(m, struct ip6_hdr *);
141
142	if (faithprefix_p != NULL && (*faithprefix_p)(&ip6->ip6_dst)) {
143		/* XXX send icmp6 host/port unreach? */
144		m_freem(m);
145		return IPPROTO_DONE;
146	}
147
148#ifndef PULLDOWN_TEST
149	IP6_EXTHDR_CHECK(m, off, sizeof(struct udphdr), IPPROTO_DONE);
150	ip6 = mtod(m, struct ip6_hdr *);
151	uh = (struct udphdr *)((caddr_t)ip6 + off);
152#else
153	IP6_EXTHDR_GET(uh, struct udphdr *, m, off, sizeof(*uh));
154	if (!uh)
155		return IPPROTO_DONE;
156#endif
157
158	udpstat.udps_ipackets++;
159
160	plen = ntohs(ip6->ip6_plen) - off + sizeof(*ip6);
161	ulen = ntohs((u_short)uh->uh_ulen);
162
163	if (plen != ulen) {
164		udpstat.udps_badlen++;
165		goto bad;
166	}
167
168	/*
169	 * Checksum extended UDP header and data.
170	 */
171	if (uh->uh_sum == 0)
172		udpstat.udps_nosum++;
173	else if (in6_cksum(m, IPPROTO_UDP, off, ulen) != 0) {
174		udpstat.udps_badsum++;
175		goto bad;
176	}
177
178	if (IN6_IS_ADDR_MULTICAST(&ip6->ip6_dst)) {
179		struct	inpcb *last;
180
181		/*
182		 * Deliver a multicast datagram to all sockets
183		 * for which the local and remote addresses and ports match
184		 * those of the incoming datagram.  This allows more than
185		 * one process to receive multicasts on the same port.
186		 * (This really ought to be done for unicast datagrams as
187		 * well, but that would cause problems with existing
188		 * applications that open both address-specific sockets and
189		 * a wildcard socket listening to the same port -- they would
190		 * end up receiving duplicates of every unicast datagram.
191		 * Those applications open the multiple sockets to overcome an
192		 * inadequacy of the UDP socket interface, but for backwards
193		 * compatibility we avoid the problem here rather than
194		 * fixing the interface.  Maybe 4.5BSD will remedy this?)
195		 */
196
197		/*
198		 * In a case that laddr should be set to the link-local
199		 * address (this happens in RIPng), the multicast address
200		 * specified in the received packet does not match with
201		 * laddr. To cure this situation, the matching is relaxed
202		 * if the receiving interface is the same as one specified
203		 * in the socket and if the destination multicast address
204		 * matches one of the multicast groups specified in the socket.
205		 */
206
207		/*
208		 * Construct sockaddr format source address.
209		 */
210		init_sin6(&fromsa, m);
211		fromsa.sin6_port = uh->uh_sport;
212		/*
213		 * KAME note: traditionally we dropped udpiphdr from mbuf here.
214		 * We need udphdr for IPsec processing so we do that later.
215		 */
216
217		/*
218		 * Locate pcb(s) for datagram.
219		 * (Algorithm copied from raw_intr().)
220		 */
221		last = NULL;
222		LIST_FOREACH(in6p, &udb, inp_list) {
223			if ((in6p->inp_vflag & INP_IPV6) == 0)
224				continue;
225			if (in6p->in6p_lport != uh->uh_dport)
226				continue;
227			if (!IN6_IS_ADDR_UNSPECIFIED(&in6p->in6p_laddr)) {
228				if (!IN6_ARE_ADDR_EQUAL(&in6p->in6p_laddr,
229							&ip6->ip6_dst))
230					continue;
231			}
232			if (!IN6_IS_ADDR_UNSPECIFIED(&in6p->in6p_faddr)) {
233				if (!IN6_ARE_ADDR_EQUAL(&in6p->in6p_faddr,
234							&ip6->ip6_src) ||
235				    in6p->in6p_fport != uh->uh_sport)
236					continue;
237			}
238
239			if (last != NULL) {
240				struct mbuf *n;
241
242#if defined(IPSEC) || defined(FAST_IPSEC)
243				/*
244				 * Check AH/ESP integrity.
245				 */
246				if (ipsec6_in_reject(m, last)) {
247#ifdef IPSEC
248					ipsec6stat.in_polvio++;
249#endif /* IPSEC */
250					/* do not inject data into pcb */
251				} else
252#endif /*IPSEC || FAST_IPSEC*/
253				if ((n = m_copy(m, 0, M_COPYALL)) != NULL) {
254					/*
255					 * KAME NOTE: do not
256					 * m_copy(m, offset, ...) above.
257					 * sbappendaddr() expects M_PKTHDR,
258					 * and m_copy() will copy M_PKTHDR
259					 * only if offset is 0.
260					 */
261					if (last->in6p_flags & IN6P_CONTROLOPTS
262					    || last->in6p_socket->so_options & SO_TIMESTAMP)
263						ip6_savecontrol(last, n, &opts);
264
265					m_adj(n, off + sizeof(struct udphdr));
266					if (sbappendaddr(&last->in6p_socket->so_rcv,
267							(struct sockaddr *)&fromsa,
268							n, opts) == 0) {
269						m_freem(n);
270						if (opts)
271							m_freem(opts);
272						udpstat.udps_fullsock++;
273					} else
274						sorwakeup(last->in6p_socket);
275					opts = NULL;
276				}
277			}
278			last = in6p;
279			/*
280			 * Don't look for additional matches if this one does
281			 * not have either the SO_REUSEPORT or SO_REUSEADDR
282			 * socket options set.  This heuristic avoids searching
283			 * through all pcbs in the common case of a non-shared
284			 * port.  It assumes that an application will never
285			 * clear these options after setting them.
286			 */
287			if ((last->in6p_socket->so_options &
288			     (SO_REUSEPORT|SO_REUSEADDR)) == 0)
289				break;
290		}
291
292		if (last == NULL) {
293			/*
294			 * No matching pcb found; discard datagram.
295			 * (No need to send an ICMP Port Unreachable
296			 * for a broadcast or multicast datgram.)
297			 */
298			udpstat.udps_noport++;
299			udpstat.udps_noportmcast++;
300			goto bad;
301		}
302#if defined(IPSEC) || defined(FAST_IPSEC)
303		/*
304		 * Check AH/ESP integrity.
305		 */
306		if (ipsec6_in_reject(m, last)) {
307#ifdef IPSEC
308			ipsec6stat.in_polvio++;
309#endif /* IPSEC */
310			goto bad;
311		}
312#endif /*IPSEC || FAST_IPSEC*/
313		if (last->in6p_flags & IN6P_CONTROLOPTS
314		    || last->in6p_socket->so_options & SO_TIMESTAMP)
315			ip6_savecontrol(last, m, &opts);
316
317		m_adj(m, off + sizeof(struct udphdr));
318		if (sbappendaddr(&last->in6p_socket->so_rcv,
319				(struct sockaddr *)&fromsa,
320				m, opts) == 0) {
321			udpstat.udps_fullsock++;
322			goto bad;
323		}
324		sorwakeup(last->in6p_socket);
325		return IPPROTO_DONE;
326	}
327	/*
328	 * Locate pcb for datagram.
329	 */
330	in6p = in6_pcblookup_hash(&udbinfo, &ip6->ip6_src, uh->uh_sport,
331				  &ip6->ip6_dst, uh->uh_dport, 1,
332				  m->m_pkthdr.rcvif);
333	if (in6p == 0) {
334		if (log_in_vain) {
335			char buf[INET6_ADDRSTRLEN];
336
337			strcpy(buf, ip6_sprintf(&ip6->ip6_dst));
338			log(LOG_INFO,
339			    "Connection attempt to UDP [%s]:%d from [%s]:%d\n",
340			    buf, ntohs(uh->uh_dport),
341			    ip6_sprintf(&ip6->ip6_src), ntohs(uh->uh_sport));
342		}
343		udpstat.udps_noport++;
344		if (m->m_flags & M_MCAST) {
345			printf("UDP6: M_MCAST is set in a unicast packet.\n");
346			udpstat.udps_noportmcast++;
347			goto bad;
348		}
349		icmp6_error(m, ICMP6_DST_UNREACH, ICMP6_DST_UNREACH_NOPORT, 0);
350		return IPPROTO_DONE;
351	}
352#if defined(IPSEC) || defined(FAST_IPSEC)
353	/*
354	 * Check AH/ESP integrity.
355	 */
356	if (ipsec6_in_reject(m, in6p)) {
357#ifdef IPSEC
358		ipsec6stat.in_polvio++;
359#endif /* IPSEC */
360		goto bad;
361	}
362#endif /*IPSEC || FAST_IPSEC*/
363
364	/*
365	 * Construct sockaddr format source address.
366	 * Stuff source address and datagram in user buffer.
367	 */
368	init_sin6(&fromsa, m);
369	fromsa.sin6_port = uh->uh_sport;
370	if (in6p->in6p_flags & IN6P_CONTROLOPTS
371	    || in6p->in6p_socket->so_options & SO_TIMESTAMP)
372		ip6_savecontrol(in6p, m, &opts);
373	m_adj(m, off + sizeof(struct udphdr));
374	if (sbappendaddr(&in6p->in6p_socket->so_rcv,
375			(struct sockaddr *)&fromsa, m, opts) == 0) {
376		udpstat.udps_fullsock++;
377		goto bad;
378	}
379	sorwakeup(in6p->in6p_socket);
380	return IPPROTO_DONE;
381bad:
382	if (m)
383		m_freem(m);
384	if (opts)
385		m_freem(opts);
386	return IPPROTO_DONE;
387}
388
389void
390udp6_ctlinput(cmd, sa, d)
391	int cmd;
392	struct sockaddr *sa;
393	void *d;
394{
395	struct udphdr uh;
396	struct ip6_hdr *ip6;
397	struct mbuf *m;
398	int off = 0;
399	struct ip6ctlparam *ip6cp = NULL;
400	const struct sockaddr_in6 *sa6_src = NULL;
401	void *cmdarg;
402	struct inpcb *(*notify) __P((struct inpcb *, int)) = udp_notify;
403	struct udp_portonly {
404		u_int16_t uh_sport;
405		u_int16_t uh_dport;
406	} *uhp;
407
408	if (sa->sa_family != AF_INET6 ||
409	    sa->sa_len != sizeof(struct sockaddr_in6))
410		return;
411
412	if ((unsigned)cmd >= PRC_NCMDS)
413		return;
414	if (PRC_IS_REDIRECT(cmd))
415		notify = in6_rtchange, d = NULL;
416	else if (cmd == PRC_HOSTDEAD)
417		d = NULL;
418	else if (inet6ctlerrmap[cmd] == 0)
419		return;
420
421	/* if the parameter is from icmp6, decode it. */
422	if (d != NULL) {
423		ip6cp = (struct ip6ctlparam *)d;
424		m = ip6cp->ip6c_m;
425		ip6 = ip6cp->ip6c_ip6;
426		off = ip6cp->ip6c_off;
427		cmdarg = ip6cp->ip6c_cmdarg;
428		sa6_src = ip6cp->ip6c_src;
429	} else {
430		m = NULL;
431		ip6 = NULL;
432		cmdarg = NULL;
433		sa6_src = &sa6_any;
434	}
435
436	if (ip6) {
437		/*
438		 * XXX: We assume that when IPV6 is non NULL,
439		 * M and OFF are valid.
440		 */
441
442		/* check if we can safely examine src and dst ports */
443		if (m->m_pkthdr.len < off + sizeof(*uhp))
444			return;
445
446		bzero(&uh, sizeof(uh));
447		m_copydata(m, off, sizeof(*uhp), (caddr_t)&uh);
448
449		(void) in6_pcbnotify(&udb, sa, uh.uh_dport,
450				     (struct sockaddr *)ip6cp->ip6c_src,
451				     uh.uh_sport, cmd, cmdarg, notify);
452	} else
453		(void) in6_pcbnotify(&udb, sa, 0,
454				     (const struct sockaddr *)sa6_src,
455				     0, cmd, cmdarg, notify);
456}
457
458static int
459udp6_getcred(SYSCTL_HANDLER_ARGS)
460{
461	struct xucred xuc;
462	struct sockaddr_in6 addrs[2];
463	struct inpcb *inp;
464	int error, s;
465
466	error = suser(req->td);
467	if (error)
468		return (error);
469
470	if (req->newlen != sizeof(addrs))
471		return (EINVAL);
472	if (req->oldlen != sizeof(struct xucred))
473		return (EINVAL);
474	error = SYSCTL_IN(req, addrs, sizeof(addrs));
475	if (error)
476		return (error);
477	s = splnet();
478	inp = in6_pcblookup_hash(&udbinfo, &addrs[1].sin6_addr,
479				 addrs[1].sin6_port,
480				 &addrs[0].sin6_addr, addrs[0].sin6_port,
481				 1, NULL);
482	if (!inp || !inp->inp_socket) {
483		error = ENOENT;
484		goto out;
485	}
486	cru2x(inp->inp_socket->so_cred, &xuc);
487	error = SYSCTL_OUT(req, &xuc, sizeof(struct xucred));
488out:
489	splx(s);
490	return (error);
491}
492
493SYSCTL_PROC(_net_inet6_udp6, OID_AUTO, getcred, CTLTYPE_OPAQUE|CTLFLAG_RW,
494	    0, 0,
495	    udp6_getcred, "S,xucred", "Get the xucred of a UDP6 connection");
496
497static int
498udp6_abort(struct socket *so)
499{
500	struct inpcb *inp;
501	int s;
502
503	inp = sotoinpcb(so);
504	if (inp == 0)
505		return EINVAL;	/* ??? possible? panic instead? */
506	soisdisconnected(so);
507	s = splnet();
508	in6_pcbdetach(inp);
509	splx(s);
510	return 0;
511}
512
513static int
514udp6_attach(struct socket *so, int proto, struct thread *td)
515{
516	struct inpcb *inp;
517	int s, error;
518
519	inp = sotoinpcb(so);
520	if (inp != 0)
521		return EINVAL;
522
523	if (so->so_snd.sb_hiwat == 0 || so->so_rcv.sb_hiwat == 0) {
524		error = soreserve(so, udp_sendspace, udp_recvspace);
525		if (error)
526			return error;
527	}
528	s = splnet();
529	error = in_pcballoc(so, &udbinfo, "udp6inp");
530	splx(s);
531	if (error)
532		return error;
533	inp = (struct inpcb *)so->so_pcb;
534	inp->inp_vflag |= INP_IPV6;
535	if (!ip6_v6only)
536		inp->inp_vflag |= INP_IPV4;
537	inp->in6p_hops = -1;	/* use kernel default */
538	inp->in6p_cksum = -1;	/* just to be sure */
539	/*
540	 * XXX: ugly!!
541	 * IPv4 TTL initialization is necessary for an IPv6 socket as well,
542	 * because the socket may be bound to an IPv6 wildcard address,
543	 * which may match an IPv4-mapped IPv6 address.
544	 */
545	inp->inp_ip_ttl = ip_defttl;
546	return 0;
547}
548
549static int
550udp6_bind(struct socket *so, struct sockaddr *nam, struct thread *td)
551{
552	struct inpcb *inp;
553	int s, error;
554
555	inp = sotoinpcb(so);
556	if (inp == 0)
557		return EINVAL;
558
559	inp->inp_vflag &= ~INP_IPV4;
560	inp->inp_vflag |= INP_IPV6;
561	if ((inp->inp_flags & IN6P_IPV6_V6ONLY) == 0) {
562		struct sockaddr_in6 *sin6_p;
563
564		sin6_p = (struct sockaddr_in6 *)nam;
565
566		if (IN6_IS_ADDR_UNSPECIFIED(&sin6_p->sin6_addr))
567			inp->inp_vflag |= INP_IPV4;
568		else if (IN6_IS_ADDR_V4MAPPED(&sin6_p->sin6_addr)) {
569			struct sockaddr_in sin;
570
571			in6_sin6_2_sin(&sin, sin6_p);
572			inp->inp_vflag |= INP_IPV4;
573			inp->inp_vflag &= ~INP_IPV6;
574			s = splnet();
575			error = in_pcbbind(inp, (struct sockaddr *)&sin,
576			    td->td_ucred);
577			splx(s);
578			return error;
579		}
580	}
581
582	s = splnet();
583	error = in6_pcbbind(inp, nam, td->td_ucred);
584	splx(s);
585	return error;
586}
587
588static int
589udp6_connect(struct socket *so, struct sockaddr *nam, struct thread *td)
590{
591	struct inpcb *inp;
592	int s, error;
593
594	inp = sotoinpcb(so);
595	if (inp == 0)
596		return EINVAL;
597
598	if ((inp->inp_flags & IN6P_IPV6_V6ONLY) == 0) {
599		struct sockaddr_in6 *sin6_p;
600
601		sin6_p = (struct sockaddr_in6 *)nam;
602		if (IN6_IS_ADDR_V4MAPPED(&sin6_p->sin6_addr)) {
603			struct sockaddr_in sin;
604
605			if (inp->inp_faddr.s_addr != INADDR_ANY)
606				return EISCONN;
607			in6_sin6_2_sin(&sin, sin6_p);
608			s = splnet();
609			error = in_pcbconnect(inp, (struct sockaddr *)&sin,
610			    td->td_ucred);
611			splx(s);
612			if (error == 0) {
613				inp->inp_vflag |= INP_IPV4;
614				inp->inp_vflag &= ~INP_IPV6;
615				soisconnected(so);
616			}
617			return error;
618		}
619	}
620	if (!IN6_IS_ADDR_UNSPECIFIED(&inp->in6p_faddr))
621		return EISCONN;
622	s = splnet();
623	error = in6_pcbconnect(inp, nam, td->td_ucred);
624	splx(s);
625	if (error == 0) {
626		if (!ip6_v6only) { /* should be non mapped addr */
627			inp->inp_vflag &= ~INP_IPV4;
628			inp->inp_vflag |= INP_IPV6;
629		}
630		soisconnected(so);
631	}
632	return error;
633}
634
635static int
636udp6_detach(struct socket *so)
637{
638	struct inpcb *inp;
639	int s;
640
641	inp = sotoinpcb(so);
642	if (inp == 0)
643		return EINVAL;
644	s = splnet();
645	in6_pcbdetach(inp);
646	splx(s);
647	return 0;
648}
649
650static int
651udp6_disconnect(struct socket *so)
652{
653	struct inpcb *inp;
654	int s;
655
656	inp = sotoinpcb(so);
657	if (inp == 0)
658		return EINVAL;
659
660#ifdef INET
661	if (inp->inp_vflag & INP_IPV4) {
662		struct pr_usrreqs *pru;
663
664		pru = inetsw[ip_protox[IPPROTO_UDP]].pr_usrreqs;
665		return ((*pru->pru_disconnect)(so));
666	}
667#endif
668
669	if (IN6_IS_ADDR_UNSPECIFIED(&inp->in6p_faddr))
670		return ENOTCONN;
671
672	s = splnet();
673	in6_pcbdisconnect(inp);
674	inp->in6p_laddr = in6addr_any;
675	splx(s);
676	so->so_state &= ~SS_ISCONNECTED;		/* XXX */
677	return 0;
678}
679
680static int
681udp6_send(struct socket *so, int flags, struct mbuf *m, struct sockaddr *addr,
682	  struct mbuf *control, struct thread *td)
683{
684	struct inpcb *inp;
685	int error = 0;
686
687	inp = sotoinpcb(so);
688	if (inp == 0) {
689		error = EINVAL;
690		goto bad;
691	}
692
693	if (addr) {
694		if (addr->sa_len != sizeof(struct sockaddr_in6)) {
695			error = EINVAL;
696			goto bad;
697		}
698		if (addr->sa_family != AF_INET6) {
699			error = EAFNOSUPPORT;
700			goto bad;
701		}
702	}
703
704#ifdef INET
705	if (!ip6_v6only) {
706		int hasv4addr;
707		struct sockaddr_in6 *sin6 = 0;
708
709		if (addr == 0)
710			hasv4addr = (inp->inp_vflag & INP_IPV4);
711		else {
712			sin6 = (struct sockaddr_in6 *)addr;
713			hasv4addr = IN6_IS_ADDR_V4MAPPED(&sin6->sin6_addr)
714				? 1 : 0;
715		}
716		if (hasv4addr) {
717			struct pr_usrreqs *pru;
718
719			if ((inp->inp_flags & IN6P_IPV6_V6ONLY)) {
720				/*
721				 * since a user of this socket set the
722				 * IPV6_V6ONLY flag, we discard this
723				 * datagram destined to a v4 addr.
724				 */
725				return EINVAL;
726			}
727			if (!IN6_IS_ADDR_UNSPECIFIED(&inp->in6p_laddr) &&
728			    !IN6_IS_ADDR_V4MAPPED(&inp->in6p_laddr)) {
729				/*
730				 * when remote addr is IPv4-mapped
731				 * address, local addr should not be
732				 * an IPv6 address; since you cannot
733				 * determine how to map IPv6 source
734				 * address to IPv4.
735				 */
736				return EINVAL;
737			}
738			if (sin6)
739				in6_sin6_2_sin_in_sock(addr);
740			pru = inetsw[ip_protox[IPPROTO_UDP]].pr_usrreqs;
741			error = ((*pru->pru_send)(so, flags, m, addr, control,
742						  td));
743			/* addr will just be freed in sendit(). */
744			return error;
745		}
746	}
747#endif
748
749	return udp6_output(inp, m, addr, control, td);
750
751  bad:
752	m_freem(m);
753	return (error);
754}
755
756struct pr_usrreqs udp6_usrreqs = {
757	udp6_abort, pru_accept_notsupp, udp6_attach, udp6_bind, udp6_connect,
758	pru_connect2_notsupp, in6_control, udp6_detach, udp6_disconnect,
759	pru_listen_notsupp, in6_mapped_peeraddr, pru_rcvd_notsupp,
760	pru_rcvoob_notsupp, udp6_send, pru_sense_null, udp_shutdown,
761	in6_mapped_sockaddr, sosend, soreceive, sopoll, in_pcbsosetlabel
762};
763