udp6_usrreq.c revision 133192
150476Speter/*	$FreeBSD: head/sys/netinet6/udp6_usrreq.c 133192 2004-08-06 03:45:45Z rwatson $	*/
215903Swosch/*	$KAME: udp6_usrreq.c,v 1.27 2001/05/21 05:45:10 jinmei Exp $	*/
315903Swosch
415903Swosch/*
515903Swosch * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project.
615903Swosch * All rights reserved.
715903Swosch *
815903Swosch * Redistribution and use in source and binary forms, with or without
915903Swosch * modification, are permitted provided that the following conditions
1015903Swosch * are met:
1115903Swosch * 1. Redistributions of source code must retain the above copyright
1215903Swosch *    notice, this list of conditions and the following disclaimer.
1315903Swosch * 2. Redistributions in binary form must reproduce the above copyright
14139761Skrion *    notice, this list of conditions and the following disclaimer in the
1534678Sbde *    documentation and/or other materials provided with the distribution.
1623546Swosch * 3. Neither the name of the project nor the names of its contributors
1723546Swosch *    may be used to endorse or promote products derived from this software
1823546Swosch *    without specific prior written permission.
1939161Sobrien *
2015903Swosch * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND
2139161Sobrien * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
2215903Swosch * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
2315903Swosch * ARE DISCLAIMED.  IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE
2415903Swosch * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
2515903Swosch * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
2615903Swosch * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
2715903Swosch * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
2815903Swosch * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
2932216Swosch * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
3032216Swosch * SUCH DAMAGE.
3132216Swosch */
3232216Swosch
33218525Skeramida/*
34218525Skeramida * Copyright (c) 1982, 1986, 1989, 1993
3515903Swosch *	The Regents of the University of California.  All rights reserved.
3615903Swosch *
3715903Swosch * Redistribution and use in source and binary forms, with or without
3815903Swosch * modification, are permitted provided that the following conditions
39119057Sobrien * are met:
4015903Swosch * 1. Redistributions of source code must retain the above copyright
4115903Swosch *    notice, this list of conditions and the following disclaimer.
4215903Swosch * 2. Redistributions in binary form must reproduce the above copyright
4315903Swosch *    notice, this list of conditions and the following disclaimer in the
4415903Swosch *    documentation and/or other materials provided with the distribution.
4515903Swosch * 4. Neither the name of the University nor the names of its contributors
4615903Swosch *    may be used to endorse or promote products derived from this software
4765501Sobrien *    without specific prior written permission.
4815903Swosch *
49186894Sbz * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
5015903Swosch * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
51186894Sbz * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
5215903Swosch * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
5353033Sphantom * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
5415903Swosch * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
5515903Swosch * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
5615903Swosch * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
5715903Swosch * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
5815903Swosch * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
5939161Sobrien * SUCH DAMAGE.
6015903Swosch *
6139161Sobrien *	@(#)udp_var.h	8.1 (Berkeley) 6/10/93
6215903Swosch */
6315903Swosch
6415903Swosch#include "opt_inet.h"
6515903Swosch#include "opt_inet6.h"
6615903Swosch#include "opt_ipsec.h"
6715903Swosch
6815903Swosch#include <sys/param.h>
6915903Swosch#include <sys/errno.h>
7015903Swosch#include <sys/kernel.h>
7115903Swosch#include <sys/lock.h>
7215903Swosch#include <sys/mbuf.h>
7315903Swosch#include <sys/proc.h>
7415903Swosch#include <sys/protosw.h>
7515903Swosch#include <sys/signalvar.h>
7615903Swosch#include <sys/socket.h>
7715903Swosch#include <sys/socketvar.h>
7815903Swosch#include <sys/stat.h>
7915903Swosch#include <sys/sx.h>
8015903Swosch#include <sys/sysctl.h>
8115903Swosch#include <sys/syslog.h>
8215903Swosch#include <sys/systm.h>
8315903Swosch
8415903Swosch#include <net/if.h>
8515903Swosch#include <net/if_types.h>
8615903Swosch#include <net/route.h>
8715903Swosch
8815903Swosch#include <netinet/in.h>
8915903Swosch#include <netinet/in_pcb.h>
9015903Swosch#include <netinet/in_systm.h>
9115903Swosch#include <netinet/in_var.h>
9215903Swosch#include <netinet/ip.h>
9315903Swosch#include <netinet/ip6.h>
9415903Swosch#include <netinet/icmp6.h>
9515903Swosch#include <netinet/ip_var.h>
9690627Sphantom#include <netinet/udp.h>
9715903Swosch#include <netinet/udp_var.h>
9890626Sphantom#include <netinet6/ip6protosw.h>
9915903Swosch#include <netinet6/ip6_var.h>
10090626Sphantom#include <netinet6/in6_pcb.h>
10115903Swosch#include <netinet6/udp6_var.h>
10261462Sghelmer
10315903Swosch#ifdef IPSEC
10432216Swosch#include <netinet6/ipsec.h>
10515903Swosch#include <netinet6/ipsec6.h>
10694982Sru#endif /* IPSEC */
10794982Sru
10894982Sru#ifdef FAST_IPSEC
109164411Sru#include <netipsec/ipsec.h>
110156813Sru#include <netipsec/ipsec6.h>
111156836Sru#endif /* FAST_IPSEC */
112156836Sru
113156836Sru/*
114164411Sru * UDP protocol inplementation.
115156813Sru * Per RFC 768, August, 1980.
11614968Swosch */
11739161Sobrien
11839161Sobrienextern	struct protosw inetsw[];
11914573Swoschstatic	int udp6_detach __P((struct socket *so));
12014968Swosch
12114573Swoschint
122111853Sruudp6_input(mp, offp, proto)
123111853Sru	struct mbuf **mp;
124111853Sru	int *offp, proto;
12565501Sobrien{
126111853Sru	struct mbuf *m = *mp, *opts;
12748204Sjmg	register struct ip6_hdr *ip6;
12848204Sjmg	register struct udphdr *uh;
12948204Sjmg	register struct inpcb *in6p;
13048204Sjmg	int off = *offp;
13114573Swosch	int plen, ulen;
13232226Ssteve	struct sockaddr_in6 fromsa;
13332226Ssteve
134218525Skeramida	opts = NULL;
13514573Swosch
13614573Swosch	ip6 = mtod(m, struct ip6_hdr *);
13714968Swosch
13814968Swosch	if (faithprefix_p != NULL && (*faithprefix_p)(&ip6->ip6_dst)) {
13914968Swosch		/* XXX send icmp6 host/port unreach? */
14014573Swosch		m_freem(m);
14114968Swosch		return IPPROTO_DONE;
14214968Swosch	}
14314968Swosch
14439161Sobrien#ifndef PULLDOWN_TEST
14539161Sobrien	IP6_EXTHDR_CHECK(m, off, sizeof(struct udphdr), IPPROTO_DONE);
14614968Swosch	ip6 = mtod(m, struct ip6_hdr *);
14714968Swosch	uh = (struct udphdr *)((caddr_t)ip6 + off);
14814968Swosch#else
14914968Swosch	IP6_EXTHDR_GET(uh, struct udphdr *, m, off, sizeof(*uh));
15014968Swosch	if (!uh)
15114968Swosch		return IPPROTO_DONE;
15214968Swosch#endif
15314968Swosch
15414968Swosch	udpstat.udps_ipackets++;
15514968Swosch
15614968Swosch	plen = ntohs(ip6->ip6_plen) - off + sizeof(*ip6);
15714968Swosch	ulen = ntohs((u_short)uh->uh_ulen);
15814968Swosch
15914968Swosch	if (plen != ulen) {
16014968Swosch		udpstat.udps_badlen++;
16114968Swosch		goto bad;
16214968Swosch	}
16314968Swosch
16490626Sphantom	/*
16590626Sphantom	 * Checksum extended UDP header and data.
16661462Sghelmer	 */
16714968Swosch	if (uh->uh_sum == 0) {
16832216Swosch		udpstat.udps_nosum++;
16932216Swosch		goto bad;
17014968Swosch	}
171125494Sru	if (in6_cksum(m, IPPROTO_UDP, off, ulen) != 0) {
172125494Sru		udpstat.udps_badsum++;
173125494Sru		goto bad;
174125494Sru	}
17534678Sbde
17623546Swosch	if (IN6_IS_ADDR_MULTICAST(&ip6->ip6_dst)) {
17794982Sru		struct	inpcb *last;
178164411Sru
179156813Sru		/*
180156813Sru		 * Deliver a multicast datagram to all sockets
181156813Sru		 * for which the local and remote addresses and ports match
182156813Sru		 * those of the incoming datagram.  This allows more than
183156813Sru		 * one process to receive multicasts on the same port.
184156813Sru		 * (This really ought to be done for unicast datagrams as
185156813Sru		 * well, but that would cause problems with existing
186156813Sru		 * applications that open both address-specific sockets and
187156813Sru		 * a wildcard socket listening to the same port -- they would
188156813Sru		 * end up receiving duplicates of every unicast datagram.
189156813Sru		 * Those applications open the multiple sockets to overcome an
190156813Sru		 * inadequacy of the UDP socket interface, but for backwards
191156813Sru		 * compatibility we avoid the problem here rather than
192172832Sru		 * fixing the interface.  Maybe 4.5BSD will remedy this?)
193156869Sru		 */
194156869Sru
195156813Sru		/*
196156813Sru		 * In a case that laddr should be set to the link-local
197156813Sru		 * address (this happens in RIPng), the multicast address
198156813Sru		 * specified in the received packet does not match with
199156813Sru		 * laddr. To cure this situation, the matching is relaxed
200156813Sru		 * if the receiving interface is the same as one specified
201156813Sru		 * in the socket and if the destination multicast address
202156813Sru		 * matches one of the multicast groups specified in the socket.
203156813Sru		 */
204156813Sru
205156813Sru		/*
206156813Sru		 * Construct sockaddr format source address.
207156813Sru		 */
208156813Sru		init_sin6(&fromsa, m);
209156813Sru		fromsa.sin6_port = uh->uh_sport;
210156813Sru		/*
211156813Sru		 * KAME note: traditionally we dropped udpiphdr from mbuf here.
212156813Sru		 * We need udphdr for IPsec processing so we do that later.
213156813Sru		 */
214156813Sru
215156813Sru		/*
216156813Sru		 * Locate pcb(s) for datagram.
217156813Sru		 * (Algorithm copied from raw_intr().)
218156813Sru		 */
219156813Sru		last = NULL;
220156813Sru		LIST_FOREACH(in6p, &udb, inp_list) {
221156813Sru			if ((in6p->inp_vflag & INP_IPV6) == 0)
222156813Sru				continue;
223156813Sru			if (in6p->in6p_lport != uh->uh_dport)
224156813Sru				continue;
225156813Sru			if (!IN6_IS_ADDR_UNSPECIFIED(&in6p->in6p_laddr)) {
226156813Sru				if (!IN6_ARE_ADDR_EQUAL(&in6p->in6p_laddr,
227156813Sru							&ip6->ip6_dst))
228156813Sru					continue;
229156813Sru			}
230156813Sru			if (!IN6_IS_ADDR_UNSPECIFIED(&in6p->in6p_faddr)) {
231156813Sru				if (!IN6_ARE_ADDR_EQUAL(&in6p->in6p_faddr,
232156813Sru							&ip6->ip6_src) ||
233156813Sru				    in6p->in6p_fport != uh->uh_sport)
234156813Sru					continue;
235156813Sru			}
236156813Sru
237156813Sru			if (last != NULL) {
238156813Sru				struct mbuf *n;
239156813Sru
240157115Sru#if defined(IPSEC) || defined(FAST_IPSEC)
241156813Sru				/*
242156813Sru				 * Check AH/ESP integrity.
243156813Sru				 */
244156813Sru				if (ipsec6_in_reject(m, last)) {
245156813Sru#ifdef IPSEC
246156813Sru					ipsec6stat.in_polvio++;
247156813Sru#endif /* IPSEC */
248156813Sru					/* do not inject data into pcb */
249156813Sru				} else
250158115Sume#endif /*IPSEC || FAST_IPSEC*/
251156813Sru				if ((n = m_copy(m, 0, M_COPYALL)) != NULL) {
252156813Sru					/*
253156813Sru					 * KAME NOTE: do not
254156813Sru					 * m_copy(m, offset, ...) above.
255156813Sru					 * sbappendaddr() expects M_PKTHDR,
256156813Sru					 * and m_copy() will copy M_PKTHDR
257156813Sru					 * only if offset is 0.
258156813Sru					 */
259156813Sru					if (last->in6p_flags & IN6P_CONTROLOPTS
260156813Sru					    || last->in6p_socket->so_options & SO_TIMESTAMP)
261156813Sru						ip6_savecontrol(last, n, &opts);
262156813Sru
263156813Sru					m_adj(n, off + sizeof(struct udphdr));
264156813Sru					if (sbappendaddr(&last->in6p_socket->so_rcv,
265156813Sru							(struct sockaddr *)&fromsa,
266156813Sru							n, opts) == 0) {
267156813Sru						m_freem(n);
268156813Sru						if (opts)
269156813Sru							m_freem(opts);
270156813Sru						udpstat.udps_fullsock++;
271156813Sru					} else
272156813Sru						sorwakeup(last->in6p_socket);
273156813Sru					opts = NULL;
274156813Sru				}
275156813Sru			}
276156813Sru			last = in6p;
277156813Sru			/*
278156813Sru			 * Don't look for additional matches if this one does
279156813Sru			 * not have either the SO_REUSEPORT or SO_REUSEADDR
280156813Sru			 * socket options set.  This heuristic avoids searching
281156813Sru			 * through all pcbs in the common case of a non-shared
282156813Sru			 * port.  It assumes that an application will never
283220359Simp			 * clear these options after setting them.
284183242Ssam			 */
285156813Sru			if ((last->in6p_socket->so_options &
286183242Ssam			     (SO_REUSEPORT|SO_REUSEADDR)) == 0)
287183242Ssam				break;
288162210Simp		}
289183242Ssam
290156813Sru		if (last == NULL) {
291156813Sru			/*
292156813Sru			 * No matching pcb found; discard datagram.
293156813Sru			 * (No need to send an ICMP Port Unreachable
294156813Sru			 * for a broadcast or multicast datgram.)
295156813Sru			 */
296156813Sru			udpstat.udps_noport++;
297156813Sru			udpstat.udps_noportmcast++;
298156813Sru			goto bad;
299156813Sru		}
300218936Simp#if defined(IPSEC) || defined(FAST_IPSEC)
301156813Sru		/*
302156813Sru		 * Check AH/ESP integrity.
303179815Sdougb		 */
304183242Ssam		if (ipsec6_in_reject(m, last)) {
305166255Sdelphij#ifdef IPSEC
306156813Sru			ipsec6stat.in_polvio++;
307163861Sjb#endif /* IPSEC */
308156813Sru			goto bad;
309156813Sru		}
310183242Ssam#endif /*IPSEC || FAST_IPSEC*/
311156813Sru		if (last->in6p_flags & IN6P_CONTROLOPTS
312156813Sru		    || last->in6p_socket->so_options & SO_TIMESTAMP)
313156813Sru			ip6_savecontrol(last, m, &opts);
314156813Sru
315156813Sru		m_adj(m, off + sizeof(struct udphdr));
316183242Ssam		if (sbappendaddr(&last->in6p_socket->so_rcv,
317156813Sru				(struct sockaddr *)&fromsa,
318156813Sru				m, opts) == 0) {
319183242Ssam			udpstat.udps_fullsock++;
320156813Sru			goto bad;
321218936Simp		}
322156813Sru		sorwakeup(last->in6p_socket);
323156813Sru		return IPPROTO_DONE;
324156813Sru	}
325156813Sru	/*
326220359Simp	 * Locate pcb for datagram.
327156813Sru	 */
328156813Sru	in6p = in6_pcblookup_hash(&udbinfo, &ip6->ip6_src, uh->uh_sport,
329221266Sbz				  &ip6->ip6_dst, uh->uh_dport, 1,
330156813Sru				  m->m_pkthdr.rcvif);
331156813Sru	if (in6p == 0) {
332172832Sru		if (log_in_vain) {
333156813Sru			char buf[INET6_ADDRSTRLEN];
334183242Ssam
335156813Sru			strcpy(buf, ip6_sprintf(&ip6->ip6_dst));
336183242Ssam			log(LOG_INFO,
337156813Sru			    "Connection attempt to UDP [%s]:%d from [%s]:%d\n",
338170644Ssepotvin			    buf, ntohs(uh->uh_dport),
339183242Ssam			    ip6_sprintf(&ip6->ip6_src), ntohs(uh->uh_sport));
340157115Sru		}
341156813Sru		udpstat.udps_noport++;
342156813Sru		if (m->m_flags & M_MCAST) {
343156813Sru			printf("UDP6: M_MCAST is set in a unicast packet.\n");
344183242Ssam			udpstat.udps_noportmcast++;
345156813Sru			goto bad;
346183242Ssam		}
347156813Sru		icmp6_error(m, ICMP6_DST_UNREACH, ICMP6_DST_UNREACH_NOPORT, 0);
348183242Ssam		return IPPROTO_DONE;
349156813Sru	}
350156905Sru#if defined(IPSEC) || defined(FAST_IPSEC)
351183242Ssam	/*
352156813Sru	 * Check AH/ESP integrity.
353183242Ssam	 */
354156813Sru	if (ipsec6_in_reject(m, in6p)) {
355156813Sru#ifdef IPSEC
356156813Sru		ipsec6stat.in_polvio++;
357158115Sume#endif /* IPSEC */
358183242Ssam		goto bad;
359156813Sru	}
360156813Sru#endif /*IPSEC || FAST_IPSEC*/
361156813Sru
362156813Sru	/*
363183242Ssam	 * Construct sockaddr format source address.
364183242Ssam	 * Stuff source address and datagram in user buffer.
365183242Ssam	 */
366183242Ssam	init_sin6(&fromsa, m);
367156869Sru	fromsa.sin6_port = uh->uh_sport;
368183242Ssam	if (in6p->in6p_flags & IN6P_CONTROLOPTS
369156813Sru	    || in6p->in6p_socket->so_options & SO_TIMESTAMP)
370156813Sru		ip6_savecontrol(in6p, m, &opts);
371156813Sru	m_adj(m, off + sizeof(struct udphdr));
372183242Ssam	if (sbappendaddr(&in6p->in6p_socket->so_rcv,
373156813Sru			(struct sockaddr *)&fromsa, m, opts) == 0) {
374156813Sru		udpstat.udps_fullsock++;
375156813Sru		goto bad;
376169724Skan	}
377183242Ssam	sorwakeup(in6p->in6p_socket);
378169524Sdeischen	return IPPROTO_DONE;
379156813Srubad:
380156813Sru	if (m)
381183242Ssam		m_freem(m);
382183242Ssam	if (opts)
383156813Sru		m_freem(opts);
384156813Sru	return IPPROTO_DONE;
385183242Ssam}
386168409Spjd
387175617Sruvoid
388175617Sruudp6_ctlinput(cmd, sa, d)
389220359Simp	int cmd;
390220359Simp	struct sockaddr *sa;
391220359Simp	void *d;
392220359Simp{
393220359Simp	struct udphdr uh;
394220359Simp	struct ip6_hdr *ip6;
395220359Simp	struct mbuf *m;
396220359Simp	int off = 0;
397220359Simp	struct ip6ctlparam *ip6cp = NULL;
398220359Simp	const struct sockaddr_in6 *sa6_src = NULL;
399220359Simp	void *cmdarg;
400220359Simp	struct inpcb *(*notify) __P((struct inpcb *, int)) = udp_notify;
401220359Simp	struct udp_portonly {
402220359Simp		u_int16_t uh_sport;
403220359Simp		u_int16_t uh_dport;
404220359Simp	} *uhp;
405220359Simp
406220359Simp	if (sa->sa_family != AF_INET6 ||
407220359Simp	    sa->sa_len != sizeof(struct sockaddr_in6))
408220359Simp		return;
409220359Simp
410220359Simp	if ((unsigned)cmd >= PRC_NCMDS)
411220359Simp		return;
412220359Simp	if (PRC_IS_REDIRECT(cmd))
413220359Simp		notify = in6_rtchange, d = NULL;
414220359Simp	else if (cmd == PRC_HOSTDEAD)
415220359Simp		d = NULL;
416220359Simp	else if (inet6ctlerrmap[cmd] == 0)
417220359Simp		return;
418220359Simp
419220359Simp	/* if the parameter is from icmp6, decode it. */
420220359Simp	if (d != NULL) {
421220359Simp		ip6cp = (struct ip6ctlparam *)d;
422220359Simp		m = ip6cp->ip6c_m;
423220359Simp		ip6 = ip6cp->ip6c_ip6;
424220359Simp		off = ip6cp->ip6c_off;
425220359Simp		cmdarg = ip6cp->ip6c_cmdarg;
426220359Simp		sa6_src = ip6cp->ip6c_src;
427220359Simp	} else {
428220359Simp		m = NULL;
429220359Simp		ip6 = NULL;
430220359Simp		cmdarg = NULL;
431220359Simp		sa6_src = &sa6_any;
432220359Simp	}
433220359Simp
434156813Sru	if (ip6) {
435156813Sru		/*
436156813Sru		 * XXX: We assume that when IPV6 is non NULL,
437156813Sru		 * M and OFF are valid.
438156813Sru		 */
439156813Sru
440156813Sru		/* check if we can safely examine src and dst ports */
441156813Sru		if (m->m_pkthdr.len < off + sizeof(*uhp))
442156813Sru			return;
443156813Sru
444156813Sru		bzero(&uh, sizeof(uh));
445156813Sru		m_copydata(m, off, sizeof(*uhp), (caddr_t)&uh);
446220359Simp
447156813Sru		(void) in6_pcbnotify(&udbinfo, sa, uh.uh_dport,
448156813Sru				     (struct sockaddr *)ip6cp->ip6c_src,
449156813Sru				     uh.uh_sport, cmd, cmdarg, notify);
450156813Sru	} else
451220359Simp		(void) in6_pcbnotify(&udbinfo, sa, 0,
452156813Sru				     (const struct sockaddr *)sa6_src,
453156813Sru				     0, cmd, cmdarg, notify);
454156813Sru}
455156813Sru
456156813Srustatic int
457156813Sruudp6_getcred(SYSCTL_HANDLER_ARGS)
458156813Sru{
459156813Sru	struct xucred xuc;
460156813Sru	struct sockaddr_in6 addrs[2];
461156813Sru	struct inpcb *inp;
462156813Sru	int error, s;
463156813Sru
464220359Simp	error = suser(req->td);
465156813Sru	if (error)
466156813Sru		return (error);
467156813Sru
468172571Sru	if (req->newlen != sizeof(addrs))
469156813Sru		return (EINVAL);
470172571Sru	if (req->oldlen != sizeof(struct xucred))
471172571Sru		return (EINVAL);
472172571Sru	error = SYSCTL_IN(req, addrs, sizeof(addrs));
473172571Sru	if (error)
474177714Sru		return (error);
475172571Sru	s = splnet();
476172571Sru	inp = in6_pcblookup_hash(&udbinfo, &addrs[1].sin6_addr,
477172571Sru				 addrs[1].sin6_port,
478156813Sru				 &addrs[0].sin6_addr, addrs[0].sin6_port,
479156813Sru				 1, NULL);
480156813Sru	if (!inp || !inp->inp_socket) {
481156813Sru		error = ENOENT;
482156813Sru		goto out;
483156813Sru	}
484156813Sru	cru2x(inp->inp_socket->so_cred, &xuc);
485156813Sru	error = SYSCTL_OUT(req, &xuc, sizeof(struct xucred));
486156813Sruout:
487156813Sru	splx(s);
488157378Sphk	return (error);
489157378Sphk}
490157378Sphk
491157378SphkSYSCTL_PROC(_net_inet6_udp6, OID_AUTO, getcred, CTLTYPE_OPAQUE|CTLFLAG_RW,
492168409Spjd	    0, 0,
493168409Spjd	    udp6_getcred, "S,xucred", "Get the xucred of a UDP6 connection");
494168409Spjd
495168409Spjdstatic int
496156813Sruudp6_abort(struct socket *so)
497156813Sru{
498156813Sru	struct inpcb *inp;
499156813Sru	int s;
500156813Sru
501156813Sru	inp = sotoinpcb(so);
502220401Suqs	if (inp == 0)
503220401Suqs		return EINVAL;	/* ??? possible? panic instead? */
504220401Suqs	soisdisconnected(so);
505220401Suqs	s = splnet();
506220401Suqs	in6_pcbdetach(inp);
507156905Sru	splx(s);
508156905Sru	return 0;
509156905Sru}
510156905Sru
511183242Ssamstatic int
512183242Ssamudp6_attach(struct socket *so, int proto, struct thread *td)
513183242Ssam{
514183242Ssam	struct inpcb *inp;
515183242Ssam	int s, error;
516202440Santoine
517202440Santoine	INP_INFO_WLOCK(&udbinfo);
518202440Santoine	inp = sotoinpcb(so);
519202440Santoine	if (inp != 0) {
520202440Santoine		INP_INFO_WUNLOCK(&udbinfo);
521156813Sru		return EINVAL;
522156813Sru	}
523156813Sru
524156813Sru	if (so->so_snd.sb_hiwat == 0 || so->so_rcv.sb_hiwat == 0) {
525156813Sru		error = soreserve(so, udp_sendspace, udp_recvspace);
526156813Sru		if (error) {
527156813Sru			INP_INFO_WUNLOCK(&udbinfo);
528156813Sru			return error;
529156813Sru		}
530183242Ssam	}
531183242Ssam	s = splnet();
532183242Ssam	error = in_pcballoc(so, &udbinfo, "udp6inp");
533183242Ssam	splx(s);
534156813Sru	if (error) {
535208964Srdivacky		INP_INFO_WUNLOCK(&udbinfo);
536156813Sru		return error;
537156813Sru	}
538156813Sru	inp = (struct inpcb *)so->so_pcb;
539156813Sru	INP_LOCK(inp);
540156813Sru	INP_INFO_WUNLOCK(&udbinfo);
541156813Sru	inp->inp_vflag |= INP_IPV6;
542156813Sru	if (!ip6_v6only)
543156813Sru		inp->inp_vflag |= INP_IPV4;
544156813Sru	inp->in6p_hops = -1;	/* use kernel default */
545156813Sru	inp->in6p_cksum = -1;	/* just to be sure */
546156813Sru	/*
547156813Sru	 * XXX: ugly!!
548166255Sdelphij	 * IPv4 TTL initialization is necessary for an IPv6 socket as well,
549156813Sru	 * because the socket may be bound to an IPv6 wildcard address,
550221266Sbz	 * which may match an IPv4-mapped IPv6 address.
551156813Sru	 */
552156813Sru	inp->inp_ip_ttl = ip_defttl;
553156813Sru	INP_UNLOCK(inp);
554170644Ssepotvin	return 0;
555183242Ssam}
556183242Ssam
557183242Ssamstatic int
558156813Sruudp6_bind(struct socket *so, struct sockaddr *nam, struct thread *td)
559156813Sru{
560156813Sru	struct inpcb *inp;
561156813Sru	int s, error;
562156813Sru
563156813Sru	INP_INFO_WLOCK(&udbinfo);
564156813Sru	inp = sotoinpcb(so);
565156813Sru	if (inp == 0) {
566156813Sru		INP_INFO_WUNLOCK(&udbinfo);
567156813Sru		return EINVAL;
568156813Sru	}
569156813Sru	INP_LOCK(inp);
570174548Sru
571174548Sru	inp->inp_vflag &= ~INP_IPV4;
572174548Sru	inp->inp_vflag |= INP_IPV6;
573174548Sru	if ((inp->inp_flags & IN6P_IPV6_V6ONLY) == 0) {
574174548Sru		struct sockaddr_in6 *sin6_p;
575208320Sjkim
576208320Sjkim		sin6_p = (struct sockaddr_in6 *)nam;
577174548Sru
578174548Sru		if (IN6_IS_ADDR_UNSPECIFIED(&sin6_p->sin6_addr))
579174548Sru			inp->inp_vflag |= INP_IPV4;
580174548Sru		else if (IN6_IS_ADDR_V4MAPPED(&sin6_p->sin6_addr)) {
581174548Sru			struct sockaddr_in sin;
582174548Sru
583174548Sru			in6_sin6_2_sin(&sin, sin6_p);
584174548Sru			inp->inp_vflag |= INP_IPV4;
585174548Sru			inp->inp_vflag &= ~INP_IPV6;
586174548Sru			s = splnet();
587174548Sru			error = in_pcbbind(inp, (struct sockaddr *)&sin,
588174548Sru			    td->td_ucred);
589174548Sru			goto out;
590174548Sru		}
591174548Sru	}
592164411Sru
593156813Sru	s = splnet();
594144893Sharti	error = in6_pcbbind(inp, nam, td->td_ucred);
595out:
596	INP_UNLOCK(inp);
597	INP_INFO_WUNLOCK(&udbinfo);
598	splx(s);
599	return error;
600}
601
602static int
603udp6_connect(struct socket *so, struct sockaddr *nam, struct thread *td)
604{
605	struct inpcb *inp;
606	int s, error;
607
608	INP_INFO_WLOCK(&udbinfo);
609	inp = sotoinpcb(so);
610	if (inp == 0) {
611		INP_INFO_WUNLOCK(&udbinfo);
612		return EINVAL;
613	}
614	INP_LOCK(inp);
615
616	if ((inp->inp_flags & IN6P_IPV6_V6ONLY) == 0) {
617		struct sockaddr_in6 *sin6_p;
618
619		sin6_p = (struct sockaddr_in6 *)nam;
620		if (IN6_IS_ADDR_V4MAPPED(&sin6_p->sin6_addr)) {
621			struct sockaddr_in sin;
622
623			if (inp->inp_faddr.s_addr != INADDR_ANY)
624				return EISCONN;
625			in6_sin6_2_sin(&sin, sin6_p);
626			s = splnet();
627			error = in_pcbconnect(inp, (struct sockaddr *)&sin,
628			    td->td_ucred);
629			splx(s);
630			if (error == 0) {
631				inp->inp_vflag |= INP_IPV4;
632				inp->inp_vflag &= ~INP_IPV6;
633				soisconnected(so);
634			}
635			goto out;
636		}
637	}
638	if (!IN6_IS_ADDR_UNSPECIFIED(&inp->in6p_faddr)) {
639		error = EISCONN;
640		goto out;
641	}
642	s = splnet();
643	error = in6_pcbconnect(inp, nam, td->td_ucred);
644	splx(s);
645	if (error == 0) {
646		if (!ip6_v6only) { /* should be non mapped addr */
647			inp->inp_vflag &= ~INP_IPV4;
648			inp->inp_vflag |= INP_IPV6;
649		}
650		soisconnected(so);
651	}
652out:
653	INP_UNLOCK(inp);
654	INP_INFO_WUNLOCK(&udbinfo);
655	return error;
656}
657
658static int
659udp6_detach(struct socket *so)
660{
661	struct inpcb *inp;
662	int s;
663
664	INP_INFO_WLOCK(&udbinfo);
665	inp = sotoinpcb(so);
666	if (inp == 0) {
667		INP_INFO_WUNLOCK(&udbinfo);
668		return EINVAL;
669	}
670	INP_LOCK(inp);
671	s = splnet();
672	in6_pcbdetach(inp);
673	splx(s);
674	INP_INFO_WUNLOCK(&udbinfo);
675	return 0;
676}
677
678static int
679udp6_disconnect(struct socket *so)
680{
681	struct inpcb *inp;
682	int error, s;
683
684	INP_INFO_WLOCK(&udbinfo);
685	inp = sotoinpcb(so);
686	if (inp == 0) {
687		INP_INFO_WUNLOCK(&udbinfo);
688		return EINVAL;
689	}
690	INP_LOCK(inp);
691
692#ifdef INET
693	if (inp->inp_vflag & INP_IPV4) {
694		struct pr_usrreqs *pru;
695
696		pru = inetsw[ip_protox[IPPROTO_UDP]].pr_usrreqs;
697		error = (*pru->pru_disconnect)(so);
698		goto out;
699	}
700#endif
701
702	if (IN6_IS_ADDR_UNSPECIFIED(&inp->in6p_faddr)) {
703		error = ENOTCONN;
704		goto out;
705	}
706
707	s = splnet();
708	in6_pcbdisconnect(inp);
709	inp->in6p_laddr = in6addr_any;
710	splx(s);
711	/* XXXRW: so_state locking? */
712	so->so_state &= ~SS_ISCONNECTED;		/* XXX */
713out:
714	INP_UNLOCK(inp);
715	INP_INFO_WUNLOCK(&udbinfo);
716	return 0;
717}
718
719static int
720udp6_send(struct socket *so, int flags, struct mbuf *m, struct sockaddr *addr,
721	  struct mbuf *control, struct thread *td)
722{
723	struct inpcb *inp;
724	int error = 0;
725
726	INP_INFO_WLOCK(&udbinfo);
727	inp = sotoinpcb(so);
728	if (inp == 0) {
729		INP_INFO_WUNLOCK(&udbinfo);
730		m_freem(m);
731		return EINVAL;
732	}
733	INP_LOCK(inp);
734
735	if (addr) {
736		if (addr->sa_len != sizeof(struct sockaddr_in6)) {
737			error = EINVAL;
738			goto bad;
739		}
740		if (addr->sa_family != AF_INET6) {
741			error = EAFNOSUPPORT;
742			goto bad;
743		}
744	}
745
746#ifdef INET
747	if (!ip6_v6only) {
748		int hasv4addr;
749		struct sockaddr_in6 *sin6 = 0;
750
751		if (addr == 0)
752			hasv4addr = (inp->inp_vflag & INP_IPV4);
753		else {
754			sin6 = (struct sockaddr_in6 *)addr;
755			hasv4addr = IN6_IS_ADDR_V4MAPPED(&sin6->sin6_addr)
756				? 1 : 0;
757		}
758		if (hasv4addr) {
759			struct pr_usrreqs *pru;
760
761			if ((inp->inp_flags & IN6P_IPV6_V6ONLY)) {
762				/*
763				 * since a user of this socket set the
764				 * IPV6_V6ONLY flag, we discard this
765				 * datagram destined to a v4 addr.
766				 */
767				error = EINVAL;
768				goto out;
769			}
770			if (!IN6_IS_ADDR_UNSPECIFIED(&inp->in6p_laddr) &&
771			    !IN6_IS_ADDR_V4MAPPED(&inp->in6p_laddr)) {
772				/*
773				 * when remote addr is IPv4-mapped
774				 * address, local addr should not be
775				 * an IPv6 address; since you cannot
776				 * determine how to map IPv6 source
777				 * address to IPv4.
778				 */
779				error = EINVAL;
780				goto out;
781			}
782			if (sin6)
783				in6_sin6_2_sin_in_sock(addr);
784			pru = inetsw[ip_protox[IPPROTO_UDP]].pr_usrreqs;
785			error = ((*pru->pru_send)(so, flags, m, addr, control,
786						  td));
787			/* addr will just be freed in sendit(). */
788			goto out;
789		}
790	}
791#endif
792
793	error = udp6_output(inp, m, addr, control, td);
794out:
795	INP_UNLOCK(inp);
796	INP_INFO_WUNLOCK(&udbinfo);
797	return error;
798
799  bad:
800	INP_UNLOCK(inp);
801	INP_INFO_WUNLOCK(&udbinfo);
802	m_freem(m);
803	return (error);
804}
805
806struct pr_usrreqs udp6_usrreqs = {
807	udp6_abort, pru_accept_notsupp, udp6_attach, udp6_bind, udp6_connect,
808	pru_connect2_notsupp, in6_control, udp6_detach, udp6_disconnect,
809	pru_listen_notsupp, in6_mapped_peeraddr, pru_rcvd_notsupp,
810	pru_rcvoob_notsupp, udp6_send, pru_sense_null, udp_shutdown,
811	in6_mapped_sockaddr, sosend, soreceive, sopoll, in_pcbsosetlabel
812};
813