1 /* $KAME: rthdr.c,v 1.19 2003/06/06 10:48:51 itojun 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 __FBSDID("$FreeBSD$");
34
35 #include <sys/param.h>
36 #include <sys/types.h>
37 #include <sys/socket.h>
38
39 #include <netinet/in.h>
40 #include <netinet/ip6.h>
41
42 #include <string.h>
43 #include <stdio.h>
44
45 /*
46 * RFC2292 API
47 */
48
49 size_t
inet6_rthdr_space(int type,int seg)50 inet6_rthdr_space(int type, int seg)
51 {
52 switch (type) {
53 case IPV6_RTHDR_TYPE_0:
54 if (seg < 1 || seg > 23)
55 return (0);
56 #ifdef COMPAT_RFC2292
57 return (CMSG_SPACE(sizeof(struct in6_addr) * (seg - 1) +
58 sizeof(struct ip6_rthdr0)));
59 #else
60 return (CMSG_SPACE(sizeof(struct in6_addr) * seg +
61 sizeof(struct ip6_rthdr0)));
62 #endif
63 default:
64 return (0);
65 }
66 }
67
68 struct cmsghdr *
inet6_rthdr_init(void * bp,int type)69 inet6_rthdr_init(void *bp, int type)
70 {
71 struct cmsghdr *ch = (struct cmsghdr *)bp;
72 struct ip6_rthdr *rthdr;
73
74 rthdr = (struct ip6_rthdr *)CMSG_DATA(ch);
75
76 ch->cmsg_level = IPPROTO_IPV6;
77 ch->cmsg_type = IPV6_RTHDR;
78
79 switch (type) {
80 case IPV6_RTHDR_TYPE_0:
81 #ifdef COMPAT_RFC2292
82 ch->cmsg_len = CMSG_LEN(sizeof(struct ip6_rthdr0) -
83 sizeof(struct in6_addr));
84 #else
85 ch->cmsg_len = CMSG_LEN(sizeof(struct ip6_rthdr0));
86 #endif
87
88 bzero(rthdr, sizeof(struct ip6_rthdr0));
89 rthdr->ip6r_type = IPV6_RTHDR_TYPE_0;
90 return (ch);
91 default:
92 return (NULL);
93 }
94 }
95
96 /* ARGSUSED */
97 int
inet6_rthdr_add(struct cmsghdr * cmsg,const struct in6_addr * addr,u_int flags)98 inet6_rthdr_add(struct cmsghdr *cmsg, const struct in6_addr *addr, u_int flags)
99 {
100 struct ip6_rthdr *rthdr;
101
102 rthdr = (struct ip6_rthdr *)CMSG_DATA(cmsg);
103
104 switch (rthdr->ip6r_type) {
105 case IPV6_RTHDR_TYPE_0:
106 {
107 struct ip6_rthdr0 *rt0 = (struct ip6_rthdr0 *)rthdr;
108 if (flags != IPV6_RTHDR_LOOSE && flags != IPV6_RTHDR_STRICT)
109 return (-1);
110 if (rt0->ip6r0_segleft == 23)
111 return (-1);
112
113 #ifdef COMPAT_RFC1883 /* XXX */
114 if (flags == IPV6_RTHDR_STRICT) {
115 int c, b;
116 c = rt0->ip6r0_segleft / 8;
117 b = rt0->ip6r0_segleft % 8;
118 rt0->ip6r0_slmap[c] |= (1 << (7 - b));
119 }
120 #else
121 if (flags != IPV6_RTHDR_LOOSE)
122 return (-1);
123 #endif
124 rt0->ip6r0_segleft++;
125 bcopy(addr, (caddr_t)rt0 + ((rt0->ip6r0_len + 1) << 3),
126 sizeof(struct in6_addr));
127 rt0->ip6r0_len += sizeof(struct in6_addr) >> 3;
128 cmsg->cmsg_len = CMSG_LEN((rt0->ip6r0_len + 1) << 3);
129 break;
130 }
131 default:
132 return (-1);
133 }
134
135 return (0);
136 }
137
138 /* ARGSUSED */
139 int
inet6_rthdr_lasthop(struct cmsghdr * cmsg,unsigned int flags)140 inet6_rthdr_lasthop(struct cmsghdr *cmsg, unsigned int flags)
141 {
142 struct ip6_rthdr *rthdr;
143
144 rthdr = (struct ip6_rthdr *)CMSG_DATA(cmsg);
145
146 switch (rthdr->ip6r_type) {
147 case IPV6_RTHDR_TYPE_0:
148 {
149 struct ip6_rthdr0 *rt0 = (struct ip6_rthdr0 *)rthdr;
150 #ifdef COMPAT_RFC1883 /* XXX */
151 if (flags != IPV6_RTHDR_LOOSE && flags != IPV6_RTHDR_STRICT)
152 return (-1);
153 #endif /* COMPAT_RFC1883 */
154 if (rt0->ip6r0_segleft > 23)
155 return (-1);
156 #ifdef COMPAT_RFC1883 /* XXX */
157 if (flags == IPV6_RTHDR_STRICT) {
158 int c, b;
159 c = rt0->ip6r0_segleft / 8;
160 b = rt0->ip6r0_segleft % 8;
161 rt0->ip6r0_slmap[c] |= (1 << (7 - b));
162 }
163 #else
164 if (flags != IPV6_RTHDR_LOOSE)
165 return (-1);
166 #endif /* COMPAT_RFC1883 */
167 break;
168 }
169 default:
170 return (-1);
171 }
172
173 return (0);
174 }
175
176 #if 0
177 int
178 inet6_rthdr_reverse(const struct cmsghdr *in, struct cmsghdr *out)
179 {
180
181 return (-1);
182 }
183 #endif
184
185 int
inet6_rthdr_segments(const struct cmsghdr * cmsg)186 inet6_rthdr_segments(const struct cmsghdr *cmsg)
187 {
188 struct ip6_rthdr *rthdr;
189
190 rthdr = (struct ip6_rthdr *)CMSG_DATA(cmsg);
191
192 switch (rthdr->ip6r_type) {
193 case IPV6_RTHDR_TYPE_0:
194 {
195 struct ip6_rthdr0 *rt0 = (struct ip6_rthdr0 *)rthdr;
196
197 if (rt0->ip6r0_len % 2 || 46 < rt0->ip6r0_len)
198 return (-1);
199
200 return (rt0->ip6r0_len * 8) / sizeof(struct in6_addr);
201 }
202
203 default:
204 return (-1);
205 }
206 }
207
208 struct in6_addr *
inet6_rthdr_getaddr(struct cmsghdr * cmsg,int idx)209 inet6_rthdr_getaddr(struct cmsghdr *cmsg, int idx)
210 {
211 struct ip6_rthdr *rthdr;
212
213 rthdr = (struct ip6_rthdr *)CMSG_DATA(cmsg);
214
215 switch (rthdr->ip6r_type) {
216 case IPV6_RTHDR_TYPE_0:
217 {
218 struct ip6_rthdr0 *rt0 = (struct ip6_rthdr0 *)rthdr;
219 int naddr;
220
221 if (rt0->ip6r0_len % 2 || 46 < rt0->ip6r0_len)
222 return NULL;
223 naddr = (rt0->ip6r0_len * 8) / sizeof(struct in6_addr);
224 if (idx <= 0 || naddr < idx)
225 return NULL;
226 #ifdef COMPAT_RFC2292
227 return (((struct in6_addr *)(rt0 + 1)) + idx - 1);
228 #else
229 return (((struct in6_addr *)(rt0 + 1)) + idx);
230 #endif
231 }
232
233 default:
234 return NULL;
235 }
236 }
237
238 int
inet6_rthdr_getflags(const struct cmsghdr * cmsg,int idx)239 inet6_rthdr_getflags(const struct cmsghdr *cmsg, int idx)
240 {
241 struct ip6_rthdr *rthdr;
242
243 rthdr = (struct ip6_rthdr *)CMSG_DATA(cmsg);
244
245 switch (rthdr->ip6r_type) {
246 case IPV6_RTHDR_TYPE_0:
247 {
248 struct ip6_rthdr0 *rt0 = (struct ip6_rthdr0 *)rthdr;
249 int naddr;
250
251 if (rt0->ip6r0_len % 2 || 46 < rt0->ip6r0_len)
252 return (-1);
253 naddr = (rt0->ip6r0_len * 8) / sizeof(struct in6_addr);
254 if (idx < 0 || naddr < idx)
255 return (-1);
256 #ifdef COMPAT_RFC1883 /* XXX */
257 if (rt0->ip6r0_slmap[idx / 8] & (0x80 >> (idx % 8)))
258 return IPV6_RTHDR_STRICT;
259 else
260 return IPV6_RTHDR_LOOSE;
261 #else
262 return IPV6_RTHDR_LOOSE;
263 #endif /* COMPAT_RFC1883 */
264 }
265
266 default:
267 return (-1);
268 }
269 }
270
271 /*
272 * RFC3542 API
273 */
274
275 socklen_t
inet6_rth_space(int type,int segments)276 inet6_rth_space(int type, int segments)
277 {
278 switch (type) {
279 case IPV6_RTHDR_TYPE_0:
280 if ((segments >= 0) && (segments <= 127))
281 return (((segments * 2) + 1) << 3);
282 /* FALLTHROUGH */
283 default:
284 return (0); /* type not suppported */
285 }
286 }
287
288 void *
inet6_rth_init(void * bp,socklen_t bp_len,int type,int segments)289 inet6_rth_init(void *bp, socklen_t bp_len, int type, int segments)
290 {
291 struct ip6_rthdr *rth = (struct ip6_rthdr *)bp;
292 struct ip6_rthdr0 *rth0;
293
294 switch (type) {
295 case IPV6_RTHDR_TYPE_0:
296 /* length validation */
297 if (bp_len < inet6_rth_space(IPV6_RTHDR_TYPE_0, segments))
298 return (NULL);
299 /* segment validation */
300 if ((segments < 0) || (segments > 127))
301 return (NULL);
302
303 memset(bp, 0, bp_len);
304 rth0 = (struct ip6_rthdr0 *)rth;
305 rth0->ip6r0_len = segments * 2;
306 rth0->ip6r0_type = IPV6_RTHDR_TYPE_0;
307 rth0->ip6r0_segleft = 0;
308 rth0->ip6r0_reserved = 0;
309 break;
310 default:
311 return (NULL); /* type not supported */
312 }
313
314 return (bp);
315 }
316
317 int
inet6_rth_add(void * bp,const struct in6_addr * addr)318 inet6_rth_add(void *bp, const struct in6_addr *addr)
319 {
320 struct ip6_rthdr *rth = (struct ip6_rthdr *)bp;
321 struct ip6_rthdr0 *rth0;
322 struct in6_addr *nextaddr;
323
324 switch (rth->ip6r_type) {
325 case IPV6_RTHDR_TYPE_0:
326 rth0 = (struct ip6_rthdr0 *)rth;
327 /* Don't exceed the number of stated segments */
328 if (rth0->ip6r0_segleft == (rth0->ip6r0_len / 2))
329 return (-1);
330 nextaddr = (struct in6_addr *)(rth0 + 1) + rth0->ip6r0_segleft;
331 *nextaddr = *addr;
332 rth0->ip6r0_segleft++;
333 break;
334 default:
335 return (-1); /* type not supported */
336 }
337
338 return (0);
339 }
340
341 int
inet6_rth_reverse(const void * in,void * out)342 inet6_rth_reverse(const void *in, void *out)
343 {
344 struct ip6_rthdr *rth_in = (struct ip6_rthdr *)in;
345 struct ip6_rthdr0 *rth0_in, *rth0_out;
346 int i, segments;
347
348 switch (rth_in->ip6r_type) {
349 case IPV6_RTHDR_TYPE_0:
350 rth0_in = (struct ip6_rthdr0 *)in;
351 rth0_out = (struct ip6_rthdr0 *)out;
352
353 /* parameter validation XXX too paranoid? */
354 if (rth0_in->ip6r0_len % 2)
355 return (-1);
356 segments = rth0_in->ip6r0_len / 2;
357
358 /* we can't use memcpy here, since in and out may overlap */
359 memmove((void *)rth0_out, (void *)rth0_in,
360 ((rth0_in->ip6r0_len) + 1) << 3);
361 rth0_out->ip6r0_segleft = segments;
362
363 /* reverse the addresses */
364 for (i = 0; i < segments / 2; i++) {
365 struct in6_addr addr_tmp, *addr1, *addr2;
366
367 addr1 = (struct in6_addr *)(rth0_out + 1) + i;
368 addr2 = (struct in6_addr *)(rth0_out + 1) +
369 (segments - i - 1);
370 addr_tmp = *addr1;
371 *addr1 = *addr2;
372 *addr2 = addr_tmp;
373 }
374
375 break;
376 default:
377 return (-1); /* type not supported */
378 }
379
380 return (0);
381 }
382
383 int
inet6_rth_segments(const void * bp)384 inet6_rth_segments(const void *bp)
385 {
386 struct ip6_rthdr *rh = (struct ip6_rthdr *)bp;
387 struct ip6_rthdr0 *rh0;
388 int addrs;
389
390 switch (rh->ip6r_type) {
391 case IPV6_RTHDR_TYPE_0:
392 rh0 = (struct ip6_rthdr0 *)bp;
393
394 /*
395 * Validation for a type-0 routing header.
396 * Is this too strict?
397 */
398 if ((rh0->ip6r0_len % 2) != 0 ||
399 (addrs = (rh0->ip6r0_len >> 1)) < rh0->ip6r0_segleft)
400 return (-1);
401
402 return (addrs);
403 default:
404 return (-1); /* unknown type */
405 }
406 }
407
408 struct in6_addr *
inet6_rth_getaddr(const void * bp,int idx)409 inet6_rth_getaddr(const void *bp, int idx)
410 {
411 struct ip6_rthdr *rh = (struct ip6_rthdr *)bp;
412 struct ip6_rthdr0 *rh0;
413 int addrs;
414
415 switch (rh->ip6r_type) {
416 case IPV6_RTHDR_TYPE_0:
417 rh0 = (struct ip6_rthdr0 *)bp;
418
419 /*
420 * Validation for a type-0 routing header.
421 * Is this too strict?
422 */
423 if ((rh0->ip6r0_len % 2) != 0 ||
424 (addrs = (rh0->ip6r0_len >> 1)) < rh0->ip6r0_segleft)
425 return (NULL);
426
427 if (idx < 0 || addrs <= idx)
428 return (NULL);
429
430 return (((struct in6_addr *)(rh0 + 1)) + idx);
431 default:
432 return (NULL); /* unknown type */
433 break;
434 }
435 }
436