1 /* $NetBSD: rthdr.c,v 1.18 2012/03/13 21:13:42 christos Exp $ */
4 * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project.
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
10 * 1. Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
12 * 2. Redistributions in binary form must reproduce the above copyright
13 * notice, this list of conditions and the following disclaimer in the
14 * documentation and/or other materials provided with the distribution.
15 * 3. Neither the name of the project nor the names of its contributors
16 * may be used to endorse or promote products derived from this software
17 * without specific prior written permission.
19 * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND
20 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
22 * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE
23 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
24 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
25 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
26 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
27 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
28 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
32 #include <sys/cdefs.h>
33 #if defined(LIBC_SCCS) && !defined(lint)
34 __RCSID("$NetBSD: rthdr.c,v 1.18 2012/03/13 21:13:42 christos Exp $");
35 #endif /* LIBC_SCCS and not lint */
37 #include "namespace.h"
38 #include <sys/param.h>
39 #include <sys/types.h>
40 #include <sys/socket.h>
42 #include <netinet/in.h>
43 #include <netinet/ip6.h>
50 __weak_alias(inet6_rthdr_add
,_inet6_rthdr_add
)
51 __weak_alias(inet6_rthdr_getaddr
,_inet6_rthdr_getaddr
)
52 __weak_alias(inet6_rthdr_getflags
,_inet6_rthdr_getflags
)
53 __weak_alias(inet6_rthdr_init
,_inet6_rthdr_init
)
54 __weak_alias(inet6_rthdr_lasthop
,_inet6_rthdr_lasthop
)
55 __weak_alias(inet6_rthdr_segments
,_inet6_rthdr_segments
)
56 __weak_alias(inet6_rthdr_space
,_inet6_rthdr_space
)
57 __weak_alias(inet6_rth_space
, _inet6_rth_space
)
58 __weak_alias(inet6_rth_init
, _inet6_rth_init
)
59 __weak_alias(inet6_rth_add
, _inet6_rth_add
)
60 __weak_alias(inet6_rth_reverse
, _inet6_rth_reverse
)
61 __weak_alias(inet6_rth_segments
, _inet6_rth_segments
)
62 __weak_alias(inet6_rth_getaddr
, _inet6_rth_getaddr
)
70 inet6_rthdr_space(int type
, int seg
)
73 case IPV6_RTHDR_TYPE_0
:
74 if (seg
< 1 || seg
> 23)
76 return (CMSG_SPACE(sizeof(struct in6_addr
) * seg
+
77 sizeof(struct ip6_rthdr0
)));
84 inet6_rthdr_init(void *bp
, int type
)
87 struct ip6_rthdr
*rthdr
;
89 _DIAGASSERT(bp
!= NULL
);
91 ch
= (struct cmsghdr
*)bp
;
92 rthdr
= (struct ip6_rthdr
*)(void *)CMSG_DATA(ch
);
94 ch
->cmsg_level
= IPPROTO_IPV6
;
95 ch
->cmsg_type
= IPV6_RTHDR
;
98 case IPV6_RTHDR_TYPE_0
:
100 ch
->cmsg_len
= CMSG_LEN(sizeof(struct ip6_rthdr0
) -
101 sizeof(struct in6_addr
));
103 ch
->cmsg_len
= CMSG_LEN(sizeof(struct ip6_rthdr0
));
105 (void)memset(rthdr
, 0, sizeof(struct ip6_rthdr0
));
106 rthdr
->ip6r_type
= IPV6_RTHDR_TYPE_0
;
114 inet6_rthdr_add(struct cmsghdr
*cmsg
, const struct in6_addr
*addr
, u_int flags
)
116 struct ip6_rthdr
*rthdr
;
118 _DIAGASSERT(cmsg
!= NULL
);
119 _DIAGASSERT(addr
!= NULL
);
121 rthdr
= (struct ip6_rthdr
*)(void *)CMSG_DATA(cmsg
);
123 switch (rthdr
->ip6r_type
) {
124 case IPV6_RTHDR_TYPE_0
:
127 struct ip6_rthdr0
*rt0
= (struct ip6_rthdr0
*)(void *)rthdr
;
128 if (flags
!= IPV6_RTHDR_LOOSE
&& flags
!= IPV6_RTHDR_STRICT
)
130 if (rt0
->ip6r0_segleft
== 23)
132 if (flags
!= IPV6_RTHDR_LOOSE
)
134 rt0
->ip6r0_segleft
++;
135 (void)memcpy(((caddr_t
)(void *)rt0
) +
136 ((rt0
->ip6r0_len
+ 1) << 3), addr
, sizeof(struct in6_addr
));
137 rt0
->ip6r0_len
+= sizeof(struct in6_addr
) >> 3;
138 len
= CMSG_LEN((rt0
->ip6r0_len
+ 1) << 3);
139 _DIAGASSERT(__type_fit(socklen_t
, len
));
140 cmsg
->cmsg_len
= (socklen_t
)len
;
151 inet6_rthdr_lasthop(struct cmsghdr
*cmsg
, unsigned int flags
)
153 struct ip6_rthdr
*rthdr
;
155 _DIAGASSERT(cmsg
!= NULL
);
157 rthdr
= (struct ip6_rthdr
*)(void *)CMSG_DATA(cmsg
);
159 switch (rthdr
->ip6r_type
) {
160 case IPV6_RTHDR_TYPE_0
:
162 struct ip6_rthdr0
*rt0
= (struct ip6_rthdr0
*)(void *)rthdr
;
163 if (rt0
->ip6r0_segleft
> 23)
165 if (flags
!= IPV6_RTHDR_LOOSE
)
178 inet6_rthdr_reverse(const struct cmsghdr
*in
, struct cmsghdr
*out
)
186 inet6_rthdr_segments(const struct cmsghdr
*cmsg
)
188 const struct ip6_rthdr
*rthdr
;
190 _DIAGASSERT(cmsg
!= NULL
);
192 rthdr
= __UNCONST(CCMSG_DATA(cmsg
));
194 switch (rthdr
->ip6r_type
) {
195 case IPV6_RTHDR_TYPE_0
:
197 const struct ip6_rthdr0
*rt0
=
198 (const struct ip6_rthdr0
*)(const void *)rthdr
;
201 if (rt0
->ip6r0_len
% 2 || 46 < rt0
->ip6r0_len
)
204 len
= (rt0
->ip6r0_len
* 8) / sizeof(struct in6_addr
);
205 _DIAGASSERT(__type_fit(int, len
));
215 inet6_rthdr_getaddr(struct cmsghdr
*cmsg
, int idx
)
217 struct ip6_rthdr
*rthdr
;
219 _DIAGASSERT(cmsg
!= NULL
);
221 rthdr
= (struct ip6_rthdr
*)(void *)CMSG_DATA(cmsg
);
223 switch (rthdr
->ip6r_type
) {
224 case IPV6_RTHDR_TYPE_0
:
226 struct ip6_rthdr0
*rt0
= (struct ip6_rthdr0
*)(void *)rthdr
;
230 if (rt0
->ip6r0_len
% 2 || 46 < rt0
->ip6r0_len
)
232 len
= (rt0
->ip6r0_len
* 8) / sizeof(struct in6_addr
);
233 _DIAGASSERT(__type_fit(int, len
));
235 if (idx
<= 0 || naddr
< idx
)
237 #ifdef COMPAT_RFC2292
238 return ((struct in6_addr
*)(void *)(rt0
+ 1)) + idx
- 1;
240 return ((struct in6_addr
*)(void *)(rt0
+ 1)) + idx
;
250 inet6_rthdr_getflags(const struct cmsghdr
*cmsg
, int idx
)
252 const struct ip6_rthdr
*rthdr
;
254 _DIAGASSERT(cmsg
!= NULL
);
256 rthdr
= __UNCONST(CCMSG_DATA(cmsg
));
258 switch (rthdr
->ip6r_type
) {
259 case IPV6_RTHDR_TYPE_0
:
261 const struct ip6_rthdr0
*rt0
= (const struct ip6_rthdr0
*)
266 if (rt0
->ip6r0_len
% 2 || 46 < rt0
->ip6r0_len
)
268 len
= (rt0
->ip6r0_len
* 8) / sizeof(struct in6_addr
);
269 _DIAGASSERT(__type_fit(int, len
));
271 if (idx
< 0 || naddr
< idx
)
273 return IPV6_RTHDR_LOOSE
;
282 * RFC3542 (2292bis) API
286 inet6_rth_space(int type
, int segments
)
289 case IPV6_RTHDR_TYPE_0
:
290 return (((segments
* 2) + 1) << 3);
292 return (0); /* type not suppported */
297 inet6_rth_init(void *bp
, socklen_t bp_len
, int type
, int segments
)
299 struct ip6_rthdr
*rth
;
300 struct ip6_rthdr0
*rth0
;
302 _DIAGASSERT(bp
!= NULL
);
304 rth
= (struct ip6_rthdr
*)bp
;
307 case IPV6_RTHDR_TYPE_0
:
308 /* length validation */
309 if (bp_len
< inet6_rth_space(IPV6_RTHDR_TYPE_0
, segments
))
312 memset(bp
, 0, bp_len
);
313 rth0
= (struct ip6_rthdr0
*)(void *)rth
;
314 rth0
->ip6r0_len
= segments
* 2;
315 rth0
->ip6r0_type
= IPV6_RTHDR_TYPE_0
;
316 rth0
->ip6r0_segleft
= 0;
317 rth0
->ip6r0_reserved
= 0;
320 return (NULL
); /* type not supported */
327 inet6_rth_add(void *bp
, const struct in6_addr
*addr
)
329 struct ip6_rthdr
*rth
;
330 struct ip6_rthdr0
*rth0
;
331 struct in6_addr
*nextaddr
;
333 _DIAGASSERT(bp
!= NULL
);
335 rth
= (struct ip6_rthdr
*)bp
;
337 switch (rth
->ip6r_type
) {
338 case IPV6_RTHDR_TYPE_0
:
339 rth0
= (struct ip6_rthdr0
*)(void *)rth
;
340 nextaddr
= (struct in6_addr
*)(void *)(rth0
+ 1)
341 + rth0
->ip6r0_segleft
;
343 rth0
->ip6r0_segleft
++;
346 return (-1); /* type not supported */
353 inet6_rth_reverse(const void *in
, void *out
)
355 const struct ip6_rthdr
*rth_in
;
356 const struct ip6_rthdr0
*rth0_in
;
357 struct ip6_rthdr0
*rth0_out
;
360 _DIAGASSERT(in
!= NULL
);
361 _DIAGASSERT(out
!= NULL
);
363 rth_in
= (const struct ip6_rthdr
*)in
;
365 switch (rth_in
->ip6r_type
) {
366 case IPV6_RTHDR_TYPE_0
:
367 rth0_in
= (const struct ip6_rthdr0
*)in
;
368 rth0_out
= (struct ip6_rthdr0
*)out
;
370 /* parameter validation XXX too paranoid? */
371 if (rth0_in
->ip6r0_len
% 2)
373 segments
= rth0_in
->ip6r0_len
/ 2;
375 /* we can't use memcpy here, since in and out may overlap */
376 memmove((void *)rth0_out
, (const void *)rth0_in
,
377 (unsigned int)(((rth0_in
->ip6r0_len
) + 1) << 3));
378 rth0_out
->ip6r0_segleft
= segments
;
380 /* reverse the addresses */
381 for (i
= 0; i
< segments
/ 2; i
++) {
382 struct in6_addr addr_tmp
, *addr1
, *addr2
;
384 addr1
= (struct in6_addr
*)(void *)(rth0_out
+ 1) + i
;
385 addr2
= (struct in6_addr
*)(void *)(rth0_out
+ 1) +
394 return (-1); /* type not supported */
401 inet6_rth_segments(const void *bp
)
403 const struct ip6_rthdr
*rh
;
404 const struct ip6_rthdr0
*rh0
;
407 _DIAGASSERT(bp
!= NULL
);
409 rh
= (const struct ip6_rthdr
*)bp
;
411 switch (rh
->ip6r_type
) {
412 case IPV6_RTHDR_TYPE_0
:
413 rh0
= (const struct ip6_rthdr0
*)bp
;
416 * Validation for a type-0 routing header.
417 * Is this too strict?
419 if ((rh0
->ip6r0_len
% 2) != 0 ||
420 (addrs
= (rh0
->ip6r0_len
/ 2)) < rh0
->ip6r0_segleft
)
425 return (-1); /* unknown type */
430 inet6_rth_getaddr(const void *bp
, int idx
)
432 const struct ip6_rthdr
*rh
;
433 const struct ip6_rthdr0
*rh0
;
436 _DIAGASSERT(bp
!= NULL
);
438 rh
= (const struct ip6_rthdr
*)bp
;
440 switch (rh
->ip6r_type
) {
441 case IPV6_RTHDR_TYPE_0
:
442 rh0
= (const struct ip6_rthdr0
*)bp
;
445 * Validation for a type-0 routing header.
446 * Is this too strict?
448 if ((rh0
->ip6r0_len
% 2) != 0 ||
449 (addrs
= (rh0
->ip6r0_len
/ 2)) < rh0
->ip6r0_segleft
)
452 if (idx
< 0 || addrs
<= (unsigned int)idx
)
455 return (((struct in6_addr
*)(void *)__UNCONST(rh0
+ 1)) + idx
);
457 return (NULL
); /* unknown type */