raw_ip6.c revision 171167
1219820Sjeff/*-
2219820Sjeff * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project.
3219820Sjeff * All rights reserved.
4219820Sjeff *
5219820Sjeff * Redistribution and use in source and binary forms, with or without
6219820Sjeff * modification, are permitted provided that the following conditions
7219820Sjeff * are met:
8219820Sjeff * 1. Redistributions of source code must retain the above copyright
9219820Sjeff *    notice, this list of conditions and the following disclaimer.
10219820Sjeff * 2. Redistributions in binary form must reproduce the above copyright
11219820Sjeff *    notice, this list of conditions and the following disclaimer in the
12219820Sjeff *    documentation and/or other materials provided with the distribution.
13219820Sjeff * 3. Neither the name of the project nor the names of its contributors
14219820Sjeff *    may be used to endorse or promote products derived from this software
15219820Sjeff *    without specific prior written permission.
16219820Sjeff *
17219820Sjeff * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND
18219820Sjeff * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19219820Sjeff * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20219820Sjeff * ARE DISCLAIMED.  IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE
21219820Sjeff * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22219820Sjeff * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23219820Sjeff * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24219820Sjeff * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25219820Sjeff * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26219820Sjeff * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27219820Sjeff * SUCH DAMAGE.
28219820Sjeff *
29219820Sjeff * $FreeBSD: head/sys/netinet6/raw_ip6.c 171167 2007-07-03 12:13:45Z gnn $
30219820Sjeff */
31219820Sjeff
32219820Sjeff/*-
33219820Sjeff * Copyright (c) 1982, 1986, 1988, 1993
34219820Sjeff *	The Regents of the University of California.  All rights reserved.
35219820Sjeff *
36219820Sjeff * Redistribution and use in source and binary forms, with or without
37219820Sjeff * modification, are permitted provided that the following conditions
38219820Sjeff * are met:
39219820Sjeff * 1. Redistributions of source code must retain the above copyright
40219820Sjeff *    notice, this list of conditions and the following disclaimer.
41219820Sjeff * 2. Redistributions in binary form must reproduce the above copyright
42219820Sjeff *    notice, this list of conditions and the following disclaimer in the
43219820Sjeff *    documentation and/or other materials provided with the distribution.
44219820Sjeff * 4. Neither the name of the University nor the names of its contributors
45219820Sjeff *    may be used to endorse or promote products derived from this software
46219820Sjeff *    without specific prior written permission.
47219820Sjeff *
48219820Sjeff * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
49219820Sjeff * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
50219820Sjeff * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
51219820Sjeff * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
52219820Sjeff * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
53219820Sjeff * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
54219820Sjeff * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
55219820Sjeff * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
56219820Sjeff * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
57219820Sjeff * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
58219820Sjeff * SUCH DAMAGE.
59219820Sjeff *
60219820Sjeff *	@(#)raw_ip.c	8.2 (Berkeley) 1/4/94
61219820Sjeff */
62219820Sjeff
63219820Sjeff#include "opt_ipsec.h"
64219820Sjeff#include "opt_inet6.h"
65219820Sjeff
66219820Sjeff#include <sys/param.h>
67219820Sjeff#include <sys/errno.h>
68219820Sjeff#include <sys/lock.h>
69219820Sjeff#include <sys/malloc.h>
70219820Sjeff#include <sys/mbuf.h>
71219820Sjeff#include <sys/priv.h>
72219820Sjeff#include <sys/proc.h>
73219820Sjeff#include <sys/protosw.h>
74219820Sjeff#include <sys/signalvar.h>
75219820Sjeff#include <sys/socket.h>
76219820Sjeff#include <sys/socketvar.h>
77219820Sjeff#include <sys/sx.h>
78219820Sjeff#include <sys/syslog.h>
79219820Sjeff
80219820Sjeff#include <net/if.h>
81219820Sjeff#include <net/if_types.h>
82219820Sjeff#include <net/route.h>
83219820Sjeff
84219820Sjeff#include <netinet/in.h>
85219820Sjeff#include <netinet/in_var.h>
86219820Sjeff#include <netinet/in_systm.h>
87219820Sjeff#include <netinet/icmp6.h>
88219820Sjeff#include <netinet/in_pcb.h>
89219820Sjeff#include <netinet/ip6.h>
90219820Sjeff#include <netinet6/ip6protosw.h>
91219820Sjeff#include <netinet6/ip6_mroute.h>
92219820Sjeff#include <netinet6/in6_pcb.h>
93219820Sjeff#include <netinet6/ip6_var.h>
94219820Sjeff#include <netinet6/nd6.h>
95219820Sjeff#include <netinet6/raw_ip6.h>
96219820Sjeff#include <netinet6/scope6_var.h>
97219820Sjeff
98219820Sjeff#ifdef IPSEC
99219820Sjeff#include <netipsec/ipsec.h>
100219820Sjeff#include <netipsec/ipsec6.h>
101219820Sjeff#endif /* IPSEC */
102219820Sjeff
103219820Sjeff#include <machine/stdarg.h>
104219820Sjeff
105219820Sjeff#define	satosin6(sa)	((struct sockaddr_in6 *)(sa))
106219820Sjeff#define	ifatoia6(ifa)	((struct in6_ifaddr *)(ifa))
107219820Sjeff
108219820Sjeff/*
109219820Sjeff * Raw interface to IP6 protocol.
110219820Sjeff */
111219820Sjeff
112219820Sjeffextern struct	inpcbhead ripcb;
113219820Sjeffextern struct	inpcbinfo ripcbinfo;
114219820Sjeffextern u_long	rip_sendspace;
115219820Sjeffextern u_long	rip_recvspace;
116219820Sjeff
117219820Sjeffstruct rip6stat rip6stat;
118219820Sjeff
119219820Sjeff/*
120219820Sjeff * Hooks for multicast forwarding.
121219820Sjeff */
122219820Sjeffstruct socket *ip6_mrouter = NULL;
123219820Sjeffint (*ip6_mrouter_set)(struct socket *, struct sockopt *);
124219820Sjeffint (*ip6_mrouter_get)(struct socket *, struct sockopt *);
125219820Sjeffint (*ip6_mrouter_done)(void);
126219820Sjeffint (*ip6_mforward)(struct ip6_hdr *, struct ifnet *, struct mbuf *);
127219820Sjeffint (*mrt6_ioctl)(int, caddr_t);
128219820Sjeff
129219820Sjeff/*
130219820Sjeff * Setup generic address and protocol structures
131219820Sjeff * for raw_input routine, then pass them along with
132219820Sjeff * mbuf chain.
133219820Sjeff */
134219820Sjeffint
135219820Sjeffrip6_input(mp, offp, proto)
136219820Sjeff	struct	mbuf **mp;
137219820Sjeff	int	*offp, proto;
138219820Sjeff{
139219820Sjeff	struct mbuf *m = *mp;
140219820Sjeff	register struct ip6_hdr *ip6 = mtod(m, struct ip6_hdr *);
141219820Sjeff	register struct inpcb *in6p;
142219820Sjeff	struct inpcb *last = 0;
143219820Sjeff	struct mbuf *opts = NULL;
144219820Sjeff	struct sockaddr_in6 fromsa;
145219820Sjeff
146219820Sjeff	rip6stat.rip6s_ipackets++;
147219820Sjeff
148219820Sjeff	if (faithprefix_p != NULL && (*faithprefix_p)(&ip6->ip6_dst)) {
149219820Sjeff		/* XXX send icmp6 host/port unreach? */
150219820Sjeff		m_freem(m);
151219820Sjeff		return IPPROTO_DONE;
152219820Sjeff	}
153219820Sjeff
154219820Sjeff	init_sin6(&fromsa, m); /* general init */
155219820Sjeff
156219820Sjeff	INP_INFO_RLOCK(&ripcbinfo);
157219820Sjeff	LIST_FOREACH(in6p, &ripcb, inp_list) {
158219820Sjeff		INP_LOCK(in6p);
159219820Sjeff		if ((in6p->in6p_vflag & INP_IPV6) == 0) {
160219820Sjeffdocontinue:
161219820Sjeff			INP_UNLOCK(in6p);
162219820Sjeff			continue;
163219820Sjeff		}
164219820Sjeff		if (in6p->in6p_ip6_nxt &&
165219820Sjeff		    in6p->in6p_ip6_nxt != proto)
166219820Sjeff			goto docontinue;
167219820Sjeff		if (!IN6_IS_ADDR_UNSPECIFIED(&in6p->in6p_laddr) &&
168219820Sjeff		    !IN6_ARE_ADDR_EQUAL(&in6p->in6p_laddr, &ip6->ip6_dst))
169219820Sjeff			goto docontinue;
170219820Sjeff		if (!IN6_IS_ADDR_UNSPECIFIED(&in6p->in6p_faddr) &&
171219820Sjeff		    !IN6_ARE_ADDR_EQUAL(&in6p->in6p_faddr, &ip6->ip6_src))
172219820Sjeff			goto docontinue;
173219820Sjeff		if (in6p->in6p_cksum != -1) {
174219820Sjeff			rip6stat.rip6s_isum++;
175219820Sjeff			if (in6_cksum(m, proto, *offp,
176219820Sjeff			    m->m_pkthdr.len - *offp)) {
177219820Sjeff				rip6stat.rip6s_badsum++;
178219820Sjeff				goto docontinue;
179219820Sjeff			}
180219820Sjeff		}
181219820Sjeff		if (last) {
182219820Sjeff			struct mbuf *n = m_copy(m, 0, (int)M_COPYALL);
183219820Sjeff
184219820Sjeff#ifdef IPSEC
185219820Sjeff			/*
186219820Sjeff			 * Check AH/ESP integrity.
187219820Sjeff			 */
188219820Sjeff			if (n && ipsec6_in_reject(n, last)) {
189219820Sjeff				m_freem(n);
190219820Sjeff				ipsec6stat.in_polvio++;
191219820Sjeff				/* do not inject data into pcb */
192219820Sjeff			} else
193219820Sjeff#endif /* IPSEC */
194219820Sjeff			if (n) {
195219820Sjeff				if (last->in6p_flags & IN6P_CONTROLOPTS ||
196219820Sjeff				    last->in6p_socket->so_options & SO_TIMESTAMP)
197219820Sjeff					ip6_savecontrol(last, n, &opts);
198219820Sjeff				/* strip intermediate headers */
199219820Sjeff				m_adj(n, *offp);
200219820Sjeff				if (sbappendaddr(&last->in6p_socket->so_rcv,
201219820Sjeff						(struct sockaddr *)&fromsa,
202219820Sjeff						 n, opts) == 0) {
203219820Sjeff					m_freem(n);
204219820Sjeff					if (opts)
205219820Sjeff						m_freem(opts);
206219820Sjeff					rip6stat.rip6s_fullsock++;
207219820Sjeff				} else
208219820Sjeff					sorwakeup(last->in6p_socket);
209219820Sjeff				opts = NULL;
210219820Sjeff			}
211219820Sjeff			INP_UNLOCK(last);
212219820Sjeff		}
213219820Sjeff		last = in6p;
214219820Sjeff	}
215219820Sjeff#ifdef IPSEC
216219820Sjeff	/*
217219820Sjeff	 * Check AH/ESP integrity.
218219820Sjeff	 */
219219820Sjeff	if (last && ipsec6_in_reject(m, last)) {
220219820Sjeff		m_freem(m);
221219820Sjeff		ipsec6stat.in_polvio++;
222219820Sjeff		ip6stat.ip6s_delivered--;
223219820Sjeff		/* do not inject data into pcb */
224219820Sjeff		INP_UNLOCK(last);
225219820Sjeff	} else
226219820Sjeff#endif /* IPSEC */
227219820Sjeff	if (last) {
228219820Sjeff		if (last->in6p_flags & IN6P_CONTROLOPTS ||
229219820Sjeff		    last->in6p_socket->so_options & SO_TIMESTAMP)
230219820Sjeff			ip6_savecontrol(last, m, &opts);
231219820Sjeff		/* strip intermediate headers */
232219820Sjeff		m_adj(m, *offp);
233219820Sjeff		if (sbappendaddr(&last->in6p_socket->so_rcv,
234219820Sjeff				(struct sockaddr *)&fromsa, m, opts) == 0) {
235219820Sjeff			m_freem(m);
236219820Sjeff			if (opts)
237219820Sjeff				m_freem(opts);
238219820Sjeff			rip6stat.rip6s_fullsock++;
239219820Sjeff		} else
240219820Sjeff			sorwakeup(last->in6p_socket);
241219820Sjeff		INP_UNLOCK(last);
242219820Sjeff	} else {
243219820Sjeff		rip6stat.rip6s_nosock++;
244219820Sjeff		if (m->m_flags & M_MCAST)
245219820Sjeff			rip6stat.rip6s_nosockmcast++;
246219820Sjeff		if (proto == IPPROTO_NONE)
247219820Sjeff			m_freem(m);
248219820Sjeff		else {
249219820Sjeff			char *prvnxtp = ip6_get_prevhdr(m, *offp); /* XXX */
250219820Sjeff			icmp6_error(m, ICMP6_PARAM_PROB,
251219820Sjeff				    ICMP6_PARAMPROB_NEXTHEADER,
252219820Sjeff				    prvnxtp - mtod(m, char *));
253219820Sjeff		}
254219820Sjeff		ip6stat.ip6s_delivered--;
255219820Sjeff	}
256219820Sjeff	INP_INFO_RUNLOCK(&ripcbinfo);
257219820Sjeff	return IPPROTO_DONE;
258219820Sjeff}
259219820Sjeff
260219820Sjeffvoid
261219820Sjeffrip6_ctlinput(cmd, sa, d)
262219820Sjeff	int cmd;
263219820Sjeff	struct sockaddr *sa;
264219820Sjeff	void *d;
265219820Sjeff{
266219820Sjeff	struct ip6_hdr *ip6;
267219820Sjeff	struct mbuf *m;
268219820Sjeff	int off = 0;
269219820Sjeff	struct ip6ctlparam *ip6cp = NULL;
270219820Sjeff	const struct sockaddr_in6 *sa6_src = NULL;
271219820Sjeff	void *cmdarg;
272219820Sjeff	struct inpcb *(*notify) __P((struct inpcb *, int)) = in6_rtchange;
273219820Sjeff
274219820Sjeff	if (sa->sa_family != AF_INET6 ||
275219820Sjeff	    sa->sa_len != sizeof(struct sockaddr_in6))
276219820Sjeff		return;
277219820Sjeff
278219820Sjeff	if ((unsigned)cmd >= PRC_NCMDS)
279219820Sjeff		return;
280219820Sjeff	if (PRC_IS_REDIRECT(cmd))
281219820Sjeff		notify = in6_rtchange, d = NULL;
282219820Sjeff	else if (cmd == PRC_HOSTDEAD)
283219820Sjeff		d = NULL;
284219820Sjeff	else if (inet6ctlerrmap[cmd] == 0)
285219820Sjeff		return;
286219820Sjeff
287219820Sjeff	/* if the parameter is from icmp6, decode it. */
288219820Sjeff	if (d != NULL) {
289219820Sjeff		ip6cp = (struct ip6ctlparam *)d;
290219820Sjeff		m = ip6cp->ip6c_m;
291219820Sjeff		ip6 = ip6cp->ip6c_ip6;
292219820Sjeff		off = ip6cp->ip6c_off;
293219820Sjeff		cmdarg = ip6cp->ip6c_cmdarg;
294219820Sjeff		sa6_src = ip6cp->ip6c_src;
295219820Sjeff	} else {
296219820Sjeff		m = NULL;
297219820Sjeff		ip6 = NULL;
298219820Sjeff		cmdarg = NULL;
299219820Sjeff		sa6_src = &sa6_any;
300219820Sjeff	}
301219820Sjeff
302219820Sjeff	(void) in6_pcbnotify(&ripcbinfo, sa, 0,
303219820Sjeff			     (const struct sockaddr *)sa6_src,
304219820Sjeff			     0, cmd, cmdarg, notify);
305219820Sjeff}
306219820Sjeff
307219820Sjeff/*
308219820Sjeff * Generate IPv6 header and pass packet to ip6_output.
309219820Sjeff * Tack on options user may have setup with control call.
310219820Sjeff */
311219820Sjeffint
312219820Sjeff#if __STDC__
313219820Sjeffrip6_output(struct mbuf *m, ...)
314219820Sjeff#else
315219820Sjeffrip6_output(m, va_alist)
316219820Sjeff	struct mbuf *m;
317219820Sjeff	va_dcl
318219820Sjeff#endif
319219820Sjeff{
320219820Sjeff	struct mbuf *control;
321219820Sjeff	struct socket *so;
322219820Sjeff	struct sockaddr_in6 *dstsock;
323219820Sjeff	struct in6_addr *dst;
324219820Sjeff	struct ip6_hdr *ip6;
325219820Sjeff	struct inpcb *in6p;
326219820Sjeff	u_int	plen = m->m_pkthdr.len;
327219820Sjeff	int error = 0;
328219820Sjeff	struct ip6_pktopts opt, *optp;
329219820Sjeff	struct ifnet *oifp = NULL;
330219820Sjeff	int type = 0, code = 0;		/* for ICMPv6 output statistics only */
331219820Sjeff	int priv = 0;
332219820Sjeff	int scope_ambiguous = 0;
333219820Sjeff	struct in6_addr *in6a;
334219820Sjeff	va_list ap;
335219820Sjeff
336219820Sjeff	va_start(ap, m);
337219820Sjeff	so = va_arg(ap, struct socket *);
338219820Sjeff	dstsock = va_arg(ap, struct sockaddr_in6 *);
339219820Sjeff	control = va_arg(ap, struct mbuf *);
340219820Sjeff	va_end(ap);
341219820Sjeff
342219820Sjeff	in6p = sotoin6pcb(so);
343219820Sjeff	INP_LOCK(in6p);
344219820Sjeff
345219820Sjeff	priv = 0;
346219820Sjeff	if (suser_cred(so->so_cred, 0) == 0)
347219820Sjeff		priv = 1;
348219820Sjeff	dst = &dstsock->sin6_addr;
349219820Sjeff	if (control) {
350219820Sjeff		if ((error = ip6_setpktopts(control, &opt,
351219820Sjeff		    in6p->in6p_outputopts, priv, so->so_proto->pr_protocol))
352219820Sjeff		    != 0) {
353219820Sjeff			goto bad;
354219820Sjeff		}
355219820Sjeff		optp = &opt;
356219820Sjeff	} else
357219820Sjeff		optp = in6p->in6p_outputopts;
358219820Sjeff
359219820Sjeff	/*
360219820Sjeff	 * Check and convert scope zone ID into internal form.
361219820Sjeff	 * XXX: we may still need to determine the zone later.
362219820Sjeff	 */
363219820Sjeff	if (!(so->so_state & SS_ISCONNECTED)) {
364219820Sjeff		if (dstsock->sin6_scope_id == 0 && !ip6_use_defzone)
365219820Sjeff			scope_ambiguous = 1;
366219820Sjeff		if ((error = sa6_embedscope(dstsock, ip6_use_defzone)) != 0)
367219820Sjeff			goto bad;
368219820Sjeff	}
369219820Sjeff
370219820Sjeff	/*
371219820Sjeff	 * For an ICMPv6 packet, we should know its type and code
372219820Sjeff	 * to update statistics.
373219820Sjeff	 */
374219820Sjeff	if (so->so_proto->pr_protocol == IPPROTO_ICMPV6) {
375219820Sjeff		struct icmp6_hdr *icmp6;
376219820Sjeff		if (m->m_len < sizeof(struct icmp6_hdr) &&
377219820Sjeff		    (m = m_pullup(m, sizeof(struct icmp6_hdr))) == NULL) {
378219820Sjeff			error = ENOBUFS;
379219820Sjeff			goto bad;
380219820Sjeff		}
381219820Sjeff		icmp6 = mtod(m, struct icmp6_hdr *);
382219820Sjeff		type = icmp6->icmp6_type;
383219820Sjeff		code = icmp6->icmp6_code;
384219820Sjeff	}
385219820Sjeff
386219820Sjeff	M_PREPEND(m, sizeof(*ip6), M_DONTWAIT);
387219820Sjeff	if (m == NULL) {
388219820Sjeff		error = ENOBUFS;
389219820Sjeff		goto bad;
390219820Sjeff	}
391219820Sjeff	ip6 = mtod(m, struct ip6_hdr *);
392219820Sjeff
393219820Sjeff	/*
394219820Sjeff	 * Source address selection.
395219820Sjeff	 */
396219820Sjeff	if ((in6a = in6_selectsrc(dstsock, optp, in6p->in6p_moptions, NULL,
397219820Sjeff	    &in6p->in6p_laddr, &oifp, &error)) == NULL) {
398219820Sjeff		if (error == 0)
399219820Sjeff			error = EADDRNOTAVAIL;
400219820Sjeff		goto bad;
401219820Sjeff	}
402219820Sjeff	ip6->ip6_src = *in6a;
403219820Sjeff
404219820Sjeff	if (oifp && scope_ambiguous) {
405219820Sjeff		/*
406219820Sjeff		 * Application should provide a proper zone ID or the use of
407219820Sjeff		 * default zone IDs should be enabled.  Unfortunately, some
408219820Sjeff		 * applications do not behave as it should, so we need a
409219820Sjeff		 * workaround.  Even if an appropriate ID is not determined
410219820Sjeff		 * (when it's required), if we can determine the outgoing
411219820Sjeff		 * interface. determine the zone ID based on the interface.
412219820Sjeff		 */
413219820Sjeff		error = in6_setscope(&dstsock->sin6_addr, oifp, NULL);
414219820Sjeff		if (error != 0)
415219820Sjeff			goto bad;
416219820Sjeff	}
417219820Sjeff	ip6->ip6_dst = dstsock->sin6_addr;
418219820Sjeff
419219820Sjeff	/* fill in the rest of the IPv6 header fields */
420219820Sjeff	ip6->ip6_flow = (ip6->ip6_flow & ~IPV6_FLOWINFO_MASK) |
421219820Sjeff		(in6p->in6p_flowinfo & IPV6_FLOWINFO_MASK);
422219820Sjeff	ip6->ip6_vfc = (ip6->ip6_vfc & ~IPV6_VERSION_MASK) |
423219820Sjeff		(IPV6_VERSION & IPV6_VERSION_MASK);
424219820Sjeff	/* ip6_plen will be filled in ip6_output, so not fill it here. */
425219820Sjeff	ip6->ip6_nxt = in6p->in6p_ip6_nxt;
426219820Sjeff	ip6->ip6_hlim = in6_selecthlim(in6p, oifp);
427219820Sjeff
428219820Sjeff	if (so->so_proto->pr_protocol == IPPROTO_ICMPV6 ||
429219820Sjeff	    in6p->in6p_cksum != -1) {
430219820Sjeff		struct mbuf *n;
431219820Sjeff		int off;
432219820Sjeff		u_int16_t *p;
433219820Sjeff
434219820Sjeff		/* compute checksum */
435219820Sjeff		if (so->so_proto->pr_protocol == IPPROTO_ICMPV6)
436219820Sjeff			off = offsetof(struct icmp6_hdr, icmp6_cksum);
437219820Sjeff		else
438219820Sjeff			off = in6p->in6p_cksum;
439219820Sjeff		if (plen < off + 1) {
440219820Sjeff			error = EINVAL;
441219820Sjeff			goto bad;
442219820Sjeff		}
443219820Sjeff		off += sizeof(struct ip6_hdr);
444219820Sjeff
445219820Sjeff		n = m;
446219820Sjeff		while (n && n->m_len <= off) {
447219820Sjeff			off -= n->m_len;
448219820Sjeff			n = n->m_next;
449219820Sjeff		}
450219820Sjeff		if (!n)
451219820Sjeff			goto bad;
452219820Sjeff		p = (u_int16_t *)(mtod(n, caddr_t) + off);
453219820Sjeff		*p = 0;
454219820Sjeff		*p = in6_cksum(m, ip6->ip6_nxt, sizeof(*ip6), plen);
455219820Sjeff	}
456219820Sjeff
457219820Sjeff	error = ip6_output(m, optp, NULL, 0, in6p->in6p_moptions, &oifp, in6p);
458219820Sjeff	if (so->so_proto->pr_protocol == IPPROTO_ICMPV6) {
459219820Sjeff		if (oifp)
460219820Sjeff			icmp6_ifoutstat_inc(oifp, type, code);
461219820Sjeff		icmp6stat.icp6s_outhist[type]++;
462219820Sjeff	} else
463219820Sjeff		rip6stat.rip6s_opackets++;
464219820Sjeff
465219820Sjeff	goto freectl;
466219820Sjeff
467219820Sjeff bad:
468219820Sjeff	if (m)
469219820Sjeff		m_freem(m);
470219820Sjeff
471219820Sjeff freectl:
472219820Sjeff	if (control) {
473219820Sjeff		ip6_clearpktopts(&opt, -1);
474219820Sjeff		m_freem(control);
475219820Sjeff	}
476219820Sjeff	INP_UNLOCK(in6p);
477219820Sjeff	return (error);
478219820Sjeff}
479219820Sjeff
480219820Sjeff/*
481219820Sjeff * Raw IPv6 socket option processing.
482219820Sjeff */
483219820Sjeffint
484219820Sjeffrip6_ctloutput(so, sopt)
485219820Sjeff	struct socket *so;
486219820Sjeff	struct sockopt *sopt;
487219820Sjeff{
488219820Sjeff	int error;
489219820Sjeff
490219820Sjeff	if (sopt->sopt_level == IPPROTO_ICMPV6)
491219820Sjeff		/*
492219820Sjeff		 * XXX: is it better to call icmp6_ctloutput() directly
493219820Sjeff		 * from protosw?
494219820Sjeff		 */
495219820Sjeff		return (icmp6_ctloutput(so, sopt));
496219820Sjeff	else if (sopt->sopt_level != IPPROTO_IPV6)
497219820Sjeff		return (EINVAL);
498219820Sjeff
499219820Sjeff	error = 0;
500219820Sjeff
501219820Sjeff	switch (sopt->sopt_dir) {
502219820Sjeff	case SOPT_GET:
503219820Sjeff		switch (sopt->sopt_name) {
504219820Sjeff		case MRT6_INIT:
505219820Sjeff		case MRT6_DONE:
506219820Sjeff		case MRT6_ADD_MIF:
507219820Sjeff		case MRT6_DEL_MIF:
508219820Sjeff		case MRT6_ADD_MFC:
509219820Sjeff		case MRT6_DEL_MFC:
510219820Sjeff		case MRT6_PIM:
511219820Sjeff			error = ip6_mrouter_get ?  ip6_mrouter_get(so, sopt) :
512219820Sjeff			    EOPNOTSUPP;
513219820Sjeff			break;
514219820Sjeff		case IPV6_CHECKSUM:
515219820Sjeff			error = ip6_raw_ctloutput(so, sopt);
516219820Sjeff			break;
517219820Sjeff		default:
518219820Sjeff			error = ip6_ctloutput(so, sopt);
519219820Sjeff			break;
520219820Sjeff		}
521219820Sjeff		break;
522219820Sjeff
523219820Sjeff	case SOPT_SET:
524219820Sjeff		switch (sopt->sopt_name) {
525219820Sjeff		case MRT6_INIT:
526219820Sjeff		case MRT6_DONE:
527219820Sjeff		case MRT6_ADD_MIF:
528219820Sjeff		case MRT6_DEL_MIF:
529219820Sjeff		case MRT6_ADD_MFC:
530219820Sjeff		case MRT6_DEL_MFC:
531219820Sjeff		case MRT6_PIM:
532219820Sjeff			error = ip6_mrouter_set ?  ip6_mrouter_set(so, sopt) :
533219820Sjeff			    EOPNOTSUPP;
534219820Sjeff			break;
535219820Sjeff		case IPV6_CHECKSUM:
536219820Sjeff			error = ip6_raw_ctloutput(so, sopt);
537219820Sjeff			break;
538219820Sjeff		default:
539219820Sjeff			error = ip6_ctloutput(so, sopt);
540219820Sjeff			break;
541219820Sjeff		}
542219820Sjeff		break;
543219820Sjeff	}
544219820Sjeff
545219820Sjeff	return (error);
546219820Sjeff}
547219820Sjeff
548219820Sjeffstatic int
549219820Sjeffrip6_attach(struct socket *so, int proto, struct thread *td)
550219820Sjeff{
551219820Sjeff	struct inpcb *inp;
552219820Sjeff	struct icmp6_filter *filter;
553219820Sjeff	int error;
554219820Sjeff
555219820Sjeff	inp = sotoinpcb(so);
556219820Sjeff	KASSERT(inp == NULL, ("rip6_attach: inp != NULL"));
557219820Sjeff	if (td && (error = suser(td)) != 0)
558219820Sjeff		return error;
559219820Sjeff	error = soreserve(so, rip_sendspace, rip_recvspace);
560219820Sjeff	if (error)
561219820Sjeff		return error;
562219820Sjeff	MALLOC(filter, struct icmp6_filter *,
563219820Sjeff	       sizeof(struct icmp6_filter), M_PCB, M_NOWAIT);
564219820Sjeff	if (filter == NULL)
565219820Sjeff		return ENOMEM;
566219820Sjeff	INP_INFO_WLOCK(&ripcbinfo);
567219820Sjeff	error = in_pcballoc(so, &ripcbinfo);
568219820Sjeff	if (error) {
569219820Sjeff		INP_INFO_WUNLOCK(&ripcbinfo);
570219820Sjeff		FREE(filter, M_PCB);
571219820Sjeff		return error;
572219820Sjeff	}
573219820Sjeff	inp = (struct inpcb *)so->so_pcb;
574219820Sjeff	INP_INFO_WUNLOCK(&ripcbinfo);
575219820Sjeff	inp->inp_vflag |= INP_IPV6;
576219820Sjeff	inp->in6p_ip6_nxt = (long)proto;
577219820Sjeff	inp->in6p_hops = -1;	/* use kernel default */
578219820Sjeff	inp->in6p_cksum = -1;
579219820Sjeff	inp->in6p_icmp6filt = filter;
580219820Sjeff	ICMP6_FILTER_SETPASSALL(inp->in6p_icmp6filt);
581219820Sjeff	INP_UNLOCK(inp);
582219820Sjeff	return 0;
583219820Sjeff}
584219820Sjeff
585219820Sjeffstatic void
586219820Sjeffrip6_detach(struct socket *so)
587219820Sjeff{
588219820Sjeff	struct inpcb *inp;
589219820Sjeff
590219820Sjeff	inp = sotoinpcb(so);
591219820Sjeff	KASSERT(inp != NULL, ("rip6_detach: inp == NULL"));
592219820Sjeff
593219820Sjeff	if (so == ip6_mrouter && ip6_mrouter_done)
594219820Sjeff		ip6_mrouter_done();
595219820Sjeff	/* xxx: RSVP */
596219820Sjeff	INP_INFO_WLOCK(&ripcbinfo);
597219820Sjeff	INP_LOCK(inp);
598219820Sjeff	if (inp->in6p_icmp6filt) {
599219820Sjeff		FREE(inp->in6p_icmp6filt, M_PCB);
600219820Sjeff		inp->in6p_icmp6filt = NULL;
601219820Sjeff	}
602219820Sjeff	in6_pcbdetach(inp);
603219820Sjeff	in6_pcbfree(inp);
604219820Sjeff	INP_INFO_WUNLOCK(&ripcbinfo);
605219820Sjeff}
606219820Sjeff
607219820Sjeff/* XXXRW: This can't ever be called. */
608219820Sjeffstatic void
609219820Sjeffrip6_abort(struct socket *so)
610219820Sjeff{
611219820Sjeff	struct inpcb *inp;
612219820Sjeff
613219820Sjeff	inp = sotoinpcb(so);
614219820Sjeff	KASSERT(inp != NULL, ("rip6_abort: inp == NULL"));
615219820Sjeff
616219820Sjeff	soisdisconnected(so);
617219820Sjeff}
618219820Sjeff
619219820Sjeffstatic void
620219820Sjeffrip6_close(struct socket *so)
621219820Sjeff{
622219820Sjeff	struct inpcb *inp;
623219820Sjeff
624219820Sjeff	inp = sotoinpcb(so);
625219820Sjeff	KASSERT(inp != NULL, ("rip6_close: inp == NULL"));
626219820Sjeff
627219820Sjeff	soisdisconnected(so);
628219820Sjeff}
629219820Sjeff
630219820Sjeffstatic int
631219820Sjeffrip6_disconnect(struct socket *so)
632219820Sjeff{
633219820Sjeff	struct inpcb *inp = sotoinpcb(so);
634219820Sjeff
635219820Sjeff	if ((so->so_state & SS_ISCONNECTED) == 0)
636219820Sjeff		return ENOTCONN;
637219820Sjeff	inp->in6p_faddr = in6addr_any;
638219820Sjeff	rip6_abort(so);
639219820Sjeff	return (0);
640219820Sjeff}
641219820Sjeff
642219820Sjeffstatic int
643219820Sjeffrip6_bind(struct socket *so, struct sockaddr *nam, struct thread *td)
644219820Sjeff{
645219820Sjeff	struct inpcb *inp = sotoinpcb(so);
646219820Sjeff	struct sockaddr_in6 *addr = (struct sockaddr_in6 *)nam;
647219820Sjeff	struct ifaddr *ia = NULL;
648219820Sjeff	int error = 0;
649219820Sjeff
650219820Sjeff	KASSERT(inp != NULL, ("rip6_bind: inp == NULL"));
651219820Sjeff	if (nam->sa_len != sizeof(*addr))
652219820Sjeff		return EINVAL;
653219820Sjeff	if (TAILQ_EMPTY(&ifnet) || addr->sin6_family != AF_INET6)
654219820Sjeff		return EADDRNOTAVAIL;
655219820Sjeff	if ((error = sa6_embedscope(addr, ip6_use_defzone)) != 0)
656219820Sjeff		return(error);
657219820Sjeff
658219820Sjeff	if (!IN6_IS_ADDR_UNSPECIFIED(&addr->sin6_addr) &&
659219820Sjeff	    (ia = ifa_ifwithaddr((struct sockaddr *)addr)) == 0)
660219820Sjeff		return EADDRNOTAVAIL;
661219820Sjeff	if (ia &&
662219820Sjeff	    ((struct in6_ifaddr *)ia)->ia6_flags &
663219820Sjeff	    (IN6_IFF_ANYCAST|IN6_IFF_NOTREADY|
664219820Sjeff	     IN6_IFF_DETACHED|IN6_IFF_DEPRECATED)) {
665219820Sjeff		return (EADDRNOTAVAIL);
666219820Sjeff	}
667219820Sjeff	INP_INFO_WLOCK(&ripcbinfo);
668219820Sjeff	INP_LOCK(inp);
669219820Sjeff	inp->in6p_laddr = addr->sin6_addr;
670219820Sjeff	INP_UNLOCK(inp);
671219820Sjeff	INP_INFO_WUNLOCK(&ripcbinfo);
672219820Sjeff	return 0;
673219820Sjeff}
674219820Sjeff
675219820Sjeffstatic int
676219820Sjeffrip6_connect(struct socket *so, struct sockaddr *nam, struct thread *td)
677219820Sjeff{
678219820Sjeff	struct inpcb *inp = sotoinpcb(so);
679219820Sjeff	struct sockaddr_in6 *addr = (struct sockaddr_in6 *)nam;
680219820Sjeff	struct in6_addr *in6a = NULL;
681219820Sjeff	struct ifnet *ifp = NULL;
682219820Sjeff	int error = 0, scope_ambiguous = 0;
683219820Sjeff
684219820Sjeff	KASSERT(inp != NULL, ("rip6_connect: inp == NULL"));
685219820Sjeff	if (nam->sa_len != sizeof(*addr))
686219820Sjeff		return EINVAL;
687219820Sjeff	if (TAILQ_EMPTY(&ifnet))
688219820Sjeff		return EADDRNOTAVAIL;
689219820Sjeff	if (addr->sin6_family != AF_INET6)
690219820Sjeff		return EAFNOSUPPORT;
691219820Sjeff
692219820Sjeff	/*
693219820Sjeff	 * Application should provide a proper zone ID or the use of
694219820Sjeff	 * default zone IDs should be enabled.  Unfortunately, some
695219820Sjeff	 * applications do not behave as it should, so we need a
696219820Sjeff	 * workaround.  Even if an appropriate ID is not determined,
697219820Sjeff	 * we'll see if we can determine the outgoing interface.  If we
698219820Sjeff	 * can, determine the zone ID based on the interface below.
699219820Sjeff	 */
700219820Sjeff	if (addr->sin6_scope_id == 0 && !ip6_use_defzone)
701219820Sjeff		scope_ambiguous = 1;
702219820Sjeff	if ((error = sa6_embedscope(addr, ip6_use_defzone)) != 0)
703219820Sjeff		return(error);
704219820Sjeff
705219820Sjeff	INP_INFO_WLOCK(&ripcbinfo);
706219820Sjeff	INP_LOCK(inp);
707219820Sjeff	/* Source address selection. XXX: need pcblookup? */
708219820Sjeff	in6a = in6_selectsrc(addr, inp->in6p_outputopts,
709219820Sjeff			     inp->in6p_moptions, NULL,
710219820Sjeff			     &inp->in6p_laddr, &ifp, &error);
711219820Sjeff	if (in6a == NULL) {
712219820Sjeff		INP_UNLOCK(inp);
713219820Sjeff		INP_INFO_WUNLOCK(&ripcbinfo);
714219820Sjeff		return (error ? error : EADDRNOTAVAIL);
715219820Sjeff	}
716219820Sjeff
717219820Sjeff	/* XXX: see above */
718219820Sjeff	if (ifp && scope_ambiguous &&
719219820Sjeff	    (error = in6_setscope(&addr->sin6_addr, ifp, NULL)) != 0) {
720219820Sjeff		INP_UNLOCK(inp);
721219820Sjeff		INP_INFO_WUNLOCK(&ripcbinfo);
722219820Sjeff		return(error);
723219820Sjeff	}
724219820Sjeff	inp->in6p_faddr = addr->sin6_addr;
725219820Sjeff	inp->in6p_laddr = *in6a;
726219820Sjeff	soisconnected(so);
727219820Sjeff	INP_UNLOCK(inp);
728219820Sjeff	INP_INFO_WUNLOCK(&ripcbinfo);
729219820Sjeff	return 0;
730219820Sjeff}
731219820Sjeff
732219820Sjeffstatic int
733219820Sjeffrip6_shutdown(struct socket *so)
734219820Sjeff{
735219820Sjeff	struct inpcb *inp;
736219820Sjeff
737219820Sjeff	inp = sotoinpcb(so);
738219820Sjeff	KASSERT(inp != NULL, ("rip6_shutdown: inp == NULL"));
739219820Sjeff	INP_LOCK(inp);
740219820Sjeff	socantsendmore(so);
741219820Sjeff	INP_UNLOCK(inp);
742219820Sjeff	return 0;
743219820Sjeff}
744219820Sjeff
745219820Sjeffstatic int
746219820Sjeffrip6_send(struct socket *so, int flags, struct mbuf *m, struct sockaddr *nam,
747219820Sjeff	 struct mbuf *control, struct thread *td)
748219820Sjeff{
749219820Sjeff	struct inpcb *inp = sotoinpcb(so);
750219820Sjeff	struct sockaddr_in6 tmp;
751219820Sjeff	struct sockaddr_in6 *dst;
752219820Sjeff	int ret;
753219820Sjeff
754219820Sjeff	KASSERT(inp != NULL, ("rip6_send: inp == NULL"));
755219820Sjeff	INP_INFO_WLOCK(&ripcbinfo);
756219820Sjeff	/* always copy sockaddr to avoid overwrites */
757219820Sjeff	/* Unlocked read. */
758219820Sjeff	if (so->so_state & SS_ISCONNECTED) {
759		if (nam) {
760			INP_INFO_WUNLOCK(&ripcbinfo);
761			m_freem(m);
762			return EISCONN;
763		}
764		/* XXX */
765		bzero(&tmp, sizeof(tmp));
766		tmp.sin6_family = AF_INET6;
767		tmp.sin6_len = sizeof(struct sockaddr_in6);
768		bcopy(&inp->in6p_faddr, &tmp.sin6_addr,
769		      sizeof(struct in6_addr));
770		dst = &tmp;
771	} else {
772		if (nam == NULL) {
773			INP_INFO_WUNLOCK(&ripcbinfo);
774			m_freem(m);
775			return ENOTCONN;
776		}
777		if (nam->sa_len != sizeof(struct sockaddr_in6)) {
778			INP_INFO_WUNLOCK(&ripcbinfo);
779			m_freem(m);
780			return(EINVAL);
781		}
782		tmp = *(struct sockaddr_in6 *)nam;
783		dst = &tmp;
784
785		if (dst->sin6_family == AF_UNSPEC) {
786			/*
787			 * XXX: we allow this case for backward
788			 * compatibility to buggy applications that
789			 * rely on old (and wrong) kernel behavior.
790			 */
791			log(LOG_INFO, "rip6 SEND: address family is "
792			    "unspec. Assume AF_INET6\n");
793			dst->sin6_family = AF_INET6;
794		} else if (dst->sin6_family != AF_INET6) {
795			INP_INFO_WUNLOCK(&ripcbinfo);
796			m_freem(m);
797			return(EAFNOSUPPORT);
798		}
799	}
800	ret = rip6_output(m, so, dst, control);
801	INP_INFO_WUNLOCK(&ripcbinfo);
802	return (ret);
803}
804
805struct pr_usrreqs rip6_usrreqs = {
806	.pru_abort =		rip6_abort,
807	.pru_attach =		rip6_attach,
808	.pru_bind =		rip6_bind,
809	.pru_connect =		rip6_connect,
810	.pru_control =		in6_control,
811	.pru_detach =		rip6_detach,
812	.pru_disconnect =	rip6_disconnect,
813	.pru_peeraddr =		in6_getpeeraddr,
814	.pru_send =		rip6_send,
815	.pru_shutdown =		rip6_shutdown,
816	.pru_sockaddr =		in6_getsockaddr,
817	.pru_close =		rip6_close,
818};
819