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