1 /* $NetBSD: rthdr.c,v 1.17 2009/02/05 23:22:39 lukem 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.17 2009/02/05 23:22:39 lukem 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(type
, seg
)
74 case IPV6_RTHDR_TYPE_0
:
75 if (seg
< 1 || seg
> 23)
77 return (CMSG_SPACE(sizeof(struct in6_addr
) * seg
+
78 sizeof(struct ip6_rthdr0
)));
85 inet6_rthdr_init(bp
, type
)
90 struct ip6_rthdr
*rthdr
;
92 _DIAGASSERT(bp
!= NULL
);
94 ch
= (struct cmsghdr
*)bp
;
95 rthdr
= (struct ip6_rthdr
*)(void *)CMSG_DATA(ch
);
97 ch
->cmsg_level
= IPPROTO_IPV6
;
98 ch
->cmsg_type
= IPV6_RTHDR
;
101 case IPV6_RTHDR_TYPE_0
:
102 #ifdef COMPAT_RFC2292
103 ch
->cmsg_len
= CMSG_LEN(sizeof(struct ip6_rthdr0
) -
104 sizeof(struct in6_addr
));
106 ch
->cmsg_len
= CMSG_LEN(sizeof(struct ip6_rthdr0
));
108 (void)memset(rthdr
, 0, sizeof(struct ip6_rthdr0
));
109 rthdr
->ip6r_type
= IPV6_RTHDR_TYPE_0
;
117 inet6_rthdr_add(cmsg
, addr
, flags
)
118 struct cmsghdr
*cmsg
;
119 const struct in6_addr
*addr
;
122 struct ip6_rthdr
*rthdr
;
124 _DIAGASSERT(cmsg
!= NULL
);
125 _DIAGASSERT(addr
!= NULL
);
127 rthdr
= (struct ip6_rthdr
*)(void *)CMSG_DATA(cmsg
);
129 switch (rthdr
->ip6r_type
) {
130 case IPV6_RTHDR_TYPE_0
:
132 struct ip6_rthdr0
*rt0
= (struct ip6_rthdr0
*)(void *)rthdr
;
133 if (flags
!= IPV6_RTHDR_LOOSE
&& flags
!= IPV6_RTHDR_STRICT
)
135 if (rt0
->ip6r0_segleft
== 23)
137 if (flags
!= IPV6_RTHDR_LOOSE
)
139 rt0
->ip6r0_segleft
++;
140 (void)memcpy(((caddr_t
)(void *)rt0
) +
141 ((rt0
->ip6r0_len
+ 1) << 3), addr
, sizeof(struct in6_addr
));
142 rt0
->ip6r0_len
+= sizeof(struct in6_addr
) >> 3;
143 cmsg
->cmsg_len
= CMSG_LEN((rt0
->ip6r0_len
+ 1) << 3);
154 inet6_rthdr_lasthop(cmsg
, flags
)
155 struct cmsghdr
*cmsg
;
158 struct ip6_rthdr
*rthdr
;
160 _DIAGASSERT(cmsg
!= NULL
);
162 rthdr
= (struct ip6_rthdr
*)(void *)CMSG_DATA(cmsg
);
164 switch (rthdr
->ip6r_type
) {
165 case IPV6_RTHDR_TYPE_0
:
167 struct ip6_rthdr0
*rt0
= (struct ip6_rthdr0
*)(void *)rthdr
;
168 if (rt0
->ip6r0_segleft
> 23)
170 if (flags
!= IPV6_RTHDR_LOOSE
)
183 inet6_rthdr_reverse(in
, out
)
184 const struct cmsghdr
*in
;
193 inet6_rthdr_segments(cmsg
)
194 const struct cmsghdr
*cmsg
;
196 const struct ip6_rthdr
*rthdr
;
198 _DIAGASSERT(cmsg
!= NULL
);
200 rthdr
= __UNCONST(CCMSG_DATA(cmsg
));
202 switch (rthdr
->ip6r_type
) {
203 case IPV6_RTHDR_TYPE_0
:
205 const struct ip6_rthdr0
*rt0
=
206 (const struct ip6_rthdr0
*)(const void *)rthdr
;
208 if (rt0
->ip6r0_len
% 2 || 46 < rt0
->ip6r0_len
)
211 return (rt0
->ip6r0_len
* 8) / sizeof(struct in6_addr
);
220 inet6_rthdr_getaddr(cmsg
, idx
)
221 struct cmsghdr
*cmsg
;
224 struct ip6_rthdr
*rthdr
;
226 _DIAGASSERT(cmsg
!= NULL
);
228 rthdr
= (struct ip6_rthdr
*)(void *)CMSG_DATA(cmsg
);
230 switch (rthdr
->ip6r_type
) {
231 case IPV6_RTHDR_TYPE_0
:
233 struct ip6_rthdr0
*rt0
= (struct ip6_rthdr0
*)(void *)rthdr
;
236 if (rt0
->ip6r0_len
% 2 || 46 < rt0
->ip6r0_len
)
238 naddr
= (rt0
->ip6r0_len
* 8) / sizeof(struct in6_addr
);
239 if (idx
<= 0 || naddr
< idx
)
241 #ifdef COMPAT_RFC2292
242 return ((struct in6_addr
*)(void *)(rt0
+ 1)) + idx
- 1;
244 return ((struct in6_addr
*)(void *)(rt0
+ 1)) + idx
;
254 inet6_rthdr_getflags(cmsg
, idx
)
255 const struct cmsghdr
*cmsg
;
258 const struct ip6_rthdr
*rthdr
;
260 _DIAGASSERT(cmsg
!= NULL
);
262 rthdr
= __UNCONST(CCMSG_DATA(cmsg
));
264 switch (rthdr
->ip6r_type
) {
265 case IPV6_RTHDR_TYPE_0
:
267 const struct ip6_rthdr0
*rt0
= (const struct ip6_rthdr0
*)
271 if (rt0
->ip6r0_len
% 2 || 46 < rt0
->ip6r0_len
)
273 naddr
= (rt0
->ip6r0_len
* 8) / sizeof(struct in6_addr
);
274 if (idx
< 0 || naddr
< idx
)
276 return IPV6_RTHDR_LOOSE
;
285 * RFC3542 (2292bis) API
289 inet6_rth_space(int type
, int segments
)
292 case IPV6_RTHDR_TYPE_0
:
293 return (((segments
* 2) + 1) << 3);
295 return (0); /* type not suppported */
300 inet6_rth_init(void *bp
, socklen_t bp_len
, int type
, int segments
)
302 struct ip6_rthdr
*rth
;
303 struct ip6_rthdr0
*rth0
;
305 _DIAGASSERT(bp
!= NULL
);
307 rth
= (struct ip6_rthdr
*)bp
;
310 case IPV6_RTHDR_TYPE_0
:
311 /* length validation */
312 if (bp_len
< inet6_rth_space(IPV6_RTHDR_TYPE_0
, segments
))
315 memset(bp
, 0, bp_len
);
316 rth0
= (struct ip6_rthdr0
*)(void *)rth
;
317 rth0
->ip6r0_len
= segments
* 2;
318 rth0
->ip6r0_type
= IPV6_RTHDR_TYPE_0
;
319 rth0
->ip6r0_segleft
= 0;
320 rth0
->ip6r0_reserved
= 0;
323 return (NULL
); /* type not supported */
330 inet6_rth_add(void *bp
, const struct in6_addr
*addr
)
332 struct ip6_rthdr
*rth
;
333 struct ip6_rthdr0
*rth0
;
334 struct in6_addr
*nextaddr
;
336 _DIAGASSERT(bp
!= NULL
);
338 rth
= (struct ip6_rthdr
*)bp
;
340 switch (rth
->ip6r_type
) {
341 case IPV6_RTHDR_TYPE_0
:
342 rth0
= (struct ip6_rthdr0
*)(void *)rth
;
343 nextaddr
= (struct in6_addr
*)(void *)(rth0
+ 1)
344 + rth0
->ip6r0_segleft
;
346 rth0
->ip6r0_segleft
++;
349 return (-1); /* type not supported */
356 inet6_rth_reverse(const void *in
, void *out
)
358 const struct ip6_rthdr
*rth_in
;
359 const struct ip6_rthdr0
*rth0_in
;
360 struct ip6_rthdr0
*rth0_out
;
363 _DIAGASSERT(in
!= NULL
);
364 _DIAGASSERT(out
!= NULL
);
366 rth_in
= (const struct ip6_rthdr
*)in
;
368 switch (rth_in
->ip6r_type
) {
369 case IPV6_RTHDR_TYPE_0
:
370 rth0_in
= (const struct ip6_rthdr0
*)in
;
371 rth0_out
= (struct ip6_rthdr0
*)out
;
373 /* parameter validation XXX too paranoid? */
374 if (rth0_in
->ip6r0_len
% 2)
376 segments
= rth0_in
->ip6r0_len
/ 2;
378 /* we can't use memcpy here, since in and out may overlap */
379 memmove((void *)rth0_out
, (const void *)rth0_in
,
380 (unsigned int)(((rth0_in
->ip6r0_len
) + 1) << 3));
381 rth0_out
->ip6r0_segleft
= segments
;
383 /* reverse the addresses */
384 for (i
= 0; i
< segments
/ 2; i
++) {
385 struct in6_addr addr_tmp
, *addr1
, *addr2
;
387 addr1
= (struct in6_addr
*)(void *)(rth0_out
+ 1) + i
;
388 addr2
= (struct in6_addr
*)(void *)(rth0_out
+ 1) +
397 return (-1); /* type not supported */
404 inet6_rth_segments(const void *bp
)
406 const struct ip6_rthdr
*rh
;
407 const struct ip6_rthdr0
*rh0
;
410 _DIAGASSERT(bp
!= NULL
);
412 rh
= (const struct ip6_rthdr
*)bp
;
414 switch (rh
->ip6r_type
) {
415 case IPV6_RTHDR_TYPE_0
:
416 rh0
= (const struct ip6_rthdr0
*)bp
;
419 * Validation for a type-0 routing header.
420 * Is this too strict?
422 if ((rh0
->ip6r0_len
% 2) != 0 ||
423 (addrs
= (rh0
->ip6r0_len
/ 2)) < rh0
->ip6r0_segleft
)
428 return (-1); /* unknown type */
433 inet6_rth_getaddr(const void *bp
, int idx
)
435 const struct ip6_rthdr
*rh
;
436 const struct ip6_rthdr0
*rh0
;
439 _DIAGASSERT(bp
!= NULL
);
441 rh
= (const struct ip6_rthdr
*)bp
;
443 switch (rh
->ip6r_type
) {
444 case IPV6_RTHDR_TYPE_0
:
445 rh0
= (const struct ip6_rthdr0
*)bp
;
448 * Validation for a type-0 routing header.
449 * Is this too strict?
451 if ((rh0
->ip6r0_len
% 2) != 0 ||
452 (addrs
= (rh0
->ip6r0_len
/ 2)) < rh0
->ip6r0_segleft
)
455 if (idx
< 0 || addrs
<= (unsigned int)idx
)
458 return (((struct in6_addr
*)(void *)__UNCONST(rh0
+ 1)) + idx
);
460 return (NULL
); /* unknown type */