xref: /netbsd-src/lib/libc/net/rthdr.c (revision a582b4ae9fa01802e52769545392ca48a6663064)
1*a582b4aeSmsaitoh /*	$NetBSD: rthdr.c,v 1.19 2019/05/29 02:30:42 msaitoh Exp $	*/
24620b004Sitojun 
337e81591Sitojun /*
437e81591Sitojun  * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project.
537e81591Sitojun  * All rights reserved.
637e81591Sitojun  *
737e81591Sitojun  * Redistribution and use in source and binary forms, with or without
837e81591Sitojun  * modification, are permitted provided that the following conditions
937e81591Sitojun  * are met:
1037e81591Sitojun  * 1. Redistributions of source code must retain the above copyright
1137e81591Sitojun  *    notice, this list of conditions and the following disclaimer.
1237e81591Sitojun  * 2. Redistributions in binary form must reproduce the above copyright
1337e81591Sitojun  *    notice, this list of conditions and the following disclaimer in the
1437e81591Sitojun  *    documentation and/or other materials provided with the distribution.
1537e81591Sitojun  * 3. Neither the name of the project nor the names of its contributors
1637e81591Sitojun  *    may be used to endorse or promote products derived from this software
1737e81591Sitojun  *    without specific prior written permission.
1837e81591Sitojun  *
1937e81591Sitojun  * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND
2037e81591Sitojun  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
2137e81591Sitojun  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
2237e81591Sitojun  * ARE DISCLAIMED.  IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE
2337e81591Sitojun  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
2437e81591Sitojun  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
2537e81591Sitojun  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
2637e81591Sitojun  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
2737e81591Sitojun  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
2837e81591Sitojun  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
2937e81591Sitojun  * SUCH DAMAGE.
3037e81591Sitojun  */
3137e81591Sitojun 
3272eddcacSitojun #include <sys/cdefs.h>
3372eddcacSitojun #if defined(LIBC_SCCS) && !defined(lint)
34*a582b4aeSmsaitoh __RCSID("$NetBSD: rthdr.c,v 1.19 2019/05/29 02:30:42 msaitoh Exp $");
3572eddcacSitojun #endif /* LIBC_SCCS and not lint */
3672eddcacSitojun 
37c6bf4b09Sitojun #include "namespace.h"
3837e81591Sitojun #include <sys/param.h>
3937e81591Sitojun #include <sys/types.h>
4037e81591Sitojun #include <sys/socket.h>
4137e81591Sitojun 
4237e81591Sitojun #include <netinet/in.h>
4337e81591Sitojun #include <netinet/ip6.h>
4437e81591Sitojun 
45b48252f3Slukem #include <assert.h>
4637e81591Sitojun #include <string.h>
4737e81591Sitojun #include <stdio.h>
4837e81591Sitojun 
49c6bf4b09Sitojun #ifdef __weak_alias
__weak_alias(inet6_rthdr_add,_inet6_rthdr_add)50c6bf4b09Sitojun __weak_alias(inet6_rthdr_add,_inet6_rthdr_add)
51c6bf4b09Sitojun __weak_alias(inet6_rthdr_getaddr,_inet6_rthdr_getaddr)
52c6bf4b09Sitojun __weak_alias(inet6_rthdr_getflags,_inet6_rthdr_getflags)
53c6bf4b09Sitojun __weak_alias(inet6_rthdr_init,_inet6_rthdr_init)
54c6bf4b09Sitojun __weak_alias(inet6_rthdr_lasthop,_inet6_rthdr_lasthop)
55c6bf4b09Sitojun __weak_alias(inet6_rthdr_segments,_inet6_rthdr_segments)
56c6bf4b09Sitojun __weak_alias(inet6_rthdr_space,_inet6_rthdr_space)
57de8db475Srpaulo __weak_alias(inet6_rth_space, _inet6_rth_space)
58de8db475Srpaulo __weak_alias(inet6_rth_init, _inet6_rth_init)
59de8db475Srpaulo __weak_alias(inet6_rth_add, _inet6_rth_add)
60de8db475Srpaulo __weak_alias(inet6_rth_reverse, _inet6_rth_reverse)
61de8db475Srpaulo __weak_alias(inet6_rth_segments, _inet6_rth_segments)
62de8db475Srpaulo __weak_alias(inet6_rth_getaddr, _inet6_rth_getaddr)
63c6bf4b09Sitojun #endif
64c6bf4b09Sitojun 
65de8db475Srpaulo /*
66de8db475Srpaulo  * RFC2292 API
67de8db475Srpaulo  */
68de8db475Srpaulo 
6937e81591Sitojun size_t
70c5e820caSchristos inet6_rthdr_space(int type, int seg)
7137e81591Sitojun {
7237e81591Sitojun 	switch (type) {
7337e81591Sitojun 	case IPV6_RTHDR_TYPE_0:
7437e81591Sitojun 		if (seg < 1 || seg > 23)
7537e81591Sitojun 			return (0);
767a574165Sitojun 		return (CMSG_SPACE(sizeof(struct in6_addr) * seg +
77e72a5afeSitojun 		    sizeof(struct ip6_rthdr0)));
7837e81591Sitojun 	default:
7937e81591Sitojun 		return (0);
8037e81591Sitojun 	}
8137e81591Sitojun }
8237e81591Sitojun 
8337e81591Sitojun struct cmsghdr *
inet6_rthdr_init(void * bp,int type)84c5e820caSchristos inet6_rthdr_init(void *bp, int type)
8537e81591Sitojun {
86ae40c8edSchristos 	struct cmsghdr *ch;
87ae40c8edSchristos 	struct ip6_rthdr *rthdr;
88b48252f3Slukem 
89b48252f3Slukem 	_DIAGASSERT(bp != NULL);
90b48252f3Slukem 
91b48252f3Slukem 	ch = (struct cmsghdr *)bp;
92ae40c8edSchristos 	rthdr = (struct ip6_rthdr *)(void *)CMSG_DATA(ch);
9337e81591Sitojun 
9437e81591Sitojun 	ch->cmsg_level = IPPROTO_IPV6;
9537e81591Sitojun 	ch->cmsg_type = IPV6_RTHDR;
9637e81591Sitojun 
9737e81591Sitojun 	switch (type) {
9837e81591Sitojun 	case IPV6_RTHDR_TYPE_0:
99de8db475Srpaulo #ifdef COMPAT_RFC2292
100de8db475Srpaulo 		ch->cmsg_len = CMSG_LEN(sizeof(struct ip6_rthdr0) -
101de8db475Srpaulo 		    sizeof(struct in6_addr));
102de8db475Srpaulo #else
1037a574165Sitojun 		ch->cmsg_len = CMSG_LEN(sizeof(struct ip6_rthdr0));
104de8db475Srpaulo #endif
105d574ffc6Skleink 		(void)memset(rthdr, 0, sizeof(struct ip6_rthdr0));
10637e81591Sitojun 		rthdr->ip6r_type = IPV6_RTHDR_TYPE_0;
10737e81591Sitojun 		return (ch);
10837e81591Sitojun 	default:
10937e81591Sitojun 		return (NULL);
11037e81591Sitojun 	}
11137e81591Sitojun }
11237e81591Sitojun 
11337e81591Sitojun int
inet6_rthdr_add(struct cmsghdr * cmsg,const struct in6_addr * addr,u_int flags)114c5e820caSchristos inet6_rthdr_add(struct cmsghdr *cmsg, const struct in6_addr *addr, u_int flags)
11537e81591Sitojun {
116ae40c8edSchristos 	struct ip6_rthdr *rthdr;
117b48252f3Slukem 
118b48252f3Slukem 	_DIAGASSERT(cmsg != NULL);
119b48252f3Slukem 	_DIAGASSERT(addr != NULL);
120b48252f3Slukem 
121ae40c8edSchristos 	rthdr = (struct ip6_rthdr *)(void *)CMSG_DATA(cmsg);
12237e81591Sitojun 
12337e81591Sitojun 	switch (rthdr->ip6r_type) {
12437e81591Sitojun 	case IPV6_RTHDR_TYPE_0:
12537e81591Sitojun 	{
126c5e820caSchristos 		size_t len;
127ae40c8edSchristos 		struct ip6_rthdr0 *rt0 = (struct ip6_rthdr0 *)(void *)rthdr;
128de8db475Srpaulo 		if (flags != IPV6_RTHDR_LOOSE && flags != IPV6_RTHDR_STRICT)
12937e81591Sitojun 			return (-1);
130e72a5afeSitojun 		if (rt0->ip6r0_segleft == 23)
13137e81591Sitojun 			return (-1);
132de8db475Srpaulo 		if (flags != IPV6_RTHDR_LOOSE)
133de8db475Srpaulo 			return (-1);
13437e81591Sitojun 		rt0->ip6r0_segleft++;
135e72a5afeSitojun 		(void)memcpy(((caddr_t)(void *)rt0) +
136e72a5afeSitojun 		    ((rt0->ip6r0_len + 1) << 3), addr, sizeof(struct in6_addr));
13737e81591Sitojun 		rt0->ip6r0_len += sizeof(struct in6_addr) >> 3;
138c5e820caSchristos 		len = CMSG_LEN((rt0->ip6r0_len + 1) << 3);
139c5e820caSchristos 		_DIAGASSERT(__type_fit(socklen_t, len));
140c5e820caSchristos 		cmsg->cmsg_len = (socklen_t)len;
14137e81591Sitojun 		break;
14237e81591Sitojun 	}
14337e81591Sitojun 	default:
14437e81591Sitojun 		return (-1);
14537e81591Sitojun 	}
14637e81591Sitojun 
14737e81591Sitojun 	return (0);
14837e81591Sitojun }
14937e81591Sitojun 
15037e81591Sitojun int
inet6_rthdr_lasthop(struct cmsghdr * cmsg,unsigned int flags)151c5e820caSchristos inet6_rthdr_lasthop(struct cmsghdr *cmsg, unsigned int flags)
15237e81591Sitojun {
153ae40c8edSchristos 	struct ip6_rthdr *rthdr;
154b48252f3Slukem 
155b48252f3Slukem 	_DIAGASSERT(cmsg != NULL);
156b48252f3Slukem 
157ae40c8edSchristos 	rthdr = (struct ip6_rthdr *)(void *)CMSG_DATA(cmsg);
15837e81591Sitojun 
15937e81591Sitojun 	switch (rthdr->ip6r_type) {
16037e81591Sitojun 	case IPV6_RTHDR_TYPE_0:
16137e81591Sitojun 	{
162ae40c8edSchristos 		struct ip6_rthdr0 *rt0 = (struct ip6_rthdr0 *)(void *)rthdr;
163e72a5afeSitojun 		if (rt0->ip6r0_segleft > 23)
16437e81591Sitojun 			return (-1);
165de8db475Srpaulo 		if (flags != IPV6_RTHDR_LOOSE)
166de8db475Srpaulo 			return (-1);
16737e81591Sitojun 		break;
16837e81591Sitojun 	}
16937e81591Sitojun 	default:
17037e81591Sitojun 		return (-1);
17137e81591Sitojun 	}
17237e81591Sitojun 
17337e81591Sitojun 	return (0);
17437e81591Sitojun }
17537e81591Sitojun 
17637e81591Sitojun #if 0
17737e81591Sitojun int
178c5e820caSchristos inet6_rthdr_reverse(const struct cmsghdr *in, struct cmsghdr *out)
17937e81591Sitojun {
180e72a5afeSitojun 
181e72a5afeSitojun 	return (-1);
18237e81591Sitojun }
18337e81591Sitojun #endif
18437e81591Sitojun 
18537e81591Sitojun int
inet6_rthdr_segments(const struct cmsghdr * cmsg)186c5e820caSchristos inet6_rthdr_segments(const struct cmsghdr *cmsg)
18737e81591Sitojun {
188ae40c8edSchristos 	const struct ip6_rthdr *rthdr;
189b48252f3Slukem 
190b48252f3Slukem 	_DIAGASSERT(cmsg != NULL);
191b48252f3Slukem 
19203256c6eSchristos 	rthdr = __UNCONST(CCMSG_DATA(cmsg));
19337e81591Sitojun 
19437e81591Sitojun 	switch (rthdr->ip6r_type) {
19537e81591Sitojun 	case IPV6_RTHDR_TYPE_0:
19637e81591Sitojun 	{
197ae40c8edSchristos 		const struct ip6_rthdr0 *rt0 =
198ae40c8edSchristos 		    (const struct ip6_rthdr0 *)(const void *)rthdr;
199c5e820caSchristos 		size_t len;
20037e81591Sitojun 
201e72a5afeSitojun 		if (rt0->ip6r0_len % 2 || 46 < rt0->ip6r0_len)
202e72a5afeSitojun 			return (-1);
20337e81591Sitojun 
204c5e820caSchristos 		len = (rt0->ip6r0_len * 8) / sizeof(struct in6_addr);
205c5e820caSchristos 		_DIAGASSERT(__type_fit(int, len));
206c5e820caSchristos 		return (int)len;
20737e81591Sitojun 	}
20837e81591Sitojun 
20937e81591Sitojun 	default:
210e72a5afeSitojun 		return (-1);
21137e81591Sitojun 	}
21237e81591Sitojun }
21337e81591Sitojun 
21437e81591Sitojun struct in6_addr *
inet6_rthdr_getaddr(struct cmsghdr * cmsg,int idx)215c5e820caSchristos inet6_rthdr_getaddr(struct cmsghdr *cmsg, int idx)
21637e81591Sitojun {
217ae40c8edSchristos 	struct ip6_rthdr *rthdr;
218b48252f3Slukem 
219b48252f3Slukem 	_DIAGASSERT(cmsg != NULL);
220b48252f3Slukem 
221ae40c8edSchristos 	rthdr = (struct ip6_rthdr *)(void *)CMSG_DATA(cmsg);
22237e81591Sitojun 
22337e81591Sitojun 	switch (rthdr->ip6r_type) {
22437e81591Sitojun 	case IPV6_RTHDR_TYPE_0:
22537e81591Sitojun 	{
226ae40c8edSchristos 		struct ip6_rthdr0 *rt0 = (struct ip6_rthdr0 *)(void *)rthdr;
22737e81591Sitojun 		int naddr;
228c5e820caSchristos 		size_t len;
22937e81591Sitojun 
230e72a5afeSitojun 		if (rt0->ip6r0_len % 2 || 46 < rt0->ip6r0_len)
23137e81591Sitojun 			return NULL;
232c5e820caSchristos 		len = (rt0->ip6r0_len * 8) / sizeof(struct in6_addr);
233c5e820caSchristos 		_DIAGASSERT(__type_fit(int, len));
234c5e820caSchristos 		naddr = (int)len;
235e72a5afeSitojun 		if (idx <= 0 || naddr < idx)
23637e81591Sitojun 			return NULL;
237de8db475Srpaulo #ifdef COMPAT_RFC2292
238de8db475Srpaulo 		return ((struct in6_addr *)(void *)(rt0 + 1)) + idx - 1;
239de8db475Srpaulo #else
2407a574165Sitojun 		return ((struct in6_addr *)(void *)(rt0 + 1)) + idx;
241de8db475Srpaulo #endif
24237e81591Sitojun 	}
24337e81591Sitojun 
24437e81591Sitojun 	default:
24537e81591Sitojun 		return NULL;
24637e81591Sitojun 	}
24737e81591Sitojun }
24837e81591Sitojun 
24937e81591Sitojun int
inet6_rthdr_getflags(const struct cmsghdr * cmsg,int idx)250c5e820caSchristos inet6_rthdr_getflags(const struct cmsghdr *cmsg, int idx)
25137e81591Sitojun {
252ae40c8edSchristos 	const struct ip6_rthdr *rthdr;
253b48252f3Slukem 
254b48252f3Slukem 	_DIAGASSERT(cmsg != NULL);
255b48252f3Slukem 
25603256c6eSchristos 	rthdr = __UNCONST(CCMSG_DATA(cmsg));
25737e81591Sitojun 
25837e81591Sitojun 	switch (rthdr->ip6r_type) {
25937e81591Sitojun 	case IPV6_RTHDR_TYPE_0:
26037e81591Sitojun 	{
261ae40c8edSchristos 		const struct ip6_rthdr0 *rt0 = (const struct ip6_rthdr0 *)
262ae40c8edSchristos 		(const void *)rthdr;
26337e81591Sitojun 		int naddr;
264c5e820caSchristos 		size_t len;
26537e81591Sitojun 
266e72a5afeSitojun 		if (rt0->ip6r0_len % 2 || 46 < rt0->ip6r0_len)
267e72a5afeSitojun 			return (-1);
268c5e820caSchristos 		len = (rt0->ip6r0_len * 8) / sizeof(struct in6_addr);
269c5e820caSchristos 		_DIAGASSERT(__type_fit(int, len));
270c5e820caSchristos 		naddr = (int)len;
271e72a5afeSitojun 		if (idx < 0 || naddr < idx)
272e72a5afeSitojun 			return (-1);
27337e81591Sitojun 		return IPV6_RTHDR_LOOSE;
27437e81591Sitojun 	}
27537e81591Sitojun 
27637e81591Sitojun 	default:
277e72a5afeSitojun 		return (-1);
27837e81591Sitojun 	}
27937e81591Sitojun }
280de8db475Srpaulo 
281de8db475Srpaulo /*
282de8db475Srpaulo  * RFC3542 (2292bis) API
283de8db475Srpaulo  */
284de8db475Srpaulo 
285de8db475Srpaulo socklen_t
inet6_rth_space(int type,int segments)286de8db475Srpaulo inet6_rth_space(int type, int segments)
287de8db475Srpaulo {
288de8db475Srpaulo 	switch (type) {
289de8db475Srpaulo 	case IPV6_RTHDR_TYPE_0:
290de8db475Srpaulo 		return (((segments * 2) + 1) << 3);
291de8db475Srpaulo 	default:
292*a582b4aeSmsaitoh 		return (0);	/* type not supported */
293de8db475Srpaulo 	}
294de8db475Srpaulo }
295de8db475Srpaulo 
296de8db475Srpaulo void *
inet6_rth_init(void * bp,socklen_t bp_len,int type,int segments)297de8db475Srpaulo inet6_rth_init(void *bp, socklen_t bp_len, int type, int segments)
298de8db475Srpaulo {
299de8db475Srpaulo 	struct ip6_rthdr *rth;
300de8db475Srpaulo 	struct ip6_rthdr0 *rth0;
301de8db475Srpaulo 
302de8db475Srpaulo 	_DIAGASSERT(bp != NULL);
303de8db475Srpaulo 
304de8db475Srpaulo 	rth = (struct ip6_rthdr *)bp;
305de8db475Srpaulo 
306de8db475Srpaulo 	switch (type) {
307de8db475Srpaulo 	case IPV6_RTHDR_TYPE_0:
308de8db475Srpaulo 		/* length validation */
309de8db475Srpaulo 		if (bp_len < inet6_rth_space(IPV6_RTHDR_TYPE_0, segments))
310de8db475Srpaulo 			return (NULL);
311de8db475Srpaulo 
312de8db475Srpaulo 		memset(bp, 0, bp_len);
313de8db475Srpaulo 		rth0 = (struct ip6_rthdr0 *)(void *)rth;
314de8db475Srpaulo 		rth0->ip6r0_len = segments * 2;
315de8db475Srpaulo 		rth0->ip6r0_type = IPV6_RTHDR_TYPE_0;
316de8db475Srpaulo 		rth0->ip6r0_segleft = 0;
317de8db475Srpaulo 		rth0->ip6r0_reserved = 0;
318de8db475Srpaulo 		break;
319de8db475Srpaulo 	default:
320de8db475Srpaulo 		return (NULL);	/* type not supported */
321de8db475Srpaulo 	}
322de8db475Srpaulo 
323de8db475Srpaulo 	return (bp);
324de8db475Srpaulo }
325de8db475Srpaulo 
326de8db475Srpaulo int
inet6_rth_add(void * bp,const struct in6_addr * addr)327de8db475Srpaulo inet6_rth_add(void *bp, const struct in6_addr *addr)
328de8db475Srpaulo {
329de8db475Srpaulo 	struct ip6_rthdr *rth;
330de8db475Srpaulo 	struct ip6_rthdr0 *rth0;
331de8db475Srpaulo 	struct in6_addr *nextaddr;
332de8db475Srpaulo 
333de8db475Srpaulo 	_DIAGASSERT(bp != NULL);
334de8db475Srpaulo 
335de8db475Srpaulo 	rth = (struct ip6_rthdr *)bp;
336de8db475Srpaulo 
337de8db475Srpaulo 	switch (rth->ip6r_type) {
338de8db475Srpaulo 	case IPV6_RTHDR_TYPE_0:
339de8db475Srpaulo 		rth0 = (struct ip6_rthdr0 *)(void *)rth;
340de8db475Srpaulo 		nextaddr = (struct in6_addr *)(void *)(rth0 + 1)
341de8db475Srpaulo 		    + rth0->ip6r0_segleft;
342de8db475Srpaulo 		*nextaddr = *addr;
343de8db475Srpaulo 		rth0->ip6r0_segleft++;
344de8db475Srpaulo 		break;
345de8db475Srpaulo 	default:
346de8db475Srpaulo 		return (-1);	/* type not supported */
347de8db475Srpaulo 	}
348de8db475Srpaulo 
349de8db475Srpaulo 	return (0);
350de8db475Srpaulo }
351de8db475Srpaulo 
352de8db475Srpaulo int
inet6_rth_reverse(const void * in,void * out)353de8db475Srpaulo inet6_rth_reverse(const void *in, void *out)
354de8db475Srpaulo {
355de8db475Srpaulo 	const struct ip6_rthdr *rth_in;
356de8db475Srpaulo 	const struct ip6_rthdr0 *rth0_in;
357de8db475Srpaulo 	struct ip6_rthdr0 *rth0_out;
358de8db475Srpaulo 	int i, segments;
359de8db475Srpaulo 
360de8db475Srpaulo 	_DIAGASSERT(in != NULL);
361de8db475Srpaulo 	_DIAGASSERT(out != NULL);
362de8db475Srpaulo 
363de8db475Srpaulo 	rth_in = (const struct ip6_rthdr *)in;
364de8db475Srpaulo 
365de8db475Srpaulo 	switch (rth_in->ip6r_type) {
366de8db475Srpaulo 	case IPV6_RTHDR_TYPE_0:
367de8db475Srpaulo 		rth0_in = (const struct ip6_rthdr0 *)in;
368de8db475Srpaulo 		rth0_out = (struct ip6_rthdr0 *)out;
369de8db475Srpaulo 
370de8db475Srpaulo 		/* parameter validation XXX too paranoid? */
371de8db475Srpaulo 		if (rth0_in->ip6r0_len % 2)
372de8db475Srpaulo 			return (-1);
373de8db475Srpaulo 		segments = rth0_in->ip6r0_len / 2;
374de8db475Srpaulo 
375de8db475Srpaulo 		/* we can't use memcpy here, since in and out may overlap */
376de8db475Srpaulo 		memmove((void *)rth0_out, (const void *)rth0_in,
377de8db475Srpaulo 			(unsigned int)(((rth0_in->ip6r0_len) + 1) << 3));
378de8db475Srpaulo 		rth0_out->ip6r0_segleft = segments;
379de8db475Srpaulo 
380de8db475Srpaulo 		/* reverse the addresses */
381de8db475Srpaulo 		for (i = 0; i < segments / 2; i++) {
382de8db475Srpaulo 			struct in6_addr addr_tmp, *addr1, *addr2;
383de8db475Srpaulo 
384de8db475Srpaulo 			addr1 = (struct in6_addr *)(void *)(rth0_out + 1) + i;
385de8db475Srpaulo 			addr2 = (struct in6_addr *)(void *)(rth0_out + 1) +
386de8db475Srpaulo 				(segments - i - 1);
387de8db475Srpaulo 			addr_tmp = *addr1;
388de8db475Srpaulo 			*addr1 = *addr2;
389de8db475Srpaulo 			*addr2 = addr_tmp;
390de8db475Srpaulo 		}
391de8db475Srpaulo 
392de8db475Srpaulo 		break;
393de8db475Srpaulo 	default:
394de8db475Srpaulo 		return (-1);	/* type not supported */
395de8db475Srpaulo 	}
396de8db475Srpaulo 
397de8db475Srpaulo 	return (0);
398de8db475Srpaulo }
399de8db475Srpaulo 
400de8db475Srpaulo int
inet6_rth_segments(const void * bp)401de8db475Srpaulo inet6_rth_segments(const void *bp)
402de8db475Srpaulo {
403de8db475Srpaulo 	const struct ip6_rthdr *rh;
404de8db475Srpaulo 	const struct ip6_rthdr0 *rh0;
405de8db475Srpaulo 	unsigned int addrs;
406de8db475Srpaulo 
407de8db475Srpaulo 	_DIAGASSERT(bp != NULL);
408de8db475Srpaulo 
409de8db475Srpaulo 	rh = (const struct ip6_rthdr *)bp;
410de8db475Srpaulo 
411de8db475Srpaulo 	switch (rh->ip6r_type) {
412de8db475Srpaulo 	case IPV6_RTHDR_TYPE_0:
413de8db475Srpaulo 		rh0 = (const struct ip6_rthdr0 *)bp;
414de8db475Srpaulo 
415de8db475Srpaulo 		/*
416de8db475Srpaulo 		 * Validation for a type-0 routing header.
417de8db475Srpaulo 		 * Is this too strict?
418de8db475Srpaulo 		 */
419de8db475Srpaulo 		if ((rh0->ip6r0_len % 2) != 0 ||
420de8db475Srpaulo 		    (addrs = (rh0->ip6r0_len / 2)) < rh0->ip6r0_segleft)
421de8db475Srpaulo 			return (-1);
422de8db475Srpaulo 
423de8db475Srpaulo 		return (addrs);
424de8db475Srpaulo 	default:
425de8db475Srpaulo 		return (-1);	/* unknown type */
426de8db475Srpaulo 	}
427de8db475Srpaulo }
428de8db475Srpaulo 
429de8db475Srpaulo struct in6_addr *
inet6_rth_getaddr(const void * bp,int idx)430de8db475Srpaulo inet6_rth_getaddr(const void *bp, int idx)
431de8db475Srpaulo {
432de8db475Srpaulo 	const struct ip6_rthdr *rh;
433de8db475Srpaulo 	const struct ip6_rthdr0 *rh0;
434de8db475Srpaulo 	unsigned int addrs;
435de8db475Srpaulo 
436de8db475Srpaulo 	_DIAGASSERT(bp != NULL);
437de8db475Srpaulo 
438de8db475Srpaulo 	rh = (const struct ip6_rthdr *)bp;
439de8db475Srpaulo 
440de8db475Srpaulo 	switch (rh->ip6r_type) {
441de8db475Srpaulo 	case IPV6_RTHDR_TYPE_0:
442de8db475Srpaulo 		 rh0 = (const struct ip6_rthdr0 *)bp;
443de8db475Srpaulo 
444de8db475Srpaulo 		/*
445de8db475Srpaulo 		 * Validation for a type-0 routing header.
446de8db475Srpaulo 		 * Is this too strict?
447de8db475Srpaulo 		 */
448de8db475Srpaulo 		if ((rh0->ip6r0_len % 2) != 0 ||
449de8db475Srpaulo 		    (addrs = (rh0->ip6r0_len / 2)) < rh0->ip6r0_segleft)
450de8db475Srpaulo 			return (NULL);
451de8db475Srpaulo 
452fc2df2f1Slukem 		if (idx < 0 || addrs <= (unsigned int)idx)
453de8db475Srpaulo 			return (NULL);
454de8db475Srpaulo 
455de8db475Srpaulo 		return (((struct in6_addr *)(void *)__UNCONST(rh0 + 1)) + idx);
456de8db475Srpaulo 	default:
457de8db475Srpaulo 		return (NULL);	/* unknown type */
458de8db475Srpaulo 	}
459de8db475Srpaulo }
460