raw_ip6.c revision 53541
1/*
2 * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project.
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 *    notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 *    notice, this list of conditions and the following disclaimer in the
12 *    documentation and/or other materials provided with the distribution.
13 * 3. Neither the name of the project nor the names of its contributors
14 *    may be used to endorse or promote products derived from this software
15 *    without specific prior written permission.
16 *
17 * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND
18 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20 * ARE DISCLAIMED.  IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE
21 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27 * SUCH DAMAGE.
28 *
29 * $FreeBSD: head/sys/netinet6/raw_ip6.c 53541 1999-11-22 02:45:11Z shin $
30 */
31
32/*
33 * Copyright (c) 1982, 1986, 1988, 1993
34 *	The Regents of the University of California.  All rights reserved.
35 *
36 * Redistribution and use in source and binary forms, with or without
37 * modification, are permitted provided that the following conditions
38 * are met:
39 * 1. Redistributions of source code must retain the above copyright
40 *    notice, this list of conditions and the following disclaimer.
41 * 2. Redistributions in binary form must reproduce the above copyright
42 *    notice, this list of conditions and the following disclaimer in the
43 *    documentation and/or other materials provided with the distribution.
44 * 3. All advertising materials mentioning features or use of this software
45 *    must display the following acknowledgement:
46 *	This product includes software developed by the University of
47 *	California, Berkeley and its contributors.
48 * 4. Neither the name of the University nor the names of its contributors
49 *    may be used to endorse or promote products derived from this software
50 *    without specific prior written permission.
51 *
52 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
53 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
54 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
55 * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
56 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
57 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
58 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
59 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
60 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
61 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
62 * SUCH DAMAGE.
63 *
64 *	@(#)raw_ip.c	8.2 (Berkeley) 1/4/94
65 */
66
67#include "opt_inet.h"
68#include "opt_ipsec.h"
69
70#include <stddef.h>
71
72#include <sys/param.h>
73#include <sys/malloc.h>
74#include <sys/proc.h>
75#include <sys/mbuf.h>
76#include <sys/socket.h>
77#include <sys/protosw.h>
78#include <sys/socketvar.h>
79#include <sys/errno.h>
80#include <sys/systm.h>
81
82#include <net/if.h>
83#include <net/route.h>
84#include <net/if_types.h>
85
86#include <netinet/in.h>
87#include <netinet/in_var.h>
88#include <netinet/in_systm.h>
89#include <netinet6/ip6.h>
90#include <netinet6/ip6_var.h>
91#include <netinet6/icmp6.h>
92#include <netinet/in_pcb.h>
93#include <netinet6/in6_pcb.h>
94#include <netinet6/nd6.h>
95
96#ifdef IPSEC
97#include <netinet6/ipsec.h>
98#ifdef INET6
99#include <netinet6/ipsec6.h>
100#endif /* INET6 */
101#endif /*IPSEC*/
102
103#include <machine/stdarg.h>
104
105/* #include "faith.h" */
106
107#define	satosin6(sa)	((struct sockaddr_in6 *)(sa))
108#define	ifatoia6(ifa)	((struct in6_ifaddr *)(ifa))
109
110/*
111 * Raw interface to IP6 protocol.
112 */
113
114extern struct	inpcbhead ripcb;
115extern struct	inpcbinfo ripcbinfo;
116extern u_long	rip_sendspace;
117extern u_long	rip_recvspace;
118
119/*
120 * Setup generic address and protocol structures
121 * for raw_input routine, then pass them along with
122 * mbuf chain.
123 */
124int
125rip6_input(mp, offp, proto)
126	struct	mbuf **mp;
127	int	*offp, proto;
128{
129	struct mbuf *m = *mp;
130	register struct ip6_hdr *ip6 = mtod(m, struct ip6_hdr *);
131	register struct inpcb *in6p;
132	struct inpcb *last = 0;
133	struct mbuf *opts = 0;
134	struct sockaddr_in6 rip6src;
135
136#if defined(NFAITH) && 0 < NFAITH
137	if (m->m_pkthdr.rcvif) {
138		if (m->m_pkthdr.rcvif->if_type == IFT_FAITH) {
139			/* XXX send icmp6 host/port unreach? */
140			m_freem(m);
141			return IPPROTO_DONE;
142		}
143	}
144#endif
145	init_sin6(&rip6src, m); /* general init */
146
147	LIST_FOREACH(in6p, &ripcb, inp_list) {
148		if ((in6p->in6p_vflag & INP_IPV6) == NULL)
149			continue;
150		if (in6p->in6p_ip6_nxt &&
151		    in6p->in6p_ip6_nxt != proto)
152			continue;
153		if (!IN6_IS_ADDR_UNSPECIFIED(&in6p->in6p_laddr) &&
154		    !IN6_ARE_ADDR_EQUAL(&in6p->in6p_laddr, &ip6->ip6_dst))
155			continue;
156		if (!IN6_IS_ADDR_UNSPECIFIED(&in6p->in6p_faddr) &&
157		    !IN6_ARE_ADDR_EQUAL(&in6p->in6p_faddr, &ip6->ip6_src))
158			continue;
159		if (in6p->in6p_cksum != -1
160		    && in6_cksum(m, ip6->ip6_nxt, *offp,
161				 m->m_pkthdr.len - *offp)) {
162			/* XXX bark something */
163			continue;
164		}
165		if (last) {
166			struct mbuf *n = m_copy(m, 0, (int)M_COPYALL);
167			if (n) {
168				if (last->in6p_flags & IN6P_CONTROLOPTS ||
169				    last->in6p_socket->so_options & SO_TIMESTAMP)
170					ip6_savecontrol(last, &opts, ip6, n);
171				/* strip intermediate headers */
172				m_adj(n, *offp);
173				if (sbappendaddr(&last->in6p_socket->so_rcv,
174						(struct sockaddr *)&rip6src,
175						 n, opts) == 0) {
176					/* should notify about lost packet */
177					m_freem(n);
178					if (opts)
179						m_freem(opts);
180				} else
181					sorwakeup(last->in6p_socket);
182				opts = NULL;
183			}
184		}
185		last = in6p;
186	}
187	if (last) {
188		if (last->in6p_flags & IN6P_CONTROLOPTS ||
189		    last->in6p_socket->so_options & SO_TIMESTAMP)
190			ip6_savecontrol(last, &opts, ip6, m);
191		/* strip intermediate headers */
192		m_adj(m, *offp);
193		if (sbappendaddr(&last->in6p_socket->so_rcv,
194				(struct sockaddr *)&rip6src, m, opts) == 0) {
195			m_freem(m);
196			if (opts)
197				m_freem(opts);
198		} else
199			sorwakeup(last->in6p_socket);
200	} else {
201		if (proto == IPPROTO_NONE)
202			m_freem(m);
203		else {
204			char *prvnxtp = ip6_get_prevhdr(m, *offp); /* XXX */
205			icmp6_error(m, ICMP6_PARAM_PROB,
206				    ICMP6_PARAMPROB_NEXTHEADER,
207				    prvnxtp - mtod(m, char *));
208		}
209		ip6stat.ip6s_delivered--;
210	}
211	return IPPROTO_DONE;
212}
213
214/*
215 * Generate IPv6 header and pass packet to ip6_output.
216 * Tack on options user may have setup with control call.
217 */
218int
219#if __STDC__
220rip6_output(struct mbuf *m, ...)
221#else
222rip6_output(m, va_alist)
223	struct mbuf *m;
224	va_dcl
225#endif
226{
227	struct socket *so;
228	struct sockaddr_in6 *dstsock;
229	struct mbuf *control;
230	struct in6_addr *dst;
231	struct ip6_hdr *ip6;
232	struct inpcb *in6p;
233	u_int	plen = m->m_pkthdr.len;
234	int error = 0;
235	struct ip6_pktopts opt, *optp = 0;
236	struct ifnet *oifp = NULL;
237	int type = 0, code = 0;		/* for ICMPv6 output statistics only */
238	int priv = 0;
239	va_list ap;
240
241	va_start(ap, m);
242	so = va_arg(ap, struct socket *);
243	dstsock = va_arg(ap, struct sockaddr_in6 *);
244	control = va_arg(ap, struct mbuf *);
245	va_end(ap);
246
247	in6p = sotoin6pcb(so);
248
249	priv = 0;
250	if (so->so_cred->cr_uid == 0)
251		priv = 1;
252	dst = &dstsock->sin6_addr;
253	if (control) {
254		if ((error = ip6_setpktoptions(control, &opt, priv)) != 0)
255			goto bad;
256		optp = &opt;
257	} else
258		optp = in6p->in6p_outputopts;
259
260	/*
261	 * For an ICMPv6 packet, we should know its type and code
262	 * to update statistics.
263	 */
264	if (so->so_proto->pr_protocol == IPPROTO_ICMPV6) {
265		struct icmp6_hdr *icmp6;
266		if (m->m_len < sizeof(struct icmp6_hdr) &&
267		    (m = m_pullup(m, sizeof(struct icmp6_hdr))) == NULL) {
268			error = ENOBUFS;
269			goto bad;
270		}
271		icmp6 = mtod(m, struct icmp6_hdr *);
272		type = icmp6->icmp6_type;
273		code = icmp6->icmp6_code;
274	}
275
276	M_PREPEND(m, sizeof(*ip6), M_WAIT);
277	ip6 = mtod(m, struct ip6_hdr *);
278
279	/*
280	 * Next header might not be ICMP6 but use its pseudo header anyway.
281	 */
282	ip6->ip6_dst = *dst;
283
284	/*
285	 * If the scope of the destination is link-local, embed the interface
286	 * index in the address.
287	 *
288	 * XXX advanced-api value overrides sin6_scope_id
289	 */
290	if (IN6_IS_SCOPE_LINKLOCAL(&ip6->ip6_dst)) {
291		struct in6_pktinfo *pi;
292
293		/*
294		 * XXX Boundary check is assumed to be already done in
295		 * ip6_setpktoptions().
296		 */
297		if (optp && (pi = optp->ip6po_pktinfo) && pi->ipi6_ifindex) {
298			ip6->ip6_dst.s6_addr16[1] = htons(pi->ipi6_ifindex);
299			oifp = ifindex2ifnet[pi->ipi6_ifindex];
300		} else if (IN6_IS_ADDR_MULTICAST(&ip6->ip6_dst) &&
301			 in6p->in6p_moptions &&
302			 in6p->in6p_moptions->im6o_multicast_ifp) {
303			oifp = in6p->in6p_moptions->im6o_multicast_ifp;
304			ip6->ip6_dst.s6_addr16[1] = htons(oifp->if_index);
305		} else if (dstsock->sin6_scope_id) {
306			/* boundary check */
307			if (dstsock->sin6_scope_id < 0
308			 || if_index < dstsock->sin6_scope_id) {
309				error = ENXIO;  /* XXX EINVAL? */
310				goto bad;
311			}
312			ip6->ip6_dst.s6_addr16[1]
313				= htons(dstsock->sin6_scope_id & 0xffff);/*XXX*/
314		}
315	}
316
317	/*
318	 * Source address selection.
319	 */
320	{
321		struct in6_addr *in6a;
322
323		if ((in6a = in6_selectsrc(dstsock, optp,
324					  in6p->in6p_moptions,
325					  &in6p->in6p_route,
326					  &in6p->in6p_laddr,
327					  &error)) == 0) {
328			if (error == 0)
329				error = EADDRNOTAVAIL;
330			goto bad;
331		}
332		ip6->ip6_src = *in6a;
333		if (in6p->in6p_route.ro_rt)
334			oifp = ifindex2ifnet[in6p->in6p_route.ro_rt->rt_ifp->if_index];
335	}
336
337	ip6->ip6_flow = in6p->in6p_flowinfo & IPV6_FLOWINFO_MASK;
338	ip6->ip6_vfc  = IPV6_VERSION;
339	/* ip6_plen will be filled in ip6_output, so not fill it here. */
340	ip6->ip6_nxt = in6p->in6p_ip6_nxt;
341	ip6->ip6_hlim = in6_selecthlim(in6p, oifp);
342
343	if (so->so_proto->pr_protocol == IPPROTO_ICMPV6 ||
344	    in6p->in6p_cksum != -1) {
345		struct mbuf *n;
346		int off;
347		u_int16_t *p;
348
349#define	offsetof(type, member)	((size_t)(&((type *)0)->member)) /* XXX */
350
351		/* compute checksum */
352		if (so->so_proto->pr_protocol == IPPROTO_ICMPV6)
353			off = offsetof(struct icmp6_hdr, icmp6_cksum);
354		else
355			off = in6p->in6p_cksum;
356		if (plen < off + 1) {
357			error = EINVAL;
358			goto bad;
359		}
360		off += sizeof(struct ip6_hdr);
361
362		n = m;
363		while (n && n->m_len <= off) {
364			off -= n->m_len;
365			n = n->m_next;
366		}
367		if (!n)
368			goto bad;
369		p = (u_int16_t *)(mtod(n, caddr_t) + off);
370		*p = 0;
371		*p = in6_cksum(m, ip6->ip6_nxt, sizeof(*ip6), plen);
372	}
373
374#ifdef IPSEC
375	m->m_pkthdr.rcvif = (struct ifnet *)so;
376#endif /*IPSEC*/
377
378	error = ip6_output(m, optp, &in6p->in6p_route, 0, in6p->in6p_moptions,
379			   &oifp);
380	if (so->so_proto->pr_protocol == IPPROTO_ICMPV6) {
381		if (oifp)
382			icmp6_ifoutstat_inc(oifp, type, code);
383		icmp6stat.icp6s_outhist[type]++;
384	}
385
386	goto freectl;
387
388 bad:
389	if (m)
390		m_freem(m);
391
392 freectl:
393	if (optp == &opt && optp->ip6po_rthdr && optp->ip6po_route.ro_rt)
394		RTFREE(optp->ip6po_route.ro_rt);
395	if (control)
396		m_freem(control);
397	return(error);
398}
399
400/*
401 * Raw IPv6 socket option processing.
402 */
403int
404rip6_ctloutput(so, sopt)
405	struct socket *so;
406	struct sockopt *sopt;
407{
408	int error;
409
410	if (sopt->sopt_level == IPPROTO_ICMPV6)
411		/*
412		 * XXX: is it better to call icmp6_ctloutput() directly
413		 * from protosw?
414		 */
415		return(icmp6_ctloutput(so, sopt));
416	else if (sopt->sopt_level != IPPROTO_IPV6)
417		return (EINVAL);
418
419	error = 0;
420
421	switch (sopt->sopt_dir) {
422	case SOPT_GET:
423		switch (sopt->sopt_name) {
424		default:
425			error = ip6_ctloutput(so, sopt);
426			break;
427		}
428		break;
429
430	case SOPT_SET:
431		switch (sopt->sopt_name) {
432		default:
433			error = ip6_ctloutput(so, sopt);
434			break;
435		}
436		break;
437	}
438
439	return (error);
440}
441
442static int
443rip6_attach(struct socket *so, int proto, struct proc *p)
444{
445	struct inpcb *inp;
446	int error, s;
447
448	inp = sotoinpcb(so);
449	if (inp)
450		panic("rip6_attach");
451	if (p && (error = suser(p)) != 0)
452		return error;
453
454	if (so->so_snd.sb_hiwat == 0 || so->so_rcv.sb_hiwat == 0) {
455		error = soreserve(so, rip_sendspace, rip_recvspace);
456		if (error)
457			return error;
458	}
459	s = splnet();
460	error = in_pcballoc(so, &ripcbinfo, p);
461	splx(s);
462	if (error)
463		return error;
464	inp = (struct inpcb *)so->so_pcb;
465	inp->inp_vflag |= INP_IPV6;
466	inp->in6p_ip6_nxt = (long)proto;
467	inp->in6p_hops = -1;	/* use kernel default */
468	inp->in6p_cksum = -1;
469#ifdef IPSEC
470	error = ipsec_init_policy(so, &inp->in6p_sp);
471	if (error != 0) {
472		in6_pcbdetach(inp);
473		return (error);
474	}
475#endif /*IPSEC*/
476	MALLOC(inp->in6p_icmp6filt, struct icmp6_filter *,
477	       sizeof(struct icmp6_filter), M_PCB, M_NOWAIT);
478	ICMP6_FILTER_SETPASSALL(inp->in6p_icmp6filt);
479	return 0;
480}
481
482static int
483rip6_detach(struct socket *so)
484{
485	struct inpcb *inp;
486
487	inp = sotoinpcb(so);
488	if (inp == 0)
489		panic("rip6_detach");
490	/* xxx: RSVP */
491	if (inp->in6p_icmp6filt) {
492		FREE(inp->in6p_icmp6filt, M_PCB);
493		inp->in6p_icmp6filt = NULL;
494	}
495	in6_pcbdetach(inp);
496	return 0;
497}
498
499static int
500rip6_abort(struct socket *so)
501{
502	soisdisconnected(so);
503	return rip6_detach(so);
504}
505
506static int
507rip6_disconnect(struct socket *so)
508{
509	struct inpcb *inp = sotoinpcb(so);
510
511	if ((so->so_state & SS_ISCONNECTED) == 0)
512		return ENOTCONN;
513	inp->in6p_faddr = in6addr_any;
514	return rip6_abort(so);
515}
516
517static int
518rip6_bind(struct socket *so, struct sockaddr *nam, struct proc *p)
519{
520	struct inpcb *inp = sotoinpcb(so);
521	struct sockaddr_in6 *addr = (struct sockaddr_in6 *)nam;
522	struct ifaddr *ia = NULL;
523
524	if (nam->sa_len != sizeof(*addr))
525		return EINVAL;
526
527	if (TAILQ_EMPTY(&ifnet) || addr->sin6_family != AF_INET6)
528		return EADDRNOTAVAIL;
529	if (!IN6_IS_ADDR_UNSPECIFIED(&addr->sin6_addr) &&
530	    (ia = ifa_ifwithaddr((struct sockaddr *)addr)) == 0)
531		return EADDRNOTAVAIL;
532	if (ia &&
533	    ((struct in6_ifaddr *)ia)->ia6_flags &
534	    (IN6_IFF_ANYCAST|IN6_IFF_NOTREADY|
535	     IN6_IFF_DETACHED|IN6_IFF_DEPRECATED)) {
536		return(EADDRNOTAVAIL);
537	}
538	inp->in6p_laddr = addr->sin6_addr;
539	return 0;
540}
541
542static int
543rip6_connect(struct socket *so, struct sockaddr *nam, struct proc *p)
544{
545	struct inpcb *inp = sotoinpcb(so);
546	struct sockaddr_in6 *addr = (struct sockaddr_in6 *)nam;
547	struct in6_addr *in6a = NULL;
548	int error = 0;
549
550	if (nam->sa_len != sizeof(*addr))
551		return EINVAL;
552	if (TAILQ_EMPTY(&ifnet))
553		return EADDRNOTAVAIL;
554	if (addr->sin6_family != AF_INET6)
555		return EAFNOSUPPORT;
556
557	/* Source address selection. XXX: need pcblookup? */
558	in6a = in6_selectsrc(addr, inp->in6p_outputopts,
559			     inp->in6p_moptions, &inp->in6p_route,
560			     &inp->in6p_laddr, &error);
561	if (in6a == NULL)
562		return (error ? error : EADDRNOTAVAIL);
563	inp->in6p_laddr = *in6a;
564	inp->in6p_faddr = addr->sin6_addr;
565	soisconnected(so);
566	return 0;
567}
568
569static int
570rip6_shutdown(struct socket *so)
571{
572	socantsendmore(so);
573	return 0;
574}
575
576static int
577rip6_send(struct socket *so, int flags, struct mbuf *m, struct sockaddr *nam,
578	 struct mbuf *control, struct proc *p)
579{
580	struct inpcb *inp = sotoinpcb(so);
581	struct sockaddr_in6 tmp;
582	struct sockaddr_in6 *dst;
583
584	if (so->so_state & SS_ISCONNECTED) {
585		if (nam) {
586			m_freem(m);
587			return EISCONN;
588		}
589		/* XXX */
590		bzero(&tmp, sizeof(tmp));
591		tmp.sin6_family = AF_INET6;
592		tmp.sin6_len = sizeof(struct sockaddr_in6);
593		bcopy(&inp->in6p_faddr, &tmp.sin6_addr,
594		      sizeof(struct in6_addr));
595		dst = &tmp;
596	} else {
597		if (nam == NULL) {
598			m_freem(m);
599			return ENOTCONN;
600		}
601		dst = (struct sockaddr_in6 *)nam;
602	}
603	return rip6_output(m, so, dst, control);
604}
605
606struct pr_usrreqs rip6_usrreqs = {
607	rip6_abort, pru_accept_notsupp, rip6_attach, rip6_bind, rip6_connect,
608	pru_connect2_notsupp, in6_control, rip6_detach, rip6_disconnect,
609	pru_listen_notsupp, in6_setpeeraddr, pru_rcvd_notsupp,
610	pru_rcvoob_notsupp, rip6_send, pru_sense_null, rip6_shutdown,
611	in6_setsockaddr, sosend, soreceive, sopoll
612};
613