ip6_output.c revision 56743
160484Sobrien/*
277298Sobrien * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project.
3130561Sobrien * All rights reserved.
433965Sjdp *
533965Sjdp * Redistribution and use in source and binary forms, with or without
660484Sobrien * modification, are permitted provided that the following conditions
760484Sobrien * are met:
833965Sjdp * 1. Redistributions of source code must retain the above copyright
933965Sjdp *    notice, this list of conditions and the following disclaimer.
1033965Sjdp * 2. Redistributions in binary form must reproduce the above copyright
1133965Sjdp *    notice, this list of conditions and the following disclaimer in the
1233965Sjdp *    documentation and/or other materials provided with the distribution.
1333965Sjdp * 3. Neither the name of the project nor the names of its contributors
14130561Sobrien *    may be used to endorse or promote products derived from this software
15130561Sobrien *    without specific prior written permission.
16130561Sobrien *
17130561Sobrien * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND
18130561Sobrien * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19130561Sobrien * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20130561Sobrien * ARE DISCLAIMED.  IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE
21130561Sobrien * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22130561Sobrien * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
2333965Sjdp * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
2433965Sjdp * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
2533965Sjdp * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
2633965Sjdp * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
2733965Sjdp * SUCH DAMAGE.
2833965Sjdp *
2933965Sjdp * $FreeBSD: head/sys/netinet6/ip6_output.c 56743 2000-01-28 13:16:34Z shin $
30218822Sdim */
31218822Sdim
3233965Sjdp/*
3333965Sjdp * Copyright (c) 1982, 1986, 1988, 1990, 1993
3433965Sjdp *	The Regents of the University of California.  All rights reserved.
3533965Sjdp *
3633965Sjdp * Redistribution and use in source and binary forms, with or without
3733965Sjdp * modification, are permitted provided that the following conditions
3833965Sjdp * are met:
3933965Sjdp * 1. Redistributions of source code must retain the above copyright
4033965Sjdp *    notice, this list of conditions and the following disclaimer.
4133965Sjdp * 2. Redistributions in binary form must reproduce the above copyright
4260484Sobrien *    notice, this list of conditions and the following disclaimer in the
4360484Sobrien *    documentation and/or other materials provided with the distribution.
4460484Sobrien * 3. All advertising materials mentioning features or use of this software
4560484Sobrien *    must display the following acknowledgement:
4677298Sobrien *	This product includes software developed by the University of
4777298Sobrien *	California, Berkeley and its contributors.
4860484Sobrien * 4. Neither the name of the University nor the names of its contributors
4933965Sjdp *    may be used to endorse or promote products derived from this software
5033965Sjdp *    without specific prior written permission.
5133965Sjdp *
5260484Sobrien * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
5360484Sobrien * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
5460484Sobrien * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
5560484Sobrien * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
5660484Sobrien * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
5760484Sobrien * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
5860484Sobrien * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
5933965Sjdp * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
6033965Sjdp * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
6133965Sjdp * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
6233965Sjdp * SUCH DAMAGE.
6360484Sobrien *
6433965Sjdp *	@(#)ip_output.c	8.3 (Berkeley) 1/21/94
65218822Sdim */
6677298Sobrien
6760484Sobrien#include "opt_ipsec.h"
6860484Sobrien
6960484Sobrien#include <sys/param.h>
7060484Sobrien#include <sys/malloc.h>
7160484Sobrien#include <sys/mbuf.h>
7260484Sobrien#include <sys/errno.h>
73218822Sdim#include <sys/protosw.h>
7460484Sobrien#include <sys/socket.h>
7533965Sjdp#include <sys/socketvar.h>
7633965Sjdp#include <sys/systm.h>
7733965Sjdp#include <sys/kernel.h>
7833965Sjdp#include <sys/proc.h>
7933965Sjdp
8033965Sjdp#include <net/if.h>
8133965Sjdp#include <net/route.h>
8233965Sjdp
8333965Sjdp#include <netinet/in.h>
8433965Sjdp#include <netinet/in_var.h>
8533965Sjdp#include <netinet6/ip6.h>
8633965Sjdp#include <netinet6/icmp6.h>
8733965Sjdp#include <netinet/in_pcb.h>
8833965Sjdp#include <netinet6/ip6_var.h>
8933965Sjdp#include <netinet6/nd6.h>
9033965Sjdp
9133965Sjdp#ifdef IPSEC
9233965Sjdp#include <netinet6/ipsec.h>
9333965Sjdp#include <netinet6/ipsec6.h>
9433965Sjdp#include <netkey/key.h>
9577298Sobrien#ifdef IPSEC_DEBUG
9633965Sjdp#include <netkey/key_debug.h>
9733965Sjdp#else
9833965Sjdp#define KEYDEBUG(lev,arg)
9960484Sobrien#endif
10060484Sobrien#endif /* IPSEC */
10133965Sjdp
102218822Sdim#include "loop.h"
10333965Sjdp
10433965Sjdp#include <net/net_osdep.h>
10533965Sjdp
10633965Sjdp#ifdef IPV6FIREWALL
10760484Sobrien#include <netinet6/ip6_fw.h>
10860484Sobrien#endif
10960484Sobrien
11060484Sobrienstatic MALLOC_DEFINE(M_IPMOPTS, "ip6_moptions", "internet multicast options");
11160484Sobrien
11260484Sobrienstruct ip6_exthdrs {
11360484Sobrien	struct	mbuf *ip6e_ip6;
11433965Sjdp	struct	mbuf *ip6e_hbh;
11533965Sjdp	struct	mbuf *ip6e_dest1;
11633965Sjdp	struct	mbuf *ip6e_rthdr;
11733965Sjdp	struct	mbuf *ip6e_dest2;
11833965Sjdp};
11933965Sjdp
12033965Sjdpstatic int	ip6_pcbopts __P((struct ip6_pktopts **, struct mbuf *,
12160484Sobrien			    struct socket *, struct sockopt *sopt));
12260484Sobrienstatic int	ip6_setmoptions __P((int, struct ip6_moptions **, struct mbuf *));
12360484Sobrienstatic int	ip6_getmoptions __P((int, struct ip6_moptions *, struct mbuf **));
12460484Sobrienstatic int	ip6_copyexthdr __P((struct mbuf **, caddr_t, int));
12560484Sobrienstatic int	ip6_insertfraghdr __P((struct mbuf *, struct mbuf *, int,
12660484Sobrien				  struct ip6_frag **));
12733965Sjdpstatic int	ip6_insert_jumboopt __P((struct ip6_exthdrs *, u_int32_t));
12833965Sjdpstatic int	ip6_splithdr __P((struct mbuf *, struct ip6_exthdrs *));
12933965Sjdp
13033965Sjdp/*
13133965Sjdp * IP6 output. The packet in mbuf chain m contains a skeletal IP6
13260484Sobrien * header (with pri, len, nxt, hlim, src, dst).
13360484Sobrien * This function may modify ver and hlim only.
13460484Sobrien * The mbuf chain containing the packet will be freed.
13538889Sjdp * The mbuf opt, if present, will not be freed.
13638889Sjdp */
13760484Sobrienint
13860484Sobrienip6_output(m0, opt, ro, flags, im6o, ifpp)
13960484Sobrien	struct mbuf *m0;
14060484Sobrien	struct ip6_pktopts *opt;
14160484Sobrien	struct route_in6 *ro;
14233965Sjdp	int flags;
14333965Sjdp	struct ip6_moptions *im6o;
14433965Sjdp	struct ifnet **ifpp;		/* XXX: just for statistics */
14533965Sjdp{
14633965Sjdp	struct ip6_hdr *ip6, *mhip6;
14733965Sjdp	struct ifnet *ifp;
14833965Sjdp	struct mbuf *m = m0;
14989857Sobrien	int hlen, tlen, len, off;
15089857Sobrien	struct route_in6 ip6route;
15189857Sobrien	struct sockaddr_in6 *dst;
15233965Sjdp	int error = 0;
15333965Sjdp	struct in6_ifaddr *ia;
15433965Sjdp	u_long mtu;
15533965Sjdp	u_int32_t optlen = 0, plen = 0, unfragpartlen = 0;
15633965Sjdp	struct ip6_exthdrs exthdrs;
15733965Sjdp	struct in6_addr finaldst;
15833965Sjdp	struct route_in6 *ro_pmtu = NULL;
15933965Sjdp	int hdrsplit = 0;
16033965Sjdp	int needipsec = 0;
16133965Sjdp#ifdef IPSEC
16233965Sjdp	int needipsectun = 0;
16333965Sjdp	struct socket *so;
16433965Sjdp	struct secpolicy *sp = NULL;
16533965Sjdp
16633965Sjdp	/* for AH processing. stupid to have "socket" variable in IP layer... */
16733965Sjdp	if ((flags & IPV6_SOCKINMRCVIF) != 0) {
16833965Sjdp		so = (struct socket *)m->m_pkthdr.rcvif;
16933965Sjdp		m->m_pkthdr.rcvif = NULL;
17033965Sjdp	} else
17133965Sjdp		so = NULL;
17233965Sjdp	ip6 = mtod(m, struct ip6_hdr *);
17333965Sjdp#endif /* IPSEC */
17433965Sjdp
17533965Sjdp#define MAKE_EXTHDR(hp,mp)						\
17633965Sjdp    {									\
17733965Sjdp	if (hp) {							\
17833965Sjdp		struct ip6_ext *eh = (struct ip6_ext *)(hp);		\
17933965Sjdp		error = ip6_copyexthdr((mp), (caddr_t)(hp), 		\
18033965Sjdp				       ((eh)->ip6e_len + 1) << 3);	\
18133965Sjdp		if (error)						\
18233965Sjdp			goto freehdrs;					\
18333965Sjdp	}								\
18433965Sjdp    }
18533965Sjdp
18633965Sjdp	bzero(&exthdrs, sizeof(exthdrs));
18733965Sjdp	if (opt) {
18833965Sjdp		/* Hop-by-Hop options header */
18933965Sjdp		MAKE_EXTHDR(opt->ip6po_hbh, &exthdrs.ip6e_hbh);
19033965Sjdp		/* Destination options header(1st part) */
19133965Sjdp		MAKE_EXTHDR(opt->ip6po_dest1, &exthdrs.ip6e_dest1);
19233965Sjdp		/* Routing header */
19333965Sjdp		MAKE_EXTHDR(opt->ip6po_rthdr, &exthdrs.ip6e_rthdr);
19433965Sjdp		/* Destination options header(2nd part) */
19533965Sjdp		MAKE_EXTHDR(opt->ip6po_dest2, &exthdrs.ip6e_dest2);
19633965Sjdp	}
19733965Sjdp
19833965Sjdp#ifdef IPSEC
19933965Sjdp	/* get a security policy for this packet */
20033965Sjdp	if (so == NULL)
20133965Sjdp		sp = ipsec6_getpolicybyaddr(m, IPSEC_DIR_OUTBOUND, 0, &error);
20233965Sjdp	else
20333965Sjdp		sp = ipsec6_getpolicybysock(m, IPSEC_DIR_OUTBOUND, so, &error);
20433965Sjdp
20533965Sjdp	if (sp == NULL) {
20633965Sjdp		ipsec6stat.out_inval++;
20733965Sjdp		goto bad;
20833965Sjdp	}
20933965Sjdp
21033965Sjdp	error = 0;
21133965Sjdp
21233965Sjdp	/* check policy */
21333965Sjdp	switch (sp->policy) {
21433965Sjdp	case IPSEC_POLICY_DISCARD:
21533965Sjdp		/*
21633965Sjdp		 * This packet is just discarded.
21733965Sjdp		 */
21833965Sjdp		ipsec6stat.out_polvio++;
21933965Sjdp		goto bad;
22033965Sjdp
22133965Sjdp	case IPSEC_POLICY_BYPASS:
22233965Sjdp	case IPSEC_POLICY_NONE:
22333965Sjdp		/* no need to do IPsec. */
22433965Sjdp		needipsec = 0;
22533965Sjdp		break;
22633965Sjdp
22733965Sjdp	case IPSEC_POLICY_IPSEC:
22833965Sjdp		if (sp->req == NULL) {
22933965Sjdp			/* XXX should be panic ? */
23060484Sobrien			printf("ip6_output: No IPsec request specified.\n");
23160484Sobrien			error = EINVAL;
23233965Sjdp			goto bad;
23333965Sjdp		}
23460484Sobrien		needipsec = 1;
23560484Sobrien		break;
23660484Sobrien
23760484Sobrien	case IPSEC_POLICY_ENTRUST:
23860484Sobrien	default:
23960484Sobrien		printf("ip6_output: Invalid policy found. %d\n", sp->policy);
24060484Sobrien	}
24160484Sobrien#endif /* IPSEC */
24260484Sobrien
24360484Sobrien	/*
24460484Sobrien	 * Calculate the total length of the extension header chain.
24560484Sobrien	 * Keep the length of the unfragmentable part for fragmentation.
24660484Sobrien	 */
24733965Sjdp	optlen = 0;
24889857Sobrien	if (exthdrs.ip6e_hbh) optlen += exthdrs.ip6e_hbh->m_len;
24968765Sobrien	if (exthdrs.ip6e_dest1) optlen += exthdrs.ip6e_dest1->m_len;
25068765Sobrien	if (exthdrs.ip6e_rthdr) optlen += exthdrs.ip6e_rthdr->m_len;
25189857Sobrien	unfragpartlen = optlen + sizeof(struct ip6_hdr);
25289857Sobrien	/* NOTE: we don't add AH/ESP length here. do that later. */
25389857Sobrien	if (exthdrs.ip6e_dest2) optlen += exthdrs.ip6e_dest2->m_len;
25489857Sobrien
25589857Sobrien	/*
25689857Sobrien	 * If we need IPsec, or there is at least one extension header,
25768765Sobrien	 * separate IP6 header from the payload.
25868765Sobrien	 */
25968765Sobrien	if ((needipsec || optlen) && !hdrsplit) {
26068765Sobrien		if ((error = ip6_splithdr(m, &exthdrs)) != 0) {
26168765Sobrien			m = NULL;
26268765Sobrien			goto freehdrs;
26368765Sobrien		}
26468765Sobrien		m = exthdrs.ip6e_ip6;
26568765Sobrien		hdrsplit++;
26668765Sobrien	}
26768765Sobrien
26868765Sobrien	/* adjust pointer */
26968765Sobrien	ip6 = mtod(m, struct ip6_hdr *);
27068765Sobrien
27168765Sobrien	/* adjust mbuf packet header length */
27268765Sobrien	m->m_pkthdr.len += optlen;
27368765Sobrien	plen = m->m_pkthdr.len - sizeof(*ip6);
27468765Sobrien
27568765Sobrien	/* If this is a jumbo payload, insert a jumbo payload option. */
27668765Sobrien	if (plen > IPV6_MAXPACKET) {
27768765Sobrien		if (!hdrsplit) {
27868765Sobrien			if ((error = ip6_splithdr(m, &exthdrs)) != 0) {
27968765Sobrien				m = NULL;
28068765Sobrien				goto freehdrs;
28168765Sobrien			}
28268765Sobrien			m = exthdrs.ip6e_ip6;
28368765Sobrien			hdrsplit++;
28468765Sobrien		}
28568765Sobrien		/* adjust pointer */
28668765Sobrien		ip6 = mtod(m, struct ip6_hdr *);
28768765Sobrien		if ((error = ip6_insert_jumboopt(&exthdrs, plen)) != 0)
28868765Sobrien			goto freehdrs;
28968765Sobrien		ip6->ip6_plen = 0;
29068765Sobrien	} else
29168765Sobrien		ip6->ip6_plen = htons(plen);
29268765Sobrien
29377298Sobrien	/*
29477298Sobrien	 * Concatenate headers and fill in next header fields.
29577298Sobrien	 * Here we have, on "m"
29668765Sobrien	 *	IPv6 payload
29768765Sobrien	 * and we insert headers accordingly.  Finally, we should be getting:
29868765Sobrien	 *	IPv6 hbh dest1 rthdr ah* [esp* dest2 payload]
29977298Sobrien	 *
30077298Sobrien	 * during the header composing process, "m" points to IPv6 header.
30177298Sobrien	 * "mprev" points to an extension header prior to esp.
30277298Sobrien	 */
30377298Sobrien	{
30477298Sobrien		u_char *nexthdrp = &ip6->ip6_nxt;
30577298Sobrien		struct mbuf *mprev = m;
30677298Sobrien
30777298Sobrien		/*
30877298Sobrien		 * we treat dest2 specially.  this makes IPsec processing
30977298Sobrien		 * much easier.
31077298Sobrien		 *
31168765Sobrien		 * result: IPv6 dest2 payload
31268765Sobrien		 * m and mprev will point to IPv6 header.
31368765Sobrien		 */
31468765Sobrien		if (exthdrs.ip6e_dest2) {
31533965Sjdp			if (!hdrsplit)
31633965Sjdp				panic("assumption failed: hdr not split");
31733965Sjdp			exthdrs.ip6e_dest2->m_next = m->m_next;
31860484Sobrien			m->m_next = exthdrs.ip6e_dest2;
31933965Sjdp			*mtod(exthdrs.ip6e_dest2, u_char *) = ip6->ip6_nxt;
32060484Sobrien			ip6->ip6_nxt = IPPROTO_DSTOPTS;
32160484Sobrien		}
32260484Sobrien
32360484Sobrien#define MAKE_CHAIN(m,mp,p,i)\
32433965Sjdp    {\
32533965Sjdp	if (m) {\
32633965Sjdp		if (!hdrsplit) \
32733965Sjdp			panic("assumption failed: hdr not split"); \
32833965Sjdp		*mtod((m), u_char *) = *(p);\
329218822Sdim		*(p) = (i);\
33068765Sobrien		p = mtod((m), u_char *);\
331218822Sdim		(m)->m_next = (mp)->m_next;\
33268765Sobrien		(mp)->m_next = (m);\
333218822Sdim		(mp) = (m);\
33433965Sjdp	}\
335218822Sdim    }
33660484Sobrien		/*
337218822Sdim		 * result: IPv6 hbh dest1 rthdr dest2 payload
33868765Sobrien		 * m will point to IPv6 header.  mprev will point to the
33933965Sjdp		 * extension header prior to dest2 (rthdr in the above case).
34033965Sjdp		 */
341218822Sdim		MAKE_CHAIN(exthdrs.ip6e_hbh, mprev,
34233965Sjdp			   nexthdrp, IPPROTO_HOPOPTS);
34333965Sjdp		MAKE_CHAIN(exthdrs.ip6e_dest1, mprev,
34460484Sobrien			   nexthdrp, IPPROTO_DSTOPTS);
345218822Sdim		MAKE_CHAIN(exthdrs.ip6e_rthdr, mprev,
34660484Sobrien			   nexthdrp, IPPROTO_ROUTING);
34733965Sjdp
348218822Sdim#ifdef IPSEC
349218822Sdim		if (!needipsec)
35060484Sobrien			goto skip_ipsec2;
35160484Sobrien
352218822Sdim		/*
353218822Sdim		 * pointers after IPsec headers are not valid any more.
35433965Sjdp		 * other pointers need a great care too.
35533965Sjdp		 * (IPsec routines should not mangle mbufs prior to AH/ESP)
356218822Sdim		 */
357218822Sdim		exthdrs.ip6e_dest2 = NULL;
35833965Sjdp
35933965Sjdp	    {
360218822Sdim		struct ip6_rthdr *rh = NULL;
36133965Sjdp		int segleft_org = 0;
36233965Sjdp		struct ipsec_output_state state;
363218822Sdim
364218822Sdim		if (exthdrs.ip6e_rthdr) {
36533965Sjdp			rh = mtod(exthdrs.ip6e_rthdr, struct ip6_rthdr *);
366218822Sdim			segleft_org = rh->ip6r_segleft;
36733965Sjdp			rh->ip6r_segleft = 0;
368218822Sdim		}
36933965Sjdp
370218822Sdim		bzero(&state, sizeof(state));
37133965Sjdp		state.m = m;
372218822Sdim		error = ipsec6_output_trans(&state, nexthdrp, mprev, sp, flags,
37333965Sjdp			&needipsectun);
374218822Sdim		m = state.m;
37533965Sjdp		if (error) {
376218822Sdim			/* mbuf is already reclaimed in ipsec6_output_trans. */
37733965Sjdp			m = NULL;
378218822Sdim			switch (error) {
37933965Sjdp			case EHOSTUNREACH:
380218822Sdim			case ENETUNREACH:
38133965Sjdp			case EMSGSIZE:
38233965Sjdp			case ENOBUFS:
383218822Sdim			case ENOMEM:
38433965Sjdp				break;
385218822Sdim			default:
38633965Sjdp				printf("ip6_output (ipsec): error code %d\n", error);
38733965Sjdp				/*fall through*/
388218822Sdim			case ENOENT:
38933965Sjdp				/* don't show these error codes to the user */
39033965Sjdp				error = 0;
391218822Sdim				break;
39233965Sjdp			}
393218822Sdim			goto bad;
39433965Sjdp		}
395218822Sdim		if (exthdrs.ip6e_rthdr) {
39633965Sjdp			/* ah6_output doesn't modify mbuf chain */
397218822Sdim			rh->ip6r_segleft = segleft_org;
39833965Sjdp		}
399218822Sdim	    }
40033965Sjdpskip_ipsec2:;
401218822Sdim#endif
40260484Sobrien	}
403218822Sdim
40433965Sjdp	/*
405218822Sdim	 * If there is a routing header, replace destination address field
40633965Sjdp	 * with the first hop of the routing header.
407218822Sdim	 */
40838889Sjdp	if (exthdrs.ip6e_rthdr) {
409218822Sdim		struct ip6_rthdr *rh =
41033965Sjdp			(struct ip6_rthdr *)(mtod(exthdrs.ip6e_rthdr,
411218822Sdim						  struct ip6_rthdr *));
41260484Sobrien		struct ip6_rthdr0 *rh0;
413218822Sdim
41433965Sjdp		finaldst = ip6->ip6_dst;
415218822Sdim		switch(rh->ip6r_type) {
41633965Sjdp		case IPV6_RTHDR_TYPE_0:
41733965Sjdp			 rh0 = (struct ip6_rthdr0 *)rh;
418218822Sdim			 ip6->ip6_dst = rh0->ip6r0_addr[0];
419218822Sdim			 bcopy((caddr_t)&rh0->ip6r0_addr[1],
42033965Sjdp				 (caddr_t)&rh0->ip6r0_addr[0],
42168765Sobrien				 sizeof(struct in6_addr)*(rh0->ip6r0_segleft - 1)
422218822Sdim				 );
423218822Sdim			 rh0->ip6r0_addr[rh0->ip6r0_segleft - 1] = finaldst;
42468765Sobrien			 break;
425218822Sdim		default:	/* is it possible? */
42633965Sjdp			 error = EINVAL;
427218822Sdim			 goto bad;
42860484Sobrien		}
429218822Sdim	}
43060484Sobrien
431218822Sdim	/* Source address validation */
43260484Sobrien	if (IN6_IS_ADDR_UNSPECIFIED(&ip6->ip6_src) &&
433218822Sdim	    (flags & IPV6_DADOUTPUT) == 0) {
43433965Sjdp		error = EOPNOTSUPP;
435218822Sdim		ip6stat.ip6s_badscope++;
43660484Sobrien		goto bad;
437218822Sdim	}
43833965Sjdp	if (IN6_IS_ADDR_MULTICAST(&ip6->ip6_src)) {
43960484Sobrien		error = EOPNOTSUPP;
440218822Sdim		ip6stat.ip6s_badscope++;
441218822Sdim		goto bad;
44233965Sjdp	}
44333965Sjdp
444218822Sdim	ip6stat.ip6s_localout++;
44560484Sobrien
44660484Sobrien	/*
447218822Sdim	 * Route packet.
44860484Sobrien	 */
449218822Sdim	if (ro == 0) {
45060484Sobrien		ro = &ip6route;
45160484Sobrien		bzero((caddr_t)ro, sizeof(*ro));
45260484Sobrien	}
45360484Sobrien	ro_pmtu = ro;
45460484Sobrien	if (opt && opt->ip6po_rthdr)
45560484Sobrien		ro = &opt->ip6po_route;
45660484Sobrien	dst = (struct sockaddr_in6 *)&ro->ro_dst;
45760484Sobrien	/*
45860484Sobrien	 * If there is a cached route,
45960484Sobrien	 * check that it is to the same destination
460218822Sdim	 * and is still up. If not, free it and try again.
46160484Sobrien	 */
462218822Sdim	if (ro->ro_rt && ((ro->ro_rt->rt_flags & RTF_UP) == 0 ||
46360484Sobrien			 !IN6_ARE_ADDR_EQUAL(&dst->sin6_addr, &ip6->ip6_dst))) {
464218822Sdim		RTFREE(ro->ro_rt);
46560484Sobrien		ro->ro_rt = (struct rtentry *)0;
466218822Sdim	}
467218822Sdim	if (ro->ro_rt == 0) {
46860484Sobrien		bzero(dst, sizeof(*dst));
46960484Sobrien		dst->sin6_family = AF_INET6;
470218822Sdim		dst->sin6_len = sizeof(struct sockaddr_in6);
47160484Sobrien		dst->sin6_addr = ip6->ip6_dst;
47260484Sobrien	}
473218822Sdim#ifdef IPSEC
47460484Sobrien	if (needipsec && needipsectun) {
47560484Sobrien		struct ipsec_output_state state;
476218822Sdim
47760484Sobrien		/*
47860484Sobrien		 * All the extension headers will become inaccessible
479218822Sdim		 * (since they can be encrypted).
48060484Sobrien		 * Don't panic, we need no more updates to extension headers
481218822Sdim		 * on inner IPv6 packet (since they are now encapsulated).
48277298Sobrien		 *
48360484Sobrien		 * IPv6 [ESP|AH] IPv6 [extension headers] payload
48460484Sobrien		 */
48560484Sobrien		bzero(&exthdrs, sizeof(exthdrs));
48660484Sobrien		exthdrs.ip6e_ip6 = m;
48760484Sobrien
48860484Sobrien		bzero(&state, sizeof(state));
48960484Sobrien		state.m = m;
49060484Sobrien		state.ro = (struct route *)ro;
49160484Sobrien		state.dst = (struct sockaddr *)dst;
492218822Sdim
49333965Sjdp		error = ipsec6_output_tunnel(&state, sp, flags);
49433965Sjdp
49533965Sjdp		m = state.m;
49677298Sobrien		ro = (struct route_in6 *)state.ro;
49760484Sobrien		dst = (struct sockaddr_in6 *)state.dst;
49860484Sobrien		if (error) {
49977298Sobrien			/* mbuf is already reclaimed in ipsec6_output_tunnel. */
50033965Sjdp			m0 = m = NULL;
50133965Sjdp			m = NULL;
50260484Sobrien			switch (error) {
50360484Sobrien			case EHOSTUNREACH:
50460484Sobrien			case ENETUNREACH:
50560484Sobrien			case EMSGSIZE:
50660484Sobrien			case ENOBUFS:
50760484Sobrien			case ENOMEM:
50860484Sobrien				break;
50960484Sobrien			default:
51077298Sobrien				printf("ip6_output (ipsec): error code %d\n", error);
51160484Sobrien				/*fall through*/
51260484Sobrien			case ENOENT:
51360484Sobrien				/* don't show these error codes to the user */
51460484Sobrien				error = 0;
51533965Sjdp				break;
51633965Sjdp			}
51733965Sjdp			goto bad;
51860484Sobrien		}
51989857Sobrien
52089857Sobrien		exthdrs.ip6e_ip6 = m;
52189857Sobrien	}
52233965Sjdp#endif /*IPESC*/
52333965Sjdp
52433965Sjdp	if (!IN6_IS_ADDR_MULTICAST(&ip6->ip6_dst)) {
52538889Sjdp		/* Unicast */
52660484Sobrien
52738889Sjdp#define ifatoia6(ifa)	((struct in6_ifaddr *)(ifa))
52838889Sjdp#define sin6tosa(sin6)	((struct sockaddr *)(sin6))
52938889Sjdp		/* xxx
53038889Sjdp		 * interface selection comes here
531218822Sdim		 * if an interface is specified from an upper layer,
53238889Sjdp		 * ifp must point it.
53338889Sjdp		 */
53438889Sjdp		if (ro->ro_rt == 0)
53538889Sjdp			rtalloc((struct route *)ro);
53638889Sjdp		if (ro->ro_rt == 0) {
53738889Sjdp			ip6stat.ip6s_noroute++;
53877298Sobrien			error = EHOSTUNREACH;
53938889Sjdp			/* XXX in6_ifstat_inc(ifp, ifs6_out_discard); */
54038889Sjdp			goto bad;
54138889Sjdp		}
54238889Sjdp		ia = ifatoia6(ro->ro_rt->rt_ifa);
54338889Sjdp		ifp = ro->ro_rt->rt_ifp;
54438889Sjdp		ro->ro_rt->rt_use++;
54560484Sobrien		if (ro->ro_rt->rt_flags & RTF_GATEWAY)
54638889Sjdp			dst = (struct sockaddr_in6 *)ro->ro_rt->rt_gateway;
54738889Sjdp		m->m_flags &= ~(M_BCAST | M_MCAST);	/* just in case */
54838889Sjdp
54938889Sjdp		in6_ifstat_inc(ifp, ifs6_out_request);
55038889Sjdp
55138889Sjdp		/*
55260484Sobrien		 * Check if there is the outgoing interface conflicts with
55338889Sjdp		 * the interface specified by ifi6_ifindex(if specified).
55438889Sjdp		 * Note that loopback interface is always okay.
55538889Sjdp		 * (this happens when we are sending packet toward my
55638889Sjdp		 * interface)
55738889Sjdp		 */
55838889Sjdp		if (opt && opt->ip6po_pktinfo
55938889Sjdp		 && opt->ip6po_pktinfo->ipi6_ifindex) {
56060484Sobrien			if (!(ifp->if_flags & IFF_LOOPBACK)
56160484Sobrien			 && ifp->if_index != opt->ip6po_pktinfo->ipi6_ifindex) {
56260484Sobrien				ip6stat.ip6s_noroute++;
56360484Sobrien				in6_ifstat_inc(ifp, ifs6_out_discard);
564218822Sdim				error = EHOSTUNREACH;
56560484Sobrien				goto bad;
56660484Sobrien			}
56760484Sobrien		}
56860484Sobrien
56960484Sobrien		if (opt && opt->ip6po_hlim != -1)
57060484Sobrien			ip6->ip6_hlim = opt->ip6po_hlim & 0xff;
57160484Sobrien	} else {
57260484Sobrien		/* Multicast */
57360484Sobrien		struct	in6_multi *in6m;
57460484Sobrien
57560484Sobrien		m->m_flags = (m->m_flags & ~M_BCAST) | M_MCAST;
57660484Sobrien
57760484Sobrien		/*
57860484Sobrien		 * See if the caller provided any multicast options
57960484Sobrien		 */
58060484Sobrien		ifp = NULL;
58160484Sobrien		if (im6o != NULL) {
58260484Sobrien			ip6->ip6_hlim = im6o->im6o_multicast_hlim;
58360484Sobrien			if (im6o->im6o_multicast_ifp != NULL)
58460484Sobrien				ifp = im6o->im6o_multicast_ifp;
58560484Sobrien		} else
58660484Sobrien			ip6->ip6_hlim = ip6_defmcasthlim;
58760484Sobrien
58860484Sobrien		/*
589218822Sdim		 * See if the caller provided the outgoing interface
59060484Sobrien		 * as an ancillary data.
59160484Sobrien		 * Boundary check for ifindex is assumed to be already done.
59260484Sobrien		 */
59360484Sobrien		if (opt && opt->ip6po_pktinfo && opt->ip6po_pktinfo->ipi6_ifindex)
59460484Sobrien			ifp = ifindex2ifnet[opt->ip6po_pktinfo->ipi6_ifindex];
59560484Sobrien
59660484Sobrien		/*
59760484Sobrien		 * If the destination is a node-local scope multicast,
59860484Sobrien		 * the packet should be loop-backed only.
59960484Sobrien		 */
60060484Sobrien		if (IN6_IS_ADDR_MC_NODELOCAL(&ip6->ip6_dst)) {
60160484Sobrien			/*
60260484Sobrien			 * If the outgoing interface is already specified,
60360484Sobrien			 * it should be a loopback interface.
60460484Sobrien			 */
60560484Sobrien			if (ifp && (ifp->if_flags & IFF_LOOPBACK) == 0) {
60660484Sobrien				ip6stat.ip6s_badscope++;
60760484Sobrien				error = ENETUNREACH; /* XXX: better error? */
60860484Sobrien				/* XXX correct ifp? */
60960484Sobrien				in6_ifstat_inc(ifp, ifs6_out_discard);
61060484Sobrien				goto bad;
61160484Sobrien			} else {
61260484Sobrien				ifp = &loif[0];
61360484Sobrien			}
61460484Sobrien		}
61560484Sobrien
61660484Sobrien		if (opt && opt->ip6po_hlim != -1)
61760484Sobrien			ip6->ip6_hlim = opt->ip6po_hlim & 0xff;
61860484Sobrien
61960484Sobrien		/*
62060484Sobrien		 * If caller did not provide an interface lookup a
62160484Sobrien		 * default in the routing table.  This is either a
62260484Sobrien		 * default for the speicfied group (i.e. a host
62360484Sobrien		 * route), or a multicast default (a route for the
62460484Sobrien		 * ``net'' ff00::/8).
62560484Sobrien		 */
62660484Sobrien		if (ifp == NULL) {
62760484Sobrien			if (ro->ro_rt == 0) {
62860484Sobrien				ro->ro_rt = rtalloc1((struct sockaddr *)
62960484Sobrien						&ro->ro_dst, 0, 0UL);
630218822Sdim			}
63160484Sobrien			if (ro->ro_rt == 0) {
63260484Sobrien				ip6stat.ip6s_noroute++;
63360484Sobrien				error = EHOSTUNREACH;
63460484Sobrien				/* XXX in6_ifstat_inc(ifp, ifs6_out_discard) */
63533965Sjdp				goto bad;
636218822Sdim			}
63733965Sjdp			ia = ifatoia6(ro->ro_rt->rt_ifa);
63860484Sobrien			ifp = ro->ro_rt->rt_ifp;
63933965Sjdp			ro->ro_rt->rt_use++;
64033965Sjdp		}
64133965Sjdp
64233965Sjdp		if ((flags & IPV6_FORWARDING) == 0)
64333965Sjdp			in6_ifstat_inc(ifp, ifs6_out_request);
64433965Sjdp		in6_ifstat_inc(ifp, ifs6_out_mcast);
64533965Sjdp
64660484Sobrien		/*
64733965Sjdp		 * Confirm that the outgoing interface supports multicast.
64860484Sobrien		 */
64933965Sjdp		if ((ifp->if_flags & IFF_MULTICAST) == 0) {
65033965Sjdp			ip6stat.ip6s_noroute++;
65133965Sjdp			in6_ifstat_inc(ifp, ifs6_out_discard);
65233965Sjdp			error = ENETUNREACH;
65333965Sjdp			goto bad;
65433965Sjdp		}
65533965Sjdp		IN6_LOOKUP_MULTI(ip6->ip6_dst, ifp, in6m);
65633965Sjdp		if (in6m != NULL &&
65733965Sjdp		   (im6o == NULL || im6o->im6o_multicast_loop)) {
65833965Sjdp			/*
65933965Sjdp			 * If we belong to the destination multicast group
66033965Sjdp			 * on the outgoing interface, and the caller did not
66133965Sjdp			 * forbid loopback, loop back a copy.
66233965Sjdp			 */
66333965Sjdp			ip6_mloopback(ifp, m, dst);
66477298Sobrien		} else {
66577298Sobrien			/*
66633965Sjdp			 * If we are acting as a multicast router, perform
66733965Sjdp			 * multicast forwarding as if the packet had just
66833965Sjdp			 * arrived on the interface to which we are about
66933965Sjdp			 * to send.  The multicast forwarding function
67060484Sobrien			 * recursively calls this function, using the
67177298Sobrien			 * IPV6_FORWARDING flag to prevent infinite recursion.
67233965Sjdp			 *
67333965Sjdp			 * Multicasts that are looped back by ip6_mloopback(),
67433965Sjdp			 * above, will be forwarded by the ip6_input() routine,
67533965Sjdp			 * if necessary.
67633965Sjdp			 */
67733965Sjdp			if (ip6_mrouter && (flags & IPV6_FORWARDING) == 0) {
67833965Sjdp				if (ip6_mforward(ip6, ifp, m) != NULL) {
67933965Sjdp					m_freem(m);
68033965Sjdp					goto done;
68133965Sjdp				}
68233965Sjdp			}
68333965Sjdp		}
68433965Sjdp		/*
68533965Sjdp		 * Multicasts with a hoplimit of zero may be looped back,
68633965Sjdp		 * above, but must not be transmitted on a network.
68733965Sjdp		 * Also, multicasts addressed to the loopback interface
68860484Sobrien		 * are not sent -- the above call to ip6_mloopback() will
68977298Sobrien		 * loop back a copy if this host actually belongs to the
69033965Sjdp		 * destination group on the loopback interface.
69133965Sjdp		 */
69233965Sjdp		if (ip6->ip6_hlim == 0 || (ifp->if_flags & IFF_LOOPBACK)) {
69333965Sjdp			m_freem(m);
69433965Sjdp			goto done;
69533965Sjdp		}
69633965Sjdp	}
69733965Sjdp
69860484Sobrien	/*
69933965Sjdp	 * Fill the outgoing inteface to tell the upper layer
70033965Sjdp	 * to increment per-interface statistics.
70133965Sjdp	 */
70233965Sjdp	if (ifpp)
70360484Sobrien		*ifpp = ifp;
70433965Sjdp
70533965Sjdp	/*
70633965Sjdp	 * Determine path MTU.
70733965Sjdp	 */
70833965Sjdp	if (ro_pmtu != ro) {
70933965Sjdp		/* The first hop and the final destination may differ. */
71033965Sjdp		struct sockaddr_in6 *sin6_fin =
71133965Sjdp			(struct sockaddr_in6 *)&ro_pmtu->ro_dst;
71260484Sobrien		if (ro_pmtu->ro_rt && ((ro->ro_rt->rt_flags & RTF_UP) == 0 ||
71377298Sobrien				       !IN6_ARE_ADDR_EQUAL(&sin6_fin->sin6_addr,
71433965Sjdp							   &finaldst))) {
71533965Sjdp			RTFREE(ro_pmtu->ro_rt);
71660484Sobrien			ro_pmtu->ro_rt = (struct rtentry *)0;
71733965Sjdp		}
71833965Sjdp		if (ro_pmtu->ro_rt == 0) {
71933965Sjdp			bzero(sin6_fin, sizeof(*sin6_fin));
72033965Sjdp			sin6_fin->sin6_family = AF_INET6;
72133965Sjdp			sin6_fin->sin6_len = sizeof(struct sockaddr_in6);
72233965Sjdp			sin6_fin->sin6_addr = finaldst;
72333965Sjdp
72433965Sjdp			rtalloc((struct route *)ro_pmtu);
72533965Sjdp		}
72633965Sjdp	}
72733965Sjdp	if (ro_pmtu->ro_rt != NULL) {
72833965Sjdp		u_int32_t ifmtu = nd_ifinfo[ifp->if_index].linkmtu;
72960484Sobrien
73077298Sobrien		mtu = ro_pmtu->ro_rt->rt_rmx.rmx_mtu;
73133965Sjdp		if (mtu > ifmtu) {
73233965Sjdp			/*
73360484Sobrien			 * The MTU on the route is larger than the MTU on
73433965Sjdp			 * the interface!  This shouldn't happen, unless the
73533965Sjdp			 * MTU of the interface has been changed after the
73633965Sjdp			 * interface was brought up.  Change the MTU in the
73733965Sjdp			 * route to match the interface MTU (as long as the
73833965Sjdp			 * field isn't locked).
73933965Sjdp			 */
74033965Sjdp			 mtu = ifmtu;
74133965Sjdp			 if ((ro_pmtu->ro_rt->rt_rmx.rmx_locks & RTV_MTU) == 0)
74233965Sjdp				 ro_pmtu->ro_rt->rt_rmx.rmx_mtu = mtu; /* XXX */
74333965Sjdp		}
74433965Sjdp	} else {
74533965Sjdp		mtu = nd_ifinfo[ifp->if_index].linkmtu;
74633965Sjdp	}
74733965Sjdp
74833965Sjdp	/*
74933965Sjdp	 * Fake link-local scope-class addresses
75033965Sjdp	 */
75133965Sjdp	if ((ifp->if_flags & IFF_LOOPBACK) == 0) {
75233965Sjdp		if (IN6_IS_SCOPE_LINKLOCAL(&ip6->ip6_src))
75333965Sjdp			ip6->ip6_src.s6_addr16[1] = 0;
75433965Sjdp		if (IN6_IS_SCOPE_LINKLOCAL(&ip6->ip6_dst))
75533965Sjdp			ip6->ip6_dst.s6_addr16[1] = 0;
75633965Sjdp	}
75760484Sobrien
75833965Sjdp#ifdef IPV6FIREWALL
75933965Sjdp	/*
76033965Sjdp	 * Check with the firewall...
76168765Sobrien	 */
76233965Sjdp	if (ip6_fw_chk_ptr) {
76333965Sjdp		u_short port = 0;
76433965Sjdp		/* If ipfw says divert, we have to just drop packet */
76533965Sjdp		if ((*ip6_fw_chk_ptr)(&ip6, ifp, &port, &m)) {
76633965Sjdp			m_freem(m);
76733965Sjdp			goto done;
76833965Sjdp		}
769218822Sdim		if (!m) {
77033965Sjdp			error = EACCES;
77160484Sobrien			goto done;
77233965Sjdp		}
77333965Sjdp	}
77433965Sjdp#endif
77577298Sobrien
77633965Sjdp	/*
77760484Sobrien	 * If the outgoing packet contains a hop-by-hop options header,
77833965Sjdp	 * it must be examined and processed even by the source node.
77933965Sjdp	 * (RFC 2460, section 4.)
78033965Sjdp	 */
78133965Sjdp	if (exthdrs.ip6e_hbh) {
78233965Sjdp		struct ip6_hbh *hbh = mtod(exthdrs.ip6e_hbh,
78333965Sjdp					   struct ip6_hbh *);
78433965Sjdp		u_int32_t dummy1; /* XXX unused */
78568765Sobrien		u_int32_t dummy2; /* XXX unused */
78668765Sobrien
78768765Sobrien		/*
78868765Sobrien		 *  XXX: if we have to send an ICMPv6 error to the sender,
789218822Sdim		 *       we need the M_LOOP flag since icmp6_error() expects
79068765Sobrien		 *       the IPv6 and the hop-by-hop options header are
79189857Sobrien		 *       continuous unless the flag is set.
79268765Sobrien		 */
79368765Sobrien		m->m_flags |= M_LOOP;
79468765Sobrien		m->m_pkthdr.rcvif = ifp;
79568765Sobrien		if (ip6_process_hopopts(m,
79668765Sobrien					(u_int8_t *)(hbh + 1),
79768765Sobrien					((hbh->ip6h_len + 1) << 3) -
79868765Sobrien					sizeof(struct ip6_hbh),
79968765Sobrien					&dummy1, &dummy2) < 0) {
80068765Sobrien			/* m was already freed at this point */
80168765Sobrien			error = EINVAL;/* better error? */
80268765Sobrien			goto done;
80368765Sobrien		}
80468765Sobrien		m->m_flags &= ~M_LOOP; /* XXX */
80568765Sobrien		m->m_pkthdr.rcvif = NULL;
806218822Sdim	}
80768765Sobrien
80889857Sobrien	/*
80968765Sobrien	 * Send the packet to the outgoing interface.
81068765Sobrien	 * If necessary, do IPv6 fragmentation before sending.
81168765Sobrien	 */
81268765Sobrien	tlen = m->m_pkthdr.len;
81368765Sobrien	if (tlen <= mtu
81468765Sobrien#ifdef notyet
81568765Sobrien	    /*
81668765Sobrien	     * On any link that cannot convey a 1280-octet packet in one piece,
81733965Sjdp	     * link-specific fragmentation and reassembly must be provided at
81833965Sjdp	     * a layer below IPv6. [RFC 2460, sec.5]
81933965Sjdp	     * Thus if the interface has ability of link-level fragmentation,
82089857Sobrien	     * we can just send the packet even if the packet size is
82133965Sjdp	     * larger than the link's MTU.
82233965Sjdp	     * XXX: IFF_FRAGMENTABLE (or such) flag has not been defined yet...
82333965Sjdp	     */
82433965Sjdp
82533965Sjdp	    || ifp->if_flags & IFF_FRAGMENTABLE
82633965Sjdp#endif
82733965Sjdp	    )
82833965Sjdp	{
82933965Sjdp#if defined(__NetBSD__) && defined(IFA_STATS)
83033965Sjdp		if (IFA_STATS) {
83133965Sjdp			struct in6_ifaddr *ia6;
83260484Sobrien			ip6 = mtod(m, struct ip6_hdr *);
83333965Sjdp			ia6 = in6_ifawithifp(ifp, &ip6->ip6_src);
83433965Sjdp			if (ia6) {
83533965Sjdp				ia->ia_ifa.ifa_data.ifad_outbytes +=
83633965Sjdp					m->m_pkthdr.len;
83733965Sjdp			}
83833965Sjdp		}
83933965Sjdp#endif
84033965Sjdp		error = nd6_output(ifp, m, dst, ro->ro_rt);
84133965Sjdp		goto done;
84233965Sjdp	} else if (mtu < IPV6_MMTU) {
84333965Sjdp		/*
84433965Sjdp		 * note that path MTU is never less than IPV6_MMTU
84533965Sjdp		 * (see icmp6_input).
846218822Sdim		 */
84733965Sjdp		error = EMSGSIZE;
84860484Sobrien		in6_ifstat_inc(ifp, ifs6_out_fragfail);
84960484Sobrien		goto bad;
85089857Sobrien	} else if (ip6->ip6_plen == 0) { /* jumbo payload cannot be fragmented */
85189857Sobrien		error = EMSGSIZE;
85289857Sobrien		in6_ifstat_inc(ifp, ifs6_out_fragfail);
85389857Sobrien		goto bad;
85460484Sobrien	} else {
85577298Sobrien		struct mbuf **mnext, *m_frgpart;
85677298Sobrien		struct ip6_frag *ip6f;
85777298Sobrien		u_int32_t id = htonl(ip6_id++);
85860484Sobrien		u_char nextproto;
85977298Sobrien
86077298Sobrien		/*
86177298Sobrien		 * Too large for the destination or interface;
86291041Sobrien		 * fragment if possible.
86377298Sobrien		 * Must be able to put at least 8 bytes per fragment.
86477298Sobrien		 */
86577298Sobrien		hlen = unfragpartlen;
86668765Sobrien		if (mtu > IPV6_MAXPACKET)
86789857Sobrien			mtu = IPV6_MAXPACKET;
86889857Sobrien		len = (mtu - hlen - sizeof(struct ip6_frag)) & ~7;
86989857Sobrien		if (len < 8) {
87089857Sobrien			error = EMSGSIZE;
87189857Sobrien			in6_ifstat_inc(ifp, ifs6_out_fragfail);
87289857Sobrien			goto bad;
87389857Sobrien		}
87477298Sobrien
87577298Sobrien		mnext = &m->m_nextpkt;
87677298Sobrien
87760484Sobrien		/*
87860484Sobrien		 * Change the next header field of the last header in the
87960484Sobrien		 * unfragmentable part.
88060484Sobrien		 */
88160484Sobrien		if (exthdrs.ip6e_rthdr) {
88260484Sobrien			nextproto = *mtod(exthdrs.ip6e_rthdr, u_char *);
88377298Sobrien			*mtod(exthdrs.ip6e_rthdr, u_char *) = IPPROTO_FRAGMENT;
88477298Sobrien		} else if (exthdrs.ip6e_dest1) {
88577298Sobrien			nextproto = *mtod(exthdrs.ip6e_dest1, u_char *);
88677298Sobrien			*mtod(exthdrs.ip6e_dest1, u_char *) = IPPROTO_FRAGMENT;
88777298Sobrien		} else if (exthdrs.ip6e_hbh) {
888218822Sdim			nextproto = *mtod(exthdrs.ip6e_hbh, u_char *);
88977298Sobrien			*mtod(exthdrs.ip6e_hbh, u_char *) = IPPROTO_FRAGMENT;
89077298Sobrien		} else {
89177298Sobrien			nextproto = ip6->ip6_nxt;
89277298Sobrien			ip6->ip6_nxt = IPPROTO_FRAGMENT;
89377298Sobrien		}
89477298Sobrien
895218822Sdim		/*
89677298Sobrien		 * Loop through length of segment after first fragment,
89777298Sobrien		 * make new header and copy data of each part and link onto chain.
89877298Sobrien		 */
89977298Sobrien		m0 = m;
90077298Sobrien		for (off = hlen; off < tlen; off += len) {
90177298Sobrien			MGETHDR(m, M_DONTWAIT, MT_HEADER);
90277298Sobrien			if (!m) {
90377298Sobrien				error = ENOBUFS;
90477298Sobrien				ip6stat.ip6s_odropped++;
90577298Sobrien				goto sendorfree;
90677298Sobrien			}
90777298Sobrien			m->m_flags = m0->m_flags & M_COPYFLAGS;
908218822Sdim			*mnext = m;
90977298Sobrien			mnext = &m->m_nextpkt;
91077298Sobrien			m->m_data += max_linkhdr;
91177298Sobrien			mhip6 = mtod(m, struct ip6_hdr *);
91277298Sobrien			*mhip6 = *ip6;
91377298Sobrien			m->m_len = sizeof(*mhip6);
91477298Sobrien 			error = ip6_insertfraghdr(m0, m, hlen, &ip6f);
915130561Sobrien 			if (error) {
91677298Sobrien				ip6stat.ip6s_odropped++;
91777298Sobrien				goto sendorfree;
91877298Sobrien			}
91977298Sobrien			ip6f->ip6f_offlg = htons((u_short)((off - hlen) & ~7));
92077298Sobrien			if (off + len >= tlen)
92177298Sobrien				len = tlen - off;
92277298Sobrien			else
92377298Sobrien				ip6f->ip6f_offlg |= IP6F_MORE_FRAG;
92477298Sobrien			mhip6->ip6_plen = htons((u_short)(len + hlen +
92577298Sobrien							  sizeof(*ip6f) -
92677298Sobrien							  sizeof(struct ip6_hdr)));
92777298Sobrien			if ((m_frgpart = m_copy(m0, off, len)) == 0) {
92877298Sobrien				error = ENOBUFS;
92977298Sobrien				ip6stat.ip6s_odropped++;
93077298Sobrien				goto sendorfree;
93177298Sobrien			}
93277298Sobrien			m_cat(m, m_frgpart);
93377298Sobrien			m->m_pkthdr.len = len + hlen + sizeof(*ip6f);
93477298Sobrien			m->m_pkthdr.rcvif = (struct ifnet *)0;
93577298Sobrien			ip6f->ip6f_reserved = 0;
93677298Sobrien			ip6f->ip6f_ident = id;
93777298Sobrien			ip6f->ip6f_nxt = nextproto;
93877298Sobrien			ip6stat.ip6s_ofragments++;
93977298Sobrien			in6_ifstat_inc(ifp, ifs6_out_fragcreat);
94077298Sobrien		}
94177298Sobrien
94277298Sobrien		in6_ifstat_inc(ifp, ifs6_out_fragok);
943130561Sobrien	}
944130561Sobrien
94577298Sobrien	/*
94677298Sobrien	 * Remove leading garbages.
94777298Sobrien	 */
94877298Sobriensendorfree:
94977298Sobrien	m = m0->m_nextpkt;
95077298Sobrien	m0->m_nextpkt = 0;
95177298Sobrien	m_freem(m0);
95277298Sobrien	for (m0 = m; m; m = m0) {
95377298Sobrien		m0 = m->m_nextpkt;
95477298Sobrien		m->m_nextpkt = 0;
95577298Sobrien		if (error == 0) {
95677298Sobrien#if defined(__NetBSD__) && defined(IFA_STATS)
95777298Sobrien			if (IFA_STATS) {
95877298Sobrien				struct in6_ifaddr *ia6;
95977298Sobrien				ip6 = mtod(m, struct ip6_hdr *);
96077298Sobrien				ia6 = in6_ifawithifp(ifp, &ip6->ip6_src);
96177298Sobrien				if (ia6) {
96277298Sobrien					ia->ia_ifa.ifa_data.ifad_outbytes +=
96377298Sobrien						m->m_pkthdr.len;
96477298Sobrien				}
96577298Sobrien			}
96677298Sobrien#endif
96777298Sobrien			error = nd6_output(ifp, m, dst, ro->ro_rt);
96877298Sobrien		} else
96977298Sobrien			m_freem(m);
97077298Sobrien	}
971218822Sdim
97277298Sobrien	if (error == 0)
97377298Sobrien		ip6stat.ip6s_fragmented++;
97477298Sobrien
97577298Sobriendone:
97677298Sobrien	if (ro == &ip6route && ro->ro_rt) { /* brace necessary for RTFREE */
97777298Sobrien		RTFREE(ro->ro_rt);
97877298Sobrien	} else if (ro_pmtu == &ip6route && ro_pmtu->ro_rt) {
97977298Sobrien		RTFREE(ro_pmtu->ro_rt);
98077298Sobrien	}
98177298Sobrien
98277298Sobrien#ifdef IPSEC
98377298Sobrien	if (sp != NULL)
98477298Sobrien		key_freesp(sp);
98577298Sobrien#endif /* IPSEC */
98677298Sobrien
98777298Sobrien	return(error);
98877298Sobrien
98977298Sobrienfreehdrs:
99077298Sobrien	m_freem(exthdrs.ip6e_hbh);	/* m_freem will check if mbuf is 0 */
99177298Sobrien	m_freem(exthdrs.ip6e_dest1);
992130561Sobrien	m_freem(exthdrs.ip6e_rthdr);
993130561Sobrien	m_freem(exthdrs.ip6e_dest2);
99477298Sobrien	/* fall through */
995130561Sobrienbad:
99677298Sobrien	m_freem(m);
99777298Sobrien	goto done;
99877298Sobrien}
99977298Sobrien
100077298Sobrienstatic int
100177298Sobrienip6_copyexthdr(mp, hdr, hlen)
100277298Sobrien	struct mbuf **mp;
100377298Sobrien	caddr_t hdr;
100460484Sobrien	int hlen;
100560484Sobrien{
100660484Sobrien	struct mbuf *m;
1007130561Sobrien
100860484Sobrien	if (hlen > MCLBYTES)
100960484Sobrien		return(ENOBUFS); /* XXX */
101060484Sobrien
1011218822Sdim	MGET(m, M_DONTWAIT, MT_DATA);
101260484Sobrien	if (!m)
101360484Sobrien		return(ENOBUFS);
101433965Sjdp
101533965Sjdp	if (hlen > MLEN) {
101633965Sjdp		MCLGET(m, M_DONTWAIT);
101777298Sobrien		if ((m->m_flags & M_EXT) == 0) {
101860484Sobrien			m_free(m);
101960484Sobrien			return(ENOBUFS);
102060484Sobrien		}
102160484Sobrien	}
102260484Sobrien	m->m_len = hlen;
102360484Sobrien	if (hdr)
102460484Sobrien		bcopy(hdr, mtod(m, caddr_t), hlen);
102533965Sjdp
102633965Sjdp	*mp = m;
102733965Sjdp	return(0);
102833965Sjdp}
102933965Sjdp
103033965Sjdp/*
103133965Sjdp * Insert jumbo payload option.
103233965Sjdp */
103333965Sjdpstatic int
103433965Sjdpip6_insert_jumboopt(exthdrs, plen)
103533965Sjdp	struct ip6_exthdrs *exthdrs;
103633965Sjdp	u_int32_t plen;
103733965Sjdp{
103833965Sjdp	struct mbuf *mopt;
103933965Sjdp	u_char *optbuf;
104033965Sjdp
104133965Sjdp#define JUMBOOPTLEN	8	/* length of jumbo payload option and padding */
104233965Sjdp
104333965Sjdp	/*
104433965Sjdp	 * If there is no hop-by-hop options header, allocate new one.
104533965Sjdp	 * If there is one but it doesn't have enough space to store the
104633965Sjdp	 * jumbo payload option, allocate a cluster to store the whole options.
104733965Sjdp	 * Otherwise, use it to store the options.
104833965Sjdp	 */
104933965Sjdp	if (exthdrs->ip6e_hbh == 0) {
105033965Sjdp		MGET(mopt, M_DONTWAIT, MT_DATA);
105160484Sobrien		if (mopt == 0)
105233965Sjdp			return(ENOBUFS);
105333965Sjdp		mopt->m_len = JUMBOOPTLEN;
105433965Sjdp		optbuf = mtod(mopt, u_char *);
105533965Sjdp		optbuf[1] = 0;	/* = ((JUMBOOPTLEN) >> 3) - 1 */
105660484Sobrien		exthdrs->ip6e_hbh = mopt;
105733965Sjdp	} else {
105833965Sjdp		struct ip6_hbh *hbh;
105960484Sobrien
106060484Sobrien		mopt = exthdrs->ip6e_hbh;
106160484Sobrien		if (M_TRAILINGSPACE(mopt) < JUMBOOPTLEN) {
106260484Sobrien			caddr_t oldoptp = mtod(mopt, caddr_t);
106360484Sobrien			int oldoptlen = mopt->m_len;
106433965Sjdp
106533965Sjdp			if (mopt->m_flags & M_EXT)
106660484Sobrien				return(ENOBUFS); /* XXX */
106760484Sobrien			MCLGET(mopt, M_DONTWAIT);
106860484Sobrien			if ((mopt->m_flags & M_EXT) == 0)
106960484Sobrien				return(ENOBUFS);
107077298Sobrien
107133965Sjdp			bcopy(oldoptp, mtod(mopt, caddr_t), oldoptlen);
107233965Sjdp			optbuf = mtod(mopt, caddr_t) + oldoptlen;
107360484Sobrien			mopt->m_len = oldoptlen + JUMBOOPTLEN;
107460484Sobrien		} else {
107560484Sobrien			optbuf = mtod(mopt, u_char *) + mopt->m_len;
1076218822Sdim			mopt->m_len += JUMBOOPTLEN;
107760484Sobrien		}
107860484Sobrien		optbuf[0] = IP6OPT_PADN;
107960484Sobrien		optbuf[1] = 1;
108060484Sobrien
108160484Sobrien		/*
108260484Sobrien		 * Adjust the header length according to the pad and
108360484Sobrien		 * the jumbo payload option.
108460484Sobrien		 */
108560484Sobrien		hbh = mtod(mopt, struct ip6_hbh *);
108660484Sobrien		hbh->ip6h_len += (JUMBOOPTLEN >> 3);
108760484Sobrien	}
108860484Sobrien
108960484Sobrien	/* fill in the option. */
109060484Sobrien	optbuf[2] = IP6OPT_JUMBO;
109168765Sobrien	optbuf[3] = 4;
109268765Sobrien	*(u_int32_t *)&optbuf[4] = htonl(plen + JUMBOOPTLEN);
109368765Sobrien
1094218822Sdim	/* finally, adjust the packet header length */
109568765Sobrien	exthdrs->ip6e_ip6->m_pkthdr.len += JUMBOOPTLEN;
109668765Sobrien
109768765Sobrien	return(0);
109868765Sobrien#undef JUMBOOPTLEN
109968765Sobrien}
110068765Sobrien
110168765Sobrien/*
110268765Sobrien * Insert fragment header and copy unfragmentable header portions.
110368765Sobrien */
110468765Sobrienstatic int
1105218822Sdimip6_insertfraghdr(m0, m, hlen, frghdrp)
110668765Sobrien	struct mbuf *m0, *m;
110768765Sobrien	int hlen;
110868765Sobrien	struct ip6_frag **frghdrp;
110968765Sobrien{
111068765Sobrien	struct mbuf *n, *mlast;
1111218822Sdim
111268765Sobrien	if (hlen > sizeof(struct ip6_hdr)) {
111368765Sobrien		n = m_copym(m0, sizeof(struct ip6_hdr),
111468765Sobrien			    hlen - sizeof(struct ip6_hdr), M_DONTWAIT);
111568765Sobrien		if (n == 0)
1116218822Sdim			return(ENOBUFS);
111768765Sobrien		m->m_next = n;
111868765Sobrien	} else
111968765Sobrien		n = m;
112068765Sobrien
112168765Sobrien	/* Search for the last mbuf of unfragmentable part. */
1122218822Sdim	for (mlast = n; mlast->m_next; mlast = mlast->m_next)
112368765Sobrien		;
112468765Sobrien
112568765Sobrien	if ((mlast->m_flags & M_EXT) == 0 &&
112668765Sobrien	    M_TRAILINGSPACE(mlast) < sizeof(struct ip6_frag)) {
1127218822Sdim		/* use the trailing space of the last mbuf for the fragment hdr */
112868765Sobrien		*frghdrp =
112968765Sobrien			(struct ip6_frag *)(mtod(mlast, caddr_t) + mlast->m_len);
113068765Sobrien		mlast->m_len += sizeof(struct ip6_frag);
113168765Sobrien		m->m_pkthdr.len += sizeof(struct ip6_frag);
113268765Sobrien	} else {
1133218822Sdim		/* allocate a new mbuf for the fragment header */
113468765Sobrien		struct mbuf *mfrg;
113568765Sobrien
113668765Sobrien		MGET(mfrg, M_DONTWAIT, MT_DATA);
113768765Sobrien		if (mfrg == 0)
1138218822Sdim			return(ENOBUFS);
113968765Sobrien		mfrg->m_len = sizeof(struct ip6_frag);
114068765Sobrien		*frghdrp = mtod(mfrg, struct ip6_frag *);
114168765Sobrien		mlast->m_next = mfrg;
114268765Sobrien	}
114368765Sobrien
1144218822Sdim	return(0);
114568765Sobrien}
114668765Sobrien
114768765Sobrien/*
114868765Sobrien * IP6 socket option processing.
114968765Sobrien */
1150218822Sdimint
115168765Sobrienip6_ctloutput(so, sopt)
115268765Sobrien	struct socket *so;
115368765Sobrien	struct sockopt *sopt;
115468765Sobrien{
115568765Sobrien	int privileged;
115668765Sobrien	register struct inpcb *in6p = sotoinpcb(so);
115768765Sobrien	int error, optval;
115868765Sobrien	int level, op, optname;
115968765Sobrien	int optlen;
1160218822Sdim	struct proc *p;
116133965Sjdp
116233965Sjdp	if (sopt) {
116360484Sobrien		level = sopt->sopt_level;
116433965Sjdp		op = sopt->sopt_dir;
116533965Sjdp		optname = sopt->sopt_name;
116633965Sjdp		optlen = sopt->sopt_valsize;
116733965Sjdp		p = sopt->sopt_p;
116860484Sobrien	} else {
116960484Sobrien		panic("ip6_ctloutput: arg soopt is NULL");
117033965Sjdp	}
117138889Sjdp	error = optval = 0;
117238889Sjdp
117338889Sjdp	privileged = (p == 0 || suser(p)) ? 0 : 1;
117438889Sjdp
117538889Sjdp	if (level == IPPROTO_IPV6) {
117638889Sjdp		switch (op) {
117738889Sjdp		case SOPT_SET:
117860484Sobrien			switch (optname) {
117938889Sjdp			case IPV6_PKTOPTIONS:
118060484Sobrien			    {
118138889Sjdp				struct mbuf *m;
118260484Sobrien
118360484Sobrien				error = soopt_getm(sopt, &m); /* XXX */
118460484Sobrien				if (error != 0)
118560484Sobrien					break;
118660484Sobrien				error = soopt_mcopyin(sopt, m); /* XXX */
118760484Sobrien				if (error != 0)
118868765Sobrien					break;
118938889Sjdp				return (ip6_pcbopts(&in6p->in6p_outputopts,
119068765Sobrien						    m, so, sopt));
119168765Sobrien			    }
119268765Sobrien			case IPV6_HOPOPTS:
1193218822Sdim			case IPV6_DSTOPTS:
119468765Sobrien				if (!privileged) {
119568765Sobrien					error = EPERM;
119668765Sobrien					break;
119768765Sobrien				}
119868765Sobrien				/* fall through */
119968765Sobrien			case IPV6_UNICAST_HOPS:
120068765Sobrien			case IPV6_RECVOPTS:
120168765Sobrien			case IPV6_RECVRETOPTS:
120268765Sobrien			case IPV6_RECVDSTADDR:
1203218822Sdim			case IPV6_PKTINFO:
120468765Sobrien			case IPV6_HOPLIMIT:
120568765Sobrien			case IPV6_RTHDR:
120668765Sobrien			case IPV6_CHECKSUM:
120768765Sobrien			case IPV6_FAITH:
120868765Sobrien			case IPV6_BINDV6ONLY:
120933965Sjdp				if (optlen != sizeof(int))
121033965Sjdp					error = EINVAL;
121160484Sobrien				else {
121233965Sjdp					error = sooptcopyin(sopt, &optval,
121333965Sjdp						sizeof optval, sizeof optval);
121433965Sjdp					if (error)
121533965Sjdp						break;
121633965Sjdp					switch (optname) {
121733965Sjdp
121833965Sjdp					case IPV6_UNICAST_HOPS:
121977298Sobrien						if (optval < -1 || optval >= 256)
122033965Sjdp							error = EINVAL;
122133965Sjdp						else {
122233965Sjdp							/* -1 = kernel default */
122333965Sjdp							in6p->in6p_hops = optval;
122433965Sjdp							if ((in6p->in6p_vflag &
122533965Sjdp							     INP_IPV4) != 0)
122633965Sjdp								in6p->inp_ip_ttl = optval;
122733965Sjdp						}
122833965Sjdp						break;
122933965Sjdp#define OPTSET(bit) \
123033965Sjdp	if (optval) \
123133965Sjdp		in6p->in6p_flags |= bit; \
123233965Sjdp	else \
123333965Sjdp		in6p->in6p_flags &= ~bit;
123433965Sjdp
123533965Sjdp					case IPV6_RECVOPTS:
123633965Sjdp						OPTSET(IN6P_RECVOPTS);
123733965Sjdp						break;
123833965Sjdp
123933965Sjdp					case IPV6_RECVRETOPTS:
124033965Sjdp						OPTSET(IN6P_RECVRETOPTS);
124133965Sjdp						break;
124233965Sjdp
124333965Sjdp					case IPV6_RECVDSTADDR:
124433965Sjdp						OPTSET(IN6P_RECVDSTADDR);
124533965Sjdp						break;
124633965Sjdp
124733965Sjdp					case IPV6_PKTINFO:
124833965Sjdp						OPTSET(IN6P_PKTINFO);
124933965Sjdp						break;
125033965Sjdp
125133965Sjdp					case IPV6_HOPLIMIT:
125233965Sjdp						OPTSET(IN6P_HOPLIMIT);
125333965Sjdp						break;
125433965Sjdp
1255218822Sdim					case IPV6_HOPOPTS:
1256218822Sdim						OPTSET(IN6P_HOPOPTS);
125733965Sjdp						break;
125833965Sjdp
125933965Sjdp					case IPV6_DSTOPTS:
126033965Sjdp						OPTSET(IN6P_DSTOPTS);
126138889Sjdp						break;
126233965Sjdp
126333965Sjdp					case IPV6_RTHDR:
126433965Sjdp						OPTSET(IN6P_RTHDR);
126533965Sjdp						break;
126633965Sjdp
126733965Sjdp					case IPV6_CHECKSUM:
126833965Sjdp						in6p->in6p_cksum = optval;
126933965Sjdp						break;
127033965Sjdp
127133965Sjdp					case IPV6_FAITH:
127233965Sjdp						OPTSET(IN6P_FAITH);
127333965Sjdp						break;
127460484Sobrien
127533965Sjdp					case IPV6_BINDV6ONLY:
127660484Sobrien						OPTSET(IN6P_BINDV6ONLY);
127760484Sobrien						break;
127860484Sobrien					}
127960484Sobrien				}
128060484Sobrien				break;
128160484Sobrien#undef OPTSET
128260484Sobrien
128360484Sobrien			case IPV6_MULTICAST_IF:
128433965Sjdp			case IPV6_MULTICAST_HOPS:
128533965Sjdp			case IPV6_MULTICAST_LOOP:
128633965Sjdp			case IPV6_JOIN_GROUP:
128733965Sjdp			case IPV6_LEAVE_GROUP:
128833965Sjdp			    {
128960484Sobrien				struct mbuf *m;
129033965Sjdp				if (sopt->sopt_valsize > MLEN) {
129133965Sjdp					error = EMSGSIZE;
129233965Sjdp					break;
129333965Sjdp				}
129433965Sjdp				/* XXX */
129533965Sjdp				MGET(m, sopt->sopt_p ? M_WAIT : M_DONTWAIT, MT_HEADER);
129633965Sjdp				if (m == 0) {
129733965Sjdp					error = ENOBUFS;
129833965Sjdp					break;
129933965Sjdp				}
130033965Sjdp				m->m_len = sopt->sopt_valsize;
130160484Sobrien				error = sooptcopyin(sopt, mtod(m, char *),
130260484Sobrien						    m->m_len, m->m_len);
130360484Sobrien				error =	ip6_setmoptions(sopt->sopt_name,
130460484Sobrien							&in6p->in6p_moptions,
130560484Sobrien							m);
130633965Sjdp				(void)m_free(m);
130760484Sobrien			    }
130860484Sobrien				break;
130960484Sobrien
131060484Sobrien		case IPV6_PORTRANGE:
131160484Sobrien			error = sooptcopyin(sopt, &optval, sizeof optval,
131260484Sobrien					    sizeof optval);
131360484Sobrien			if (error)
131433965Sjdp				break;
131560484Sobrien
131660484Sobrien			switch (optval) {
131760484Sobrien			case IPV6_PORTRANGE_DEFAULT:
131860484Sobrien				in6p->in6p_flags &= ~(IN6P_LOWPORT);
131960484Sobrien				in6p->in6p_flags &= ~(IN6P_HIGHPORT);
132060484Sobrien				break;
132133965Sjdp
132260484Sobrien			case IPV6_PORTRANGE_HIGH:
132360484Sobrien				in6p->in6p_flags &= ~(IN6P_LOWPORT);
132433965Sjdp				in6p->in6p_flags |= IN6P_HIGHPORT;
132560484Sobrien				break;
132633965Sjdp
132733965Sjdp			case IPV6_PORTRANGE_LOW:
132833965Sjdp				in6p->in6p_flags &= ~(IN6P_HIGHPORT);
132933965Sjdp				in6p->in6p_flags |= IN6P_LOWPORT;
133033965Sjdp				break;
133133965Sjdp
133260484Sobrien			default:
133333965Sjdp				error = EINVAL;
133433965Sjdp				break;
133533965Sjdp			}
133633965Sjdp			break;
133733965Sjdp
133860484Sobrien#ifdef IPSEC
133933965Sjdp			case IPV6_IPSEC_POLICY:
134060484Sobrien			    {
134160484Sobrien				caddr_t req = NULL;
134260484Sobrien				struct mbuf *m;
134360484Sobrien
134433965Sjdp				if ((error = soopt_getm(sopt, &m))
134533965Sjdp				    != 0) /* XXX */
134633965Sjdp					break;
134760484Sobrien				if ((error = soopt_mcopyin(sopt, m))
134860484Sobrien				    != 0) /* XXX */
134960484Sobrien					break;
135060484Sobrien				if (m != 0)
135160484Sobrien					req = mtod(m, caddr_t);
135260484Sobrien				error = ipsec6_set_policy(in6p, optname, req,
135360484Sobrien				                          privileged);
135460484Sobrien				m_freem(m);
135560484Sobrien			    }
1356130561Sobrien				break;
135760484Sobrien#endif /* IPSEC */
135860484Sobrien
135960484Sobrien#ifdef IPV6FIREWALL
136060484Sobrien			case IPV6_FW_ADD:
136160484Sobrien			case IPV6_FW_DEL:
136260484Sobrien			case IPV6_FW_FLUSH:
136333965Sjdp			case IPV6_FW_ZERO:
136433965Sjdp			    {
136560484Sobrien				struct mbuf *m;
136633965Sjdp				struct mbuf **mp = &m;
136733965Sjdp
136833965Sjdp				if (ip6_fw_ctl_ptr == NULL)
136933965Sjdp					return EINVAL;
137033965Sjdp				if ((error = soopt_getm(sopt, &m))
137133965Sjdp				    != 0) /* XXX */
137233965Sjdp					break;
137333965Sjdp				if ((error = soopt_mcopyin(sopt, m))
137460484Sobrien				    != 0) /* XXX */
137533965Sjdp					break;
137633965Sjdp				error = (*ip6_fw_ctl_ptr)(optname, mp);
137733965Sjdp				m = *mp;
137833965Sjdp			    }
137960484Sobrien				break;
138033965Sjdp#endif
138133965Sjdp
138233965Sjdp			default:
138333965Sjdp				error = ENOPROTOOPT;
138460484Sobrien				break;
138560484Sobrien			}
138660484Sobrien			break;
138760484Sobrien
138860484Sobrien		case SOPT_GET:
138960484Sobrien			switch (optname) {
139060484Sobrien
139160484Sobrien			case IPV6_OPTIONS:
139260484Sobrien			case IPV6_RETOPTS:
139360484Sobrien				error = ENOPROTOOPT;
139460484Sobrien				break;
139533965Sjdp
139660484Sobrien			case IPV6_PKTOPTIONS:
139733965Sjdp				if (in6p->in6p_options) {
139833965Sjdp					error = soopt_mcopyout(sopt,
139960484Sobrien							       in6p->in6p_options);
140033965Sjdp				} else
140133965Sjdp					sopt->sopt_valsize = 0;
140233965Sjdp				break;
140333965Sjdp
140433965Sjdp			case IPV6_HOPOPTS:
140560484Sobrien			case IPV6_DSTOPTS:
140660484Sobrien				if (!privileged) {
140733965Sjdp					error = EPERM;
140833965Sjdp					break;
140933965Sjdp				}
141033965Sjdp				/* fall through */
141160484Sobrien			case IPV6_UNICAST_HOPS:
141260484Sobrien			case IPV6_RECVOPTS:
141333965Sjdp			case IPV6_RECVRETOPTS:
141433965Sjdp			case IPV6_RECVDSTADDR:
141533965Sjdp			case IPV6_PKTINFO:
141633965Sjdp			case IPV6_HOPLIMIT:
141733965Sjdp			case IPV6_RTHDR:
141833965Sjdp			case IPV6_CHECKSUM:
141933965Sjdp			case IPV6_FAITH:
142033965Sjdp			case IPV6_BINDV6ONLY:
142133965Sjdp			case IPV6_PORTRANGE:
142233965Sjdp				switch (optname) {
142333965Sjdp
142433965Sjdp				case IPV6_UNICAST_HOPS:
142533965Sjdp					optval = in6p->in6p_hops;
142633965Sjdp					break;
142733965Sjdp
142833965Sjdp#define OPTBIT(bit) (in6p->in6p_flags & bit ? 1 : 0)
142933965Sjdp
143033965Sjdp				case IPV6_RECVOPTS:
143133965Sjdp					optval = OPTBIT(IN6P_RECVOPTS);
143268765Sobrien					break;
143338889Sjdp
143438889Sjdp				case IPV6_RECVRETOPTS:
143538889Sjdp					optval = OPTBIT(IN6P_RECVRETOPTS);
143638889Sjdp					break;
143738889Sjdp
143838889Sjdp				case IPV6_RECVDSTADDR:
143938889Sjdp					optval = OPTBIT(IN6P_RECVDSTADDR);
144038889Sjdp					break;
144138889Sjdp
144238889Sjdp				case IPV6_PKTINFO:
144338889Sjdp					optval = OPTBIT(IN6P_PKTINFO);
144438889Sjdp					break;
144538889Sjdp
144638889Sjdp				case IPV6_HOPLIMIT:
144738889Sjdp					optval = OPTBIT(IN6P_HOPLIMIT);
144838889Sjdp					break;
144938889Sjdp
145038889Sjdp				case IPV6_HOPOPTS:
145160484Sobrien					optval = OPTBIT(IN6P_HOPOPTS);
145260484Sobrien					break;
145360484Sobrien
145460484Sobrien				case IPV6_DSTOPTS:
145560484Sobrien					optval = OPTBIT(IN6P_DSTOPTS);
145660484Sobrien					break;
145777298Sobrien
145860484Sobrien				case IPV6_RTHDR:
145960484Sobrien					optval = OPTBIT(IN6P_RTHDR);
146060484Sobrien					break;
146160484Sobrien
146233965Sjdp				case IPV6_CHECKSUM:
146333965Sjdp					optval = in6p->in6p_cksum;
146438889Sjdp					break;
146568765Sobrien
146638889Sjdp				case IPV6_FAITH:
146738889Sjdp					optval = OPTBIT(IN6P_FAITH);
146860484Sobrien					break;
146960484Sobrien
147038889Sjdp				case IPV6_BINDV6ONLY:
147138889Sjdp					optval = OPTBIT(IN6P_BINDV6ONLY);
147238889Sjdp					break;
147338889Sjdp
147438889Sjdp				case IPV6_PORTRANGE:
147538889Sjdp				    {
147638889Sjdp					int flags;
147760484Sobrien
147838889Sjdp					flags = in6p->in6p_flags;
147933965Sjdp					if (flags & IN6P_HIGHPORT)
148033965Sjdp						optval = IPV6_PORTRANGE_HIGH;
148133965Sjdp					else if (flags & IN6P_LOWPORT)
148233965Sjdp						optval = IPV6_PORTRANGE_LOW;
148333965Sjdp					else
148433965Sjdp						optval = 0;
148533965Sjdp					break;
148633965Sjdp				    }
148733965Sjdp				}
148833965Sjdp				error = sooptcopyout(sopt, &optval,
148933965Sjdp					sizeof optval);
149033965Sjdp				break;
149160484Sobrien
149233965Sjdp			case IPV6_MULTICAST_IF:
149333965Sjdp			case IPV6_MULTICAST_HOPS:
149433965Sjdp			case IPV6_MULTICAST_LOOP:
149533965Sjdp			case IPV6_JOIN_GROUP:
149633965Sjdp			case IPV6_LEAVE_GROUP:
149733965Sjdp			    {
149833965Sjdp				struct mbuf *m;
149933965Sjdp				error = ip6_getmoptions(sopt->sopt_name,
150033965Sjdp						in6p->in6p_moptions, &m);
150133965Sjdp				if (error == 0)
150233965Sjdp					error = sooptcopyout(sopt,
150333965Sjdp						mtod(m, char *), m->m_len);
150460484Sobrien				m_freem(m);
150560484Sobrien			    }
150660484Sobrien				break;
150760484Sobrien
150833965Sjdp#ifdef IPSEC
150938889Sjdp			case IPV6_IPSEC_POLICY:
151038889Sjdp			  {
151138889Sjdp				caddr_t req = NULL;
151238889Sjdp				int len = 0;
151333965Sjdp				struct mbuf *m;
151433965Sjdp				struct mbuf **mp = &m;
151533965Sjdp
151633965Sjdp				if (m != 0) {
151733965Sjdp					req = mtod(m, caddr_t);
151833965Sjdp					len = m->m_len;
151933965Sjdp				}
152033965Sjdp				error = ipsec6_get_policy(in6p, req, mp);
152133965Sjdp				if (error == 0)
152233965Sjdp					error = soopt_mcopyout(sopt, m); /*XXX*/
152360484Sobrien				m_freem(m);
152433965Sjdp				break;
152533965Sjdp			  }
152633965Sjdp#endif /* IPSEC */
152733965Sjdp
152833965Sjdp#ifdef IPV6FIREWALL
152960484Sobrien			case IPV6_FW_GET:
153033965Sjdp			  {
153160484Sobrien				struct mbuf *m;
153260484Sobrien				struct mbuf **mp = &m;
153360484Sobrien
153460484Sobrien				if (ip6_fw_ctl_ptr == NULL)
153560484Sobrien			        {
153660484Sobrien					return EINVAL;
153760484Sobrien				}
153833965Sjdp				error = (*ip6_fw_ctl_ptr)(optname, mp);
153960484Sobrien				if (error == 0)
154033965Sjdp					error = soopt_mcopyout(sopt, m); /* XXX */
154133965Sjdp				if (m)
154233965Sjdp					m_freem(m);
154333965Sjdp			  }
154433965Sjdp				break;
154533965Sjdp#endif
1546218822Sdim
1547218822Sdim			default:
154833965Sjdp				error = ENOPROTOOPT;
154933965Sjdp				break;
155033965Sjdp			}
155133965Sjdp			break;
155233965Sjdp		}
155333965Sjdp	} else {
155433965Sjdp		error = EINVAL;
155533965Sjdp	}
155633965Sjdp	return(error);
155733965Sjdp}
155833965Sjdp
155933965Sjdp/*
156033965Sjdp * Set up IP6 options in pcb for insertion in output packets.
156133965Sjdp * Store in mbuf with pointer in pcbopt, adding pseudo-option
156233965Sjdp * with destination address if source routed.
156333965Sjdp */
156433965Sjdpstatic int
156533965Sjdpip6_pcbopts(pktopt, m, so, sopt)
156633965Sjdp	struct ip6_pktopts **pktopt;
1567218822Sdim	register struct mbuf *m;
1568218822Sdim	struct socket *so;
156960484Sobrien	struct sockopt *sopt;
157060484Sobrien{
157160484Sobrien	register struct ip6_pktopts *opt = *pktopt;
157260484Sobrien	int error = 0;
157360484Sobrien	struct proc *p = sopt->sopt_p;
157460484Sobrien	int priv = 0;
157560484Sobrien
157660484Sobrien	/* turn off any old options. */
157760484Sobrien	if (opt) {
157860484Sobrien		if (opt->ip6po_m)
157960484Sobrien			(void)m_free(opt->ip6po_m);
158060484Sobrien	} else
158160484Sobrien		opt = malloc(sizeof(*opt), M_IP6OPT, M_WAITOK);
158260484Sobrien	*pktopt = 0;
158360484Sobrien
158460484Sobrien	if (!m || m->m_len == 0) {
158560484Sobrien		/*
158660484Sobrien		 * Only turning off any previous options.
158760484Sobrien		 */
158860484Sobrien		if (opt)
158960484Sobrien			free(opt, M_IP6OPT);
159060484Sobrien		if (m)
159160484Sobrien			(void)m_free(m);
159260484Sobrien		return(0);
159360484Sobrien	}
159460484Sobrien
159560484Sobrien	/*  set options specified by user. */
159660484Sobrien	if (p && !suser(p))
159760484Sobrien		priv = 1;
159860484Sobrien	if ((error = ip6_setpktoptions(m, opt, priv)) != 0) {
159960484Sobrien		(void)m_free(m);
160060484Sobrien		return(error);
160160484Sobrien	}
160260484Sobrien	*pktopt = opt;
160360484Sobrien	return(0);
160460484Sobrien}
160560484Sobrien
160660484Sobrien/*
160760484Sobrien * Set the IP6 multicast options in response to user setsockopt().
160860484Sobrien */
160960484Sobrienstatic int
161060484Sobrienip6_setmoptions(optname, im6op, m)
161160484Sobrien	int optname;
161260484Sobrien	struct ip6_moptions **im6op;
161360484Sobrien	struct mbuf *m;
161460484Sobrien{
161560484Sobrien	int error = 0;
161660484Sobrien	u_int loop, ifindex;
161760484Sobrien	struct ipv6_mreq *mreq;
161860484Sobrien	struct ifnet *ifp;
161960484Sobrien	struct ip6_moptions *im6o = *im6op;
162060484Sobrien	struct route_in6 ro;
162160484Sobrien	struct sockaddr_in6 *dst;
162260484Sobrien	struct in6_multi_mship *imm;
162360484Sobrien	struct proc *p = curproc;	/* XXX */
162460484Sobrien
162560484Sobrien	if (im6o == NULL) {
162660484Sobrien		/*
162760484Sobrien		 * No multicast option buffer attached to the pcb;
162860484Sobrien		 * allocate one and initialize to default values.
1629218822Sdim		 */
1630218822Sdim		im6o = (struct ip6_moptions *)
163160484Sobrien			malloc(sizeof(*im6o), M_IPMOPTS, M_WAITOK);
163260484Sobrien
163360484Sobrien		if (im6o == NULL)
163460484Sobrien			return(ENOBUFS);
163560484Sobrien		*im6op = im6o;
163660484Sobrien		im6o->im6o_multicast_ifp = NULL;
163760484Sobrien		im6o->im6o_multicast_hlim = ip6_defmcasthlim;
163860484Sobrien		im6o->im6o_multicast_loop = IPV6_DEFAULT_MULTICAST_LOOP;
163960484Sobrien		LIST_INIT(&im6o->im6o_memberships);
164060484Sobrien	}
164160484Sobrien
164260484Sobrien	switch (optname) {
164360484Sobrien
164460484Sobrien	case IPV6_MULTICAST_IF:
164560484Sobrien		/*
164660484Sobrien		 * Select the interface for outgoing multicast packets.
164760484Sobrien		 */
164860484Sobrien		if (m == NULL || m->m_len != sizeof(u_int)) {
164977298Sobrien			error = EINVAL;
165060484Sobrien			break;
165160484Sobrien		}
165260484Sobrien		ifindex = *(mtod(m, u_int *));
165360484Sobrien		if (ifindex < 0 || if_index < ifindex) {
165460484Sobrien			error = ENXIO;	/* XXX EINVAL? */
165560484Sobrien			break;
165660484Sobrien		}
165760484Sobrien		ifp = ifindex2ifnet[ifindex];
165860484Sobrien		if (ifp == NULL || (ifp->if_flags & IFF_MULTICAST) == 0) {
165960484Sobrien			error = EADDRNOTAVAIL;
166060484Sobrien			break;
166160484Sobrien		}
166260484Sobrien		im6o->im6o_multicast_ifp = ifp;
166360484Sobrien		break;
166460484Sobrien
166560484Sobrien	case IPV6_MULTICAST_HOPS:
166660484Sobrien	    {
166760484Sobrien		/*
166860484Sobrien		 * Set the IP6 hoplimit for outgoing multicast packets.
166960484Sobrien		 */
167060484Sobrien		int optval;
167160484Sobrien		if (m == NULL || m->m_len != sizeof(int)) {
167260484Sobrien			error = EINVAL;
167360484Sobrien			break;
167460484Sobrien		}
167560484Sobrien		optval = *(mtod(m, u_int *));
167660484Sobrien		if (optval < -1 || optval >= 256)
167760484Sobrien			error = EINVAL;
167860484Sobrien		else if (optval == -1)
167960484Sobrien			im6o->im6o_multicast_hlim = ip6_defmcasthlim;
168060484Sobrien		else
168160484Sobrien			im6o->im6o_multicast_hlim = optval;
168260484Sobrien		break;
168360484Sobrien	    }
168460484Sobrien
168560484Sobrien	case IPV6_MULTICAST_LOOP:
1686218822Sdim		/*
1687218822Sdim		 * Set the loopback flag for outgoing multicast packets.
168860484Sobrien		 * Must be zero or one.
168960484Sobrien		 */
169060484Sobrien		if (m == NULL || m->m_len != sizeof(u_int) ||
169160484Sobrien		   (loop = *(mtod(m, u_int *))) > 1) {
169260484Sobrien			error = EINVAL;
169360484Sobrien			break;
169460484Sobrien		}
169560484Sobrien		im6o->im6o_multicast_loop = loop;
169660484Sobrien		break;
169760484Sobrien
169860484Sobrien	case IPV6_JOIN_GROUP:
169968765Sobrien		/*
170068765Sobrien		 * Add a multicast group membership.
1701104834Sobrien		 * Group must be a valid IP6 multicast address.
170268765Sobrien		 */
170368765Sobrien		if (m == NULL || m->m_len != sizeof(struct ipv6_mreq)) {
170460484Sobrien			error = EINVAL;
170560484Sobrien			break;
1706130561Sobrien		}
1707130561Sobrien		mreq = mtod(m, struct ipv6_mreq *);
1708130561Sobrien		if (IN6_IS_ADDR_UNSPECIFIED(&mreq->ipv6mr_multiaddr)) {
1709130561Sobrien			/*
1710130561Sobrien			 * We use the unspecified address to specify to accept
1711130561Sobrien			 * all multicast addresses. Only super user is allowed
1712130561Sobrien			 * to do this.
1713130561Sobrien			 */
1714130561Sobrien			if (suser(p)) {
1715130561Sobrien				error = EACCES;
1716130561Sobrien				break;
1717130561Sobrien			}
1718130561Sobrien		} else if (!IN6_IS_ADDR_MULTICAST(&mreq->ipv6mr_multiaddr)) {
1719130561Sobrien			error = EINVAL;
1720130561Sobrien			break;
1721130561Sobrien		}
1722130561Sobrien
1723130561Sobrien		/*
1724130561Sobrien		 * If the interface is specified, validate it.
172560484Sobrien		 */
1726104834Sobrien		if (mreq->ipv6mr_interface < 0
1727104834Sobrien		 || if_index < mreq->ipv6mr_interface) {
1728130561Sobrien			error = ENXIO;	/* XXX EINVAL? */
1729130561Sobrien			break;
1730130561Sobrien		}
1731130561Sobrien		/*
1732130561Sobrien		 * If no interface was explicitly specified, choose an
1733130561Sobrien		 * appropriate one according to the given multicast address.
1734104834Sobrien		 */
1735104834Sobrien		if (mreq->ipv6mr_interface == 0) {
1736104834Sobrien			/*
1737104834Sobrien			 * If the multicast address is in node-local scope,
1738104834Sobrien			 * the interface should be a loopback interface.
1739104834Sobrien			 * Otherwise, look up the routing table for the
1740104834Sobrien			 * address, and choose the outgoing interface.
1741104834Sobrien			 *   XXX: is it a good approach?
1742104834Sobrien			 */
174360484Sobrien			if (IN6_IS_ADDR_MC_NODELOCAL(&mreq->ipv6mr_multiaddr)) {
174468765Sobrien				ifp = &loif[0];
174568765Sobrien			} else {
174668765Sobrien				ro.ro_rt = NULL;
174768765Sobrien				dst = (struct sockaddr_in6 *)&ro.ro_dst;
1748104834Sobrien				bzero(dst, sizeof(*dst));
174968765Sobrien				dst->sin6_len = sizeof(struct sockaddr_in6);
175068765Sobrien				dst->sin6_family = AF_INET6;
175168765Sobrien				dst->sin6_addr = mreq->ipv6mr_multiaddr;
175268765Sobrien				rtalloc((struct route *)&ro);
175360484Sobrien				if (ro.ro_rt == NULL) {
175460484Sobrien					error = EADDRNOTAVAIL;
175560484Sobrien					break;
175660484Sobrien				}
175760484Sobrien				ifp = ro.ro_rt->rt_ifp;
175860484Sobrien				rtfree(ro.ro_rt);
175968765Sobrien			}
176068765Sobrien		} else
176168765Sobrien			ifp = ifindex2ifnet[mreq->ipv6mr_interface];
176268765Sobrien
176368765Sobrien		/*
176468765Sobrien		 * See if we found an interface, and confirm that it
176568765Sobrien		 * supports multicast
1766104834Sobrien		 */
176768765Sobrien		if (ifp == NULL || (ifp->if_flags & IFF_MULTICAST) == 0) {
176868765Sobrien			error = EADDRNOTAVAIL;
176960484Sobrien			break;
177060484Sobrien		}
177160484Sobrien		/*
177260484Sobrien		 * Put interface index into the multicast address,
177360484Sobrien		 * if the address has link-local scope.
1774130561Sobrien		 */
177560484Sobrien		if (IN6_IS_ADDR_MC_LINKLOCAL(&mreq->ipv6mr_multiaddr)) {
177660484Sobrien			mreq->ipv6mr_multiaddr.s6_addr16[1]
177760484Sobrien				= htons(mreq->ipv6mr_interface);
177860484Sobrien		}
177960484Sobrien		/*
178060484Sobrien		 * See if the membership already exists.
178160484Sobrien		 */
1782218822Sdim		LIST_FOREACH(imm, &im6o->im6o_memberships, i6mm_chain)
1783218822Sdim			if (imm->i6mm_maddr->in6m_ifp == ifp &&
178460484Sobrien			    IN6_ARE_ADDR_EQUAL(&imm->i6mm_maddr->in6m_addr,
178560484Sobrien					       &mreq->ipv6mr_multiaddr))
178660484Sobrien				break;
178760484Sobrien		if (imm != NULL) {
178860484Sobrien			error = EADDRINUSE;
178960484Sobrien			break;
179060484Sobrien		}
179160484Sobrien		/*
179260484Sobrien		 * Everything looks good; add a new record to the multicast
179377298Sobrien		 * address list for the given interface.
179460484Sobrien		 */
179560484Sobrien		imm = malloc(sizeof(*imm), M_IPMADDR, M_WAITOK);
179660484Sobrien		if (imm == NULL) {
179760484Sobrien			error = ENOBUFS;
179860484Sobrien			break;
179960484Sobrien		}
180060484Sobrien		if ((imm->i6mm_maddr =
180160484Sobrien		     in6_addmulti(&mreq->ipv6mr_multiaddr, ifp, &error)) == NULL) {
180277298Sobrien			free(imm, M_IPMADDR);
180360484Sobrien			break;
180460484Sobrien		}
180560484Sobrien		LIST_INSERT_HEAD(&im6o->im6o_memberships, imm, i6mm_chain);
180660484Sobrien		break;
180760484Sobrien
180860484Sobrien	case IPV6_LEAVE_GROUP:
180960484Sobrien		/*
181060484Sobrien		 * Drop a multicast group membership.
181160484Sobrien		 * Group must be a valid IP6 multicast address.
181277298Sobrien		 */
181360484Sobrien		if (m == NULL || m->m_len != sizeof(struct ipv6_mreq)) {
181460484Sobrien			error = EINVAL;
181560484Sobrien			break;
181660484Sobrien		}
181760484Sobrien		mreq = mtod(m, struct ipv6_mreq *);
181860484Sobrien		if (IN6_IS_ADDR_UNSPECIFIED(&mreq->ipv6mr_multiaddr)) {
181960484Sobrien			if (suser(p)) {
182060484Sobrien				error = EACCES;
182160484Sobrien				break;
182260484Sobrien			}
1823218822Sdim		} else if (!IN6_IS_ADDR_MULTICAST(&mreq->ipv6mr_multiaddr)) {
1824218822Sdim			error = EINVAL;
182560484Sobrien			break;
182660484Sobrien		}
182760484Sobrien		/*
182860484Sobrien		 * If an interface address was specified, get a pointer
182960484Sobrien		 * to its ifnet structure.
183060484Sobrien		 */
183160484Sobrien		if (mreq->ipv6mr_interface < 0
183260484Sobrien		 || if_index < mreq->ipv6mr_interface) {
183360484Sobrien			error = ENXIO;	/* XXX EINVAL? */
183460484Sobrien			break;
183560484Sobrien		}
183660484Sobrien		ifp = ifindex2ifnet[mreq->ipv6mr_interface];
183760484Sobrien		/*
183860484Sobrien		 * Put interface index into the multicast address,
183960484Sobrien		 * if the address has link-local scope.
184060484Sobrien		 */
184160484Sobrien		if (IN6_IS_ADDR_MC_LINKLOCAL(&mreq->ipv6mr_multiaddr)) {
184260484Sobrien			mreq->ipv6mr_multiaddr.s6_addr16[1]
184360484Sobrien				= htons(mreq->ipv6mr_interface);
184460484Sobrien		}
184560484Sobrien		/*
184660484Sobrien		 * Find the membership in the membership list.
184760484Sobrien		 */
184860484Sobrien		LIST_FOREACH(imm, &im6o->im6o_memberships, i6mm_chain) {
184960484Sobrien			if ((ifp == NULL ||
185060484Sobrien			     imm->i6mm_maddr->in6m_ifp == ifp) &&
185160484Sobrien			    IN6_ARE_ADDR_EQUAL(&imm->i6mm_maddr->in6m_addr,
185260484Sobrien					       &mreq->ipv6mr_multiaddr))
185360484Sobrien				break;
185460484Sobrien		}
185560484Sobrien		if (imm == NULL) {
185660484Sobrien			/* Unable to resolve interface */
185760484Sobrien			error = EADDRNOTAVAIL;
185860484Sobrien			break;
185960484Sobrien		}
186060484Sobrien		/*
186160484Sobrien		 * Give up the multicast address record to which the
186260484Sobrien		 * membership points.
186360484Sobrien		 */
186460484Sobrien		LIST_REMOVE(imm, i6mm_chain);
186560484Sobrien		in6_delmulti(imm->i6mm_maddr);
186660484Sobrien		free(imm, M_IPMADDR);
186760484Sobrien		break;
186860484Sobrien
186960484Sobrien	default:
187060484Sobrien		error = EOPNOTSUPP;
187160484Sobrien		break;
187260484Sobrien	}
187360484Sobrien
187460484Sobrien	/*
187560484Sobrien	 * If all options have default values, no need to keep the mbuf.
187660484Sobrien	 */
187760484Sobrien	if (im6o->im6o_multicast_ifp == NULL &&
187860484Sobrien	    im6o->im6o_multicast_hlim == ip6_defmcasthlim &&
187960484Sobrien	    im6o->im6o_multicast_loop == IPV6_DEFAULT_MULTICAST_LOOP &&
188060484Sobrien	    LIST_EMPTY(&im6o->im6o_memberships)) {
188160484Sobrien		free(*im6op, M_IPMOPTS);
188260484Sobrien		*im6op = NULL;
188360484Sobrien	}
188460484Sobrien
188560484Sobrien	return(error);
188660484Sobrien}
188760484Sobrien
188860484Sobrien/*
188960484Sobrien * Return the IP6 multicast options in response to user getsockopt().
189060484Sobrien */
189160484Sobrienstatic int
189260484Sobrienip6_getmoptions(optname, im6o, mp)
189360484Sobrien	int optname;
1894218822Sdim	register struct ip6_moptions *im6o;
189560484Sobrien	register struct mbuf **mp;
189660484Sobrien{
189760484Sobrien	u_int *hlim, *loop, *ifindex;
189860484Sobrien
189960484Sobrien	*mp = m_get(M_WAIT, MT_HEADER);		/*XXX*/
190060484Sobrien
190160484Sobrien	switch (optname) {
190260484Sobrien
190360484Sobrien	case IPV6_MULTICAST_IF:
190460484Sobrien		ifindex = mtod(*mp, u_int *);
190560484Sobrien		(*mp)->m_len = sizeof(u_int);
190660484Sobrien		if (im6o == NULL || im6o->im6o_multicast_ifp == NULL)
190760484Sobrien			*ifindex = 0;
190860484Sobrien		else
190960484Sobrien			*ifindex = im6o->im6o_multicast_ifp->if_index;
191060484Sobrien		return(0);
191160484Sobrien
191260484Sobrien	case IPV6_MULTICAST_HOPS:
191360484Sobrien		hlim = mtod(*mp, u_int *);
191460484Sobrien		(*mp)->m_len = sizeof(u_int);
191560484Sobrien		if (im6o == NULL)
191660484Sobrien			*hlim = ip6_defmcasthlim;
191760484Sobrien		else
191860484Sobrien			*hlim = im6o->im6o_multicast_hlim;
191960484Sobrien		return(0);
192060484Sobrien
192160484Sobrien	case IPV6_MULTICAST_LOOP:
192260484Sobrien		loop = mtod(*mp, u_int *);
192360484Sobrien		(*mp)->m_len = sizeof(u_int);
192460484Sobrien		if (im6o == NULL)
192560484Sobrien			*loop = ip6_defmcasthlim;
192660484Sobrien		else
192760484Sobrien			*loop = im6o->im6o_multicast_loop;
192868765Sobrien		return(0);
192960484Sobrien
193060484Sobrien	default:
193160484Sobrien		return(EOPNOTSUPP);
1932218822Sdim	}
1933218822Sdim}
1934218822Sdim
193533965Sjdp/*
193633965Sjdp * Discard the IP6 multicast options.
193733965Sjdp */
193833965Sjdpvoid
193933965Sjdpip6_freemoptions(im6o)
194038889Sjdp	register struct ip6_moptions *im6o;
194133965Sjdp{
194233965Sjdp	struct in6_multi_mship *imm;
194333965Sjdp
194438889Sjdp	if (im6o == NULL)
194533965Sjdp		return;
194638889Sjdp
194760484Sobrien	while ((imm = LIST_FIRST(&im6o->im6o_memberships)) != NULL) {
194838889Sjdp		LIST_REMOVE(imm, i6mm_chain);
194960484Sobrien		if (imm->i6mm_maddr)
195060484Sobrien			in6_delmulti(imm->i6mm_maddr);
195160484Sobrien		free(imm, M_IPMADDR);
195260484Sobrien	}
195360484Sobrien	free(im6o, M_IPMOPTS);
195460484Sobrien}
195560484Sobrien
195660484Sobrien/*
195760484Sobrien * Set IPv6 outgoing packet options based on advanced API.
195860484Sobrien */
195960484Sobrienint
196060484Sobrienip6_setpktoptions(control, opt, priv)
196160484Sobrien	struct mbuf *control;
196260484Sobrien	struct ip6_pktopts *opt;
196360484Sobrien	int priv;
196460484Sobrien{
196560484Sobrien	register struct cmsghdr *cm = 0;
196660484Sobrien
196760484Sobrien	if (control == 0 || opt == 0)
196860484Sobrien		return(EINVAL);
196960484Sobrien
197060484Sobrien	bzero(opt, sizeof(*opt));
197138889Sjdp	opt->ip6po_hlim = -1; /* -1 means to use default hop limit */
197260484Sobrien
197338889Sjdp	/*
197460484Sobrien	 * XXX: Currently, we assume all the optional information is stored
197560484Sobrien	 * in a single mbuf.
197660484Sobrien	 */
197760484Sobrien	if (control->m_next)
197860484Sobrien		return(EINVAL);
197960484Sobrien
198060484Sobrien	opt->ip6po_m = control;
198160484Sobrien
198260484Sobrien	for (; control->m_len; control->m_data += CMSG_ALIGN(cm->cmsg_len),
198360484Sobrien		     control->m_len -= CMSG_ALIGN(cm->cmsg_len)) {
198460484Sobrien		cm = mtod(control, struct cmsghdr *);
198560484Sobrien		if (cm->cmsg_len == 0 || cm->cmsg_len > control->m_len)
198660484Sobrien			return(EINVAL);
198760484Sobrien		if (cm->cmsg_level != IPPROTO_IPV6)
198838889Sjdp			continue;
198933965Sjdp
199038889Sjdp		switch(cm->cmsg_type) {
199138889Sjdp		case IPV6_PKTINFO:
199233965Sjdp			if (cm->cmsg_len != CMSG_LEN(sizeof(struct in6_pktinfo)))
199333965Sjdp				return(EINVAL);
199433965Sjdp			opt->ip6po_pktinfo = (struct in6_pktinfo *)CMSG_DATA(cm);
199533965Sjdp			if (opt->ip6po_pktinfo->ipi6_ifindex &&
199633965Sjdp			    IN6_IS_ADDR_LINKLOCAL(&opt->ip6po_pktinfo->ipi6_addr))
199738889Sjdp				opt->ip6po_pktinfo->ipi6_addr.s6_addr16[1] =
199838889Sjdp					htons(opt->ip6po_pktinfo->ipi6_ifindex);
199938889Sjdp
2000218822Sdim			if (opt->ip6po_pktinfo->ipi6_ifindex > if_index
200138889Sjdp			 || opt->ip6po_pktinfo->ipi6_ifindex < 0) {
200238889Sjdp				return(ENXIO);
200338889Sjdp			}
200438889Sjdp
200533965Sjdp			if (!IN6_IS_ADDR_UNSPECIFIED(&opt->ip6po_pktinfo->ipi6_addr)) {
200633965Sjdp				struct ifaddr *ia;
200733965Sjdp				struct sockaddr_in6 sin6;
200833965Sjdp
200933965Sjdp				bzero(&sin6, sizeof(sin6));
201033965Sjdp				sin6.sin6_len = sizeof(sin6);
201133965Sjdp				sin6.sin6_family = AF_INET6;
201233965Sjdp				sin6.sin6_addr =
201333965Sjdp					opt->ip6po_pktinfo->ipi6_addr;
201433965Sjdp				ia = ifa_ifwithaddr(sin6tosa(&sin6));
201533965Sjdp				if (ia == NULL ||
201633965Sjdp				    (opt->ip6po_pktinfo->ipi6_ifindex &&
201733965Sjdp				     (ia->ifa_ifp->if_index !=
201833965Sjdp				      opt->ip6po_pktinfo->ipi6_ifindex))) {
201933965Sjdp					return(EADDRNOTAVAIL);
202038889Sjdp				}
202138889Sjdp				/*
202238889Sjdp				 * Check if the requested source address is
202338889Sjdp				 * indeed a unicast address assigned to the
202438889Sjdp				 * node.
2025218822Sdim				 */
202638889Sjdp				if (IN6_IS_ADDR_MULTICAST(&opt->ip6po_pktinfo->ipi6_addr))
202738889Sjdp					return(EADDRNOTAVAIL);
202838889Sjdp			}
202933965Sjdp			break;
203033965Sjdp
203133965Sjdp		case IPV6_HOPLIMIT:
203233965Sjdp			if (cm->cmsg_len != CMSG_LEN(sizeof(int)))
203333965Sjdp				return(EINVAL);
203433965Sjdp
203533965Sjdp			opt->ip6po_hlim = *(int *)CMSG_DATA(cm);
203660484Sobrien			if (opt->ip6po_hlim < -1 || opt->ip6po_hlim > 255)
203760484Sobrien				return(EINVAL);
203860484Sobrien			break;
203960484Sobrien
204060484Sobrien		case IPV6_NEXTHOP:
204160484Sobrien			if (!priv)
204260484Sobrien				return(EPERM);
204360484Sobrien			if (cm->cmsg_len < sizeof(u_char) ||
204460484Sobrien			    cm->cmsg_len < CMSG_LEN(*CMSG_DATA(cm)))
204560484Sobrien				return(EINVAL);
204660484Sobrien
204760484Sobrien			opt->ip6po_nexthop = (struct sockaddr *)CMSG_DATA(cm);
204860484Sobrien
204960484Sobrien			break;
205060484Sobrien
205160484Sobrien		case IPV6_HOPOPTS:
205260484Sobrien			if (cm->cmsg_len < CMSG_LEN(sizeof(struct ip6_hbh)))
2053218822Sdim				return(EINVAL);
205460484Sobrien			opt->ip6po_hbh = (struct ip6_hbh *)CMSG_DATA(cm);
205560484Sobrien			if (cm->cmsg_len !=
205660484Sobrien			    CMSG_LEN((opt->ip6po_hbh->ip6h_len + 1) << 3))
205760484Sobrien				return(EINVAL);
205860484Sobrien			break;
205960484Sobrien
206060484Sobrien		case IPV6_DSTOPTS:
206160484Sobrien			if (cm->cmsg_len < CMSG_LEN(sizeof(struct ip6_dest)))
206260484Sobrien				return(EINVAL);
206360484Sobrien
206433965Sjdp			/*
206533965Sjdp			 * If there is no routing header yet, the destination
206638889Sjdp			 * options header should be put on the 1st part.
206738889Sjdp			 * Otherwise, the header should be on the 2nd part.
206838889Sjdp			 * (See RFC 2460, section 4.1)
206933965Sjdp			 */
207060484Sobrien			if (opt->ip6po_rthdr == NULL) {
207133965Sjdp				opt->ip6po_dest1 =
207233965Sjdp					(struct ip6_dest *)CMSG_DATA(cm);
207333965Sjdp				if (cm->cmsg_len !=
207433965Sjdp				    CMSG_LEN((opt->ip6po_dest1->ip6d_len + 1)
207560484Sobrien					     << 3))
207638889Sjdp					return(EINVAL);
207738889Sjdp			} else {
207838889Sjdp				opt->ip6po_dest2 =
207938889Sjdp					(struct ip6_dest *)CMSG_DATA(cm);
208038889Sjdp				if (cm->cmsg_len !=
208138889Sjdp				    CMSG_LEN((opt->ip6po_dest2->ip6d_len + 1)
208238889Sjdp					     << 3))
208338889Sjdp					return(EINVAL);
208438889Sjdp			}
208560484Sobrien			break;
208660484Sobrien
208738889Sjdp		case IPV6_RTHDR:
208860484Sobrien			if (cm->cmsg_len < CMSG_LEN(sizeof(struct ip6_rthdr)))
208938889Sjdp				return(EINVAL);
209060484Sobrien			opt->ip6po_rthdr = (struct ip6_rthdr *)CMSG_DATA(cm);
209160484Sobrien			if (cm->cmsg_len !=
209260484Sobrien			    CMSG_LEN((opt->ip6po_rthdr->ip6r_len + 1) << 3))
209360484Sobrien				return(EINVAL);
209433965Sjdp			switch(opt->ip6po_rthdr->ip6r_type) {
209560484Sobrien			case IPV6_RTHDR_TYPE_0:
209638889Sjdp				if (opt->ip6po_rthdr->ip6r_segleft == 0)
209738889Sjdp					return(EINVAL);
209838889Sjdp				break;
2099218822Sdim			default:
210038889Sjdp				return(EINVAL);
210138889Sjdp			}
210260484Sobrien			break;
210338889Sjdp
210438889Sjdp		default:
210538889Sjdp			return(ENOPROTOOPT);
210633965Sjdp		}
210733965Sjdp	}
210833965Sjdp
210938889Sjdp	return(0);
211038889Sjdp}
211138889Sjdp
211238889Sjdp/*
211338889Sjdp * Routine called from ip6_output() to loop back a copy of an IP6 multicast
211438889Sjdp * packet to the input queue of a specified interface.  Note that this
211538889Sjdp * calls the output routine of the loopback "driver", but with an interface
211638889Sjdp * pointer that might NOT be &loif -- easier than replicating that code here.
211738889Sjdp */
211838889Sjdpvoid
211960484Sobrienip6_mloopback(ifp, m, dst)
212060484Sobrien	struct ifnet *ifp;
2121130561Sobrien	register struct mbuf *m;
2122130561Sobrien	register struct sockaddr_in6 *dst;
2123130561Sobrien{
2124130561Sobrien	struct	mbuf *copym;
212560484Sobrien
212633965Sjdp	copym = m_copy(m, 0, M_COPYALL);
212733965Sjdp	if (copym != NULL) {
212833965Sjdp		(void)if_simloop(ifp, copym, (struct sockaddr *)dst, 0);
212933965Sjdp	}
213033965Sjdp}
213133965Sjdp
213233965Sjdp/*
213333965Sjdp * Chop IPv6 header off from the payload.
213433965Sjdp */
213533965Sjdpstatic int
213633965Sjdpip6_splithdr(m, exthdrs)
213733965Sjdp	struct mbuf *m;
213833965Sjdp	struct ip6_exthdrs *exthdrs;
213933965Sjdp{
214033965Sjdp	struct mbuf *mh;
214133965Sjdp	struct ip6_hdr *ip6;
214233965Sjdp
2143218822Sdim	ip6 = mtod(m, struct ip6_hdr *);
2144218822Sdim	if (m->m_len > sizeof(*ip6)) {
214533965Sjdp		MGETHDR(mh, M_DONTWAIT, MT_HEADER);
214660484Sobrien		if (mh == 0) {
214760484Sobrien			m_freem(m);
214889857Sobrien			return ENOBUFS;
214933965Sjdp		}
215033965Sjdp		M_COPY_PKTHDR(mh, m);
215133965Sjdp		MH_ALIGN(mh, sizeof(*ip6));
215233965Sjdp		m->m_flags &= ~M_PKTHDR;
215360484Sobrien		m->m_len -= sizeof(*ip6);
215460484Sobrien		m->m_data += sizeof(*ip6);
215533965Sjdp		mh->m_next = m;
215633965Sjdp		m = mh;
215733965Sjdp		m->m_len = sizeof(*ip6);
215833965Sjdp		bcopy((caddr_t)ip6, mtod(m, caddr_t), sizeof(*ip6));
215933965Sjdp	}
216033965Sjdp	exthdrs->ip6e_ip6 = m;
216160484Sobrien	return 0;
216260484Sobrien}
216389857Sobrien
216489857Sobrien/*
216589857Sobrien * Compute IPv6 extension header length.
216660484Sobrien */
216760484Sobrienint
216860484Sobrienip6_optlen(in6p)
216960484Sobrien	struct in6pcb *in6p;
217060484Sobrien{
217160484Sobrien	int len;
217260484Sobrien
217360484Sobrien	if (!in6p->in6p_outputopts)
217460484Sobrien		return 0;
217560484Sobrien
217660484Sobrien	len = 0;
217760484Sobrien#define elen(x) \
217889857Sobrien    (((struct ip6_ext *)(x)) ? (((struct ip6_ext *)(x))->ip6e_len + 1) << 3 : 0)
217960484Sobrien
218060484Sobrien	len += elen(in6p->in6p_outputopts->ip6po_hbh);
218160484Sobrien	len += elen(in6p->in6p_outputopts->ip6po_dest1);
218260484Sobrien	len += elen(in6p->in6p_outputopts->ip6po_rthdr);
218360484Sobrien	len += elen(in6p->in6p_outputopts->ip6po_dest2);
218460484Sobrien	return len;
218560484Sobrien#undef elen
218660484Sobrien}
218760484Sobrien
218860484Sobrien