2 * Copyright (c) 2018 Apple Inc. All rights reserved.
4 /* $KAME: rthdr.c,v 1.19 2003/06/06 10:48:51 itojun Exp $ */
7 * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project.
10 * Redistribution and use in source and binary forms, with or without
11 * modification, are permitted provided that the following conditions
13 * 1. Redistributions of source code must retain the above copyright
14 * notice, this list of conditions and the following disclaimer.
15 * 2. Redistributions in binary form must reproduce the above copyright
16 * notice, this list of conditions and the following disclaimer in the
17 * documentation and/or other materials provided with the distribution.
18 * 3. Neither the name of the project nor the names of its contributors
19 * may be used to endorse or promote products derived from this software
20 * without specific prior written permission.
22 * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND
23 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
24 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
25 * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE
26 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
27 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
28 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
29 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
30 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
31 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
35 /* __FBSDID("$FreeBSD: src/lib/libc/net/rthdr.c,v 1.9.10.1.4.1 2010/06/14 02:09:06 kensmith Exp $"); */
37 #include "libinfo_common.h"
40 * These routines support RFC 2292.
41 * __APPLE_USE_RFC_2292 selects the appropriate API in <netinet6/in6.h>
43 #define __APPLE_USE_RFC_2292
45 #include <sys/cdefs.h>
47 #include <sys/param.h>
48 #include <sys/types.h>
49 #include <sys/socket.h>
51 #include <netinet/in.h>
52 #include <netinet/ip6.h>
63 inet6_rthdr_space(type
, seg
)
67 case IPV6_RTHDR_TYPE_0
:
68 if (seg
< 1 || seg
> 23)
71 return (CMSG_SPACE(sizeof(struct in6_addr
) * (seg
- 1) +
72 sizeof(struct ip6_rthdr0
)));
74 return (CMSG_SPACE(sizeof(struct in6_addr
) * seg
+
75 sizeof(struct ip6_rthdr0
)));
84 inet6_rthdr_init(bp
, type
)
88 struct cmsghdr
*ch
= (struct cmsghdr
*)bp
;
89 struct ip6_rthdr
*rthdr
;
91 rthdr
= (struct ip6_rthdr
*)CMSG_DATA(ch
);
93 ch
->cmsg_level
= IPPROTO_IPV6
;
94 ch
->cmsg_type
= IPV6_RTHDR
;
97 case IPV6_RTHDR_TYPE_0
:
99 ch
->cmsg_len
= CMSG_LEN(sizeof(struct ip6_rthdr0
) -
100 sizeof(struct in6_addr
));
102 ch
->cmsg_len
= CMSG_LEN(sizeof(struct ip6_rthdr0
));
105 bzero(rthdr
, sizeof(struct ip6_rthdr0
));
106 rthdr
->ip6r_type
= IPV6_RTHDR_TYPE_0
;
116 inet6_rthdr_add(cmsg
, addr
, flags
)
117 struct cmsghdr
*cmsg
;
118 const struct in6_addr
*addr
;
121 struct ip6_rthdr
*rthdr
;
123 rthdr
= (struct ip6_rthdr
*)CMSG_DATA(cmsg
);
125 switch (rthdr
->ip6r_type
) {
126 case IPV6_RTHDR_TYPE_0
:
128 struct ip6_rthdr0
*rt0
= (struct ip6_rthdr0
*)rthdr
;
129 if (flags
!= IPV6_RTHDR_LOOSE
&& flags
!= IPV6_RTHDR_STRICT
)
131 if (rt0
->ip6r0_segleft
== 23)
134 #ifdef COMPAT_RFC1883 /* XXX */
135 if (flags
== IPV6_RTHDR_STRICT
) {
137 c
= rt0
->ip6r0_segleft
/ 8;
138 b
= rt0
->ip6r0_segleft
% 8;
139 rt0
->ip6r0_slmap
[c
] |= (1 << (7 - b
));
142 if (flags
!= IPV6_RTHDR_LOOSE
)
145 rt0
->ip6r0_segleft
++;
146 bcopy(addr
, (caddr_t
)rt0
+ ((rt0
->ip6r0_len
+ 1) << 3),
147 sizeof(struct in6_addr
));
148 rt0
->ip6r0_len
+= sizeof(struct in6_addr
) >> 3;
149 cmsg
->cmsg_len
= CMSG_LEN((rt0
->ip6r0_len
+ 1) << 3);
162 inet6_rthdr_lasthop(cmsg
, flags
)
163 struct cmsghdr
*cmsg
;
166 struct ip6_rthdr
*rthdr
;
168 rthdr
= (struct ip6_rthdr
*)CMSG_DATA(cmsg
);
170 switch (rthdr
->ip6r_type
) {
171 case IPV6_RTHDR_TYPE_0
:
173 struct ip6_rthdr0
*rt0
= (struct ip6_rthdr0
*)rthdr
;
174 #ifdef COMPAT_RFC1883 /* XXX */
175 if (flags
!= IPV6_RTHDR_LOOSE
&& flags
!= IPV6_RTHDR_STRICT
)
177 #endif /* COMPAT_RFC1883 */
178 if (rt0
->ip6r0_segleft
> 23)
180 #ifdef COMPAT_RFC1883 /* XXX */
181 if (flags
== IPV6_RTHDR_STRICT
) {
183 c
= rt0
->ip6r0_segleft
/ 8;
184 b
= rt0
->ip6r0_segleft
% 8;
185 rt0
->ip6r0_slmap
[c
] |= (1 << (7 - b
));
188 if (flags
!= IPV6_RTHDR_LOOSE
)
190 #endif /* COMPAT_RFC1883 */
202 inet6_rthdr_reverse(in
, out
)
203 const struct cmsghdr
*in
;
213 inet6_rthdr_segments(cmsg
)
214 const struct cmsghdr
*cmsg
;
216 struct ip6_rthdr
*rthdr
;
218 rthdr
= (struct ip6_rthdr
*)CMSG_DATA(cmsg
);
220 switch (rthdr
->ip6r_type
) {
221 case IPV6_RTHDR_TYPE_0
:
223 struct ip6_rthdr0
*rt0
= (struct ip6_rthdr0
*)rthdr
;
225 if (rt0
->ip6r0_len
% 2 || 46 < rt0
->ip6r0_len
)
228 return (rt0
->ip6r0_len
* 8) / sizeof(struct in6_addr
);
238 inet6_rthdr_getaddr(cmsg
, idx
)
239 struct cmsghdr
*cmsg
;
242 struct ip6_rthdr
*rthdr
;
244 rthdr
= (struct ip6_rthdr
*)CMSG_DATA(cmsg
);
246 switch (rthdr
->ip6r_type
) {
247 case IPV6_RTHDR_TYPE_0
:
249 struct ip6_rthdr0
*rt0
= (struct ip6_rthdr0
*)rthdr
;
252 if (rt0
->ip6r0_len
% 2 || 46 < rt0
->ip6r0_len
)
254 naddr
= (rt0
->ip6r0_len
* 8) / sizeof(struct in6_addr
);
255 if (idx
<= 0 || naddr
< idx
)
257 #ifdef COMPAT_RFC2292
258 return (((struct in6_addr
*)(rt0
+ 1)) + idx
- 1);
260 return (((struct in6_addr
*)(rt0
+ 1)) + idx
);
271 inet6_rthdr_getflags(cmsg
, idx
)
272 const struct cmsghdr
*cmsg
;
275 struct ip6_rthdr
*rthdr
;
277 rthdr
= (struct ip6_rthdr
*)CMSG_DATA(cmsg
);
279 switch (rthdr
->ip6r_type
) {
280 case IPV6_RTHDR_TYPE_0
:
282 struct ip6_rthdr0
*rt0
= (struct ip6_rthdr0
*)rthdr
;
285 if (rt0
->ip6r0_len
% 2 || 46 < rt0
->ip6r0_len
)
287 naddr
= (rt0
->ip6r0_len
* 8) / sizeof(struct in6_addr
);
288 if (idx
< 0 || naddr
< idx
)
290 #ifdef COMPAT_RFC1883 /* XXX */
291 if (rt0
->ip6r0_slmap
[idx
/ 8] & (0x80 >> (idx
% 8)))
292 return IPV6_RTHDR_STRICT
;
294 return IPV6_RTHDR_LOOSE
;
296 return IPV6_RTHDR_LOOSE
;
297 #endif /* COMPAT_RFC1883 */
311 inet6_rth_space(int type
, int segments
)
314 case IPV6_RTHDR_TYPE_0
:
315 if ((segments
>= 0) && (segments
<= 127))
316 return (((segments
* 2) + 1) << 3);
319 return (0); /* type not suppported */
325 inet6_rth_init(void *bp
, socklen_t bp_len
, int type
, int segments
)
327 struct ip6_rthdr
*rth
= (struct ip6_rthdr
*)bp
;
328 struct ip6_rthdr0
*rth0
;
331 case IPV6_RTHDR_TYPE_0
:
332 /* length validation */
333 if (bp_len
< inet6_rth_space(IPV6_RTHDR_TYPE_0
, segments
))
335 /* segment validation */
336 if ((segments
< 0) || (segments
> 127))
339 memset(bp
, 0, bp_len
);
340 rth0
= (struct ip6_rthdr0
*)rth
;
341 rth0
->ip6r0_len
= segments
* 2;
342 rth0
->ip6r0_type
= IPV6_RTHDR_TYPE_0
;
343 rth0
->ip6r0_segleft
= 0;
344 rth0
->ip6r0_reserved
= 0;
347 return (NULL
); /* type not supported */
355 inet6_rth_add(void *bp
, const struct in6_addr
*addr
)
357 struct ip6_rthdr
*rth
= (struct ip6_rthdr
*)bp
;
358 struct ip6_rthdr0
*rth0
;
359 struct in6_addr
*nextaddr
;
361 switch (rth
->ip6r_type
) {
362 case IPV6_RTHDR_TYPE_0
:
363 rth0
= (struct ip6_rthdr0
*)rth
;
364 /* Don't exceed the number of stated segments */
365 if (rth0
->ip6r0_segleft
== (rth0
->ip6r0_len
/ 2))
367 nextaddr
= (struct in6_addr
*)(rth0
+ 1) + rth0
->ip6r0_segleft
;
369 rth0
->ip6r0_segleft
++;
372 return (-1); /* type not supported */
380 inet6_rth_reverse(const void *in
, void *out
)
382 struct ip6_rthdr
*rth_in
= (struct ip6_rthdr
*)in
;
383 struct ip6_rthdr0
*rth0_in
, *rth0_out
;
386 switch (rth_in
->ip6r_type
) {
387 case IPV6_RTHDR_TYPE_0
:
388 rth0_in
= (struct ip6_rthdr0
*)in
;
389 rth0_out
= (struct ip6_rthdr0
*)out
;
391 /* parameter validation XXX too paranoid? */
392 if (rth0_in
->ip6r0_len
% 2)
394 segments
= rth0_in
->ip6r0_len
/ 2;
396 /* we can't use memcpy here, since in and out may overlap */
397 memmove((void *)rth0_out
, (void *)rth0_in
,
398 ((rth0_in
->ip6r0_len
) + 1) << 3);
399 rth0_out
->ip6r0_segleft
= segments
;
401 /* reverse the addresses */
402 for (i
= 0; i
< segments
/ 2; i
++) {
403 struct in6_addr addr_tmp
, *addr1
, *addr2
;
405 addr1
= (struct in6_addr
*)(rth0_out
+ 1) + i
;
406 addr2
= (struct in6_addr
*)(rth0_out
+ 1) +
415 return (-1); /* type not supported */
423 inet6_rth_segments(const void *bp
)
425 struct ip6_rthdr
*rh
= (struct ip6_rthdr
*)bp
;
426 struct ip6_rthdr0
*rh0
;
429 switch (rh
->ip6r_type
) {
430 case IPV6_RTHDR_TYPE_0
:
431 rh0
= (struct ip6_rthdr0
*)bp
;
434 * Validation for a type-0 routing header.
435 * Is this too strict?
437 if ((rh0
->ip6r0_len
% 2) != 0 ||
438 (addrs
= (rh0
->ip6r0_len
>> 1)) < rh0
->ip6r0_segleft
)
443 return (-1); /* unknown type */
449 inet6_rth_getaddr(const void *bp
, int idx
)
451 struct ip6_rthdr
*rh
= (struct ip6_rthdr
*)bp
;
452 struct ip6_rthdr0
*rh0
;
455 switch (rh
->ip6r_type
) {
456 case IPV6_RTHDR_TYPE_0
:
457 rh0
= (struct ip6_rthdr0
*)bp
;
460 * Validation for a type-0 routing header.
461 * Is this too strict?
463 if ((rh0
->ip6r0_len
% 2) != 0 ||
464 (addrs
= (rh0
->ip6r0_len
>> 1)) < rh0
->ip6r0_segleft
)
467 if (idx
< 0 || addrs
<= idx
)
470 return (((struct in6_addr
*)(rh0
+ 1)) + idx
);
472 return (NULL
); /* unknown type */