xref: /openbsd-src/sys/netinet6/ip6_output.c (revision 84d9c64aba152a33fde7c9c690969428d4f7efb9)
1*84d9c64aSbluhm /*	$OpenBSD: ip6_output.c,v 1.294 2025/01/03 21:27:40 bluhm Exp $	*/
2672f01bcSitojun /*	$KAME: ip6_output.c,v 1.172 2001/03/25 09:55:56 itojun Exp $	*/
3287546eaSitojun 
4287546eaSitojun /*
5287546eaSitojun  * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project.
6287546eaSitojun  * All rights reserved.
7287546eaSitojun  *
8287546eaSitojun  * Redistribution and use in source and binary forms, with or without
9287546eaSitojun  * modification, are permitted provided that the following conditions
10287546eaSitojun  * are met:
11287546eaSitojun  * 1. Redistributions of source code must retain the above copyright
12287546eaSitojun  *    notice, this list of conditions and the following disclaimer.
13287546eaSitojun  * 2. Redistributions in binary form must reproduce the above copyright
14287546eaSitojun  *    notice, this list of conditions and the following disclaimer in the
15287546eaSitojun  *    documentation and/or other materials provided with the distribution.
16287546eaSitojun  * 3. Neither the name of the project nor the names of its contributors
17287546eaSitojun  *    may be used to endorse or promote products derived from this software
18287546eaSitojun  *    without specific prior written permission.
19287546eaSitojun  *
20287546eaSitojun  * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND
21287546eaSitojun  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22287546eaSitojun  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
23287546eaSitojun  * ARE DISCLAIMED.  IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE
24287546eaSitojun  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
25287546eaSitojun  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
26287546eaSitojun  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
27287546eaSitojun  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
28287546eaSitojun  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
29287546eaSitojun  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
30287546eaSitojun  * SUCH DAMAGE.
31287546eaSitojun  */
32287546eaSitojun 
33287546eaSitojun /*
34287546eaSitojun  * Copyright (c) 1982, 1986, 1988, 1990, 1993
35287546eaSitojun  *	The Regents of the University of California.  All rights reserved.
36287546eaSitojun  *
37287546eaSitojun  * Redistribution and use in source and binary forms, with or without
38287546eaSitojun  * modification, are permitted provided that the following conditions
39287546eaSitojun  * are met:
40287546eaSitojun  * 1. Redistributions of source code must retain the above copyright
41287546eaSitojun  *    notice, this list of conditions and the following disclaimer.
42287546eaSitojun  * 2. Redistributions in binary form must reproduce the above copyright
43287546eaSitojun  *    notice, this list of conditions and the following disclaimer in the
44287546eaSitojun  *    documentation and/or other materials provided with the distribution.
4529295d1cSmillert  * 3. Neither the name of the University nor the names of its contributors
46287546eaSitojun  *    may be used to endorse or promote products derived from this software
47287546eaSitojun  *    without specific prior written permission.
48287546eaSitojun  *
49287546eaSitojun  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
50287546eaSitojun  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
51287546eaSitojun  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
52287546eaSitojun  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
53287546eaSitojun  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
54287546eaSitojun  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
55287546eaSitojun  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
56287546eaSitojun  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
57287546eaSitojun  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
58287546eaSitojun  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
59287546eaSitojun  * SUCH DAMAGE.
60287546eaSitojun  *
61287546eaSitojun  *	@(#)ip_output.c	8.3 (Berkeley) 1/21/94
62287546eaSitojun  */
63287546eaSitojun 
6430620b12Sfrantzen #include "pf.h"
6530620b12Sfrantzen 
66287546eaSitojun #include <sys/param.h>
67287546eaSitojun #include <sys/malloc.h>
68287546eaSitojun #include <sys/mbuf.h>
69287546eaSitojun #include <sys/errno.h>
70287546eaSitojun #include <sys/protosw.h>
71287546eaSitojun #include <sys/socket.h>
72287546eaSitojun #include <sys/socketvar.h>
73d83f8b41Sphessler #include <sys/proc.h>
74287546eaSitojun #include <sys/systm.h>
75287546eaSitojun 
76287546eaSitojun #include <net/if.h>
770deb6685Smpi #include <net/if_var.h>
78ba8b7f53Smarkus #include <net/if_enc.h>
79287546eaSitojun #include <net/route.h>
80287546eaSitojun 
81287546eaSitojun #include <netinet/in.h>
82287546eaSitojun #include <netinet/ip.h>
8358aa7d74Sangelos #include <netinet/in_pcb.h>
84e993eaa0Shenning #include <netinet/udp.h>
85e993eaa0Shenning #include <netinet/tcp.h>
8658aa7d74Sangelos 
87095b4e20Shenning #include <netinet/ip_var.h>
88095b4e20Shenning #include <netinet/tcp_timer.h>
89095b4e20Shenning #include <netinet/tcp_var.h>
90095b4e20Shenning #include <netinet/udp_var.h>
91095b4e20Shenning 
92dc572864Sbluhm #include <netinet6/in6_var.h>
93fa86ee14Sitojun #include <netinet/ip6.h>
94fa86ee14Sitojun #include <netinet/icmp6.h>
95287546eaSitojun #include <netinet6/ip6_var.h>
96287546eaSitojun #include <netinet6/nd6.h>
97287546eaSitojun 
98ca6e56f2Sdjm #include <crypto/idgen.h>
99ca6e56f2Sdjm 
10030620b12Sfrantzen #if NPF > 0
10130620b12Sfrantzen #include <net/pfvar.h>
10230620b12Sfrantzen #endif
10330620b12Sfrantzen 
104dc48c783Sitojun #ifdef IPSEC
105537b0e2dScedric #include <netinet/ip_ipsp.h>
106dc48c783Sitojun #include <netinet/ip_ah.h>
107dc48c783Sitojun #include <netinet/ip_esp.h>
10859caf375Sbluhm 
10959caf375Sbluhm #ifdef ENCDEBUG
110698a75ddSbluhm #define DPRINTF(fmt, args...)						\
111698a75ddSbluhm 	do {								\
112698a75ddSbluhm 		if (encdebug)						\
113698a75ddSbluhm 			printf("%s: " fmt "\n", __func__, ## args);	\
114698a75ddSbluhm 	} while (0)
11559caf375Sbluhm #else
116698a75ddSbluhm #define DPRINTF(fmt, args...)						\
117698a75ddSbluhm 	do { } while (0)
11859caf375Sbluhm #endif
119dc48c783Sitojun #endif /* IPSEC */
120287546eaSitojun 
121287546eaSitojun struct ip6_exthdrs {
122287546eaSitojun 	struct mbuf *ip6e_ip6;
123287546eaSitojun 	struct mbuf *ip6e_hbh;
124287546eaSitojun 	struct mbuf *ip6e_dest1;
125287546eaSitojun 	struct mbuf *ip6e_rthdr;
126287546eaSitojun 	struct mbuf *ip6e_dest2;
127287546eaSitojun };
128287546eaSitojun 
129d4984c21Sjsing int ip6_pcbopt(int, u_char *, int, struct ip6_pktopts **, int, int);
130490e0738Sdhill int ip6_getpcbopt(struct ip6_pktopts *, int, struct mbuf *);
1313ea7534eSjca int ip6_setpktopt(int, u_char *, int, struct ip6_pktopts *, int, int, int);
13244a616dfSrzalamena int ip6_setmoptions(int, struct ip6_moptions **, struct mbuf *, unsigned int);
133490e0738Sdhill int ip6_getmoptions(int, struct ip6_moptions *, struct mbuf *);
134d4984c21Sjsing int ip6_copyexthdr(struct mbuf **, caddr_t, int);
135d4984c21Sjsing int ip6_insertfraghdr(struct mbuf *, struct mbuf *, int,
136c4071fd1Smillert 	struct ip6_frag **);
137d4984c21Sjsing int ip6_insert_jumboopt(struct ip6_exthdrs *, u_int32_t);
138d4984c21Sjsing int ip6_splithdr(struct mbuf *, struct ip6_exthdrs *);
139f3785e27Sbluhm int ip6_getpmtu(struct rtentry *, struct ifnet *, u_long *);
140f288bf99Sdhill int copypktopts(struct ip6_pktopts *, struct ip6_pktopts *);
141a2c73f7dSnaddy static __inline u_int16_t __attribute__((__unused__))
142a2c73f7dSnaddy     in6_cksum_phdr(const struct in6_addr *, const struct in6_addr *,
143a2c73f7dSnaddy     u_int32_t, u_int32_t);
144e3f16dcaShenning void in6_delayed_cksum(struct mbuf *, u_int8_t);
145287546eaSitojun 
14694c0e2bdSbluhm int ip6_output_ipsec_pmtu_update(struct tdb *, struct route *,
1476d361195Sbluhm     struct in6_addr *, int, int, int);
1486d361195Sbluhm 
149ca6e56f2Sdjm /* Context for non-repeating IDs */
150ca6e56f2Sdjm struct idgen32_ctx ip6_id_ctx;
151ca6e56f2Sdjm 
152287546eaSitojun /*
153287546eaSitojun  * IP6 output. The packet in mbuf chain m contains a skeletal IP6
154287546eaSitojun  * header (with pri, len, nxt, hlim, src, dst).
155287546eaSitojun  * This function may modify ver and hlim only.
156287546eaSitojun  * The mbuf chain containing the packet will be freed.
157287546eaSitojun  * The mbuf opt, if present, will not be freed.
158841d7adbSitojun  *
159*84d9c64aSbluhm  * type of "mtu": rt_mtu is u_int, ifnet.ifr_mtu is int.
160*84d9c64aSbluhm  * We use u_long to hold largest one.  XXX should be u_int
161287546eaSitojun  */
162287546eaSitojun int
16394c0e2bdSbluhm ip6_output(struct mbuf *m, struct ip6_pktopts *opt, struct route *ro,
164ace0f189Sbluhm     int flags, struct ip6_moptions *im6o, const struct ipsec_level *seclevel)
165287546eaSitojun {
166a8cafab0Sbluhm 	struct ip6_hdr *ip6;
167b9ab5338Smpi 	struct ifnet *ifp = NULL;
168b8646e37Sbluhm 	struct mbuf_list ml;
169a8cafab0Sbluhm 	int hlen, tlen;
17094c0e2bdSbluhm 	struct route iproute;
1716b532452Sitojun 	struct rtentry *rt = NULL;
17222723314Sbluhm 	struct sockaddr_in6 *dst;
173287546eaSitojun 	int error = 0;
174287546eaSitojun 	u_long mtu;
175f3785e27Sbluhm 	int dontfrag;
176cd5ec7edSmpi 	u_int16_t src_scope, dst_scope;
177287546eaSitojun 	u_int32_t optlen = 0, plen = 0, unfragpartlen = 0;
178287546eaSitojun 	struct ip6_exthdrs exthdrs;
179287546eaSitojun 	struct in6_addr finaldst;
18094c0e2bdSbluhm 	struct route *ro_pmtu = NULL;
181287546eaSitojun 	int hdrsplit = 0;
182bea47bb9Sitojun 	u_int8_t sproto = 0;
1832818ef99Sbluhm 	u_char nextproto;
184dc48c783Sitojun #ifdef IPSEC
1854a931851Smarkus 	struct tdb *tdb = NULL;
186bea47bb9Sitojun #endif /* IPSEC */
18758aa7d74Sangelos 
1886b532452Sitojun 	ip6 = mtod(m, struct ip6_hdr *);
1896b532452Sitojun 	finaldst = ip6->ip6_dst;
1906b532452Sitojun 
191287546eaSitojun #define MAKE_EXTHDR(hp, mp)						\
1922ed872ebSitojun     do {								\
193287546eaSitojun 	if (hp) {							\
194287546eaSitojun 		struct ip6_ext *eh = (struct ip6_ext *)(hp);		\
195287546eaSitojun 		error = ip6_copyexthdr((mp), (caddr_t)(hp),		\
196287546eaSitojun 		    ((eh)->ip6e_len + 1) << 3);				\
197287546eaSitojun 		if (error)						\
198287546eaSitojun 			goto freehdrs;					\
199287546eaSitojun 	}								\
2002ed872ebSitojun     } while (0)
201287546eaSitojun 
202287546eaSitojun 	bzero(&exthdrs, sizeof(exthdrs));
2036b532452Sitojun 
204287546eaSitojun 	if (opt) {
205287546eaSitojun 		/* Hop-by-Hop options header */
206287546eaSitojun 		MAKE_EXTHDR(opt->ip6po_hbh, &exthdrs.ip6e_hbh);
207287546eaSitojun 		/* Destination options header(1st part) */
208287546eaSitojun 		MAKE_EXTHDR(opt->ip6po_dest1, &exthdrs.ip6e_dest1);
209287546eaSitojun 		/* Routing header */
210287546eaSitojun 		MAKE_EXTHDR(opt->ip6po_rthdr, &exthdrs.ip6e_rthdr);
211287546eaSitojun 		/* Destination options header(2nd part) */
212287546eaSitojun 		MAKE_EXTHDR(opt->ip6po_dest2, &exthdrs.ip6e_dest2);
213287546eaSitojun 	}
214287546eaSitojun 
215287546eaSitojun #ifdef IPSEC
2162551e577Sbluhm 	if (ipsec_in_use || seclevel != NULL) {
2172551e577Sbluhm 		error = ip6_output_ipsec_lookup(m, seclevel, &tdb);
218e7111b64Sbluhm 		if (error) {
219dc48c783Sitojun 			/*
220dc48c783Sitojun 			 * -EINVAL is used to indicate that the packet should
221dc48c783Sitojun 			 * be silently dropped, typically because we've asked
222dc48c783Sitojun 			 * key management for an SA.
223dc48c783Sitojun 			 */
224dc48c783Sitojun 			if (error == -EINVAL) /* Should silently drop packet */
225287546eaSitojun 				error = 0;
226287546eaSitojun 
227dc48c783Sitojun 			goto freehdrs;
228dc48c783Sitojun 		}
229275d330eSangelos 	}
230287546eaSitojun #endif /* IPSEC */
231287546eaSitojun 
232287546eaSitojun 	/*
233287546eaSitojun 	 * Calculate the total length of the extension header chain.
234287546eaSitojun 	 * Keep the length of the unfragmentable part for fragmentation.
235287546eaSitojun 	 */
236287546eaSitojun 	optlen = 0;
237287546eaSitojun 	if (exthdrs.ip6e_hbh) optlen += exthdrs.ip6e_hbh->m_len;
238287546eaSitojun 	if (exthdrs.ip6e_dest1) optlen += exthdrs.ip6e_dest1->m_len;
239287546eaSitojun 	if (exthdrs.ip6e_rthdr) optlen += exthdrs.ip6e_rthdr->m_len;
240287546eaSitojun 	unfragpartlen = optlen + sizeof(struct ip6_hdr);
241287546eaSitojun 	/* NOTE: we don't add AH/ESP length here. do that later. */
242287546eaSitojun 	if (exthdrs.ip6e_dest2) optlen += exthdrs.ip6e_dest2->m_len;
243287546eaSitojun 
244287546eaSitojun 	/*
245287546eaSitojun 	 * If we need IPsec, or there is at least one extension header,
246287546eaSitojun 	 * separate IP6 header from the payload.
247287546eaSitojun 	 */
248dc48c783Sitojun 	if ((sproto || optlen) && !hdrsplit) {
249287546eaSitojun 		if ((error = ip6_splithdr(m, &exthdrs)) != 0) {
250287546eaSitojun 			m = NULL;
251287546eaSitojun 			goto freehdrs;
252287546eaSitojun 		}
253287546eaSitojun 		m = exthdrs.ip6e_ip6;
254287546eaSitojun 		hdrsplit++;
255287546eaSitojun 	}
256287546eaSitojun 
257287546eaSitojun 	/* adjust pointer */
258287546eaSitojun 	ip6 = mtod(m, struct ip6_hdr *);
259287546eaSitojun 
260287546eaSitojun 	/* adjust mbuf packet header length */
261287546eaSitojun 	m->m_pkthdr.len += optlen;
262287546eaSitojun 	plen = m->m_pkthdr.len - sizeof(*ip6);
263287546eaSitojun 
264287546eaSitojun 	/* If this is a jumbo payload, insert a jumbo payload option. */
265287546eaSitojun 	if (plen > IPV6_MAXPACKET) {
266287546eaSitojun 		if (!hdrsplit) {
267287546eaSitojun 			if ((error = ip6_splithdr(m, &exthdrs)) != 0) {
268287546eaSitojun 				m = NULL;
269287546eaSitojun 				goto freehdrs;
270287546eaSitojun 			}
271287546eaSitojun 			m = exthdrs.ip6e_ip6;
272287546eaSitojun 			hdrsplit++;
273287546eaSitojun 		}
274287546eaSitojun 		/* adjust pointer */
275287546eaSitojun 		ip6 = mtod(m, struct ip6_hdr *);
276287546eaSitojun 		if ((error = ip6_insert_jumboopt(&exthdrs, plen)) != 0)
277287546eaSitojun 			goto freehdrs;
278287546eaSitojun 		ip6->ip6_plen = 0;
279287546eaSitojun 	} else
280287546eaSitojun 		ip6->ip6_plen = htons(plen);
281287546eaSitojun 
282287546eaSitojun 	/*
283287546eaSitojun 	 * Concatenate headers and fill in next header fields.
284287546eaSitojun 	 * Here we have, on "m"
285287546eaSitojun 	 *	IPv6 payload
286287546eaSitojun 	 * and we insert headers accordingly.  Finally, we should be getting:
287287546eaSitojun 	 *	IPv6 hbh dest1 rthdr ah* [esp* dest2 payload]
288287546eaSitojun 	 *
289287546eaSitojun 	 * during the header composing process, "m" points to IPv6 header.
290287546eaSitojun 	 * "mprev" points to an extension header prior to esp.
291287546eaSitojun 	 */
292287546eaSitojun 	{
293287546eaSitojun 		u_char *nexthdrp = &ip6->ip6_nxt;
294287546eaSitojun 		struct mbuf *mprev = m;
295287546eaSitojun 
296287546eaSitojun 		/*
297287546eaSitojun 		 * we treat dest2 specially.  this makes IPsec processing
2986b532452Sitojun 		 * much easier.  the goal here is to make mprev point the
2996b532452Sitojun 		 * mbuf prior to dest2.
300287546eaSitojun 		 *
301287546eaSitojun 		 * result: IPv6 dest2 payload
302287546eaSitojun 		 * m and mprev will point to IPv6 header.
303287546eaSitojun 		 */
304287546eaSitojun 		if (exthdrs.ip6e_dest2) {
305287546eaSitojun 			if (!hdrsplit)
30690dc3aeaSbluhm 				panic("%s: assumption failed: hdr not split",
30790dc3aeaSbluhm 				    __func__);
308287546eaSitojun 			exthdrs.ip6e_dest2->m_next = m->m_next;
309287546eaSitojun 			m->m_next = exthdrs.ip6e_dest2;
310287546eaSitojun 			*mtod(exthdrs.ip6e_dest2, u_char *) = ip6->ip6_nxt;
311287546eaSitojun 			ip6->ip6_nxt = IPPROTO_DSTOPTS;
312287546eaSitojun 		}
313287546eaSitojun 
314287546eaSitojun #define MAKE_CHAIN(m, mp, p, i)\
3152ed872ebSitojun     do {\
316287546eaSitojun 	if (m) {\
317287546eaSitojun 		if (!hdrsplit) \
318287546eaSitojun 			panic("assumption failed: hdr not split"); \
319287546eaSitojun 		*mtod((m), u_char *) = *(p);\
320287546eaSitojun 		*(p) = (i);\
321287546eaSitojun 		p = mtod((m), u_char *);\
322287546eaSitojun 		(m)->m_next = (mp)->m_next;\
323287546eaSitojun 		(mp)->m_next = (m);\
324287546eaSitojun 		(mp) = (m);\
325287546eaSitojun 	}\
3262ed872ebSitojun     } while (0)
327287546eaSitojun 		/*
328287546eaSitojun 		 * result: IPv6 hbh dest1 rthdr dest2 payload
329287546eaSitojun 		 * m will point to IPv6 header.  mprev will point to the
330287546eaSitojun 		 * extension header prior to dest2 (rthdr in the above case).
331287546eaSitojun 		 */
3327b2ccaf7Sitojun 		MAKE_CHAIN(exthdrs.ip6e_hbh, mprev, nexthdrp, IPPROTO_HOPOPTS);
3337b2ccaf7Sitojun 		MAKE_CHAIN(exthdrs.ip6e_dest1, mprev, nexthdrp,
3347b2ccaf7Sitojun 		    IPPROTO_DSTOPTS);
3357b2ccaf7Sitojun 		MAKE_CHAIN(exthdrs.ip6e_rthdr, mprev, nexthdrp,
3367b2ccaf7Sitojun 		    IPPROTO_ROUTING);
337287546eaSitojun 	}
338287546eaSitojun 
339287546eaSitojun 	/*
3406b532452Sitojun 	 * If there is a routing header, replace the destination address field
341287546eaSitojun 	 * with the first hop of the routing header.
342287546eaSitojun 	 */
343287546eaSitojun 	if (exthdrs.ip6e_rthdr) {
3447b2ccaf7Sitojun 		struct ip6_rthdr *rh;
345287546eaSitojun 		struct ip6_rthdr0 *rh0;
34622a58e88Sitojun 		struct in6_addr *addr;
347287546eaSitojun 
3487b2ccaf7Sitojun 		rh = (struct ip6_rthdr *)(mtod(exthdrs.ip6e_rthdr,
3497b2ccaf7Sitojun 		    struct ip6_rthdr *));
350287546eaSitojun 		switch (rh->ip6r_type) {
351287546eaSitojun 		case IPV6_RTHDR_TYPE_0:
352287546eaSitojun 			rh0 = (struct ip6_rthdr0 *)rh;
35322a58e88Sitojun 			addr = (struct in6_addr *)(rh0 + 1);
35422a58e88Sitojun 			ip6->ip6_dst = addr[0];
35522a58e88Sitojun 			bcopy(&addr[1], &addr[0],
3567b2ccaf7Sitojun 			    sizeof(struct in6_addr) * (rh0->ip6r0_segleft - 1));
35722a58e88Sitojun 			addr[rh0->ip6r0_segleft - 1] = finaldst;
358287546eaSitojun 			break;
359287546eaSitojun 		default:	/* is it possible? */
360287546eaSitojun 			error = EINVAL;
361287546eaSitojun 			goto bad;
362287546eaSitojun 		}
363287546eaSitojun 	}
364287546eaSitojun 
365287546eaSitojun 	/* Source address validation */
3666b532452Sitojun 	if (!(flags & IPV6_UNSPECSRC) &&
3676b532452Sitojun 	    IN6_IS_ADDR_UNSPECIFIED(&ip6->ip6_src)) {
3686b532452Sitojun 		/*
3696b532452Sitojun 		 * XXX: we can probably assume validation in the caller, but
3706b532452Sitojun 		 * we explicitly check the address here for safety.
3716b532452Sitojun 		 */
372287546eaSitojun 		error = EOPNOTSUPP;
37331e14cacSjca 		ip6stat_inc(ip6s_badscope);
374287546eaSitojun 		goto bad;
375287546eaSitojun 	}
376287546eaSitojun 	if (IN6_IS_ADDR_MULTICAST(&ip6->ip6_src)) {
377287546eaSitojun 		error = EOPNOTSUPP;
37831e14cacSjca 		ip6stat_inc(ip6s_badscope);
379287546eaSitojun 		goto bad;
380287546eaSitojun 	}
381287546eaSitojun 
38231e14cacSjca 	ip6stat_inc(ip6s_localout);
383287546eaSitojun 
384287546eaSitojun 	/*
385287546eaSitojun 	 * Route packet.
386287546eaSitojun 	 */
387f1638404Sclaudio #if NPF > 0
388f1638404Sclaudio reroute:
389f1638404Sclaudio #endif
390f1638404Sclaudio 
3916b532452Sitojun 	/* initialize cached route */
392bc31a334Skrw 	if (ro == NULL) {
39394c0e2bdSbluhm 		ro = &iproute;
39400dd3069Sbluhm 		ro->ro_rt = NULL;
395287546eaSitojun 	}
396287546eaSitojun 	ro_pmtu = ro;
397287546eaSitojun 	if (opt && opt->ip6po_rthdr)
398287546eaSitojun 		ro = &opt->ip6po_route;
39994c0e2bdSbluhm 	dst = &ro->ro_dstsin6;
4006b532452Sitojun 
401287546eaSitojun 	/*
4026b532452Sitojun 	 * if specified, try to fill in the traffic class field.
4036b532452Sitojun 	 * do not override if a non-zero value is already set.
4046b532452Sitojun 	 * we check the diffserv field and the ecn field separately.
405287546eaSitojun 	 */
4066b532452Sitojun 	if (opt && opt->ip6po_tclass >= 0) {
4076b532452Sitojun 		int mask = 0;
4086b532452Sitojun 
4096b532452Sitojun 		if ((ip6->ip6_flow & htonl(0xfc << 20)) == 0)
4106b532452Sitojun 			mask |= 0xfc;
4116b532452Sitojun 		if ((ip6->ip6_flow & htonl(0x03 << 20)) == 0)
4126b532452Sitojun 			mask |= 0x03;
4136b532452Sitojun 		if (mask != 0)
41490dc3aeaSbluhm 			ip6->ip6_flow |=
41590dc3aeaSbluhm 			    htonl((opt->ip6po_tclass & mask) << 20);
416287546eaSitojun 	}
4176b532452Sitojun 
4186b532452Sitojun 	/* fill in or override the hop limit field, if necessary. */
4196b532452Sitojun 	if (opt && opt->ip6po_hlim != -1)
4206b532452Sitojun 		ip6->ip6_hlim = opt->ip6po_hlim & 0xff;
4216b532452Sitojun 	else if (IN6_IS_ADDR_MULTICAST(&ip6->ip6_dst)) {
4226b532452Sitojun 		if (im6o != NULL)
4233c687758Smpi 			ip6->ip6_hlim = im6o->im6o_hlim;
4246b532452Sitojun 		else
4256b532452Sitojun 			ip6->ip6_hlim = ip6_defmcasthlim;
426287546eaSitojun 	}
4276b532452Sitojun 
428bea47bb9Sitojun #ifdef IPSEC
42931a6915fSbluhm 	if (tdb != NULL) {
430a43d4d9bSreyk 		/*
431a43d4d9bSreyk 		 * XXX what should we do if ip6_hlim == 0 and the
432a43d4d9bSreyk 		 * packet gets tunneled?
433a43d4d9bSreyk 		 */
434065a3e52Sitojun 		/*
435065a3e52Sitojun 		 * if we are source-routing, do not attempt to tunnel the
436065a3e52Sitojun 		 * packet just because ip6_dst is different from what tdb has.
437065a3e52Sitojun 		 * XXX
438065a3e52Sitojun 		 */
43959caf375Sbluhm 		error = ip6_output_ipsec_send(tdb, m, ro,
4404a931851Smarkus 		    exthdrs.ip6e_rthdr ? 1 : 0, 0);
4414a931851Smarkus 		goto done;
442bea47bb9Sitojun 	}
443bea47bb9Sitojun #endif /* IPSEC */
444287546eaSitojun 
44522723314Sbluhm 	if (IN6_IS_ADDR_MULTICAST(&ip6->ip6_dst)) {
446b9ab5338Smpi 		struct in6_pktinfo *pi = NULL;
447b9ab5338Smpi 
448b9ab5338Smpi 		/*
449b9ab5338Smpi 		 * If the caller specify the outgoing interface
450b9ab5338Smpi 		 * explicitly, use it.
451b9ab5338Smpi 		 */
452b9ab5338Smpi 		if (opt != NULL && (pi = opt->ip6po_pktinfo) != NULL)
453b9ab5338Smpi 			ifp = if_get(pi->ipi6_ifindex);
454b9ab5338Smpi 
455b9ab5338Smpi 		if (ifp == NULL && im6o != NULL)
456b9ab5338Smpi 			ifp = if_get(im6o->im6o_ifidx);
4576b532452Sitojun 	}
458b9ab5338Smpi 
459b9ab5338Smpi 	if (ifp == NULL) {
46022723314Sbluhm 		rt = in6_selectroute(&ip6->ip6_dst, opt, ro,
46122723314Sbluhm 		    m->m_pkthdr.ph_rtableid);
462aaf34fadSmpi 		if (rt == NULL) {
46331e14cacSjca 			ip6stat_inc(ip6s_noroute);
464aaf34fadSmpi 			error = EHOSTUNREACH;
4656b532452Sitojun 			goto bad;
4666b532452Sitojun 		}
4679e948f09Smpi 		if (ISSET(rt->rt_flags, RTF_LOCAL))
4683a63dfc0Smpi 			ifp = if_get(rtable_loindex(m->m_pkthdr.ph_rtableid));
4699e948f09Smpi 		else
470824e7fc7Smpi 			ifp = if_get(rt->rt_ifidx);
47130aa4938Sbluhm 		/*
47230aa4938Sbluhm 		 * We aren't using rtisvalid() here because the UP/DOWN state
47330aa4938Sbluhm 		 * machine is broken with some Ethernet drivers like em(4).
47430aa4938Sbluhm 		 * As a result we might try to use an invalid cached route
47530aa4938Sbluhm 		 * entry while an interface is being detached.
47630aa4938Sbluhm 		 */
47730aa4938Sbluhm 		if (ifp == NULL) {
47830aa4938Sbluhm 			ip6stat_inc(ip6s_noroute);
47930aa4938Sbluhm 			error = EHOSTUNREACH;
48030aa4938Sbluhm 			goto bad;
48130aa4938Sbluhm 		}
482aaf34fadSmpi 	} else {
483caa7f414Sbluhm 		route6_cache(ro, &ip6->ip6_dst, NULL, m->m_pkthdr.ph_rtableid);
4846b532452Sitojun 	}
4856b532452Sitojun 
486cf618f1eSflorian 	if (rt && (rt->rt_flags & RTF_GATEWAY) &&
487cf618f1eSflorian 	    !IN6_IS_ADDR_MULTICAST(&ip6->ip6_dst))
488c3c56496Sbluhm 		dst = satosin6(rt->rt_gateway);
4896b532452Sitojun 
490287546eaSitojun 	if (!IN6_IS_ADDR_MULTICAST(&ip6->ip6_dst)) {
491287546eaSitojun 		/* Unicast */
492287546eaSitojun 
493287546eaSitojun 		m->m_flags &= ~(M_BCAST | M_MCAST);	/* just in case */
494287546eaSitojun 	} else {
495287546eaSitojun 		/* Multicast */
496287546eaSitojun 
497287546eaSitojun 		m->m_flags = (m->m_flags & ~M_BCAST) | M_MCAST;
498287546eaSitojun 
499287546eaSitojun 		/*
500287546eaSitojun 		 * Confirm that the outgoing interface supports multicast.
501287546eaSitojun 		 */
502287546eaSitojun 		if ((ifp->if_flags & IFF_MULTICAST) == 0) {
50331e14cacSjca 			ip6stat_inc(ip6s_noroute);
504287546eaSitojun 			error = ENETUNREACH;
505287546eaSitojun 			goto bad;
506287546eaSitojun 		}
507c91534fbSmpi 
50834dbf6d6Smpi 		if ((im6o == NULL || im6o->im6o_loop) &&
50934dbf6d6Smpi 		    in6_hasmulti(&ip6->ip6_dst, ifp)) {
510287546eaSitojun 			/*
511287546eaSitojun 			 * If we belong to the destination multicast group
512287546eaSitojun 			 * on the outgoing interface, and the caller did not
513287546eaSitojun 			 * forbid loopback, loop back a copy.
514c91534fbSmpi 			 * Can't defer TCP/UDP checksumming, do the
515c91534fbSmpi 			 * computation now.
516287546eaSitojun 			 */
517c91534fbSmpi 			in6_proto_cksum_out(m, NULL);
518287546eaSitojun 			ip6_mloopback(ifp, m, dst);
519c91534fbSmpi 		}
520c91534fbSmpi #ifdef MROUTING
521c91534fbSmpi 		else {
522287546eaSitojun 			/*
523287546eaSitojun 			 * If we are acting as a multicast router, perform
524287546eaSitojun 			 * multicast forwarding as if the packet had just
525287546eaSitojun 			 * arrived on the interface to which we are about
526287546eaSitojun 			 * to send.  The multicast forwarding function
527287546eaSitojun 			 * recursively calls this function, using the
528287546eaSitojun 			 * IPV6_FORWARDING flag to prevent infinite recursion.
529287546eaSitojun 			 *
530287546eaSitojun 			 * Multicasts that are looped back by ip6_mloopback(),
531287546eaSitojun 			 * above, will be forwarded by the ip6_input() routine,
532287546eaSitojun 			 * if necessary.
533287546eaSitojun 			 */
5345d692803Srzalamena 			if (ip6_mforwarding && ip6_mrouter[ifp->if_rdomain] &&
535d9c6379eSclaudio 			    (flags & IPV6_FORWARDING) == 0) {
53628c60e63Sbluhm 				if (ip6_mforward(ip6, ifp, m, flags) != 0) {
537287546eaSitojun 					m_freem(m);
538287546eaSitojun 					goto done;
539287546eaSitojun 				}
540287546eaSitojun 			}
541287546eaSitojun 		}
542c91534fbSmpi #endif
543287546eaSitojun 		/*
544287546eaSitojun 		 * Multicasts with a hoplimit of zero may be looped back,
545287546eaSitojun 		 * above, but must not be transmitted on a network.
546287546eaSitojun 		 * Also, multicasts addressed to the loopback interface
547287546eaSitojun 		 * are not sent -- the above call to ip6_mloopback() will
548287546eaSitojun 		 * loop back a copy if this host actually belongs to the
549287546eaSitojun 		 * destination group on the loopback interface.
550287546eaSitojun 		 */
551d4070096Sitojun 		if (ip6->ip6_hlim == 0 || (ifp->if_flags & IFF_LOOPBACK) ||
552d4070096Sitojun 		    IN6_IS_ADDR_MC_INTFACELOCAL(&ip6->ip6_dst)) {
553287546eaSitojun 			m_freem(m);
554287546eaSitojun 			goto done;
555287546eaSitojun 		}
556287546eaSitojun 	}
557287546eaSitojun 
558287546eaSitojun 	/*
559678831beSjsg 	 * If this packet is going through a loopback interface we won't
560cd5ec7edSmpi 	 * be able to restore its scope ID using the interface index.
561cd5ec7edSmpi 	 */
562cd5ec7edSmpi 	if (IN6_IS_SCOPE_EMBED(&ip6->ip6_src)) {
563cd5ec7edSmpi 		if (ifp->if_flags & IFF_LOOPBACK)
564cd5ec7edSmpi 			src_scope = ip6->ip6_src.s6_addr16[1];
565cd5ec7edSmpi 		ip6->ip6_src.s6_addr16[1] = 0;
566cd5ec7edSmpi 	}
567cd5ec7edSmpi 	if (IN6_IS_SCOPE_EMBED(&ip6->ip6_dst)) {
568cd5ec7edSmpi 		if (ifp->if_flags & IFF_LOOPBACK)
569cd5ec7edSmpi 			dst_scope = ip6->ip6_dst.s6_addr16[1];
570cd5ec7edSmpi 		ip6->ip6_dst.s6_addr16[1] = 0;
571cd5ec7edSmpi 	}
572cd5ec7edSmpi 
573d6b9e9b9Sitojun 	/* Determine path MTU. */
574f3785e27Sbluhm 	if ((error = ip6_getpmtu(ro_pmtu->ro_rt, ifp, &mtu)) != 0)
575d6b9e9b9Sitojun 		goto bad;
576287546eaSitojun 
577287546eaSitojun 	/*
578d6b9e9b9Sitojun 	 * The caller of this function may specify to use the minimum MTU
579d6b9e9b9Sitojun 	 * in some cases.
5806b532452Sitojun 	 * An advanced API option (IPV6_USE_MIN_MTU) can also override MTU
5816b532452Sitojun 	 * setting.  The logic is a bit complicated; by default, unicast
5826b532452Sitojun 	 * packets will follow path MTU while multicast packets will be sent at
5836b532452Sitojun 	 * the minimum MTU.  If IP6PO_MINMTU_ALL is specified, all packets
5846b532452Sitojun 	 * including unicast ones will be sent at the minimum MTU.  Multicast
5856b532452Sitojun 	 * packets will always be sent at the minimum MTU unless
5866b532452Sitojun 	 * IP6PO_MINMTU_DISABLE is explicitly specified.
5876b532452Sitojun 	 * See RFC 3542 for more details.
588287546eaSitojun 	 */
589d6b9e9b9Sitojun 	if (mtu > IPV6_MMTU) {
590d6b9e9b9Sitojun 		if ((flags & IPV6_MINMTU))
591d6b9e9b9Sitojun 			mtu = IPV6_MMTU;
5926b532452Sitojun 		else if (opt && opt->ip6po_minmtu == IP6PO_MINMTU_ALL)
5936b532452Sitojun 			mtu = IPV6_MMTU;
59490dc3aeaSbluhm 		else if (IN6_IS_ADDR_MULTICAST(&ip6->ip6_dst) && (opt == NULL ||
5956b532452Sitojun 		    opt->ip6po_minmtu != IP6PO_MINMTU_DISABLE)) {
5966b532452Sitojun 			mtu = IPV6_MMTU;
5976b532452Sitojun 		}
598287546eaSitojun 	}
599287546eaSitojun 
600287546eaSitojun 	/*
601287546eaSitojun 	 * If the outgoing packet contains a hop-by-hop options header,
602287546eaSitojun 	 * it must be examined and processed even by the source node.
603287546eaSitojun 	 * (RFC 2460, section 4.)
604287546eaSitojun 	 */
605287546eaSitojun 	if (exthdrs.ip6e_hbh) {
6067b2ccaf7Sitojun 		struct ip6_hbh *hbh = mtod(exthdrs.ip6e_hbh, struct ip6_hbh *);
607dfec937eSkrw 		u_int32_t rtalert; /* returned value is ignored */
608dfec937eSkrw 		u_int32_t plen = 0; /* no more than 1 jumbo payload option! */
609287546eaSitojun 
610fb492c37Smpi 		m->m_pkthdr.ph_ifidx = ifp->if_index;
61180467c39Sbluhm 		if (ip6_process_hopopts(&m, (u_int8_t *)(hbh + 1),
6127b2ccaf7Sitojun 		    ((hbh->ip6h_len + 1) << 3) - sizeof(struct ip6_hbh),
613dfec937eSkrw 		    &rtalert, &plen) < 0) {
614287546eaSitojun 			/* m was already freed at this point */
615287546eaSitojun 			error = EINVAL;/* better error? */
616287546eaSitojun 			goto done;
617287546eaSitojun 		}
618fb492c37Smpi 		m->m_pkthdr.ph_ifidx = 0;
619287546eaSitojun 	}
620287546eaSitojun 
62130620b12Sfrantzen #if NPF > 0
6221f52aa9fSmpi 	if (pf_test(AF_INET6, PF_OUT, ifp, &m) != PF_PASS) {
623840989e0Sbluhm 		error = EACCES;
624b13fd930Sjasoni 		m_freem(m);
62530620b12Sfrantzen 		goto done;
62630620b12Sfrantzen 	}
627cb3a4e31Sjasoni 	if (m == NULL)
628cb3a4e31Sjasoni 		goto done;
629a1748d09Sjasoni 	ip6 = mtod(m, struct ip6_hdr *);
630f1638404Sclaudio 	if ((m->m_pkthdr.pf.flags & (PF_TAG_REROUTE | PF_TAG_GENERATED)) ==
631f1638404Sclaudio 	    (PF_TAG_REROUTE | PF_TAG_GENERATED)) {
632f1638404Sclaudio 		/* already rerun the route lookup, go on */
633f1638404Sclaudio 		m->m_pkthdr.pf.flags &= ~(PF_TAG_GENERATED | PF_TAG_REROUTE);
634f1638404Sclaudio 	} else if (m->m_pkthdr.pf.flags & PF_TAG_REROUTE) {
635f1638404Sclaudio 		/* tag as generated to skip over pf_test on rerun */
636f1638404Sclaudio 		m->m_pkthdr.pf.flags |= PF_TAG_GENERATED;
637f1638404Sclaudio 		finaldst = ip6->ip6_dst;
638f46106f1Sbluhm 		if (ro == &iproute)
639f46106f1Sbluhm 			rtfree(ro->ro_rt);
640f1638404Sclaudio 		ro = NULL;
6410e3d2ab2Sclaudio 		if_put(ifp); /* drop reference since destination changed */
6427cf86a87Smpi 		ifp = NULL;
643f1638404Sclaudio 		goto reroute;
644f1638404Sclaudio 	}
64530620b12Sfrantzen #endif
646bc3c7896Smpi 
64728c60e63Sbluhm #ifdef IPSEC
64828c60e63Sbluhm 	if (ISSET(flags, IPV6_FORWARDING) &&
64928c60e63Sbluhm 	    ISSET(flags, IPV6_FORWARDING_IPSEC) &&
65028c60e63Sbluhm 	    !ISSET(m->m_pkthdr.ph_tagsset, PACKET_TAG_IPSEC_IN_DONE)) {
65128c60e63Sbluhm 		error = EHOSTUNREACH;
65228c60e63Sbluhm 		goto bad;
65328c60e63Sbluhm 	}
65428c60e63Sbluhm #endif
65528c60e63Sbluhm 
656bc3c7896Smpi 	/*
657df8d9afdSjsg 	 * If the packet is not going on the wire it can be destined
658bc3c7896Smpi 	 * to any local address.  In this case do not clear its scopes
659bc3c7896Smpi 	 * to let ip6_input() find a matching local route.
660bc3c7896Smpi 	 */
661cd5ec7edSmpi 	if (ifp->if_flags & IFF_LOOPBACK) {
662cd5ec7edSmpi 		if (IN6_IS_SCOPE_EMBED(&ip6->ip6_src))
663cd5ec7edSmpi 			ip6->ip6_src.s6_addr16[1] = src_scope;
664cd5ec7edSmpi 		if (IN6_IS_SCOPE_EMBED(&ip6->ip6_dst))
665cd5ec7edSmpi 			ip6->ip6_dst.s6_addr16[1] = dst_scope;
666cd5ec7edSmpi 	}
667cd5ec7edSmpi 
668287546eaSitojun 	/*
669287546eaSitojun 	 * Send the packet to the outgoing interface.
670287546eaSitojun 	 * If necessary, do IPv6 fragmentation before sending.
671a8662718Sitojun 	 *
672a8662718Sitojun 	 * the logic here is rather complex:
673f3785e27Sbluhm 	 * 1: normal case (dontfrag == 0)
674a8662718Sitojun 	 * 1-a: send as is if tlen <= path mtu
675a8662718Sitojun 	 * 1-b: fragment if tlen > path mtu
676a8662718Sitojun 	 *
677a8662718Sitojun 	 * 2: if user asks us not to fragment (dontfrag == 1)
678a8662718Sitojun 	 * 2-a: send as is if tlen <= interface mtu
679a8662718Sitojun 	 * 2-b: error if tlen > interface mtu
680287546eaSitojun 	 */
6815ebaba9dSbluhm 	tlen = ISSET(m->m_pkthdr.csum_flags, M_TCP_TSO) ?
6825ebaba9dSbluhm 	    m->m_pkthdr.ph_mss : m->m_pkthdr.len;
683a8662718Sitojun 
68445ad364fSdlg 	if (ISSET(m->m_pkthdr.csum_flags, M_IPV6_DF_OUT)) {
68545ad364fSdlg 		CLR(m->m_pkthdr.csum_flags, M_IPV6_DF_OUT);
68645ad364fSdlg 		dontfrag = 1;
68745ad364fSdlg 	} else if (opt && ISSET(opt->ip6po_flags, IP6PO_DONTFRAG))
6886b532452Sitojun 		dontfrag = 1;
6896b532452Sitojun 	else
690a8662718Sitojun 		dontfrag = 0;
6915ebaba9dSbluhm 
6925ebaba9dSbluhm 	if (dontfrag && tlen > ifp->if_mtu) {		/* case 2-b */
69389de4c79Sbluhm #ifdef IPSEC
69489de4c79Sbluhm 		if (ip_mtudisc)
69589de4c79Sbluhm 			ipsec_adjust_mtu(m, mtu);
69689de4c79Sbluhm #endif
697a8662718Sitojun 		error = EMSGSIZE;
698a8662718Sitojun 		goto bad;
699a8662718Sitojun 	}
700a8662718Sitojun 
701a8662718Sitojun 	/*
702a8662718Sitojun 	 * transmit packet without fragmentation
703a8662718Sitojun 	 */
704c06845b1Sbluhm 	if (dontfrag || tlen <= mtu) {			/* case 1-a and 2-a */
7055ebaba9dSbluhm 		error = if_output_tso(ifp, &m, sin6tosa(dst), ro->ro_rt,
7065ebaba9dSbluhm 		    ifp->if_mtu);
707510f4386Sbluhm 		if (error || m == NULL)
708c06845b1Sbluhm 			goto done;
7095ebaba9dSbluhm 		goto bad;				/* should not happen */
7105ebaba9dSbluhm 	}
711c06845b1Sbluhm 
712287546eaSitojun 	/*
713f3785e27Sbluhm 	 * try to fragment the packet.  case 1-b
714287546eaSitojun 	 */
715a8662718Sitojun 	if (mtu < IPV6_MMTU) {
716a8662718Sitojun 		/* path MTU cannot be less than IPV6_MMTU */
717287546eaSitojun 		error = EMSGSIZE;
718287546eaSitojun 		goto bad;
719a8662718Sitojun 	} else if (ip6->ip6_plen == 0) {
720a8662718Sitojun 		/* jumbo payload cannot be fragmented */
721287546eaSitojun 		error = EMSGSIZE;
722287546eaSitojun 		goto bad;
7232818ef99Sbluhm 	}
724287546eaSitojun 
725287546eaSitojun 	/*
726287546eaSitojun 	 * Too large for the destination or interface;
727287546eaSitojun 	 * fragment if possible.
728287546eaSitojun 	 * Must be able to put at least 8 bytes per fragment.
729287546eaSitojun 	 */
730287546eaSitojun 	hlen = unfragpartlen;
731287546eaSitojun 	if (mtu > IPV6_MAXPACKET)
732287546eaSitojun 		mtu = IPV6_MAXPACKET;
733a8662718Sitojun 
734287546eaSitojun 	/*
735ce9d227cSbluhm 	 * If we are doing fragmentation, we can't defer TCP/UDP
736ce9d227cSbluhm 	 * checksumming; compute the checksum and clear the flag.
737ce9d227cSbluhm 	 */
738ce9d227cSbluhm 	in6_proto_cksum_out(m, NULL);
739ce9d227cSbluhm 
740ce9d227cSbluhm 	/*
741287546eaSitojun 	 * Change the next header field of the last header in the
742287546eaSitojun 	 * unfragmentable part.
743287546eaSitojun 	 */
744287546eaSitojun 	if (exthdrs.ip6e_rthdr) {
745287546eaSitojun 		nextproto = *mtod(exthdrs.ip6e_rthdr, u_char *);
746287546eaSitojun 		*mtod(exthdrs.ip6e_rthdr, u_char *) = IPPROTO_FRAGMENT;
7476afad192Sitojun 	} else if (exthdrs.ip6e_dest1) {
748287546eaSitojun 		nextproto = *mtod(exthdrs.ip6e_dest1, u_char *);
749287546eaSitojun 		*mtod(exthdrs.ip6e_dest1, u_char *) = IPPROTO_FRAGMENT;
7506afad192Sitojun 	} else if (exthdrs.ip6e_hbh) {
751287546eaSitojun 		nextproto = *mtod(exthdrs.ip6e_hbh, u_char *);
752287546eaSitojun 		*mtod(exthdrs.ip6e_hbh, u_char *) = IPPROTO_FRAGMENT;
7536afad192Sitojun 	} else {
754287546eaSitojun 		nextproto = ip6->ip6_nxt;
755287546eaSitojun 		ip6->ip6_nxt = IPPROTO_FRAGMENT;
756287546eaSitojun 	}
757287546eaSitojun 
758b8646e37Sbluhm 	if ((error = ip6_fragment(m, &ml, hlen, nextproto, mtu)) ||
759b8646e37Sbluhm 	    (error = if_output_ml(ifp, &ml, sin6tosa(dst), ro->ro_rt)))
7602818ef99Sbluhm 		goto done;
76131e14cacSjca 	ip6stat_inc(ip6s_fragmented);
7622136a888Sbluhm 	goto done;
763287546eaSitojun 
7642136a888Sbluhm  freehdrs:
7652136a888Sbluhm 	m_freem(exthdrs.ip6e_hbh);	/* m_freem will check if mbuf is 0 */
7662136a888Sbluhm 	m_freem(exthdrs.ip6e_dest1);
7672136a888Sbluhm 	m_freem(exthdrs.ip6e_rthdr);
7682136a888Sbluhm 	m_freem(exthdrs.ip6e_dest2);
7692136a888Sbluhm  bad:
7702136a888Sbluhm 	m_freem(m);
771287546eaSitojun  done:
772f46106f1Sbluhm 	if (ro == &iproute)
77327ae666cSmpi 		rtfree(ro->ro_rt);
774f46106f1Sbluhm 	else if (ro_pmtu == &iproute)
77527ae666cSmpi 		rtfree(ro_pmtu->ro_rt);
77631a6915fSbluhm 	if_put(ifp);
77731a6915fSbluhm #ifdef IPSEC
77831a6915fSbluhm 	tdb_unref(tdb);
77931a6915fSbluhm #endif /* IPSEC */
780287546eaSitojun 	return (error);
781287546eaSitojun }
782287546eaSitojun 
783d4984c21Sjsing int
784b8646e37Sbluhm ip6_fragment(struct mbuf *m0, struct mbuf_list *ml, int hlen, u_char nextproto,
785b8646e37Sbluhm     u_long mtu)
786a8cafab0Sbluhm {
7874daa6442Sbluhm 	struct ip6_hdr *ip6;
788a8cafab0Sbluhm 	u_int32_t id;
789a8cafab0Sbluhm 	int tlen, len, off;
790a8cafab0Sbluhm 	int error;
791a8cafab0Sbluhm 
792b8646e37Sbluhm 	ml_init(ml);
793a8cafab0Sbluhm 
7944daa6442Sbluhm 	ip6 = mtod(m0, struct ip6_hdr *);
795a8cafab0Sbluhm 	tlen = m0->m_pkthdr.len;
796a8cafab0Sbluhm 	len = (mtu - hlen - sizeof(struct ip6_frag)) & ~7;
7972818ef99Sbluhm 	if (len < 8) {
7982818ef99Sbluhm 		error = EMSGSIZE;
7992818ef99Sbluhm 		goto bad;
8002818ef99Sbluhm 	}
8012818ef99Sbluhm 	id = htonl(ip6_randomid());
802a8cafab0Sbluhm 
803a8cafab0Sbluhm 	/*
804b8646e37Sbluhm 	 * Loop through length of payload,
8052818ef99Sbluhm 	 * make new header and copy data of each part and link onto chain.
806a8cafab0Sbluhm 	 */
807a8cafab0Sbluhm 	for (off = hlen; off < tlen; off += len) {
808b8646e37Sbluhm 		struct mbuf *m;
809a8cafab0Sbluhm 		struct mbuf *mlast;
8104daa6442Sbluhm 		struct ip6_hdr *mhip6;
8114daa6442Sbluhm 		struct ip6_frag *ip6f;
812a8cafab0Sbluhm 
8132818ef99Sbluhm 		MGETHDR(m, M_DONTWAIT, MT_HEADER);
8142818ef99Sbluhm 		if (m == NULL) {
8152818ef99Sbluhm 			error = ENOBUFS;
8162818ef99Sbluhm 			goto bad;
8172818ef99Sbluhm 		}
818b8646e37Sbluhm 		ml_enqueue(ml, m);
8197fbb3004Sblambert 		if ((error = m_dup_pkthdr(m, m0, M_DONTWAIT)) != 0)
8202818ef99Sbluhm 			goto bad;
821a8cafab0Sbluhm 		m->m_data += max_linkhdr;
822a8cafab0Sbluhm 		mhip6 = mtod(m, struct ip6_hdr *);
8234daa6442Sbluhm 		*mhip6 = *ip6;
8244daa6442Sbluhm 		m->m_len = sizeof(struct ip6_hdr);
8254daa6442Sbluhm 
826a8cafab0Sbluhm 		if ((error = ip6_insertfraghdr(m0, m, hlen, &ip6f)) != 0)
8272818ef99Sbluhm 			goto bad;
8284daa6442Sbluhm 		ip6f->ip6f_offlg = htons((off - hlen) & ~7);
829a8cafab0Sbluhm 		if (off + len >= tlen)
830a8cafab0Sbluhm 			len = tlen - off;
831a8cafab0Sbluhm 		else
832a8cafab0Sbluhm 			ip6f->ip6f_offlg |= IP6F_MORE_FRAG;
8334daa6442Sbluhm 
8344daa6442Sbluhm 		m->m_pkthdr.len = hlen + sizeof(struct ip6_frag) + len;
8354daa6442Sbluhm 		mhip6->ip6_plen = htons(m->m_pkthdr.len -
8364daa6442Sbluhm 		    sizeof(struct ip6_hdr));
8374daa6442Sbluhm 		for (mlast = m; mlast->m_next; mlast = mlast->m_next)
8384daa6442Sbluhm 			;
8394daa6442Sbluhm 		mlast->m_next = m_copym(m0, off, len, M_DONTWAIT);
8404daa6442Sbluhm 		if (mlast->m_next == NULL) {
8412818ef99Sbluhm 			error = ENOBUFS;
8422818ef99Sbluhm 			goto bad;
8432818ef99Sbluhm 		}
8444daa6442Sbluhm 
845a8cafab0Sbluhm 		ip6f->ip6f_reserved = 0;
846a8cafab0Sbluhm 		ip6f->ip6f_ident = id;
847a8cafab0Sbluhm 		ip6f->ip6f_nxt = nextproto;
848a8cafab0Sbluhm 	}
849a8cafab0Sbluhm 
850b8646e37Sbluhm 	ip6stat_add(ip6s_ofragments, ml_len(ml));
8512818ef99Sbluhm 	m_freem(m0);
852a8cafab0Sbluhm 	return (0);
8532818ef99Sbluhm 
8542818ef99Sbluhm bad:
8552818ef99Sbluhm 	ip6stat_inc(ip6s_odropped);
856b8646e37Sbluhm 	ml_purge(ml);
8572818ef99Sbluhm 	m_freem(m0);
8582818ef99Sbluhm 	return (error);
859a8cafab0Sbluhm }
860a8cafab0Sbluhm 
861a8cafab0Sbluhm int
862ee37ea65Smcbride ip6_copyexthdr(struct mbuf **mp, caddr_t hdr, int hlen)
863287546eaSitojun {
864287546eaSitojun 	struct mbuf *m;
865287546eaSitojun 
866287546eaSitojun 	if (hlen > MCLBYTES)
867287546eaSitojun 		return (ENOBUFS); /* XXX */
868287546eaSitojun 
869287546eaSitojun 	MGET(m, M_DONTWAIT, MT_DATA);
870287546eaSitojun 	if (!m)
871287546eaSitojun 		return (ENOBUFS);
872287546eaSitojun 
873287546eaSitojun 	if (hlen > MLEN) {
874287546eaSitojun 		MCLGET(m, M_DONTWAIT);
875287546eaSitojun 		if ((m->m_flags & M_EXT) == 0) {
876287546eaSitojun 			m_free(m);
877287546eaSitojun 			return (ENOBUFS);
878287546eaSitojun 		}
879287546eaSitojun 	}
880287546eaSitojun 	m->m_len = hlen;
881287546eaSitojun 	if (hdr)
882e3b5aff7Sdhill 		memcpy(mtod(m, caddr_t), hdr, hlen);
883287546eaSitojun 
884287546eaSitojun 	*mp = m;
885287546eaSitojun 	return (0);
886287546eaSitojun }
887287546eaSitojun 
888287546eaSitojun /*
889287546eaSitojun  * Insert jumbo payload option.
890287546eaSitojun  */
891d4984c21Sjsing int
892ee37ea65Smcbride ip6_insert_jumboopt(struct ip6_exthdrs *exthdrs, u_int32_t plen)
893287546eaSitojun {
894287546eaSitojun 	struct mbuf *mopt;
8956f04ba85Sitojun 	u_int8_t *optbuf;
8964df4d1f1Sitojun 	u_int32_t v;
897287546eaSitojun 
898287546eaSitojun #define JUMBOOPTLEN	8	/* length of jumbo payload option and padding */
899287546eaSitojun 
900287546eaSitojun 	/*
901287546eaSitojun 	 * If there is no hop-by-hop options header, allocate new one.
902287546eaSitojun 	 * If there is one but it doesn't have enough space to store the
903287546eaSitojun 	 * jumbo payload option, allocate a cluster to store the whole options.
904287546eaSitojun 	 * Otherwise, use it to store the options.
905287546eaSitojun 	 */
906287546eaSitojun 	if (exthdrs->ip6e_hbh == 0) {
907287546eaSitojun 		MGET(mopt, M_DONTWAIT, MT_DATA);
90864a3f76cSjsg 		if (mopt == NULL)
909287546eaSitojun 			return (ENOBUFS);
910287546eaSitojun 		mopt->m_len = JUMBOOPTLEN;
9116f04ba85Sitojun 		optbuf = mtod(mopt, u_int8_t *);
912287546eaSitojun 		optbuf[1] = 0;	/* = ((JUMBOOPTLEN) >> 3) - 1 */
913287546eaSitojun 		exthdrs->ip6e_hbh = mopt;
9146afad192Sitojun 	} else {
915287546eaSitojun 		struct ip6_hbh *hbh;
916287546eaSitojun 
917287546eaSitojun 		mopt = exthdrs->ip6e_hbh;
918b5b7f62eSclaudio 		if (m_trailingspace(mopt) < JUMBOOPTLEN) {
9194df4d1f1Sitojun 			/*
9204df4d1f1Sitojun 			 * XXX assumption:
9214df4d1f1Sitojun 			 * - exthdrs->ip6e_hbh is not referenced from places
9224df4d1f1Sitojun 			 *   other than exthdrs.
9234df4d1f1Sitojun 			 * - exthdrs->ip6e_hbh is not an mbuf chain.
9244df4d1f1Sitojun 			 */
925287546eaSitojun 			int oldoptlen = mopt->m_len;
9264df4d1f1Sitojun 			struct mbuf *n;
927287546eaSitojun 
9284df4d1f1Sitojun 			/*
9294df4d1f1Sitojun 			 * XXX: give up if the whole (new) hbh header does
9304df4d1f1Sitojun 			 * not fit even in an mbuf cluster.
9314df4d1f1Sitojun 			 */
9324df4d1f1Sitojun 			if (oldoptlen + JUMBOOPTLEN > MCLBYTES)
933287546eaSitojun 				return (ENOBUFS);
934287546eaSitojun 
9354df4d1f1Sitojun 			/*
9364df4d1f1Sitojun 			 * As a consequence, we must always prepare a cluster
9374df4d1f1Sitojun 			 * at this point.
9384df4d1f1Sitojun 			 */
9394df4d1f1Sitojun 			MGET(n, M_DONTWAIT, MT_DATA);
9404df4d1f1Sitojun 			if (n) {
9414df4d1f1Sitojun 				MCLGET(n, M_DONTWAIT);
9424df4d1f1Sitojun 				if ((n->m_flags & M_EXT) == 0) {
9434df4d1f1Sitojun 					m_freem(n);
9444df4d1f1Sitojun 					n = NULL;
9454df4d1f1Sitojun 				}
9464df4d1f1Sitojun 			}
9474df4d1f1Sitojun 			if (!n)
9484df4d1f1Sitojun 				return (ENOBUFS);
9494df4d1f1Sitojun 			n->m_len = oldoptlen + JUMBOOPTLEN;
950e3b5aff7Sdhill 			memcpy(mtod(n, caddr_t), mtod(mopt, caddr_t),
9514df4d1f1Sitojun 			      oldoptlen);
9526f04ba85Sitojun 			optbuf = mtod(n, u_int8_t *) + oldoptlen;
9534df4d1f1Sitojun 			m_freem(mopt);
954672f01bcSitojun 			mopt = exthdrs->ip6e_hbh = n;
9556afad192Sitojun 		} else {
9566f04ba85Sitojun 			optbuf = mtod(mopt, u_int8_t *) + mopt->m_len;
957287546eaSitojun 			mopt->m_len += JUMBOOPTLEN;
958287546eaSitojun 		}
959287546eaSitojun 		optbuf[0] = IP6OPT_PADN;
960db177b5aSitojun 		optbuf[1] = 0;
961287546eaSitojun 
962287546eaSitojun 		/*
963287546eaSitojun 		 * Adjust the header length according to the pad and
964287546eaSitojun 		 * the jumbo payload option.
965287546eaSitojun 		 */
966287546eaSitojun 		hbh = mtod(mopt, struct ip6_hbh *);
967287546eaSitojun 		hbh->ip6h_len += (JUMBOOPTLEN >> 3);
968287546eaSitojun 	}
969287546eaSitojun 
970287546eaSitojun 	/* fill in the option. */
971287546eaSitojun 	optbuf[2] = IP6OPT_JUMBO;
972287546eaSitojun 	optbuf[3] = 4;
9734df4d1f1Sitojun 	v = (u_int32_t)htonl(plen + JUMBOOPTLEN);
97441bfede9Stedu 	memcpy(&optbuf[4], &v, sizeof(u_int32_t));
975287546eaSitojun 
976287546eaSitojun 	/* finally, adjust the packet header length */
977287546eaSitojun 	exthdrs->ip6e_ip6->m_pkthdr.len += JUMBOOPTLEN;
978287546eaSitojun 
979287546eaSitojun 	return (0);
980287546eaSitojun #undef JUMBOOPTLEN
981287546eaSitojun }
982287546eaSitojun 
983287546eaSitojun /*
984287546eaSitojun  * Insert fragment header and copy unfragmentable header portions.
985287546eaSitojun  */
986d4984c21Sjsing int
987ee37ea65Smcbride ip6_insertfraghdr(struct mbuf *m0, struct mbuf *m, int hlen,
988ee37ea65Smcbride     struct ip6_frag **frghdrp)
989287546eaSitojun {
990287546eaSitojun 	struct mbuf *n, *mlast;
991287546eaSitojun 
992287546eaSitojun 	if (hlen > sizeof(struct ip6_hdr)) {
993287546eaSitojun 		n = m_copym(m0, sizeof(struct ip6_hdr),
994287546eaSitojun 		    hlen - sizeof(struct ip6_hdr), M_DONTWAIT);
995bc31a334Skrw 		if (n == NULL)
996287546eaSitojun 			return (ENOBUFS);
997287546eaSitojun 		m->m_next = n;
9986afad192Sitojun 	} else
999287546eaSitojun 		n = m;
1000287546eaSitojun 
1001287546eaSitojun 	/* Search for the last mbuf of unfragmentable part. */
1002287546eaSitojun 	for (mlast = n; mlast->m_next; mlast = mlast->m_next)
1003287546eaSitojun 		;
1004287546eaSitojun 
1005287546eaSitojun 	if ((mlast->m_flags & M_EXT) == 0 &&
1006b5b7f62eSclaudio 	    m_trailingspace(mlast) >= sizeof(struct ip6_frag)) {
100790dc3aeaSbluhm 		/* use the trailing space of the last mbuf for fragment hdr */
10087b2ccaf7Sitojun 		*frghdrp = (struct ip6_frag *)(mtod(mlast, caddr_t) +
10097b2ccaf7Sitojun 		    mlast->m_len);
1010287546eaSitojun 		mlast->m_len += sizeof(struct ip6_frag);
1011287546eaSitojun 		m->m_pkthdr.len += sizeof(struct ip6_frag);
10126afad192Sitojun 	} else {
1013287546eaSitojun 		/* allocate a new mbuf for the fragment header */
1014287546eaSitojun 		struct mbuf *mfrg;
1015287546eaSitojun 
1016287546eaSitojun 		MGET(mfrg, M_DONTWAIT, MT_DATA);
101764a3f76cSjsg 		if (mfrg == NULL)
1018287546eaSitojun 			return (ENOBUFS);
1019287546eaSitojun 		mfrg->m_len = sizeof(struct ip6_frag);
1020287546eaSitojun 		*frghdrp = mtod(mfrg, struct ip6_frag *);
1021287546eaSitojun 		mlast->m_next = mfrg;
1022287546eaSitojun 	}
1023287546eaSitojun 
1024287546eaSitojun 	return (0);
1025287546eaSitojun }
1026287546eaSitojun 
1027d4984c21Sjsing int
1028f3785e27Sbluhm ip6_getpmtu(struct rtentry *rt, struct ifnet *ifp, u_long *mtup)
1029d6b9e9b9Sitojun {
1030*84d9c64aSbluhm 	u_int mtu, rtmtu;
1031d6b9e9b9Sitojun 	int error = 0;
1032d6b9e9b9Sitojun 
103373ae3de3Smpi 	if (rt != NULL) {
1034*84d9c64aSbluhm 		mtu = rtmtu = atomic_load_int(&rt->rt_mtu);
1035d69f1ffaSitojun 		if (mtu == 0)
1036a923c847Sflorian 			mtu = ifp->if_mtu;
1037a8662718Sitojun 		else if (mtu < IPV6_MMTU) {
1038f3785e27Sbluhm 			/* RFC8021 IPv6 Atomic Fragments Considered Harmful */
1039a8662718Sitojun 			mtu = IPV6_MMTU;
1040a923c847Sflorian 		} else if (mtu > ifp->if_mtu) {
1041d6b9e9b9Sitojun 			/*
1042d6b9e9b9Sitojun 			 * The MTU on the route is larger than the MTU on
1043d6b9e9b9Sitojun 			 * the interface!  This shouldn't happen, unless the
1044d6b9e9b9Sitojun 			 * MTU of the interface has been changed after the
1045d6b9e9b9Sitojun 			 * interface was brought up.  Change the MTU in the
1046d6b9e9b9Sitojun 			 * route to match the interface MTU (as long as the
1047d6b9e9b9Sitojun 			 * field isn't locked).
1048d6b9e9b9Sitojun 			 */
1049a923c847Sflorian 			mtu = ifp->if_mtu;
10509b6a6412Sbluhm 			if (!(rt->rt_locks & RTV_MTU))
1051*84d9c64aSbluhm 				atomic_cas_uint(&rt->rt_mtu, rtmtu, mtu);
1052d6b9e9b9Sitojun 		}
105373ae3de3Smpi 	} else {
105473ae3de3Smpi 		mtu = ifp->if_mtu;
105573ae3de3Smpi 	}
1056d6b9e9b9Sitojun 
1057d6b9e9b9Sitojun 	*mtup = mtu;
1058d6b9e9b9Sitojun 	return (error);
1059d6b9e9b9Sitojun }
1060d6b9e9b9Sitojun 
1061287546eaSitojun /*
1062287546eaSitojun  * IP6 socket option processing.
1063287546eaSitojun  */
1064287546eaSitojun int
1065ee37ea65Smcbride ip6_ctloutput(int op, struct socket *so, int level, int optname,
1066490e0738Sdhill     struct mbuf *m)
1067287546eaSitojun {
10686b532452Sitojun 	int privileged, optdatalen, uproto;
10696b532452Sitojun 	void *optdata;
10709063e41fSitojun 	struct inpcb *inp = sotoinpcb(so);
1071287546eaSitojun 	int error, optval;
107244c85a19Smpi 	struct proc *p = curproc; /* For IPsec and rdomain */
1073f74da371Smvs 	u_int rtableid, rtid = 0;
1074287546eaSitojun 
1075287546eaSitojun 	error = optval = 0;
1076287546eaSitojun 
1077287546eaSitojun 	privileged = (inp->inp_socket->so_state & SS_PRIV);
10786b532452Sitojun 	uproto = (int)so->so_proto->pr_protocol;
1079287546eaSitojun 
1080e09b1b7cSmpi 	if (level != IPPROTO_IPV6)
1081127aca78Sdhill 		return (EINVAL);
1082127aca78Sdhill 
1083f74da371Smvs 	rtableid = p->p_p->ps_rtableid;
1084f74da371Smvs 
1085287546eaSitojun 	switch (op) {
1086287546eaSitojun 	case PRCO_SETOPT:
1087287546eaSitojun 		switch (optname) {
10886b532452Sitojun 		/*
10896b532452Sitojun 		 * Use of some Hop-by-Hop options or some
10906b532452Sitojun 		 * Destination options, might require special
10916b532452Sitojun 		 * privilege.  That is, normal applications
10926b532452Sitojun 		 * (without special privilege) might be forbidden
10936b532452Sitojun 		 * from setting certain options in outgoing packets,
10946b532452Sitojun 		 * and might never see certain options in received
10956b532452Sitojun 		 * packets. [RFC 2292 Section 6]
10966b532452Sitojun 		 * KAME specific note:
10976b532452Sitojun 		 *  KAME prevents non-privileged users from sending or
10986b532452Sitojun 		 *  receiving ANY hbh/dst options in order to avoid
10996b532452Sitojun 		 *  overhead of parsing options in the kernel.
11006b532452Sitojun 		 */
11016b532452Sitojun 		case IPV6_RECVHOPOPTS:
11026b532452Sitojun 		case IPV6_RECVDSTOPTS:
1103287546eaSitojun 			if (!privileged) {
1104287546eaSitojun 				error = EPERM;
1105287546eaSitojun 				break;
1106287546eaSitojun 			}
11079a9482baSitojun 			/* FALLTHROUGH */
1108287546eaSitojun 		case IPV6_UNICAST_HOPS:
1109e5ff19c7Sjca 		case IPV6_MINHOPCOUNT:
1110287546eaSitojun 		case IPV6_HOPLIMIT:
11116b532452Sitojun 
11126b532452Sitojun 		case IPV6_RECVPKTINFO:
11136b532452Sitojun 		case IPV6_RECVHOPLIMIT:
11146b532452Sitojun 		case IPV6_RECVRTHDR:
11156b532452Sitojun 		case IPV6_RECVPATHMTU:
11166b532452Sitojun 		case IPV6_RECVTCLASS:
11171782763dSitojun 		case IPV6_V6ONLY:
11186b532452Sitojun 		case IPV6_AUTOFLOWLABEL:
1119cee801dcSyasuoka 		case IPV6_RECVDSTPORT:
112053f19499Sguenther 			if (m == NULL || m->m_len != sizeof(int)) {
1121287546eaSitojun 				error = EINVAL;
11227b2ccaf7Sitojun 				break;
11237b2ccaf7Sitojun 			}
1124287546eaSitojun 			optval = *mtod(m, int *);
1125287546eaSitojun 			switch (optname) {
1126287546eaSitojun 
1127287546eaSitojun 			case IPV6_UNICAST_HOPS:
1128287546eaSitojun 				if (optval < -1 || optval >= 256)
1129287546eaSitojun 					error = EINVAL;
1130287546eaSitojun 				else {
1131287546eaSitojun 					/* -1 = kernel default */
1132287546eaSitojun 					inp->inp_hops = optval;
1133287546eaSitojun 				}
1134287546eaSitojun 				break;
1135e5ff19c7Sjca 
1136e5ff19c7Sjca 			case IPV6_MINHOPCOUNT:
1137e5ff19c7Sjca 				if (optval < 0 || optval > 255)
1138e5ff19c7Sjca 					error = EINVAL;
1139e5ff19c7Sjca 				else
1140e5ff19c7Sjca 					inp->inp_ip6_minhlim = optval;
1141e5ff19c7Sjca 				break;
1142e5ff19c7Sjca 
1143287546eaSitojun #define OPTSET(bit) \
11447b2ccaf7Sitojun do { \
1145287546eaSitojun 	if (optval) \
11467b2ccaf7Sitojun 		inp->inp_flags |= (bit); \
1147287546eaSitojun 	else \
11487b2ccaf7Sitojun 		inp->inp_flags &= ~(bit); \
11496b532452Sitojun } while (/*CONSTCOND*/ 0)
11506b532452Sitojun #define OPTBIT(bit) (inp->inp_flags & (bit) ? 1 : 0)
1151287546eaSitojun 
11526b532452Sitojun 			case IPV6_RECVPKTINFO:
1153287546eaSitojun 				OPTSET(IN6P_PKTINFO);
1154287546eaSitojun 				break;
1155287546eaSitojun 
1156287546eaSitojun 			case IPV6_HOPLIMIT:
11576b532452Sitojun 			{
11586b532452Sitojun 				struct ip6_pktopts **optp;
11596b532452Sitojun 
11606b532452Sitojun 				optp = &inp->inp_outputopts6;
11616b532452Sitojun 				error = ip6_pcbopt(IPV6_HOPLIMIT,
116290dc3aeaSbluhm 				    (u_char *)&optval, sizeof(optval), optp,
11636b532452Sitojun 				    privileged, uproto);
11646b532452Sitojun 				break;
11656b532452Sitojun 			}
11666b532452Sitojun 
11676b532452Sitojun 			case IPV6_RECVHOPLIMIT:
1168287546eaSitojun 				OPTSET(IN6P_HOPLIMIT);
1169287546eaSitojun 				break;
1170287546eaSitojun 
11716b532452Sitojun 			case IPV6_RECVHOPOPTS:
1172287546eaSitojun 				OPTSET(IN6P_HOPOPTS);
1173287546eaSitojun 				break;
1174287546eaSitojun 
11756b532452Sitojun 			case IPV6_RECVDSTOPTS:
1176287546eaSitojun 				OPTSET(IN6P_DSTOPTS);
1177287546eaSitojun 				break;
1178287546eaSitojun 
11796b532452Sitojun 			case IPV6_RECVRTHDR:
1180287546eaSitojun 				OPTSET(IN6P_RTHDR);
1181287546eaSitojun 				break;
1182287546eaSitojun 
11836b532452Sitojun 			case IPV6_RECVPATHMTU:
11846b532452Sitojun 				/*
11856b532452Sitojun 				 * We ignore this option for TCP
11866b532452Sitojun 				 * sockets.
11876b532452Sitojun 				 * (RFC3542 leaves this case
11886b532452Sitojun 				 * unspecified.)
11896b532452Sitojun 				 */
11906b532452Sitojun 				if (uproto != IPPROTO_TCP)
11916b532452Sitojun 					OPTSET(IN6P_MTU);
11920a46a566Sitojun 				break;
11930a46a566Sitojun 
11941782763dSitojun 			case IPV6_V6ONLY:
11956b532452Sitojun 				/*
11966b532452Sitojun 				 * make setsockopt(IPV6_V6ONLY)
11976b532452Sitojun 				 * available only prior to bind(2).
11986b532452Sitojun 				 * see ipng mailing list, Jun 22 2001.
11996b532452Sitojun 				 */
120090dc3aeaSbluhm 				if (inp->inp_lport || !IN6_IS_ADDR_UNSPECIFIED(
120190dc3aeaSbluhm 				    &inp->inp_laddr6)) {
12021782763dSitojun 					error = EINVAL;
12031782763dSitojun 					break;
1204287546eaSitojun 				}
12057e2f83f7Sjca 				/* No support for IPv4-mapped addresses. */
12067e2f83f7Sjca 				if (!optval)
12076b532452Sitojun 					error = EINVAL;
12087e2f83f7Sjca 				else
12097e2f83f7Sjca 					error = 0;
1210287546eaSitojun 				break;
12116b532452Sitojun 			case IPV6_RECVTCLASS:
12126b532452Sitojun 				OPTSET(IN6P_TCLASS);
12136b532452Sitojun 				break;
12146b532452Sitojun 			case IPV6_AUTOFLOWLABEL:
12156b532452Sitojun 				OPTSET(IN6P_AUTOFLOWLABEL);
12166b532452Sitojun 				break;
12176b532452Sitojun 
1218cee801dcSyasuoka 			case IPV6_RECVDSTPORT:
1219cee801dcSyasuoka 				OPTSET(IN6P_RECVDSTPORT);
1220cee801dcSyasuoka 				break;
12216b532452Sitojun 			}
12226b532452Sitojun 			break;
12236b532452Sitojun 
12246b532452Sitojun 		case IPV6_TCLASS:
12256b532452Sitojun 		case IPV6_DONTFRAG:
12266b532452Sitojun 		case IPV6_USE_MIN_MTU:
122753f19499Sguenther 			if (m == NULL || m->m_len != sizeof(optval)) {
12286b532452Sitojun 				error = EINVAL;
12296b532452Sitojun 				break;
12306b532452Sitojun 			}
12316b532452Sitojun 			optval = *mtod(m, int *);
12326b532452Sitojun 			{
12336b532452Sitojun 				struct ip6_pktopts **optp;
12346b532452Sitojun 				optp = &inp->inp_outputopts6;
123590dc3aeaSbluhm 				error = ip6_pcbopt(optname, (u_char *)&optval,
123690dc3aeaSbluhm 				    sizeof(optval), optp, privileged, uproto);
12376b532452Sitojun 				break;
12386b532452Sitojun 			}
12396b532452Sitojun 
12406b532452Sitojun 		case IPV6_PKTINFO:
12416b532452Sitojun 		case IPV6_HOPOPTS:
12426b532452Sitojun 		case IPV6_RTHDR:
12436b532452Sitojun 		case IPV6_DSTOPTS:
12446b532452Sitojun 		case IPV6_RTHDRDSTOPTS:
12456b532452Sitojun 		{
12466b532452Sitojun 			/* new advanced API (RFC3542) */
12476b532452Sitojun 			u_char *optbuf;
12486b532452Sitojun 			int optbuflen;
12496b532452Sitojun 			struct ip6_pktopts **optp;
12506b532452Sitojun 
12516b532452Sitojun 			if (m && m->m_next) {
12526b532452Sitojun 				error = EINVAL;	/* XXX */
12536b532452Sitojun 				break;
12546b532452Sitojun 			}
12556b532452Sitojun 			if (m) {
12566b532452Sitojun 				optbuf = mtod(m, u_char *);
12576b532452Sitojun 				optbuflen = m->m_len;
12586b532452Sitojun 			} else {
12596b532452Sitojun 				optbuf = NULL;
12606b532452Sitojun 				optbuflen = 0;
12616b532452Sitojun 			}
12626b532452Sitojun 			optp = &inp->inp_outputopts6;
126390dc3aeaSbluhm 			error = ip6_pcbopt(optname, optbuf, optbuflen, optp,
126490dc3aeaSbluhm 			    privileged, uproto);
12656b532452Sitojun 			break;
12666b532452Sitojun 		}
1267287546eaSitojun #undef OPTSET
1268287546eaSitojun 
1269287546eaSitojun 		case IPV6_MULTICAST_IF:
1270287546eaSitojun 		case IPV6_MULTICAST_HOPS:
1271287546eaSitojun 		case IPV6_MULTICAST_LOOP:
1272287546eaSitojun 		case IPV6_JOIN_GROUP:
1273287546eaSitojun 		case IPV6_LEAVE_GROUP:
1274287546eaSitojun 			error =	ip6_setmoptions(optname,
12756b532452Sitojun 						&inp->inp_moptions6,
127644a616dfSrzalamena 						m, inp->inp_rtableid);
1277287546eaSitojun 			break;
1278287546eaSitojun 
1279287546eaSitojun 		case IPV6_PORTRANGE:
128053f19499Sguenther 			if (m == NULL || m->m_len != sizeof(int)) {
128153f19499Sguenther 				error = EINVAL;
128253f19499Sguenther 				break;
128353f19499Sguenther 			}
1284287546eaSitojun 			optval = *mtod(m, int *);
1285287546eaSitojun 
1286287546eaSitojun 			switch (optval) {
1287287546eaSitojun 			case IPV6_PORTRANGE_DEFAULT:
12881782763dSitojun 				inp->inp_flags &= ~(IN6P_LOWPORT);
12891782763dSitojun 				inp->inp_flags &= ~(IN6P_HIGHPORT);
1290287546eaSitojun 				break;
1291287546eaSitojun 
1292287546eaSitojun 			case IPV6_PORTRANGE_HIGH:
12931782763dSitojun 				inp->inp_flags &= ~(IN6P_LOWPORT);
12941782763dSitojun 				inp->inp_flags |= IN6P_HIGHPORT;
1295287546eaSitojun 				break;
1296287546eaSitojun 
1297287546eaSitojun 			case IPV6_PORTRANGE_LOW:
12981782763dSitojun 				inp->inp_flags &= ~(IN6P_HIGHPORT);
12991782763dSitojun 				inp->inp_flags |= IN6P_LOWPORT;
1300287546eaSitojun 				break;
1301287546eaSitojun 
1302287546eaSitojun 			default:
1303287546eaSitojun 				error = EINVAL;
1304287546eaSitojun 				break;
1305287546eaSitojun 			}
1306287546eaSitojun 			break;
1307287546eaSitojun 
1308bea47bb9Sitojun 		case IPSEC6_OUTSA:
1309bea47bb9Sitojun 			error = EINVAL;
1310bea47bb9Sitojun 			break;
1311bea47bb9Sitojun 
1312bea47bb9Sitojun 		case IPV6_AUTH_LEVEL:
1313bea47bb9Sitojun 		case IPV6_ESP_TRANS_LEVEL:
1314bea47bb9Sitojun 		case IPV6_ESP_NETWORK_LEVEL:
1315781371b4Sniklas 		case IPV6_IPCOMP_LEVEL:
1316bea47bb9Sitojun #ifndef IPSEC
1317bea47bb9Sitojun 			error = EINVAL;
1318bea47bb9Sitojun #else
131964a3f76cSjsg 			if (m == NULL || m->m_len != sizeof(int)) {
1320bea47bb9Sitojun 				error = EINVAL;
1321bea47bb9Sitojun 				break;
1322bea47bb9Sitojun 			}
1323bea47bb9Sitojun 			optval = *mtod(m, int *);
1324bea47bb9Sitojun 
1325bea47bb9Sitojun 			if (optval < IPSEC_LEVEL_BYPASS ||
1326bea47bb9Sitojun 			    optval > IPSEC_LEVEL_UNIQUE) {
1327bea47bb9Sitojun 				error = EINVAL;
1328bea47bb9Sitojun 				break;
1329bea47bb9Sitojun 			}
1330bea47bb9Sitojun 
1331bea47bb9Sitojun 			switch (optname) {
1332781371b4Sniklas 			case IPV6_AUTH_LEVEL:
133372992b81Smpi 				if (optval < IPSEC_AUTH_LEVEL_DEFAULT &&
13343e676399Smpi 				    suser(p)) {
1335bea47bb9Sitojun 					error = EACCES;
1336bea47bb9Sitojun 					break;
1337bea47bb9Sitojun 				}
1338ace0f189Sbluhm 				inp->inp_seclevel.sl_auth = optval;
1339bea47bb9Sitojun 				break;
1340bea47bb9Sitojun 
1341781371b4Sniklas 			case IPV6_ESP_TRANS_LEVEL:
134272992b81Smpi 				if (optval < IPSEC_ESP_TRANS_LEVEL_DEFAULT &&
13433e676399Smpi 				    suser(p)) {
1344bea47bb9Sitojun 					error = EACCES;
1345bea47bb9Sitojun 					break;
1346bea47bb9Sitojun 				}
1347ace0f189Sbluhm 				inp->inp_seclevel.sl_esp_trans = optval;
1348bea47bb9Sitojun 				break;
1349bea47bb9Sitojun 
1350781371b4Sniklas 			case IPV6_ESP_NETWORK_LEVEL:
135172992b81Smpi 				if (optval < IPSEC_ESP_NETWORK_LEVEL_DEFAULT &&
13523e676399Smpi 				    suser(p)) {
1353bea47bb9Sitojun 					error = EACCES;
1354bea47bb9Sitojun 					break;
1355bea47bb9Sitojun 				}
1356ace0f189Sbluhm 				inp->inp_seclevel.sl_esp_network = optval;
1357bea47bb9Sitojun 				break;
1358781371b4Sniklas 
1359781371b4Sniklas 			case IPV6_IPCOMP_LEVEL:
136072992b81Smpi 				if (optval < IPSEC_IPCOMP_LEVEL_DEFAULT &&
13613e676399Smpi 				    suser(p)) {
1362781371b4Sniklas 					error = EACCES;
1363781371b4Sniklas 					break;
1364781371b4Sniklas 				}
1365ace0f189Sbluhm 				inp->inp_seclevel.sl_ipcomp = optval;
1366781371b4Sniklas 				break;
1367bea47bb9Sitojun 			}
1368bea47bb9Sitojun #endif
1369bea47bb9Sitojun 			break;
1370d83f8b41Sphessler 		case SO_RTABLE:
1371d83f8b41Sphessler 			if (m == NULL || m->m_len < sizeof(u_int)) {
1372d83f8b41Sphessler 				error = EINVAL;
1373d83f8b41Sphessler 				break;
1374d83f8b41Sphessler 			}
1375d83f8b41Sphessler 			rtid = *mtod(m, u_int *);
1376d83f8b41Sphessler 			if (inp->inp_rtableid == rtid)
1377d83f8b41Sphessler 				break;
1378d83f8b41Sphessler 			/* needs privileges to switch when already set */
1379f74da371Smvs 			if (rtableid != rtid && rtableid != 0 &&
13803e676399Smpi 			    (error = suser(p)) != 0)
1381d83f8b41Sphessler 				break;
1382cd28665aSbluhm 			error = in_pcbset_rtableid(inp, rtid);
1383d83f8b41Sphessler 			break;
1384e30e1ee7Syasuoka 		case IPV6_PIPEX:
1385e30e1ee7Syasuoka 			if (m != NULL && m->m_len == sizeof(int))
1386e30e1ee7Syasuoka 				inp->inp_pipex = *mtod(m, int *);
1387e30e1ee7Syasuoka 			else
1388e30e1ee7Syasuoka 				error = EINVAL;
1389e30e1ee7Syasuoka 			break;
1390bea47bb9Sitojun 
1391287546eaSitojun 		default:
1392287546eaSitojun 			error = ENOPROTOOPT;
1393287546eaSitojun 			break;
1394287546eaSitojun 		}
1395287546eaSitojun 		break;
1396287546eaSitojun 
1397287546eaSitojun 	case PRCO_GETOPT:
1398287546eaSitojun 		switch (optname) {
1399287546eaSitojun 
14006b532452Sitojun 		case IPV6_RECVHOPOPTS:
14016b532452Sitojun 		case IPV6_RECVDSTOPTS:
1402287546eaSitojun 		case IPV6_UNICAST_HOPS:
1403b9169547Sjca 		case IPV6_MINHOPCOUNT:
14046b532452Sitojun 		case IPV6_RECVPKTINFO:
14056b532452Sitojun 		case IPV6_RECVHOPLIMIT:
14066b532452Sitojun 		case IPV6_RECVRTHDR:
14076b532452Sitojun 		case IPV6_RECVPATHMTU:
14086b532452Sitojun 
14091782763dSitojun 		case IPV6_V6ONLY:
14102510a8f8Sitojun 		case IPV6_PORTRANGE:
14116b532452Sitojun 		case IPV6_RECVTCLASS:
14126b532452Sitojun 		case IPV6_AUTOFLOWLABEL:
1413cee801dcSyasuoka 		case IPV6_RECVDSTPORT:
1414287546eaSitojun 			switch (optname) {
1415287546eaSitojun 
14166b532452Sitojun 			case IPV6_RECVHOPOPTS:
14176b532452Sitojun 				optval = OPTBIT(IN6P_HOPOPTS);
14186b532452Sitojun 				break;
14196b532452Sitojun 
14206b532452Sitojun 			case IPV6_RECVDSTOPTS:
14216b532452Sitojun 				optval = OPTBIT(IN6P_DSTOPTS);
14226b532452Sitojun 				break;
14236b532452Sitojun 
1424287546eaSitojun 			case IPV6_UNICAST_HOPS:
1425287546eaSitojun 				optval = inp->inp_hops;
1426287546eaSitojun 				break;
1427287546eaSitojun 
1428e5ff19c7Sjca 			case IPV6_MINHOPCOUNT:
1429e5ff19c7Sjca 				optval = inp->inp_ip6_minhlim;
1430e5ff19c7Sjca 				break;
1431e5ff19c7Sjca 
14326b532452Sitojun 			case IPV6_RECVPKTINFO:
1433287546eaSitojun 				optval = OPTBIT(IN6P_PKTINFO);
1434287546eaSitojun 				break;
1435287546eaSitojun 
14366b532452Sitojun 			case IPV6_RECVHOPLIMIT:
1437287546eaSitojun 				optval = OPTBIT(IN6P_HOPLIMIT);
1438287546eaSitojun 				break;
1439287546eaSitojun 
14406b532452Sitojun 			case IPV6_RECVRTHDR:
1441287546eaSitojun 				optval = OPTBIT(IN6P_RTHDR);
1442287546eaSitojun 				break;
1443287546eaSitojun 
14446b532452Sitojun 			case IPV6_RECVPATHMTU:
14456b532452Sitojun 				optval = OPTBIT(IN6P_MTU);
14466b532452Sitojun 				break;
14476b532452Sitojun 
14481782763dSitojun 			case IPV6_V6ONLY:
14497e2f83f7Sjca 				optval = 1;
14501782763dSitojun 				break;
14511782763dSitojun 
1452287546eaSitojun 			case IPV6_PORTRANGE:
1453287546eaSitojun 			    {
1454287546eaSitojun 				int flags;
1455287546eaSitojun 				flags = inp->inp_flags;
1456287546eaSitojun 				if (flags & IN6P_HIGHPORT)
1457287546eaSitojun 					optval = IPV6_PORTRANGE_HIGH;
1458287546eaSitojun 				else if (flags & IN6P_LOWPORT)
1459287546eaSitojun 					optval = IPV6_PORTRANGE_LOW;
1460287546eaSitojun 				else
1461287546eaSitojun 					optval = 0;
1462287546eaSitojun 				break;
1463287546eaSitojun 			    }
14646b532452Sitojun 			case IPV6_RECVTCLASS:
14656b532452Sitojun 				optval = OPTBIT(IN6P_TCLASS);
14666b532452Sitojun 				break;
14670a46a566Sitojun 
14686b532452Sitojun 			case IPV6_AUTOFLOWLABEL:
14696b532452Sitojun 				optval = OPTBIT(IN6P_AUTOFLOWLABEL);
14706b532452Sitojun 				break;
1471cee801dcSyasuoka 
1472cee801dcSyasuoka 			case IPV6_RECVDSTPORT:
1473cee801dcSyasuoka 				optval = OPTBIT(IN6P_RECVDSTPORT);
1474cee801dcSyasuoka 				break;
14756b532452Sitojun 			}
14766b532452Sitojun 			if (error)
14776b532452Sitojun 				break;
14786b532452Sitojun 			m->m_len = sizeof(int);
14796b532452Sitojun 			*mtod(m, int *) = optval;
14806b532452Sitojun 			break;
14816b532452Sitojun 
14826b532452Sitojun 		case IPV6_PATHMTU:
14836b532452Sitojun 		{
14846b532452Sitojun 			u_long pmtu = 0;
14856b532452Sitojun 			struct ip6_mtuinfo mtuinfo;
148673ae3de3Smpi 			struct ifnet *ifp;
148773ae3de3Smpi 			struct rtentry *rt;
14886b532452Sitojun 
14896b532452Sitojun 			if (!(so->so_state & SS_ISCONNECTED))
14906b532452Sitojun 				return (ENOTCONN);
149173ae3de3Smpi 
14923dc61bc4Sbluhm 			rt = in6_pcbrtentry(inp);
149373ae3de3Smpi 			if (!rtisvalid(rt))
149473ae3de3Smpi 				return (EHOSTUNREACH);
149573ae3de3Smpi 
149673ae3de3Smpi 			ifp = if_get(rt->rt_ifidx);
149773ae3de3Smpi 			if (ifp == NULL)
149873ae3de3Smpi 				return (EHOSTUNREACH);
14996b532452Sitojun 			/*
15006b532452Sitojun 			 * XXX: we dot not consider the case of source
15016b532452Sitojun 			 * routing, or optional information to specify
15026b532452Sitojun 			 * the outgoing interface.
15036b532452Sitojun 			 */
1504f3785e27Sbluhm 			error = ip6_getpmtu(rt, ifp, &pmtu);
150573ae3de3Smpi 			if_put(ifp);
15066b532452Sitojun 			if (error)
15076b532452Sitojun 				break;
15086b532452Sitojun 			if (pmtu > IPV6_MAXPACKET)
15096b532452Sitojun 				pmtu = IPV6_MAXPACKET;
15106b532452Sitojun 
15116b532452Sitojun 			bzero(&mtuinfo, sizeof(mtuinfo));
15126b532452Sitojun 			mtuinfo.ip6m_mtu = (u_int32_t)pmtu;
15136b532452Sitojun 			optdata = (void *)&mtuinfo;
15146b532452Sitojun 			optdatalen = sizeof(mtuinfo);
15156b532452Sitojun 			if (optdatalen > MCLBYTES)
15166b532452Sitojun 				return (EMSGSIZE); /* XXX */
15176b532452Sitojun 			if (optdatalen > MLEN)
15186b532452Sitojun 				MCLGET(m, M_WAIT);
15196b532452Sitojun 			m->m_len = optdatalen;
15206b532452Sitojun 			bcopy(optdata, mtod(m, void *), optdatalen);
15216b532452Sitojun 			break;
15226b532452Sitojun 		}
15236b532452Sitojun 
15246b532452Sitojun 		case IPV6_PKTINFO:
15256b532452Sitojun 		case IPV6_HOPOPTS:
15266b532452Sitojun 		case IPV6_RTHDR:
15276b532452Sitojun 		case IPV6_DSTOPTS:
15286b532452Sitojun 		case IPV6_RTHDRDSTOPTS:
15296b532452Sitojun 		case IPV6_TCLASS:
15306b532452Sitojun 		case IPV6_DONTFRAG:
15316b532452Sitojun 		case IPV6_USE_MIN_MTU:
15326b532452Sitojun 			error = ip6_getpcbopt(inp->inp_outputopts6,
1533490e0738Sdhill 			    optname, m);
15346b532452Sitojun 			break;
1535287546eaSitojun 
1536287546eaSitojun 		case IPV6_MULTICAST_IF:
1537287546eaSitojun 		case IPV6_MULTICAST_HOPS:
1538287546eaSitojun 		case IPV6_MULTICAST_LOOP:
1539287546eaSitojun 		case IPV6_JOIN_GROUP:
1540287546eaSitojun 		case IPV6_LEAVE_GROUP:
15416b532452Sitojun 			error = ip6_getmoptions(optname,
1542490e0738Sdhill 			    inp->inp_moptions6, m);
1543287546eaSitojun 			break;
1544287546eaSitojun 
1545bea47bb9Sitojun 		case IPSEC6_OUTSA:
1546bea47bb9Sitojun 			error = EINVAL;
1547bea47bb9Sitojun 			break;
1548bea47bb9Sitojun 
1549bea47bb9Sitojun 		case IPV6_AUTH_LEVEL:
1550bea47bb9Sitojun 		case IPV6_ESP_TRANS_LEVEL:
1551bea47bb9Sitojun 		case IPV6_ESP_NETWORK_LEVEL:
15525ad3f7e5Sniklas 		case IPV6_IPCOMP_LEVEL:
1553bea47bb9Sitojun #ifndef IPSEC
1554bea47bb9Sitojun 			m->m_len = sizeof(int);
1555bea47bb9Sitojun 			*mtod(m, int *) = IPSEC_LEVEL_NONE;
1556bea47bb9Sitojun #else
1557bea47bb9Sitojun 			m->m_len = sizeof(int);
1558bea47bb9Sitojun 			switch (optname) {
1559e6e74a97Sitojun 			case IPV6_AUTH_LEVEL:
1560ace0f189Sbluhm 				optval = inp->inp_seclevel.sl_auth;
1561bea47bb9Sitojun 				break;
1562bea47bb9Sitojun 
1563e6e74a97Sitojun 			case IPV6_ESP_TRANS_LEVEL:
1564bea47bb9Sitojun 				optval =
1565ace0f189Sbluhm 				    inp->inp_seclevel.sl_esp_trans;
1566bea47bb9Sitojun 				break;
1567bea47bb9Sitojun 
1568e6e74a97Sitojun 			case IPV6_ESP_NETWORK_LEVEL:
1569bea47bb9Sitojun 				optval =
1570ace0f189Sbluhm 				    inp->inp_seclevel.sl_esp_network;
1571bea47bb9Sitojun 				break;
15725ad3f7e5Sniklas 
1573e6e74a97Sitojun 			case IPV6_IPCOMP_LEVEL:
1574ace0f189Sbluhm 				optval = inp->inp_seclevel.sl_ipcomp;
15755ad3f7e5Sniklas 				break;
1576bea47bb9Sitojun 			}
1577bea47bb9Sitojun 			*mtod(m, int *) = optval;
1578bea47bb9Sitojun #endif
1579bea47bb9Sitojun 			break;
1580d83f8b41Sphessler 		case SO_RTABLE:
1581d83f8b41Sphessler 			m->m_len = sizeof(u_int);
158262f883c9Skn 			*mtod(m, u_int *) = inp->inp_rtableid;
1583d83f8b41Sphessler 			break;
1584e30e1ee7Syasuoka 		case IPV6_PIPEX:
1585e30e1ee7Syasuoka 			m->m_len = sizeof(int);
158662f883c9Skn 			*mtod(m, int *) = inp->inp_pipex;
1587e30e1ee7Syasuoka 			break;
1588bea47bb9Sitojun 
1589287546eaSitojun 		default:
1590287546eaSitojun 			error = ENOPROTOOPT;
1591287546eaSitojun 			break;
1592287546eaSitojun 		}
1593287546eaSitojun 		break;
1594287546eaSitojun 	}
1595287546eaSitojun 	return (error);
1596287546eaSitojun }
1597287546eaSitojun 
1598f5bfbd04Sitojun int
1599ee37ea65Smcbride ip6_raw_ctloutput(int op, struct socket *so, int level, int optname,
1600490e0738Sdhill     struct mbuf *m)
1601f5bfbd04Sitojun {
160253f19499Sguenther 	int error = 0, optval;
1603f5bfbd04Sitojun 	const int icmp6off = offsetof(struct icmp6_hdr, icmp6_cksum);
1604f5bfbd04Sitojun 	struct inpcb *inp = sotoinpcb(so);
1605f5bfbd04Sitojun 
1606e09b1b7cSmpi 	if (level != IPPROTO_IPV6)
1607f5bfbd04Sitojun 		return (EINVAL);
1608f5bfbd04Sitojun 
1609f5bfbd04Sitojun 	switch (optname) {
1610f5bfbd04Sitojun 	case IPV6_CHECKSUM:
1611f5bfbd04Sitojun 		/*
1612f5bfbd04Sitojun 		 * For ICMPv6 sockets, no modification allowed for checksum
1613f5bfbd04Sitojun 		 * offset, permit "no change" values to help existing apps.
1614f5bfbd04Sitojun 		 *
16156b532452Sitojun 		 * RFC3542 says: "An attempt to set IPV6_CHECKSUM
1616f5bfbd04Sitojun 		 * for an ICMPv6 socket will fail."
16176b532452Sitojun 		 * The current behavior does not meet RFC3542.
1618f5bfbd04Sitojun 		 */
1619f5bfbd04Sitojun 		switch (op) {
1620f5bfbd04Sitojun 		case PRCO_SETOPT:
162153f19499Sguenther 			if (m == NULL || m->m_len != sizeof(int)) {
1622f5bfbd04Sitojun 				error = EINVAL;
1623f5bfbd04Sitojun 				break;
1624f5bfbd04Sitojun 			}
1625f5bfbd04Sitojun 			optval = *mtod(m, int *);
1626c526b7b2Sbluhm 			if (optval < -1 ||
1627c526b7b2Sbluhm 			    (optval > 0 && (optval % 2) != 0)) {
1628c526b7b2Sbluhm 				/*
1629c526b7b2Sbluhm 				 * The API assumes non-negative even offset
1630c526b7b2Sbluhm 				 * values or -1 as a special value.
1631c526b7b2Sbluhm 				 */
1632f5bfbd04Sitojun 				error = EINVAL;
163390dc3aeaSbluhm 			} else if (so->so_proto->pr_protocol ==
163490dc3aeaSbluhm 			    IPPROTO_ICMPV6) {
1635f5bfbd04Sitojun 				if (optval != icmp6off)
1636f5bfbd04Sitojun 					error = EINVAL;
1637f5bfbd04Sitojun 			} else
16388e5060ecSderaadt 				inp->inp_cksum6 = optval;
1639f5bfbd04Sitojun 			break;
1640f5bfbd04Sitojun 
1641f5bfbd04Sitojun 		case PRCO_GETOPT:
1642f5bfbd04Sitojun 			if (so->so_proto->pr_protocol == IPPROTO_ICMPV6)
1643f5bfbd04Sitojun 				optval = icmp6off;
1644f5bfbd04Sitojun 			else
16458e5060ecSderaadt 				optval = inp->inp_cksum6;
1646f5bfbd04Sitojun 
1647f5bfbd04Sitojun 			m->m_len = sizeof(int);
1648f5bfbd04Sitojun 			*mtod(m, int *) = optval;
1649f5bfbd04Sitojun 			break;
1650f5bfbd04Sitojun 
1651f5bfbd04Sitojun 		default:
1652f5bfbd04Sitojun 			error = EINVAL;
1653f5bfbd04Sitojun 			break;
1654f5bfbd04Sitojun 		}
1655f5bfbd04Sitojun 		break;
1656f5bfbd04Sitojun 
1657f5bfbd04Sitojun 	default:
1658f5bfbd04Sitojun 		error = ENOPROTOOPT;
1659f5bfbd04Sitojun 		break;
1660f5bfbd04Sitojun 	}
1661f5bfbd04Sitojun 
1662f5bfbd04Sitojun 	return (error);
1663f5bfbd04Sitojun }
1664f5bfbd04Sitojun 
1665287546eaSitojun /*
16666b532452Sitojun  * initialize ip6_pktopts.  beware that there are non-zero default values in
16676b532452Sitojun  * the struct.
16686b532452Sitojun  */
16696b532452Sitojun void
1670ee37ea65Smcbride ip6_initpktopts(struct ip6_pktopts *opt)
16716b532452Sitojun {
16726b532452Sitojun 	bzero(opt, sizeof(*opt));
16736b532452Sitojun 	opt->ip6po_hlim = -1;	/* -1 means default hop limit */
16746b532452Sitojun 	opt->ip6po_tclass = -1;	/* -1 means default traffic class */
16756b532452Sitojun 	opt->ip6po_minmtu = IP6PO_MINMTU_MCASTONLY;
16766b532452Sitojun }
16776b532452Sitojun 
1678d4984c21Sjsing int
1679ee37ea65Smcbride ip6_pcbopt(int optname, u_char *buf, int len, struct ip6_pktopts **pktopt,
1680ee37ea65Smcbride     int priv, int uproto)
16816b532452Sitojun {
16826b532452Sitojun 	struct ip6_pktopts *opt;
16836b532452Sitojun 
16846b532452Sitojun 	if (*pktopt == NULL) {
16856b532452Sitojun 		*pktopt = malloc(sizeof(struct ip6_pktopts), M_IP6OPT,
16866b532452Sitojun 		    M_WAITOK);
16876b532452Sitojun 		ip6_initpktopts(*pktopt);
16886b532452Sitojun 	}
16896b532452Sitojun 	opt = *pktopt;
16906b532452Sitojun 
16913ea7534eSjca 	return (ip6_setpktopt(optname, buf, len, opt, priv, 1, uproto));
16926b532452Sitojun }
16936b532452Sitojun 
1694d4984c21Sjsing int
1695490e0738Sdhill ip6_getpcbopt(struct ip6_pktopts *pktopt, int optname, struct mbuf *m)
16966b532452Sitojun {
16976b532452Sitojun 	void *optdata = NULL;
16986b532452Sitojun 	int optdatalen = 0;
16996b532452Sitojun 	struct ip6_ext *ip6e;
17006b532452Sitojun 	int error = 0;
17016b532452Sitojun 	struct in6_pktinfo null_pktinfo;
17026b532452Sitojun 	int deftclass = 0, on;
17036b532452Sitojun 	int defminmtu = IP6PO_MINMTU_MCASTONLY;
17046b532452Sitojun 
17056b532452Sitojun 	switch (optname) {
17066b532452Sitojun 	case IPV6_PKTINFO:
17076b532452Sitojun 		if (pktopt && pktopt->ip6po_pktinfo)
17086b532452Sitojun 			optdata = (void *)pktopt->ip6po_pktinfo;
17096b532452Sitojun 		else {
17106b532452Sitojun 			/* XXX: we don't have to do this every time... */
17116b532452Sitojun 			bzero(&null_pktinfo, sizeof(null_pktinfo));
17126b532452Sitojun 			optdata = (void *)&null_pktinfo;
17136b532452Sitojun 		}
17146b532452Sitojun 		optdatalen = sizeof(struct in6_pktinfo);
17156b532452Sitojun 		break;
17166b532452Sitojun 	case IPV6_TCLASS:
17176b532452Sitojun 		if (pktopt && pktopt->ip6po_tclass >= 0)
17186b532452Sitojun 			optdata = (void *)&pktopt->ip6po_tclass;
17196b532452Sitojun 		else
17206b532452Sitojun 			optdata = (void *)&deftclass;
17216b532452Sitojun 		optdatalen = sizeof(int);
17226b532452Sitojun 		break;
17236b532452Sitojun 	case IPV6_HOPOPTS:
17246b532452Sitojun 		if (pktopt && pktopt->ip6po_hbh) {
17256b532452Sitojun 			optdata = (void *)pktopt->ip6po_hbh;
17266b532452Sitojun 			ip6e = (struct ip6_ext *)pktopt->ip6po_hbh;
17276b532452Sitojun 			optdatalen = (ip6e->ip6e_len + 1) << 3;
17286b532452Sitojun 		}
17296b532452Sitojun 		break;
17306b532452Sitojun 	case IPV6_RTHDR:
17316b532452Sitojun 		if (pktopt && pktopt->ip6po_rthdr) {
17326b532452Sitojun 			optdata = (void *)pktopt->ip6po_rthdr;
17336b532452Sitojun 			ip6e = (struct ip6_ext *)pktopt->ip6po_rthdr;
17346b532452Sitojun 			optdatalen = (ip6e->ip6e_len + 1) << 3;
17356b532452Sitojun 		}
17366b532452Sitojun 		break;
17376b532452Sitojun 	case IPV6_RTHDRDSTOPTS:
17386b532452Sitojun 		if (pktopt && pktopt->ip6po_dest1) {
17396b532452Sitojun 			optdata = (void *)pktopt->ip6po_dest1;
17406b532452Sitojun 			ip6e = (struct ip6_ext *)pktopt->ip6po_dest1;
17416b532452Sitojun 			optdatalen = (ip6e->ip6e_len + 1) << 3;
17426b532452Sitojun 		}
17436b532452Sitojun 		break;
17446b532452Sitojun 	case IPV6_DSTOPTS:
17456b532452Sitojun 		if (pktopt && pktopt->ip6po_dest2) {
17466b532452Sitojun 			optdata = (void *)pktopt->ip6po_dest2;
17476b532452Sitojun 			ip6e = (struct ip6_ext *)pktopt->ip6po_dest2;
17486b532452Sitojun 			optdatalen = (ip6e->ip6e_len + 1) << 3;
17496b532452Sitojun 		}
17506b532452Sitojun 		break;
17516b532452Sitojun 	case IPV6_USE_MIN_MTU:
17526b532452Sitojun 		if (pktopt)
17536b532452Sitojun 			optdata = (void *)&pktopt->ip6po_minmtu;
17546b532452Sitojun 		else
17556b532452Sitojun 			optdata = (void *)&defminmtu;
17566b532452Sitojun 		optdatalen = sizeof(int);
17576b532452Sitojun 		break;
17586b532452Sitojun 	case IPV6_DONTFRAG:
17596b532452Sitojun 		if (pktopt && ((pktopt->ip6po_flags) & IP6PO_DONTFRAG))
17606b532452Sitojun 			on = 1;
17616b532452Sitojun 		else
17626b532452Sitojun 			on = 0;
17636b532452Sitojun 		optdata = (void *)&on;
17646b532452Sitojun 		optdatalen = sizeof(on);
17656b532452Sitojun 		break;
17666b532452Sitojun 	default:		/* should not happen */
17676b532452Sitojun #ifdef DIAGNOSTIC
1768ab0ea4a9Snayden 		panic("%s: unexpected option", __func__);
17696b532452Sitojun #endif
17706b532452Sitojun 		return (ENOPROTOOPT);
17716b532452Sitojun 	}
17726b532452Sitojun 
17736b532452Sitojun 	if (optdatalen > MCLBYTES)
17746b532452Sitojun 		return (EMSGSIZE); /* XXX */
17756b532452Sitojun 	if (optdatalen > MLEN)
17766b532452Sitojun 		MCLGET(m, M_WAIT);
17776b532452Sitojun 	m->m_len = optdatalen;
17786b532452Sitojun 	if (optdatalen)
17796b532452Sitojun 		bcopy(optdata, mtod(m, void *), optdatalen);
17806b532452Sitojun 
17816b532452Sitojun 	return (error);
17826b532452Sitojun }
17836b532452Sitojun 
17846b532452Sitojun void
1785ee37ea65Smcbride ip6_clearpktopts(struct ip6_pktopts *pktopt, int optname)
17866b532452Sitojun {
17876b532452Sitojun 	if (optname == -1 || optname == IPV6_PKTINFO) {
17886b532452Sitojun 		if (pktopt->ip6po_pktinfo)
1789dd168dc2Stedu 			free(pktopt->ip6po_pktinfo, M_IP6OPT, 0);
17906b532452Sitojun 		pktopt->ip6po_pktinfo = NULL;
17916b532452Sitojun 	}
17926b532452Sitojun 	if (optname == -1 || optname == IPV6_HOPLIMIT)
17936b532452Sitojun 		pktopt->ip6po_hlim = -1;
17946b532452Sitojun 	if (optname == -1 || optname == IPV6_TCLASS)
17956b532452Sitojun 		pktopt->ip6po_tclass = -1;
17966b532452Sitojun 	if (optname == -1 || optname == IPV6_HOPOPTS) {
17976b532452Sitojun 		if (pktopt->ip6po_hbh)
1798dd168dc2Stedu 			free(pktopt->ip6po_hbh, M_IP6OPT, 0);
17996b532452Sitojun 		pktopt->ip6po_hbh = NULL;
18006b532452Sitojun 	}
18016b532452Sitojun 	if (optname == -1 || optname == IPV6_RTHDRDSTOPTS) {
18026b532452Sitojun 		if (pktopt->ip6po_dest1)
1803dd168dc2Stedu 			free(pktopt->ip6po_dest1, M_IP6OPT, 0);
18046b532452Sitojun 		pktopt->ip6po_dest1 = NULL;
18056b532452Sitojun 	}
18066b532452Sitojun 	if (optname == -1 || optname == IPV6_RTHDR) {
18076b532452Sitojun 		if (pktopt->ip6po_rhinfo.ip6po_rhi_rthdr)
1808dd168dc2Stedu 			free(pktopt->ip6po_rhinfo.ip6po_rhi_rthdr, M_IP6OPT, 0);
18096b532452Sitojun 		pktopt->ip6po_rhinfo.ip6po_rhi_rthdr = NULL;
18106b532452Sitojun 		if (pktopt->ip6po_route.ro_rt) {
181127ae666cSmpi 			rtfree(pktopt->ip6po_route.ro_rt);
18126b532452Sitojun 			pktopt->ip6po_route.ro_rt = NULL;
18136b532452Sitojun 		}
18146b532452Sitojun 	}
18156b532452Sitojun 	if (optname == -1 || optname == IPV6_DSTOPTS) {
18166b532452Sitojun 		if (pktopt->ip6po_dest2)
1817dd168dc2Stedu 			free(pktopt->ip6po_dest2, M_IP6OPT, 0);
18186b532452Sitojun 		pktopt->ip6po_dest2 = NULL;
18196b532452Sitojun 	}
18206b532452Sitojun }
18216b532452Sitojun 
18226b532452Sitojun #define PKTOPT_EXTHDRCPY(type) \
18236b532452Sitojun do {\
18246b532452Sitojun 	if (src->type) {\
182590dc3aeaSbluhm 		size_t hlen;\
182690dc3aeaSbluhm 		hlen = (((struct ip6_ext *)src->type)->ip6e_len + 1) << 3;\
1827f288bf99Sdhill 		dst->type = malloc(hlen, M_IP6OPT, M_NOWAIT);\
1828f288bf99Sdhill 		if (dst->type == NULL)\
18296b532452Sitojun 			goto bad;\
183041bfede9Stedu 		memcpy(dst->type, src->type, hlen);\
18316b532452Sitojun 	}\
18326b532452Sitojun } while (/*CONSTCOND*/ 0)
18336b532452Sitojun 
1834d4984c21Sjsing int
1835f288bf99Sdhill copypktopts(struct ip6_pktopts *dst, struct ip6_pktopts *src)
18366b532452Sitojun {
18376b532452Sitojun 	dst->ip6po_hlim = src->ip6po_hlim;
18386b532452Sitojun 	dst->ip6po_tclass = src->ip6po_tclass;
18396b532452Sitojun 	dst->ip6po_flags = src->ip6po_flags;
18406b532452Sitojun 	if (src->ip6po_pktinfo) {
18416b532452Sitojun 		dst->ip6po_pktinfo = malloc(sizeof(*dst->ip6po_pktinfo),
1842f288bf99Sdhill 		    M_IP6OPT, M_NOWAIT);
184312986e1eSchl 		if (dst->ip6po_pktinfo == NULL)
18446b532452Sitojun 			goto bad;
18456b532452Sitojun 		*dst->ip6po_pktinfo = *src->ip6po_pktinfo;
18466b532452Sitojun 	}
18476b532452Sitojun 	PKTOPT_EXTHDRCPY(ip6po_hbh);
18486b532452Sitojun 	PKTOPT_EXTHDRCPY(ip6po_dest1);
18496b532452Sitojun 	PKTOPT_EXTHDRCPY(ip6po_dest2);
18506b532452Sitojun 	PKTOPT_EXTHDRCPY(ip6po_rthdr); /* not copy the cached route */
18516b532452Sitojun 	return (0);
18526b532452Sitojun 
18536b532452Sitojun   bad:
1854951dd902Scanacar 	ip6_clearpktopts(dst, -1);
18556b532452Sitojun 	return (ENOBUFS);
18566b532452Sitojun }
18576b532452Sitojun #undef PKTOPT_EXTHDRCPY
18586b532452Sitojun 
18596b532452Sitojun void
1860ee37ea65Smcbride ip6_freepcbopts(struct ip6_pktopts *pktopt)
18616b532452Sitojun {
18626b532452Sitojun 	if (pktopt == NULL)
18636b532452Sitojun 		return;
18646b532452Sitojun 
18656b532452Sitojun 	ip6_clearpktopts(pktopt, -1);
18666b532452Sitojun 
1867dd168dc2Stedu 	free(pktopt, M_IP6OPT, 0);
18686b532452Sitojun }
18696b532452Sitojun 
18706b532452Sitojun /*
1871287546eaSitojun  * Set the IP6 multicast options in response to user setsockopt().
1872287546eaSitojun  */
1873d4984c21Sjsing int
187444a616dfSrzalamena ip6_setmoptions(int optname, struct ip6_moptions **im6op, struct mbuf *m,
187544a616dfSrzalamena     unsigned int rtableid)
1876287546eaSitojun {
1877287546eaSitojun 	int error = 0;
1878287546eaSitojun 	u_int loop, ifindex;
1879287546eaSitojun 	struct ipv6_mreq *mreq;
1880287546eaSitojun 	struct ifnet *ifp;
1881287546eaSitojun 	struct ip6_moptions *im6o = *im6op;
1882287546eaSitojun 	struct in6_multi_mship *imm;
1883287546eaSitojun 	struct proc *p = curproc;	/* XXX */
1884287546eaSitojun 
1885287546eaSitojun 	if (im6o == NULL) {
1886287546eaSitojun 		/*
1887287546eaSitojun 		 * No multicast option buffer attached to the pcb;
1888287546eaSitojun 		 * allocate one and initialize to default values.
1889287546eaSitojun 		 */
1890b82d192bSmpi 		im6o = malloc(sizeof(*im6o), M_IPMOPTS, M_WAITOK);
18917b2ccaf7Sitojun 		if (im6o == NULL)
18927b2ccaf7Sitojun 			return (ENOBUFS);
1893287546eaSitojun 		*im6op = im6o;
1894ec0be06bSmpi 		im6o->im6o_ifidx = 0;
18953c687758Smpi 		im6o->im6o_hlim = ip6_defmcasthlim;
18963c687758Smpi 		im6o->im6o_loop = IPV6_DEFAULT_MULTICAST_LOOP;
1897287546eaSitojun 		LIST_INIT(&im6o->im6o_memberships);
1898287546eaSitojun 	}
1899287546eaSitojun 
1900287546eaSitojun 	switch (optname) {
1901287546eaSitojun 
1902287546eaSitojun 	case IPV6_MULTICAST_IF:
1903287546eaSitojun 		/*
1904287546eaSitojun 		 * Select the interface for outgoing multicast packets.
1905287546eaSitojun 		 */
1906287546eaSitojun 		if (m == NULL || m->m_len != sizeof(u_int)) {
1907287546eaSitojun 			error = EINVAL;
1908287546eaSitojun 			break;
1909287546eaSitojun 		}
191041bfede9Stedu 		memcpy(&ifindex, mtod(m, u_int *), sizeof(ifindex));
19110e3d2ab2Sclaudio 		if (ifindex != 0) {
191273e65003Smpi 			ifp = if_get(ifindex);
191373e65003Smpi 			if (ifp == NULL) {
1914287546eaSitojun 				error = ENXIO;	/* XXX EINVAL? */
1915287546eaSitojun 				break;
1916287546eaSitojun 			}
19171d88f30eSclaudio 			if (ifp->if_rdomain != rtable_l2(rtableid) ||
19181d88f30eSclaudio 			    (ifp->if_flags & IFF_MULTICAST) == 0) {
1919287546eaSitojun 				error = EADDRNOTAVAIL;
19200e3d2ab2Sclaudio 				if_put(ifp);
1921287546eaSitojun 				break;
1922287546eaSitojun 			}
19230e3d2ab2Sclaudio 			if_put(ifp);
1924a7baa13eSdjm 		}
1925ec0be06bSmpi 		im6o->im6o_ifidx = ifindex;
1926287546eaSitojun 		break;
1927287546eaSitojun 
1928287546eaSitojun 	case IPV6_MULTICAST_HOPS:
1929287546eaSitojun 	    {
1930287546eaSitojun 		/*
1931287546eaSitojun 		 * Set the IP6 hoplimit for outgoing multicast packets.
1932287546eaSitojun 		 */
1933287546eaSitojun 		int optval;
1934287546eaSitojun 		if (m == NULL || m->m_len != sizeof(int)) {
1935287546eaSitojun 			error = EINVAL;
1936287546eaSitojun 			break;
1937287546eaSitojun 		}
193841bfede9Stedu 		memcpy(&optval, mtod(m, u_int *), sizeof(optval));
1939287546eaSitojun 		if (optval < -1 || optval >= 256)
1940287546eaSitojun 			error = EINVAL;
1941287546eaSitojun 		else if (optval == -1)
19423c687758Smpi 			im6o->im6o_hlim = ip6_defmcasthlim;
1943287546eaSitojun 		else
19443c687758Smpi 			im6o->im6o_hlim = optval;
1945287546eaSitojun 		break;
1946287546eaSitojun 	    }
1947287546eaSitojun 
1948287546eaSitojun 	case IPV6_MULTICAST_LOOP:
1949287546eaSitojun 		/*
1950287546eaSitojun 		 * Set the loopback flag for outgoing multicast packets.
1951287546eaSitojun 		 * Must be zero or one.
1952287546eaSitojun 		 */
19537b2ccaf7Sitojun 		if (m == NULL || m->m_len != sizeof(u_int)) {
19547b2ccaf7Sitojun 			error = EINVAL;
19557b2ccaf7Sitojun 			break;
19567b2ccaf7Sitojun 		}
195741bfede9Stedu 		memcpy(&loop, mtod(m, u_int *), sizeof(loop));
19587b2ccaf7Sitojun 		if (loop > 1) {
1959287546eaSitojun 			error = EINVAL;
1960287546eaSitojun 			break;
1961287546eaSitojun 		}
19623c687758Smpi 		im6o->im6o_loop = loop;
1963287546eaSitojun 		break;
1964287546eaSitojun 
1965287546eaSitojun 	case IPV6_JOIN_GROUP:
1966287546eaSitojun 		/*
1967287546eaSitojun 		 * Add a multicast group membership.
1968287546eaSitojun 		 * Group must be a valid IP6 multicast address.
1969287546eaSitojun 		 */
1970287546eaSitojun 		if (m == NULL || m->m_len != sizeof(struct ipv6_mreq)) {
1971287546eaSitojun 			error = EINVAL;
1972287546eaSitojun 			break;
1973287546eaSitojun 		}
1974287546eaSitojun 		mreq = mtod(m, struct ipv6_mreq *);
1975287546eaSitojun 		if (IN6_IS_ADDR_UNSPECIFIED(&mreq->ipv6mr_multiaddr)) {
1976287546eaSitojun 			/*
1977287546eaSitojun 			 * We use the unspecified address to specify to accept
1978287546eaSitojun 			 * all multicast addresses. Only super user is allowed
1979287546eaSitojun 			 * to do this.
1980287546eaSitojun 			 */
19813e676399Smpi 			if (suser(p))
198234deef1eSitojun 			{
1983287546eaSitojun 				error = EACCES;
1984287546eaSitojun 				break;
1985287546eaSitojun 			}
1986287546eaSitojun 		} else if (!IN6_IS_ADDR_MULTICAST(&mreq->ipv6mr_multiaddr)) {
1987287546eaSitojun 			error = EINVAL;
1988287546eaSitojun 			break;
1989287546eaSitojun 		}
1990287546eaSitojun 
1991287546eaSitojun 		/*
1992287546eaSitojun 		 * If no interface was explicitly specified, choose an
1993287546eaSitojun 		 * appropriate one according to the given multicast address.
1994287546eaSitojun 		 */
1995287546eaSitojun 		if (mreq->ipv6mr_interface == 0) {
1996b324c4e9Smpi 			struct rtentry *rt;
1997b324c4e9Smpi 			struct sockaddr_in6 dst;
1998b324c4e9Smpi 
1999b324c4e9Smpi 			memset(&dst, 0, sizeof(dst));
2000b324c4e9Smpi 			dst.sin6_len = sizeof(dst);
2001b324c4e9Smpi 			dst.sin6_family = AF_INET6;
2002b324c4e9Smpi 			dst.sin6_addr = mreq->ipv6mr_multiaddr;
200344a616dfSrzalamena 			rt = rtalloc(sin6tosa(&dst), RT_RESOLVE, rtableid);
2004b324c4e9Smpi 			if (rt == NULL) {
2005287546eaSitojun 				error = EADDRNOTAVAIL;
2006287546eaSitojun 				break;
2007287546eaSitojun 			}
2008b324c4e9Smpi 			ifp = if_get(rt->rt_ifidx);
2009b324c4e9Smpi 			rtfree(rt);
2010a7baa13eSdjm 		} else {
2011a7baa13eSdjm 			/*
2012a7baa13eSdjm 			 * If the interface is specified, validate it.
2013a7baa13eSdjm 			 */
201473e65003Smpi 			ifp = if_get(mreq->ipv6mr_interface);
201573e65003Smpi 			if (ifp == NULL) {
2016a7baa13eSdjm 				error = ENXIO;	/* XXX EINVAL? */
2017a7baa13eSdjm 				break;
2018a7baa13eSdjm 			}
2019a7baa13eSdjm 		}
2020287546eaSitojun 
2021287546eaSitojun 		/*
2022287546eaSitojun 		 * See if we found an interface, and confirm that it
2023287546eaSitojun 		 * supports multicast
2024287546eaSitojun 		 */
20251d88f30eSclaudio 		if (ifp == NULL || ifp->if_rdomain != rtable_l2(rtableid) ||
20261d88f30eSclaudio 		    (ifp->if_flags & IFF_MULTICAST) == 0) {
20270e3d2ab2Sclaudio 			if_put(ifp);
2028287546eaSitojun 			error = EADDRNOTAVAIL;
2029287546eaSitojun 			break;
2030287546eaSitojun 		}
2031287546eaSitojun 		/*
2032287546eaSitojun 		 * Put interface index into the multicast address,
203355ee7a9bSitojun 		 * if the address has link/interface-local scope.
2034287546eaSitojun 		 */
203555ee7a9bSitojun 		if (IN6_IS_SCOPE_EMBED(&mreq->ipv6mr_multiaddr)) {
20367b2ccaf7Sitojun 			mreq->ipv6mr_multiaddr.s6_addr16[1] =
20378b94f13dSitojun 			    htons(ifp->if_index);
2038287546eaSitojun 		}
2039287546eaSitojun 		/*
2040287546eaSitojun 		 * See if the membership already exists.
2041287546eaSitojun 		 */
20421573508eSmiod 		LIST_FOREACH(imm, &im6o->im6o_memberships, i6mm_chain)
204333082e9fSmpi 			if (imm->i6mm_maddr->in6m_ifidx == ifp->if_index &&
2044287546eaSitojun 			    IN6_ARE_ADDR_EQUAL(&imm->i6mm_maddr->in6m_addr,
2045287546eaSitojun 			    &mreq->ipv6mr_multiaddr))
2046287546eaSitojun 				break;
2047287546eaSitojun 		if (imm != NULL) {
20480e3d2ab2Sclaudio 			if_put(ifp);
2049287546eaSitojun 			error = EADDRINUSE;
2050287546eaSitojun 			break;
2051287546eaSitojun 		}
2052287546eaSitojun 		/*
2053287546eaSitojun 		 * Everything looks good; add a new record to the multicast
2054287546eaSitojun 		 * address list for the given interface.
2055287546eaSitojun 		 */
20567b2ccaf7Sitojun 		imm = in6_joingroup(ifp, &mreq->ipv6mr_multiaddr, &error);
20570e3d2ab2Sclaudio 		if_put(ifp);
20587b2ccaf7Sitojun 		if (!imm)
2059287546eaSitojun 			break;
2060287546eaSitojun 		LIST_INSERT_HEAD(&im6o->im6o_memberships, imm, i6mm_chain);
2061287546eaSitojun 		break;
2062287546eaSitojun 
2063287546eaSitojun 	case IPV6_LEAVE_GROUP:
2064287546eaSitojun 		/*
2065287546eaSitojun 		 * Drop a multicast group membership.
2066287546eaSitojun 		 * Group must be a valid IP6 multicast address.
2067287546eaSitojun 		 */
2068287546eaSitojun 		if (m == NULL || m->m_len != sizeof(struct ipv6_mreq)) {
2069287546eaSitojun 			error = EINVAL;
2070287546eaSitojun 			break;
2071287546eaSitojun 		}
2072287546eaSitojun 		mreq = mtod(m, struct ipv6_mreq *);
2073287546eaSitojun 		if (IN6_IS_ADDR_UNSPECIFIED(&mreq->ipv6mr_multiaddr)) {
207470646e3dSclaudio 			if (suser(p)) {
2075287546eaSitojun 				error = EACCES;
2076287546eaSitojun 				break;
2077287546eaSitojun 			}
2078287546eaSitojun 		} else if (!IN6_IS_ADDR_MULTICAST(&mreq->ipv6mr_multiaddr)) {
2079287546eaSitojun 			error = EINVAL;
2080287546eaSitojun 			break;
2081287546eaSitojun 		}
20820e3d2ab2Sclaudio 
20830e3d2ab2Sclaudio 		/*
20840e3d2ab2Sclaudio 		 * Put interface index into the multicast address,
20850e3d2ab2Sclaudio 		 * if the address has link-local scope.
20860e3d2ab2Sclaudio 		 */
20870e3d2ab2Sclaudio 		if (IN6_IS_ADDR_MC_LINKLOCAL(&mreq->ipv6mr_multiaddr)) {
20880e3d2ab2Sclaudio 			mreq->ipv6mr_multiaddr.s6_addr16[1] =
20890e3d2ab2Sclaudio 			    htons(mreq->ipv6mr_interface);
20900e3d2ab2Sclaudio 		}
20910e3d2ab2Sclaudio 
2092287546eaSitojun 		/*
2093287546eaSitojun 		 * If an interface address was specified, get a pointer
2094287546eaSitojun 		 * to its ifnet structure.
2095287546eaSitojun 		 */
2096a7baa13eSdjm 		if (mreq->ipv6mr_interface == 0)
2097a7baa13eSdjm 			ifp = NULL;
2098a7baa13eSdjm 		else {
209973e65003Smpi 			ifp = if_get(mreq->ipv6mr_interface);
210073e65003Smpi 			if (ifp == NULL) {
2101287546eaSitojun 				error = ENXIO;	/* XXX EINVAL? */
2102287546eaSitojun 				break;
2103287546eaSitojun 			}
2104a7baa13eSdjm 		}
2105a7baa13eSdjm 
2106287546eaSitojun 		/*
2107287546eaSitojun 		 * Find the membership in the membership list.
2108287546eaSitojun 		 */
21091573508eSmiod 		LIST_FOREACH(imm, &im6o->im6o_memberships, i6mm_chain) {
211033082e9fSmpi 			if ((ifp == NULL ||
211133082e9fSmpi 			    imm->i6mm_maddr->in6m_ifidx == ifp->if_index) &&
2112287546eaSitojun 			    IN6_ARE_ADDR_EQUAL(&imm->i6mm_maddr->in6m_addr,
2113287546eaSitojun 			    &mreq->ipv6mr_multiaddr))
2114287546eaSitojun 				break;
2115287546eaSitojun 		}
21160e3d2ab2Sclaudio 
21170e3d2ab2Sclaudio 		if_put(ifp);
21180e3d2ab2Sclaudio 
2119287546eaSitojun 		if (imm == NULL) {
2120287546eaSitojun 			/* Unable to resolve interface */
2121287546eaSitojun 			error = EADDRNOTAVAIL;
2122287546eaSitojun 			break;
2123287546eaSitojun 		}
2124287546eaSitojun 		/*
2125287546eaSitojun 		 * Give up the multicast address record to which the
2126287546eaSitojun 		 * membership points.
2127287546eaSitojun 		 */
2128287546eaSitojun 		LIST_REMOVE(imm, i6mm_chain);
21297b2ccaf7Sitojun 		in6_leavegroup(imm);
2130287546eaSitojun 		break;
2131287546eaSitojun 
2132287546eaSitojun 	default:
2133287546eaSitojun 		error = EOPNOTSUPP;
2134287546eaSitojun 		break;
2135287546eaSitojun 	}
2136287546eaSitojun 
2137287546eaSitojun 	/*
213855ee7a9bSitojun 	 * If all options have default values, no need to keep the option
213955ee7a9bSitojun 	 * structure.
2140287546eaSitojun 	 */
2141ec0be06bSmpi 	if (im6o->im6o_ifidx == 0 &&
21423c687758Smpi 	    im6o->im6o_hlim == ip6_defmcasthlim &&
21433c687758Smpi 	    im6o->im6o_loop == IPV6_DEFAULT_MULTICAST_LOOP &&
21441573508eSmiod 	    LIST_EMPTY(&im6o->im6o_memberships)) {
2145b82d192bSmpi 		free(*im6op, M_IPMOPTS, sizeof(**im6op));
2146287546eaSitojun 		*im6op = NULL;
2147287546eaSitojun 	}
2148287546eaSitojun 
2149287546eaSitojun 	return (error);
2150287546eaSitojun }
2151287546eaSitojun 
2152287546eaSitojun /*
2153287546eaSitojun  * Return the IP6 multicast options in response to user getsockopt().
2154287546eaSitojun  */
2155d4984c21Sjsing int
2156490e0738Sdhill ip6_getmoptions(int optname, struct ip6_moptions *im6o, struct mbuf *m)
2157287546eaSitojun {
2158287546eaSitojun 	u_int *hlim, *loop, *ifindex;
2159287546eaSitojun 
2160287546eaSitojun 	switch (optname) {
2161287546eaSitojun 	case IPV6_MULTICAST_IF:
2162490e0738Sdhill 		ifindex = mtod(m, u_int *);
2163490e0738Sdhill 		m->m_len = sizeof(u_int);
2164ec0be06bSmpi 		if (im6o == NULL || im6o->im6o_ifidx == 0)
2165287546eaSitojun 			*ifindex = 0;
2166287546eaSitojun 		else
2167ec0be06bSmpi 			*ifindex = im6o->im6o_ifidx;
2168287546eaSitojun 		return (0);
2169287546eaSitojun 
2170287546eaSitojun 	case IPV6_MULTICAST_HOPS:
2171490e0738Sdhill 		hlim = mtod(m, u_int *);
2172490e0738Sdhill 		m->m_len = sizeof(u_int);
2173287546eaSitojun 		if (im6o == NULL)
2174287546eaSitojun 			*hlim = ip6_defmcasthlim;
2175287546eaSitojun 		else
21763c687758Smpi 			*hlim = im6o->im6o_hlim;
2177287546eaSitojun 		return (0);
2178287546eaSitojun 
2179287546eaSitojun 	case IPV6_MULTICAST_LOOP:
2180490e0738Sdhill 		loop = mtod(m, u_int *);
2181490e0738Sdhill 		m->m_len = sizeof(u_int);
2182287546eaSitojun 		if (im6o == NULL)
2183287546eaSitojun 			*loop = ip6_defmcasthlim;
2184287546eaSitojun 		else
21853c687758Smpi 			*loop = im6o->im6o_loop;
2186287546eaSitojun 		return (0);
2187287546eaSitojun 
2188287546eaSitojun 	default:
2189287546eaSitojun 		return (EOPNOTSUPP);
2190287546eaSitojun 	}
2191287546eaSitojun }
2192287546eaSitojun 
2193287546eaSitojun /*
2194287546eaSitojun  * Discard the IP6 multicast options.
2195287546eaSitojun  */
2196287546eaSitojun void
2197ee37ea65Smcbride ip6_freemoptions(struct ip6_moptions *im6o)
2198287546eaSitojun {
2199287546eaSitojun 	struct in6_multi_mship *imm;
2200287546eaSitojun 
2201287546eaSitojun 	if (im6o == NULL)
2202287546eaSitojun 		return;
2203287546eaSitojun 
22041573508eSmiod 	while (!LIST_EMPTY(&im6o->im6o_memberships)) {
22051573508eSmiod 		imm = LIST_FIRST(&im6o->im6o_memberships);
2206287546eaSitojun 		LIST_REMOVE(imm, i6mm_chain);
22077b2ccaf7Sitojun 		in6_leavegroup(imm);
2208287546eaSitojun 	}
2209b82d192bSmpi 	free(im6o, M_IPMOPTS, sizeof(*im6o));
2210287546eaSitojun }
2211287546eaSitojun 
2212287546eaSitojun /*
2213287546eaSitojun  * Set IPv6 outgoing packet options based on advanced API.
2214287546eaSitojun  */
2215287546eaSitojun int
2216ee37ea65Smcbride ip6_setpktopts(struct mbuf *control, struct ip6_pktopts *opt,
2217ee37ea65Smcbride     struct ip6_pktopts *stickyopt, int priv, int uproto)
2218287546eaSitojun {
2219021732e9Sderaadt 	u_int clen;
22209063e41fSitojun 	struct cmsghdr *cm = 0;
2221021732e9Sderaadt 	caddr_t cmsgs;
2222021732e9Sderaadt 	int error;
2223287546eaSitojun 
22246b532452Sitojun 	if (control == NULL || opt == NULL)
2225287546eaSitojun 		return (EINVAL);
2226287546eaSitojun 
22276b532452Sitojun 	ip6_initpktopts(opt);
22286b532452Sitojun 	if (stickyopt) {
22296b532452Sitojun 		int error;
22306b532452Sitojun 
22316b532452Sitojun 		/*
22326b532452Sitojun 		 * If stickyopt is provided, make a local copy of the options
22336b532452Sitojun 		 * for this particular packet, then override them by ancillary
22346b532452Sitojun 		 * objects.
22356b532452Sitojun 		 * XXX: copypktopts() does not copy the cached route to a next
22366b532452Sitojun 		 * hop (if any).  This is not very good in terms of efficiency,
22376b532452Sitojun 		 * but we can allow this since this option should be rarely
22386b532452Sitojun 		 * used.
22396b532452Sitojun 		 */
2240f288bf99Sdhill 		if ((error = copypktopts(opt, stickyopt)) != 0)
22416b532452Sitojun 			return (error);
22426b532452Sitojun 	}
2243287546eaSitojun 
2244287546eaSitojun 	/*
2245287546eaSitojun 	 * XXX: Currently, we assume all the optional information is stored
2246287546eaSitojun 	 * in a single mbuf.
2247287546eaSitojun 	 */
2248287546eaSitojun 	if (control->m_next)
2249287546eaSitojun 		return (EINVAL);
2250287546eaSitojun 
2251021732e9Sderaadt 	clen = control->m_len;
2252021732e9Sderaadt 	cmsgs = mtod(control, caddr_t);
2253021732e9Sderaadt 	do {
2254021732e9Sderaadt 		if (clen < CMSG_LEN(0))
22556b532452Sitojun 			return (EINVAL);
2256021732e9Sderaadt 		cm = (struct cmsghdr *)cmsgs;
2257bb110d83Sbluhm 		if (cm->cmsg_len < CMSG_LEN(0) || cm->cmsg_len > clen ||
2258021732e9Sderaadt 		    CMSG_ALIGN(cm->cmsg_len) > clen)
2259287546eaSitojun 			return (EINVAL);
2260021732e9Sderaadt 		if (cm->cmsg_level == IPPROTO_IPV6) {
22616b532452Sitojun 			error = ip6_setpktopt(cm->cmsg_type, CMSG_DATA(cm),
22623ea7534eSjca 			    cm->cmsg_len - CMSG_LEN(0), opt, priv, 0, uproto);
22636b532452Sitojun 			if (error)
22646b532452Sitojun 				return (error);
22656b532452Sitojun 		}
22666b532452Sitojun 
2267021732e9Sderaadt 		clen -= CMSG_ALIGN(cm->cmsg_len);
2268021732e9Sderaadt 		cmsgs += CMSG_ALIGN(cm->cmsg_len);
2269021732e9Sderaadt 	} while (clen);
2270021732e9Sderaadt 
22716b532452Sitojun 	return (0);
22726b532452Sitojun }
22736b532452Sitojun 
22746b532452Sitojun /*
22756b532452Sitojun  * Set a particular packet option, as a sticky option or an ancillary data
22766b532452Sitojun  * item.  "len" can be 0 only when it's a sticky option.
22776b532452Sitojun  */
2278d4984c21Sjsing int
2279ee37ea65Smcbride ip6_setpktopt(int optname, u_char *buf, int len, struct ip6_pktopts *opt,
22803ea7534eSjca     int priv, int sticky, int uproto)
22816b532452Sitojun {
22826b532452Sitojun 	int minmtupolicy;
22836b532452Sitojun 
22846b532452Sitojun 	switch (optname) {
2285905e951aStedu 	case IPV6_PKTINFO:
2286905e951aStedu 	{
2287905e951aStedu 		struct ifnet *ifp = NULL;
2288905e951aStedu 		struct in6_pktinfo *pktinfo;
2289905e951aStedu 
2290905e951aStedu 		if (len != sizeof(struct in6_pktinfo))
2291905e951aStedu 			return (EINVAL);
2292905e951aStedu 
2293905e951aStedu 		pktinfo = (struct in6_pktinfo *)buf;
2294905e951aStedu 
2295905e951aStedu 		/*
2296905e951aStedu 		 * An application can clear any sticky IPV6_PKTINFO option by
2297905e951aStedu 		 * doing a "regular" setsockopt with ipi6_addr being
2298905e951aStedu 		 * in6addr_any and ipi6_ifindex being zero.
2299905e951aStedu 		 * [RFC 3542, Section 6]
2300905e951aStedu 		 */
2301245e399cStedu 		if (opt->ip6po_pktinfo &&
2302905e951aStedu 		    pktinfo->ipi6_ifindex == 0 &&
2303905e951aStedu 		    IN6_IS_ADDR_UNSPECIFIED(&pktinfo->ipi6_addr)) {
2304905e951aStedu 			ip6_clearpktopts(opt, optname);
2305905e951aStedu 			break;
2306905e951aStedu 		}
2307905e951aStedu 
2308245e399cStedu 		if (uproto == IPPROTO_TCP &&
2309905e951aStedu 		    sticky && !IN6_IS_ADDR_UNSPECIFIED(&pktinfo->ipi6_addr)) {
2310905e951aStedu 			return (EINVAL);
2311905e951aStedu 		}
2312905e951aStedu 
2313905e951aStedu 		if (pktinfo->ipi6_ifindex) {
2314905e951aStedu 			ifp = if_get(pktinfo->ipi6_ifindex);
2315905e951aStedu 			if (ifp == NULL)
2316905e951aStedu 				return (ENXIO);
2317905e951aStedu 			if_put(ifp);
2318905e951aStedu 		}
2319905e951aStedu 
2320905e951aStedu 		/*
2321905e951aStedu 		 * We store the address anyway, and let in6_selectsrc()
2322905e951aStedu 		 * validate the specified address.  This is because ipi6_addr
2323905e951aStedu 		 * may not have enough information about its scope zone, and
2324905e951aStedu 		 * we may need additional information (such as outgoing
2325905e951aStedu 		 * interface or the scope zone of a destination address) to
2326905e951aStedu 		 * disambiguate the scope.
2327905e951aStedu 		 * XXX: the delay of the validation may confuse the
2328905e951aStedu 		 * application when it is used as a sticky option.
2329905e951aStedu 		 */
2330905e951aStedu 		if (opt->ip6po_pktinfo == NULL) {
2331905e951aStedu 			opt->ip6po_pktinfo = malloc(sizeof(*pktinfo),
2332905e951aStedu 			    M_IP6OPT, M_NOWAIT);
2333905e951aStedu 			if (opt->ip6po_pktinfo == NULL)
2334905e951aStedu 				return (ENOBUFS);
2335905e951aStedu 		}
2336905e951aStedu 		bcopy(pktinfo, opt->ip6po_pktinfo, sizeof(*pktinfo));
2337905e951aStedu 		break;
2338905e951aStedu 	}
2339905e951aStedu 
23406b532452Sitojun 	case IPV6_HOPLIMIT:
23416b532452Sitojun 	{
23426b532452Sitojun 		int *hlimp;
23436b532452Sitojun 
23446b532452Sitojun 		/*
23456b532452Sitojun 		 * RFC 3542 deprecated the usage of sticky IPV6_HOPLIMIT
23466b532452Sitojun 		 * to simplify the ordering among hoplimit options.
23476b532452Sitojun 		 */
2348245e399cStedu 		if (sticky)
23496b532452Sitojun 			return (ENOPROTOOPT);
23506b532452Sitojun 
23516b532452Sitojun 		if (len != sizeof(int))
23526b532452Sitojun 			return (EINVAL);
23536b532452Sitojun 		hlimp = (int *)buf;
23546b532452Sitojun 		if (*hlimp < -1 || *hlimp > 255)
23556b532452Sitojun 			return (EINVAL);
23566b532452Sitojun 
23576b532452Sitojun 		opt->ip6po_hlim = *hlimp;
23586b532452Sitojun 		break;
23596b532452Sitojun 	}
23606b532452Sitojun 
23616b532452Sitojun 	case IPV6_TCLASS:
23626b532452Sitojun 	{
23636b532452Sitojun 		int tclass;
23646b532452Sitojun 
23656b532452Sitojun 		if (len != sizeof(int))
23666b532452Sitojun 			return (EINVAL);
23676b532452Sitojun 		tclass = *(int *)buf;
23686b532452Sitojun 		if (tclass < -1 || tclass > 255)
23696b532452Sitojun 			return (EINVAL);
23706b532452Sitojun 
23716b532452Sitojun 		opt->ip6po_tclass = tclass;
23726b532452Sitojun 		break;
23736b532452Sitojun 	}
2374287546eaSitojun 	case IPV6_HOPOPTS:
23756b532452Sitojun 	{
23766b532452Sitojun 		struct ip6_hbh *hbh;
23776b532452Sitojun 		int hbhlen;
2378287546eaSitojun 
2379287546eaSitojun 		/*
23806b532452Sitojun 		 * XXX: We don't allow a non-privileged user to set ANY HbH
23816b532452Sitojun 		 * options, since per-option restriction has too much
23826b532452Sitojun 		 * overhead.
2383287546eaSitojun 		 */
23846b532452Sitojun 		if (!priv)
23856b532452Sitojun 			return (EPERM);
2386287546eaSitojun 
23876b532452Sitojun 		if (len == 0) {
23886b532452Sitojun 			ip6_clearpktopts(opt, IPV6_HOPOPTS);
23896b532452Sitojun 			break;	/* just remove the option */
23906b532452Sitojun 		}
23916b532452Sitojun 
23926b532452Sitojun 		/* message length validation */
23936b532452Sitojun 		if (len < sizeof(struct ip6_hbh))
23946b532452Sitojun 			return (EINVAL);
23956b532452Sitojun 		hbh = (struct ip6_hbh *)buf;
23966b532452Sitojun 		hbhlen = (hbh->ip6h_len + 1) << 3;
23976b532452Sitojun 		if (len != hbhlen)
23986b532452Sitojun 			return (EINVAL);
23996b532452Sitojun 
24006b532452Sitojun 		/* turn off the previous option, then set the new option. */
24016b532452Sitojun 		ip6_clearpktopts(opt, IPV6_HOPOPTS);
24026b532452Sitojun 		opt->ip6po_hbh = malloc(hbhlen, M_IP6OPT, M_NOWAIT);
24036b532452Sitojun 		if (opt->ip6po_hbh == NULL)
24046b532452Sitojun 			return (ENOBUFS);
240541bfede9Stedu 		memcpy(opt->ip6po_hbh, hbh, hbhlen);
24066b532452Sitojun 
24076b532452Sitojun 		break;
24086b532452Sitojun 	}
24096b532452Sitojun 
24106b532452Sitojun 	case IPV6_DSTOPTS:
24116b532452Sitojun 	case IPV6_RTHDRDSTOPTS:
24126b532452Sitojun 	{
24136b532452Sitojun 		struct ip6_dest *dest, **newdest = NULL;
24146b532452Sitojun 		int destlen;
24156b532452Sitojun 
24166b532452Sitojun 		if (!priv)	/* XXX: see the comment for IPV6_HOPOPTS */
24176b532452Sitojun 			return (EPERM);
24186b532452Sitojun 
24196b532452Sitojun 		if (len == 0) {
24206b532452Sitojun 			ip6_clearpktopts(opt, optname);
24216b532452Sitojun 			break;	/* just remove the option */
24226b532452Sitojun 		}
24236b532452Sitojun 
24246b532452Sitojun 		/* message length validation */
24256b532452Sitojun 		if (len < sizeof(struct ip6_dest))
24266b532452Sitojun 			return (EINVAL);
24276b532452Sitojun 		dest = (struct ip6_dest *)buf;
24286b532452Sitojun 		destlen = (dest->ip6d_len + 1) << 3;
24296b532452Sitojun 		if (len != destlen)
24306b532452Sitojun 			return (EINVAL);
24316b532452Sitojun 		/*
24326b532452Sitojun 		 * Determine the position that the destination options header
24336b532452Sitojun 		 * should be inserted; before or after the routing header.
24346b532452Sitojun 		 */
24356b532452Sitojun 		switch (optname) {
24366b532452Sitojun 		case IPV6_RTHDRDSTOPTS:
24376b532452Sitojun 			newdest = &opt->ip6po_dest1;
24386b532452Sitojun 			break;
24396b532452Sitojun 		case IPV6_DSTOPTS:
24406b532452Sitojun 			newdest = &opt->ip6po_dest2;
24416b532452Sitojun 			break;
24426b532452Sitojun 		}
24436b532452Sitojun 
24446b532452Sitojun 		/* turn off the previous option, then set the new option. */
24456b532452Sitojun 		ip6_clearpktopts(opt, optname);
24466b532452Sitojun 		*newdest = malloc(destlen, M_IP6OPT, M_NOWAIT);
24476b532452Sitojun 		if (*newdest == NULL)
24486b532452Sitojun 			return (ENOBUFS);
244941bfede9Stedu 		memcpy(*newdest, dest, destlen);
24506b532452Sitojun 
24516b532452Sitojun 		break;
24526b532452Sitojun 	}
24536b532452Sitojun 
2454287546eaSitojun 	case IPV6_RTHDR:
24556b532452Sitojun 	{
24566b532452Sitojun 		struct ip6_rthdr *rth;
24576b532452Sitojun 		int rthlen;
24586b532452Sitojun 
24596b532452Sitojun 		if (len == 0) {
24606b532452Sitojun 			ip6_clearpktopts(opt, IPV6_RTHDR);
24616b532452Sitojun 			break;	/* just remove the option */
24626b532452Sitojun 		}
24636b532452Sitojun 
24646b532452Sitojun 		/* message length validation */
24656b532452Sitojun 		if (len < sizeof(struct ip6_rthdr))
2466287546eaSitojun 			return (EINVAL);
24676b532452Sitojun 		rth = (struct ip6_rthdr *)buf;
24686b532452Sitojun 		rthlen = (rth->ip6r_len + 1) << 3;
24696b532452Sitojun 		if (len != rthlen)
2470287546eaSitojun 			return (EINVAL);
24716b532452Sitojun 
24726b532452Sitojun 		switch (rth->ip6r_type) {
2473287546eaSitojun 		case IPV6_RTHDR_TYPE_0:
24746b532452Sitojun 			if (rth->ip6r_len == 0)	/* must contain one addr */
24756b532452Sitojun 				return (EINVAL);
24766b532452Sitojun 			if (rth->ip6r_len % 2) /* length must be even */
24776b532452Sitojun 				return (EINVAL);
24786b532452Sitojun 			if (rth->ip6r_len / 2 != rth->ip6r_segleft)
2479287546eaSitojun 				return (EINVAL);
2480287546eaSitojun 			break;
2481287546eaSitojun 		default:
24826b532452Sitojun 			return (EINVAL);	/* not supported */
24836b532452Sitojun 		}
24846b532452Sitojun 		/* turn off the previous option */
24856b532452Sitojun 		ip6_clearpktopts(opt, IPV6_RTHDR);
24866b532452Sitojun 		opt->ip6po_rthdr = malloc(rthlen, M_IP6OPT, M_NOWAIT);
24876b532452Sitojun 		if (opt->ip6po_rthdr == NULL)
24886b532452Sitojun 			return (ENOBUFS);
248941bfede9Stedu 		memcpy(opt->ip6po_rthdr, rth, rthlen);
24906b532452Sitojun 		break;
24916b532452Sitojun 	}
24926b532452Sitojun 
24936b532452Sitojun 	case IPV6_USE_MIN_MTU:
24946b532452Sitojun 		if (len != sizeof(int))
24956b532452Sitojun 			return (EINVAL);
24966b532452Sitojun 		minmtupolicy = *(int *)buf;
24976b532452Sitojun 		if (minmtupolicy != IP6PO_MINMTU_MCASTONLY &&
24986b532452Sitojun 		    minmtupolicy != IP6PO_MINMTU_DISABLE &&
24996b532452Sitojun 		    minmtupolicy != IP6PO_MINMTU_ALL) {
2500287546eaSitojun 			return (EINVAL);
2501287546eaSitojun 		}
25026b532452Sitojun 		opt->ip6po_minmtu = minmtupolicy;
25036b532452Sitojun 		break;
25046b532452Sitojun 
25056b532452Sitojun 	case IPV6_DONTFRAG:
25066b532452Sitojun 		if (len != sizeof(int))
25076b532452Sitojun 			return (EINVAL);
25086b532452Sitojun 
25096b532452Sitojun 		if (uproto == IPPROTO_TCP || *(int *)buf == 0) {
25106b532452Sitojun 			/*
25116b532452Sitojun 			 * we ignore this option for TCP sockets.
25126b532452Sitojun 			 * (RFC3542 leaves this case unspecified.)
25136b532452Sitojun 			 */
25146b532452Sitojun 			opt->ip6po_flags &= ~IP6PO_DONTFRAG;
25156b532452Sitojun 		} else
25166b532452Sitojun 			opt->ip6po_flags |= IP6PO_DONTFRAG;
2517287546eaSitojun 		break;
2518287546eaSitojun 
2519287546eaSitojun 	default:
2520287546eaSitojun 		return (ENOPROTOOPT);
25216b532452Sitojun 	} /* end of switch */
2522287546eaSitojun 
2523287546eaSitojun 	return (0);
2524287546eaSitojun }
2525287546eaSitojun 
2526287546eaSitojun /*
2527287546eaSitojun  * Routine called from ip6_output() to loop back a copy of an IP6 multicast
252872064afcSmpi  * packet to the input queue of a specified interface.
2529287546eaSitojun  */
2530287546eaSitojun void
2531ee37ea65Smcbride ip6_mloopback(struct ifnet *ifp, struct mbuf *m, struct sockaddr_in6 *dst)
2532287546eaSitojun {
2533287546eaSitojun 	struct mbuf *copym;
25342ed872ebSitojun 	struct ip6_hdr *ip6;
2535287546eaSitojun 
253655ee7a9bSitojun 	/*
253755ee7a9bSitojun 	 * Duplicate the packet.
253855ee7a9bSitojun 	 */
25398990c937Smpi 	copym = m_copym(m, 0, M_COPYALL, M_NOWAIT);
25402ed872ebSitojun 	if (copym == NULL)
25412ed872ebSitojun 		return;
25422ed872ebSitojun 
25432ed872ebSitojun 	/*
25442ed872ebSitojun 	 * Make sure to deep-copy IPv6 header portion in case the data
25452ed872ebSitojun 	 * is in an mbuf cluster, so that we can safely override the IPv6
25462ed872ebSitojun 	 * header portion later.
25472ed872ebSitojun 	 */
25482ed872ebSitojun 	if ((copym->m_flags & M_EXT) != 0 ||
25492ed872ebSitojun 	    copym->m_len < sizeof(struct ip6_hdr)) {
25502ed872ebSitojun 		copym = m_pullup(copym, sizeof(struct ip6_hdr));
25512ed872ebSitojun 		if (copym == NULL)
25522ed872ebSitojun 			return;
2553287546eaSitojun 	}
25542ed872ebSitojun 
25552ed872ebSitojun #ifdef DIAGNOSTIC
25562ed872ebSitojun 	if (copym->m_len < sizeof(*ip6)) {
25572ed872ebSitojun 		m_freem(copym);
25582ed872ebSitojun 		return;
25592ed872ebSitojun 	}
25602ed872ebSitojun #endif
25612ed872ebSitojun 
25622ed872ebSitojun 	ip6 = mtod(copym, struct ip6_hdr *);
2563d4070096Sitojun 	if (IN6_IS_SCOPE_EMBED(&ip6->ip6_src))
25642ed872ebSitojun 		ip6->ip6_src.s6_addr16[1] = 0;
2565d4070096Sitojun 	if (IN6_IS_SCOPE_EMBED(&ip6->ip6_dst))
25662ed872ebSitojun 		ip6->ip6_dst.s6_addr16[1] = 0;
25672ed872ebSitojun 
25687f3eeafaSmpi 	if_input_local(ifp, copym, dst->sin6_family);
2569287546eaSitojun }
2570287546eaSitojun 
2571287546eaSitojun /*
2572287546eaSitojun  * Chop IPv6 header off from the payload.
2573287546eaSitojun  */
2574d4984c21Sjsing int
2575ee37ea65Smcbride ip6_splithdr(struct mbuf *m, struct ip6_exthdrs *exthdrs)
2576287546eaSitojun {
2577287546eaSitojun 	struct mbuf *mh;
2578287546eaSitojun 	struct ip6_hdr *ip6;
2579287546eaSitojun 
2580287546eaSitojun 	ip6 = mtod(m, struct ip6_hdr *);
2581287546eaSitojun 	if (m->m_len > sizeof(*ip6)) {
25820e1f74ccSclaudio 		MGET(mh, M_DONTWAIT, MT_HEADER);
258364a3f76cSjsg 		if (mh == NULL) {
2584287546eaSitojun 			m_freem(m);
2585287546eaSitojun 			return ENOBUFS;
2586287546eaSitojun 		}
2587406cff18Sangelos 		M_MOVE_PKTHDR(mh, m);
25880e1f74ccSclaudio 		m_align(mh, sizeof(*ip6));
2589287546eaSitojun 		m->m_len -= sizeof(*ip6);
2590287546eaSitojun 		m->m_data += sizeof(*ip6);
2591287546eaSitojun 		mh->m_next = m;
2592287546eaSitojun 		m = mh;
2593287546eaSitojun 		m->m_len = sizeof(*ip6);
2594287546eaSitojun 		bcopy((caddr_t)ip6, mtod(m, caddr_t), sizeof(*ip6));
2595287546eaSitojun 	}
2596287546eaSitojun 	exthdrs->ip6e_ip6 = m;
2597287546eaSitojun 	return 0;
2598287546eaSitojun }
2599287546eaSitojun 
2600ca6e56f2Sdjm u_int32_t
2601ca6e56f2Sdjm ip6_randomid(void)
2602ca6e56f2Sdjm {
2603ca6e56f2Sdjm 	return idgen32(&ip6_id_ctx);
2604ca6e56f2Sdjm }
2605ca6e56f2Sdjm 
2606ca6e56f2Sdjm void
2607ca6e56f2Sdjm ip6_randomid_init(void)
2608ca6e56f2Sdjm {
2609ca6e56f2Sdjm 	idgen32_init(&ip6_id_ctx);
2610ca6e56f2Sdjm }
2611e3f16dcaShenning 
2612e3f16dcaShenning /*
2613a2c73f7dSnaddy  *	Compute significant parts of the IPv6 checksum pseudo-header
2614a2c73f7dSnaddy  *	for use in a delayed TCP/UDP checksum calculation.
2615a2c73f7dSnaddy  */
2616a2c73f7dSnaddy static __inline u_int16_t __attribute__((__unused__))
2617a2c73f7dSnaddy in6_cksum_phdr(const struct in6_addr *src, const struct in6_addr *dst,
2618a2c73f7dSnaddy     u_int32_t len, u_int32_t nxt)
2619a2c73f7dSnaddy {
2620a2c73f7dSnaddy 	u_int32_t sum = 0;
2621a2c73f7dSnaddy 	const u_int16_t *w;
2622a2c73f7dSnaddy 
2623a2c73f7dSnaddy 	w = (const u_int16_t *) src;
2624a2c73f7dSnaddy 	sum += w[0];
2625a2c73f7dSnaddy 	if (!IN6_IS_SCOPE_EMBED(src))
2626a2c73f7dSnaddy 		sum += w[1];
2627a2c73f7dSnaddy 	sum += w[2]; sum += w[3]; sum += w[4]; sum += w[5];
2628a2c73f7dSnaddy 	sum += w[6]; sum += w[7];
2629a2c73f7dSnaddy 
2630a2c73f7dSnaddy 	w = (const u_int16_t *) dst;
2631a2c73f7dSnaddy 	sum += w[0];
2632a2c73f7dSnaddy 	if (!IN6_IS_SCOPE_EMBED(dst))
2633a2c73f7dSnaddy 		sum += w[1];
2634a2c73f7dSnaddy 	sum += w[2]; sum += w[3]; sum += w[4]; sum += w[5];
2635a2c73f7dSnaddy 	sum += w[6]; sum += w[7];
2636a2c73f7dSnaddy 
2637a2c73f7dSnaddy 	sum += (u_int16_t)(len >> 16) + (u_int16_t)(len /*& 0xffff*/);
2638a2c73f7dSnaddy 
2639a2c73f7dSnaddy 	sum += (u_int16_t)(nxt >> 16) + (u_int16_t)(nxt /*& 0xffff*/);
2640a2c73f7dSnaddy 
2641a2c73f7dSnaddy 	sum = (u_int16_t)(sum >> 16) + (u_int16_t)(sum /*& 0xffff*/);
2642a2c73f7dSnaddy 
2643a2c73f7dSnaddy 	if (sum > 0xffff)
2644a2c73f7dSnaddy 		sum -= 0xffff;
2645a2c73f7dSnaddy 
2646a2c73f7dSnaddy 	return (sum);
2647a2c73f7dSnaddy }
2648a2c73f7dSnaddy 
2649a2c73f7dSnaddy /*
2650e3f16dcaShenning  * Process a delayed payload checksum calculation.
2651e3f16dcaShenning  */
2652e3f16dcaShenning void
2653e3f16dcaShenning in6_delayed_cksum(struct mbuf *m, u_int8_t nxt)
2654e3f16dcaShenning {
2655e3f16dcaShenning 	int nxtp, offset;
2656e3f16dcaShenning 	u_int16_t csum;
2657e3f16dcaShenning 
2658e3f16dcaShenning 	offset = ip6_lasthdr(m, 0, IPPROTO_IPV6, &nxtp);
2659e3f16dcaShenning 	if (offset <= 0 || nxtp != nxt)
2660e3f16dcaShenning 		/* If the desired next protocol isn't found, punt. */
2661e3f16dcaShenning 		return;
266224ce3f58Snaddy 	csum = (u_int16_t)(in6_cksum(m, 0, offset, m->m_pkthdr.len - offset));
2663e3f16dcaShenning 
2664e3f16dcaShenning 	switch (nxt) {
2665e3f16dcaShenning 	case IPPROTO_TCP:
2666e3f16dcaShenning 		offset += offsetof(struct tcphdr, th_sum);
2667e3f16dcaShenning 		break;
2668e3f16dcaShenning 
2669e3f16dcaShenning 	case IPPROTO_UDP:
2670e3f16dcaShenning 		offset += offsetof(struct udphdr, uh_sum);
2671e3f16dcaShenning 		if (csum == 0)
2672e3f16dcaShenning 			csum = 0xffff;
2673e3f16dcaShenning 		break;
2674e3f16dcaShenning 
2675e3f16dcaShenning 	case IPPROTO_ICMPV6:
2676e3f16dcaShenning 		offset += offsetof(struct icmp6_hdr, icmp6_cksum);
2677e3f16dcaShenning 		break;
2678e3f16dcaShenning 	}
2679e3f16dcaShenning 
2680e3f16dcaShenning 	if ((offset + sizeof(u_int16_t)) > m->m_len)
2681e3f16dcaShenning 		m_copyback(m, offset, sizeof(csum), &csum, M_NOWAIT);
2682e3f16dcaShenning 	else
2683e3f16dcaShenning 		*(u_int16_t *)(mtod(m, caddr_t) + offset) = csum;
2684e3f16dcaShenning }
2685e3f16dcaShenning 
2686e3f16dcaShenning void
2687e3f16dcaShenning in6_proto_cksum_out(struct mbuf *m, struct ifnet *ifp)
2688e3f16dcaShenning {
2689b2b8628dSnaddy 	struct ip6_hdr *ip6 = mtod(m, struct ip6_hdr *);
2690b2b8628dSnaddy 
269124ce3f58Snaddy 	/* some hw and in6_delayed_cksum need the pseudo header cksum */
269224ce3f58Snaddy 	if (m->m_pkthdr.csum_flags &
269324ce3f58Snaddy 	    (M_TCP_CSUM_OUT|M_UDP_CSUM_OUT|M_ICMP_CSUM_OUT)) {
269424ce3f58Snaddy 		int nxt, offset;
269524ce3f58Snaddy 		u_int16_t csum;
269624ce3f58Snaddy 
269724ce3f58Snaddy 		offset = ip6_lasthdr(m, 0, IPPROTO_IPV6, &nxt);
269832c8d29bSbluhm 		if (ISSET(m->m_pkthdr.csum_flags, M_TCP_TSO) &&
269932c8d29bSbluhm 		    in_ifcap_cksum(m, ifp, IFCAP_TSOv6)) {
2700510f4386Sbluhm 			csum = in6_cksum_phdr(&ip6->ip6_src, &ip6->ip6_dst,
2701510f4386Sbluhm 			    htonl(0), htonl(nxt));
2702510f4386Sbluhm 		} else {
270324ce3f58Snaddy 			csum = in6_cksum_phdr(&ip6->ip6_src, &ip6->ip6_dst,
270424ce3f58Snaddy 			    htonl(m->m_pkthdr.len - offset), htonl(nxt));
2705510f4386Sbluhm 		}
270624ce3f58Snaddy 		if (nxt == IPPROTO_TCP)
270724ce3f58Snaddy 			offset += offsetof(struct tcphdr, th_sum);
270824ce3f58Snaddy 		else if (nxt == IPPROTO_UDP)
270924ce3f58Snaddy 			offset += offsetof(struct udphdr, uh_sum);
271024ce3f58Snaddy 		else if (nxt == IPPROTO_ICMPV6)
271124ce3f58Snaddy 			offset += offsetof(struct icmp6_hdr, icmp6_cksum);
271224ce3f58Snaddy 		if ((offset + sizeof(u_int16_t)) > m->m_len)
271324ce3f58Snaddy 			m_copyback(m, offset, sizeof(csum), &csum, M_NOWAIT);
271424ce3f58Snaddy 		else
271524ce3f58Snaddy 			*(u_int16_t *)(mtod(m, caddr_t) + offset) = csum;
271624ce3f58Snaddy 	}
271724ce3f58Snaddy 
2718e3f16dcaShenning 	if (m->m_pkthdr.csum_flags & M_TCP_CSUM_OUT) {
2719e3f16dcaShenning 		if (!ifp || !(ifp->if_capabilities & IFCAP_CSUM_TCPv6) ||
2720b2b8628dSnaddy 		    ip6->ip6_nxt != IPPROTO_TCP ||
272196c4247cSmpi 		    ifp->if_bridgeidx != 0) {
272279a86c4bSjca 			tcpstat_inc(tcps_outswcsum);
2723e3f16dcaShenning 			in6_delayed_cksum(m, IPPROTO_TCP);
2724e3f16dcaShenning 			m->m_pkthdr.csum_flags &= ~M_TCP_CSUM_OUT; /* Clear */
2725e3f16dcaShenning 		}
2726e3f16dcaShenning 	} else if (m->m_pkthdr.csum_flags & M_UDP_CSUM_OUT) {
2727e3f16dcaShenning 		if (!ifp || !(ifp->if_capabilities & IFCAP_CSUM_UDPv6) ||
2728b2b8628dSnaddy 		    ip6->ip6_nxt != IPPROTO_UDP ||
272996c4247cSmpi 		    ifp->if_bridgeidx != 0) {
2730967e9010Sdlg 			udpstat_inc(udps_outswcsum);
2731e3f16dcaShenning 			in6_delayed_cksum(m, IPPROTO_UDP);
2732e3f16dcaShenning 			m->m_pkthdr.csum_flags &= ~M_UDP_CSUM_OUT; /* Clear */
2733e3f16dcaShenning 		}
2734e3f16dcaShenning 	} else if (m->m_pkthdr.csum_flags & M_ICMP_CSUM_OUT) {
2735e3f16dcaShenning 		in6_delayed_cksum(m, IPPROTO_ICMPV6);
2736e3f16dcaShenning 		m->m_pkthdr.csum_flags &= ~M_ICMP_CSUM_OUT; /* Clear */
2737e3f16dcaShenning 	}
2738e3f16dcaShenning }
27394a931851Smarkus 
27404a931851Smarkus #ifdef IPSEC
2741e7111b64Sbluhm int
2742ace0f189Sbluhm ip6_output_ipsec_lookup(struct mbuf *m, const struct ipsec_level *seclevel,
27432551e577Sbluhm     struct tdb **tdbout)
27444a931851Smarkus {
27454a931851Smarkus 	struct tdb *tdb;
27464a931851Smarkus 	struct m_tag *mtag;
27474a931851Smarkus 	struct tdb_ident *tdbi;
2748e7111b64Sbluhm 	int error;
27494a931851Smarkus 
27504a931851Smarkus 	/*
27514a931851Smarkus 	 * Check if there was an outgoing SA bound to the flow
27524a931851Smarkus 	 * from a transport protocol.
27534a931851Smarkus 	 */
27544a931851Smarkus 
27554a931851Smarkus 	/* Do we have any pending SAs to apply ? */
2756e7111b64Sbluhm 	error = ipsp_spd_lookup(m, AF_INET6, sizeof(struct ip6_hdr),
27572551e577Sbluhm 	    IPSP_DIRECTION_OUT, NULL, seclevel, &tdb, NULL);
2758e7111b64Sbluhm 	if (error || tdb == NULL) {
2759e7111b64Sbluhm 		*tdbout = NULL;
2760e7111b64Sbluhm 		return error;
2761e7111b64Sbluhm 	}
27624a931851Smarkus 	/* Loop detection */
27638c4a105eSflorian 	for (mtag = m_tag_first(m); mtag != NULL; mtag = m_tag_next(m, mtag)) {
27644a931851Smarkus 		if (mtag->m_tag_id != PACKET_TAG_IPSEC_OUT_DONE)
27654a931851Smarkus 			continue;
27664a931851Smarkus 		tdbi = (struct tdb_ident *)(mtag + 1);
27674a931851Smarkus 		if (tdbi->spi == tdb->tdb_spi &&
27684a931851Smarkus 		    tdbi->proto == tdb->tdb_sproto &&
27694a931851Smarkus 		    tdbi->rdomain == tdb->tdb_rdomain &&
27708c4a105eSflorian 		    !memcmp(&tdbi->dst, &tdb->tdb_dst,
27718c4a105eSflorian 		    sizeof(union sockaddr_union))) {
27728c4a105eSflorian 			/* no IPsec needed */
277331a6915fSbluhm 			tdb_unref(tdb);
2774e7111b64Sbluhm 			*tdbout = NULL;
2775e7111b64Sbluhm 			return 0;
27764a931851Smarkus 		}
27774a931851Smarkus 	}
2778e7111b64Sbluhm 	*tdbout = tdb;
2779e7111b64Sbluhm 	return 0;
27804a931851Smarkus }
27814a931851Smarkus 
27824a931851Smarkus int
278394c0e2bdSbluhm ip6_output_ipsec_pmtu_update(struct tdb *tdb, struct route *ro,
27846d361195Sbluhm     struct in6_addr *dst, int ifidx, int rtableid, int transportmode)
27856d361195Sbluhm {
27866d361195Sbluhm 	struct rtentry *rt = NULL;
27876d361195Sbluhm 	int rt_mtucloned = 0;
27886d361195Sbluhm 
27896d361195Sbluhm 	/* Find a host route to store the mtu in */
27906d361195Sbluhm 	if (ro != NULL)
27916d361195Sbluhm 		rt = ro->ro_rt;
27926d361195Sbluhm 	/* but don't add a PMTU route for transport mode SAs */
27936d361195Sbluhm 	if (transportmode)
27946d361195Sbluhm 		rt = NULL;
27956d361195Sbluhm 	else if (rt == NULL || (rt->rt_flags & RTF_HOST) == 0) {
27966d361195Sbluhm 		struct sockaddr_in6 sin6;
27976d361195Sbluhm 		int error;
27986d361195Sbluhm 
27996d361195Sbluhm 		memset(&sin6, 0, sizeof(sin6));
28006d361195Sbluhm 		sin6.sin6_family = AF_INET6;
28016d361195Sbluhm 		sin6.sin6_len = sizeof(sin6);
28026d361195Sbluhm 		sin6.sin6_addr = *dst;
28036d361195Sbluhm 		sin6.sin6_scope_id = in6_addr2scopeid(ifidx, dst);
2804952c6363Sbluhm 		error = in6_embedscope(dst, &sin6, NULL, NULL);
28056d361195Sbluhm 		if (error) {
28066d361195Sbluhm 			/* should be impossible */
28076d361195Sbluhm 			return error;
28086d361195Sbluhm 		}
28096d361195Sbluhm 		rt = icmp6_mtudisc_clone(&sin6, rtableid, 1);
28106d361195Sbluhm 		rt_mtucloned = 1;
28116d361195Sbluhm 	}
28126d361195Sbluhm 	DPRINTF("spi %08x mtu %d rt %p cloned %d",
28136d361195Sbluhm 	    ntohl(tdb->tdb_spi), tdb->tdb_mtu, rt, rt_mtucloned);
28146d361195Sbluhm 	if (rt != NULL) {
2815*84d9c64aSbluhm 		atomic_store_int(&rt->rt_mtu, tdb->tdb_mtu);
28166d361195Sbluhm 		if (ro != NULL && ro->ro_rt != NULL) {
28176d361195Sbluhm 			rtfree(ro->ro_rt);
281894c0e2bdSbluhm 			ro->ro_rt = rtalloc(&ro->ro_dstsa, RT_RESOLVE,
28196d361195Sbluhm 			    rtableid);
28206d361195Sbluhm 		}
28216d361195Sbluhm 		if (rt_mtucloned)
28226d361195Sbluhm 			rtfree(rt);
28236d361195Sbluhm 	}
28246d361195Sbluhm 	return 0;
28256d361195Sbluhm }
28266d361195Sbluhm 
28276d361195Sbluhm int
282894c0e2bdSbluhm ip6_output_ipsec_send(struct tdb *tdb, struct mbuf *m, struct route *ro,
282959caf375Sbluhm     int tunalready, int fwd)
28304a931851Smarkus {
2831c06845b1Sbluhm 	struct mbuf_list ml;
2832c06845b1Sbluhm 	struct ifnet *encif = NULL;
283359caf375Sbluhm 	struct ip6_hdr *ip6;
28346d361195Sbluhm 	struct in6_addr dst;
2835c06845b1Sbluhm 	u_int len;
2836c06845b1Sbluhm 	int error, ifidx, rtableid, tso = 0;
28374a931851Smarkus 
28384a931851Smarkus #if NPF > 0
283959caf375Sbluhm 	/*
284059caf375Sbluhm 	 * Packet filter
284159caf375Sbluhm 	 */
28424a931851Smarkus 	if ((encif = enc_getif(tdb->tdb_rdomain, tdb->tdb_tap)) == NULL ||
28434a931851Smarkus 	    pf_test(AF_INET6, fwd ? PF_FWD : PF_OUT, encif, &m) != PF_PASS) {
28444a931851Smarkus 		m_freem(m);
2845840989e0Sbluhm 		return EACCES;
28464a931851Smarkus 	}
28474a931851Smarkus 	if (m == NULL)
28484a931851Smarkus 		return 0;
28494a931851Smarkus 	/*
28504a931851Smarkus 	 * PF_TAG_REROUTE handling or not...
28514a931851Smarkus 	 * Packet is entering IPsec so the routing is
28524a931851Smarkus 	 * already overruled by the IPsec policy.
28534a931851Smarkus 	 * Until now the change was not reconsidered.
28544a931851Smarkus 	 * What's the behaviour?
28554a931851Smarkus 	 */
28564a931851Smarkus #endif
285759caf375Sbluhm 
2858c06845b1Sbluhm 	/* Check if we can chop the TCP packet */
285959caf375Sbluhm 	ip6 = mtod(m, struct ip6_hdr *);
2860c06845b1Sbluhm 	if (ISSET(m->m_pkthdr.csum_flags, M_TCP_TSO) &&
2861c06845b1Sbluhm 	    m->m_pkthdr.ph_mss <= tdb->tdb_mtu) {
2862c06845b1Sbluhm 		tso = 1;
2863c06845b1Sbluhm 		len = m->m_pkthdr.ph_mss;
2864c06845b1Sbluhm 	} else
2865c06845b1Sbluhm 		len = sizeof(struct ip6_hdr) + ntohs(ip6->ip6_plen);
2866c06845b1Sbluhm 
2867c06845b1Sbluhm 	/* Check if we are allowed to fragment */
28686d361195Sbluhm 	dst = ip6->ip6_dst;
28696d361195Sbluhm 	ifidx = m->m_pkthdr.ph_ifidx;
28706d361195Sbluhm 	rtableid = m->m_pkthdr.ph_rtableid;
287159caf375Sbluhm 	if (ip_mtudisc && tdb->tdb_mtu &&
2872c06845b1Sbluhm 	    len > tdb->tdb_mtu && tdb->tdb_mtutimeout > gettime()) {
28736d361195Sbluhm 		int transportmode;
287459caf375Sbluhm 
287559caf375Sbluhm 		transportmode = (tdb->tdb_dst.sa.sa_family == AF_INET6) &&
28766d361195Sbluhm 		    (IN6_ARE_ADDR_EQUAL(&tdb->tdb_dst.sin6.sin6_addr, &dst));
28776d361195Sbluhm 		error = ip6_output_ipsec_pmtu_update(tdb, ro, &dst, ifidx,
28786d361195Sbluhm 		    rtableid, transportmode);
287959caf375Sbluhm 		if (error) {
288059caf375Sbluhm 			ipsecstat_inc(ipsec_odrops);
2881d997d144Smvs 			tdbstat_inc(tdb, tdb_odrops);
288259caf375Sbluhm 			m_freem(m);
288359caf375Sbluhm 			return error;
288459caf375Sbluhm 		}
288559caf375Sbluhm 		ipsec_adjust_mtu(m, tdb->tdb_mtu);
288659caf375Sbluhm 		m_freem(m);
288759caf375Sbluhm 		return EMSGSIZE;
288859caf375Sbluhm 	}
288989de4c79Sbluhm 	/* propagate don't fragment for v6-over-v6 */
289089de4c79Sbluhm 	if (ip_mtudisc)
289189de4c79Sbluhm 		SET(m->m_pkthdr.csum_flags, M_IPV6_DF_OUT);
289259caf375Sbluhm 
289359caf375Sbluhm 	/*
289459caf375Sbluhm 	 * Clear these -- they'll be set in the recursive invocation
289559caf375Sbluhm 	 * as needed.
289659caf375Sbluhm 	 */
289759caf375Sbluhm 	m->m_flags &= ~(M_BCAST | M_MCAST);
28984a931851Smarkus 
2899c06845b1Sbluhm 	if (tso) {
2900c06845b1Sbluhm 		error = tcp_chopper(m, &ml, encif, len);
2901c06845b1Sbluhm 		if (error)
2902c06845b1Sbluhm 			goto done;
2903c06845b1Sbluhm 	} else {
2904c06845b1Sbluhm 		CLR(m->m_pkthdr.csum_flags, M_TCP_TSO);
2905c06845b1Sbluhm 		in6_proto_cksum_out(m, encif);
2906c06845b1Sbluhm 		ml_init(&ml);
2907c06845b1Sbluhm 		ml_enqueue(&ml, m);
2908c06845b1Sbluhm 	}
2909c06845b1Sbluhm 
29105ee194bcSbluhm 	KERNEL_LOCK();
2911c06845b1Sbluhm 	while ((m = ml_dequeue(&ml)) != NULL) {
2912c06845b1Sbluhm 		/* Callee frees mbuf */
29137ced204eSmpi 		error = ipsp_process_packet(m, tdb, AF_INET6, tunalready);
2914c06845b1Sbluhm 		if (error)
2915c06845b1Sbluhm 			break;
2916c06845b1Sbluhm 	}
29175ee194bcSbluhm 	KERNEL_UNLOCK();
2918c06845b1Sbluhm  done:
29192edaa7baSmpi 	if (error) {
2920c06845b1Sbluhm 		ml_purge(&ml);
29217ced204eSmpi 		ipsecstat_inc(ipsec_odrops);
2922d997d144Smvs 		tdbstat_inc(tdb, tdb_odrops);
29232edaa7baSmpi 	}
2924c06845b1Sbluhm 	if (!error && tso)
2925c06845b1Sbluhm 		tcpstat_inc(tcps_outswtso);
29266d361195Sbluhm 	if (ip_mtudisc && error == EMSGSIZE)
29276d361195Sbluhm 		ip6_output_ipsec_pmtu_update(tdb, ro, &dst, ifidx, rtableid, 0);
29287ced204eSmpi 	return error;
29294a931851Smarkus }
29304a931851Smarkus #endif /* IPSEC */
2931