rthdr.c revision 1.10.2.1 1 /* $NetBSD: rthdr.c,v 1.10.2.1 2001/11/14 19:32:00 nathanw Exp $ */
2
3 /*
4 * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project.
5 * All rights reserved.
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
9 * are met:
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.
18 *
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
29 * SUCH DAMAGE.
30 */
31
32 #include <sys/cdefs.h>
33 #if defined(LIBC_SCCS) && !defined(lint)
34 __RCSID("$NetBSD: rthdr.c,v 1.10.2.1 2001/11/14 19:32:00 nathanw Exp $");
35 #endif /* LIBC_SCCS and not lint */
36
37 #include "namespace.h"
38 #include <sys/param.h>
39 #include <sys/types.h>
40 #include <sys/socket.h>
41
42 #include <netinet/in.h>
43 #include <netinet/ip6.h>
44
45 #include <assert.h>
46 #include <string.h>
47 #include <stdio.h>
48
49 #ifdef __weak_alias
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 #endif
58
59 size_t
60 inet6_rthdr_space(type, seg)
61 int type, seg;
62 {
63 switch(type) {
64 case IPV6_RTHDR_TYPE_0:
65 if (seg < 1 || seg > 23)
66 return(0);
67 return(CMSG_SPACE(sizeof(struct in6_addr) * (seg - 1)
68 + sizeof(struct ip6_rthdr0)));
69 default:
70 #ifdef DEBUG
71 fprintf(stderr, "inet6_rthdr_space: unknown type(%d)\n", type);
72 #endif
73 return(0);
74 }
75 }
76
77 struct cmsghdr *
78 inet6_rthdr_init(bp, type)
79 void *bp;
80 int type;
81 {
82 struct cmsghdr *ch;
83 struct ip6_rthdr *rthdr;
84
85 _DIAGASSERT(bp != NULL);
86
87 ch = (struct cmsghdr *)bp;
88 rthdr = (struct ip6_rthdr *)(void *)CMSG_DATA(ch);
89
90 ch->cmsg_level = IPPROTO_IPV6;
91 ch->cmsg_type = IPV6_RTHDR;
92
93 switch(type) {
94 case IPV6_RTHDR_TYPE_0:
95 ch->cmsg_len = CMSG_LEN(sizeof(struct ip6_rthdr0) - sizeof(struct in6_addr));
96 (void)memset(rthdr, 0, sizeof(struct ip6_rthdr0));
97 rthdr->ip6r_type = IPV6_RTHDR_TYPE_0;
98 return(ch);
99 default:
100 #ifdef DEBUG
101 fprintf(stderr, "inet6_rthdr_init: unknown type(%d)\n", type);
102 #endif
103 return(NULL);
104 }
105 }
106
107 int
108 inet6_rthdr_add(cmsg, addr, flags)
109 struct cmsghdr *cmsg;
110 const struct in6_addr *addr;
111 u_int flags;
112 {
113 struct ip6_rthdr *rthdr;
114
115 _DIAGASSERT(cmsg != NULL);
116 _DIAGASSERT(addr != NULL);
117
118 rthdr = (struct ip6_rthdr *)(void *)CMSG_DATA(cmsg);
119
120 switch(rthdr->ip6r_type) {
121 case IPV6_RTHDR_TYPE_0:
122 {
123 struct ip6_rthdr0 *rt0 = (struct ip6_rthdr0 *)(void *)rthdr;
124 if (flags != IPV6_RTHDR_LOOSE && flags != IPV6_RTHDR_STRICT) {
125 #ifdef DEBUG
126 fprintf(stderr, "inet6_rthdr_add: unsupported flag(%d)\n", flags);
127 #endif
128 return(-1);
129 }
130 if (rt0->ip6r0_segleft == 23) {
131 #ifdef DEBUG
132 fprintf(stderr, "inet6_rthdr_add: segment overflow\n");
133 #endif
134 return(-1);
135 }
136 if (flags == IPV6_RTHDR_STRICT) {
137 int c, b;
138 c = rt0->ip6r0_segleft / 8;
139 b = rt0->ip6r0_segleft % 8;
140 rt0->ip6r0_slmap[c] |= (1 << (7 - b));
141 }
142 rt0->ip6r0_segleft++;
143 (void)memcpy(((caddr_t)(void *)rt0) + ((rt0->ip6r0_len + 1) << 3), addr,
144 sizeof(struct in6_addr));
145 rt0->ip6r0_len += sizeof(struct in6_addr) >> 3;
146 cmsg->cmsg_len = CMSG_LEN((rt0->ip6r0_len + 1) << 3);
147 break;
148 }
149 default:
150 #ifdef DEBUG
151 fprintf(stderr, "inet6_rthdr_add: unknown type(%d)\n",
152 rthdr->ip6r_type);
153 #endif
154 return(-1);
155 }
156
157 return(0);
158 }
159
160 int
161 inet6_rthdr_lasthop(cmsg, flags)
162 struct cmsghdr *cmsg;
163 unsigned int flags;
164 {
165 struct ip6_rthdr *rthdr;
166
167 _DIAGASSERT(cmsg != NULL);
168
169 rthdr = (struct ip6_rthdr *)(void *)CMSG_DATA(cmsg);
170
171 switch(rthdr->ip6r_type) {
172 case IPV6_RTHDR_TYPE_0:
173 {
174 struct ip6_rthdr0 *rt0 = (struct ip6_rthdr0 *)(void *)rthdr;
175 if (flags != IPV6_RTHDR_LOOSE && flags != IPV6_RTHDR_STRICT) {
176 #ifdef DEBUG
177 fprintf(stderr, "inet6_rthdr_lasthop: unsupported flag(%d)\n", flags);
178 #endif
179 return(-1);
180 }
181 if (rt0->ip6r0_segleft > 23) {
182 #ifdef DEBUG
183 fprintf(stderr, "inet6_rthdr_add: segment overflow\n");
184 #endif
185 return(-1);
186 }
187 if (flags == IPV6_RTHDR_STRICT) {
188 int c, b;
189 c = rt0->ip6r0_segleft / 8;
190 b = rt0->ip6r0_segleft % 8;
191 rt0->ip6r0_slmap[c] |= (1 << (7 - b));
192 }
193 break;
194 }
195 default:
196 #ifdef DEBUG
197 fprintf(stderr, "inet6_rthdr_lasthop: unknown type(%d)\n",
198 rthdr->ip6r_type);
199 #endif
200 return(-1);
201 }
202
203 return(0);
204 }
205
206 #if 0
207 int
208 inet6_rthdr_reverse(in, out)
209 const struct cmsghdr *in;
210 struct cmsghdr *out;
211 {
212 #ifdef DEBUG
213 fprintf(stderr, "inet6_rthdr_reverse: not implemented yet\n");
214 #endif
215 return -1;
216 }
217 #endif
218
219 int
220 inet6_rthdr_segments(cmsg)
221 const struct cmsghdr *cmsg;
222 {
223 const struct ip6_rthdr *rthdr;
224
225 _DIAGASSERT(cmsg != NULL);
226
227 /*LINTED const castaway*/
228 rthdr = (const struct ip6_rthdr *)(const void *)CMSG_DATA(cmsg);
229
230 switch(rthdr->ip6r_type) {
231 case IPV6_RTHDR_TYPE_0:
232 {
233 const struct ip6_rthdr0 *rt0 =
234 (const struct ip6_rthdr0 *)(const void *)rthdr;
235
236 if (rt0->ip6r0_len % 2 || 46 < rt0->ip6r0_len) {
237 #ifdef DEBUG
238 fprintf(stderr, "inet6_rthdr_segments: invalid size(%d)\n",
239 rt0->ip6r0_len);
240 #endif
241 return -1;
242 }
243
244 return (rt0->ip6r0_len * 8) / sizeof(struct in6_addr);
245 }
246
247 default:
248 #ifdef DEBUG
249 fprintf(stderr, "inet6_rthdr_segments: unknown type(%d)\n",
250 rthdr->ip6r_type);
251 #endif
252 return -1;
253 }
254 }
255
256 struct in6_addr *
257 inet6_rthdr_getaddr(cmsg, idx)
258 struct cmsghdr *cmsg;
259 int idx;
260 {
261 struct ip6_rthdr *rthdr;
262
263 _DIAGASSERT(cmsg != NULL);
264
265 rthdr = (struct ip6_rthdr *)(void *)CMSG_DATA(cmsg);
266
267 switch(rthdr->ip6r_type) {
268 case IPV6_RTHDR_TYPE_0:
269 {
270 struct ip6_rthdr0 *rt0 = (struct ip6_rthdr0 *)(void *)rthdr;
271 int naddr;
272
273 if (rt0->ip6r0_len % 2 || 46 < rt0->ip6r0_len) {
274 #ifdef DEBUG
275 fprintf(stderr, "inet6_rthdr_getaddr: invalid size(%d)\n",
276 rt0->ip6r0_len);
277 #endif
278 return NULL;
279 }
280 naddr = (rt0->ip6r0_len * 8) / sizeof(struct in6_addr);
281 if (idx <= 0 || naddr < idx) {
282 #ifdef DEBUG
283 fprintf(stderr, "inet6_rthdr_getaddr: invalid index(%d)\n", idx);
284 #endif
285 return NULL;
286 }
287 return &rt0->ip6r0_addr[idx - 1];
288 }
289
290 default:
291 #ifdef DEBUG
292 fprintf(stderr, "inet6_rthdr_getaddr: unknown type(%d)\n",
293 rthdr->ip6r_type);
294 #endif
295 return NULL;
296 }
297 }
298
299 int
300 inet6_rthdr_getflags(cmsg, idx)
301 const struct cmsghdr *cmsg;
302 int idx;
303 {
304 const struct ip6_rthdr *rthdr;
305
306 _DIAGASSERT(cmsg != NULL);
307
308 /*LINTED const castaway*/
309 rthdr = (const struct ip6_rthdr *)(const void *)CMSG_DATA(cmsg);
310
311 switch(rthdr->ip6r_type) {
312 case IPV6_RTHDR_TYPE_0:
313 {
314 const struct ip6_rthdr0 *rt0 = (const struct ip6_rthdr0 *)
315 (const void *)rthdr;
316 int naddr;
317
318 if (rt0->ip6r0_len % 2 || 46 < rt0->ip6r0_len) {
319 #ifdef DEBUG
320 fprintf(stderr, "inet6_rthdr_getflags: invalid size(%d)\n",
321 rt0->ip6r0_len);
322 #endif
323 return -1;
324 }
325 naddr = (rt0->ip6r0_len * 8) / sizeof(struct in6_addr);
326 if (idx < 0 || naddr < idx) {
327 #ifdef DEBUG
328 fprintf(stderr, "inet6_rthdr_getflags: invalid index(%d)\n", idx);
329 #endif
330 return -1;
331 }
332 if (rt0->ip6r0_slmap[idx / 8] & (0x80 >> (idx % 8)))
333 return IPV6_RTHDR_STRICT;
334 else
335 return IPV6_RTHDR_LOOSE;
336 }
337
338 default:
339 #ifdef DEBUG
340 fprintf(stderr, "inet6_rthdr_getflags: unknown type(%d)\n",
341 rthdr->ip6r_type);
342 #endif
343 return -1;
344 }
345 }
346