1 /* $KAME: rthdr.c,v 1.19 2003/06/06 10:48:51 itojun Exp $ */
2
3 /*-
4 * SPDX-License-Identifier: BSD-3-Clause
5 *
6 * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project.
7 * All rights reserved.
8 *
9 * Redistribution and use in source and binary forms, with or without
10 * modification, are permitted provided that the following conditions
11 * are met:
12 * 1. Redistributions of source code must retain the above copyright
13 * notice, this list of conditions and the following disclaimer.
14 * 2. Redistributions in binary form must reproduce the above copyright
15 * notice, this list of conditions and the following disclaimer in the
16 * documentation and/or other materials provided with the distribution.
17 * 3. Neither the name of the project nor the names of its contributors
18 * may be used to endorse or promote products derived from this software
19 * without specific prior written permission.
20 *
21 * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND
22 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24 * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE
25 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
26 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
27 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
28 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
29 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
30 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
31 * SUCH DAMAGE.
32 */
33
34 #include <sys/param.h>
35 #include <sys/socket.h>
36
37 #include <netinet/in.h>
38 #include <netinet/ip6.h>
39
40 #include <string.h>
41 #include <stdio.h>
42
43 /*
44 * RFC2292 API
45 */
46
47 size_t
inet6_rthdr_space(int type,int seg)48 inet6_rthdr_space(int type, int seg)
49 {
50 switch (type) {
51 case IPV6_RTHDR_TYPE_0:
52 if (seg < 1 || seg > 23)
53 return (0);
54 #ifdef COMPAT_RFC2292
55 return (CMSG_SPACE(sizeof(struct in6_addr) * (seg - 1) +
56 sizeof(struct ip6_rthdr0)));
57 #else
58 return (CMSG_SPACE(sizeof(struct in6_addr) * seg +
59 sizeof(struct ip6_rthdr0)));
60 #endif
61 default:
62 return (0);
63 }
64 }
65
66 struct cmsghdr *
inet6_rthdr_init(void * bp,int type)67 inet6_rthdr_init(void *bp, int type)
68 {
69 struct cmsghdr *ch = (struct cmsghdr *)bp;
70 struct ip6_rthdr *rthdr;
71
72 rthdr = (struct ip6_rthdr *)CMSG_DATA(ch);
73
74 ch->cmsg_level = IPPROTO_IPV6;
75 ch->cmsg_type = IPV6_RTHDR;
76
77 switch (type) {
78 case IPV6_RTHDR_TYPE_0:
79 #ifdef COMPAT_RFC2292
80 ch->cmsg_len = CMSG_LEN(sizeof(struct ip6_rthdr0) -
81 sizeof(struct in6_addr));
82 #else
83 ch->cmsg_len = CMSG_LEN(sizeof(struct ip6_rthdr0));
84 #endif
85
86 bzero(rthdr, sizeof(struct ip6_rthdr0));
87 rthdr->ip6r_type = IPV6_RTHDR_TYPE_0;
88 return (ch);
89 default:
90 return (NULL);
91 }
92 }
93
94 /* ARGSUSED */
95 int
inet6_rthdr_add(struct cmsghdr * cmsg,const struct in6_addr * addr,u_int flags)96 inet6_rthdr_add(struct cmsghdr *cmsg, const struct in6_addr *addr, u_int flags)
97 {
98 struct ip6_rthdr *rthdr;
99
100 rthdr = (struct ip6_rthdr *)CMSG_DATA(cmsg);
101
102 switch (rthdr->ip6r_type) {
103 case IPV6_RTHDR_TYPE_0:
104 {
105 struct ip6_rthdr0 *rt0 = (struct ip6_rthdr0 *)rthdr;
106 if (flags != IPV6_RTHDR_LOOSE && flags != IPV6_RTHDR_STRICT)
107 return (-1);
108 if (rt0->ip6r0_segleft == 23)
109 return (-1);
110
111 #ifdef COMPAT_RFC1883 /* XXX */
112 if (flags == IPV6_RTHDR_STRICT) {
113 int c, b;
114 c = rt0->ip6r0_segleft / 8;
115 b = rt0->ip6r0_segleft % 8;
116 rt0->ip6r0_slmap[c] |= (1 << (7 - b));
117 }
118 #else
119 if (flags != IPV6_RTHDR_LOOSE)
120 return (-1);
121 #endif
122 rt0->ip6r0_segleft++;
123 bcopy(addr, (caddr_t)rt0 + ((rt0->ip6r0_len + 1) << 3),
124 sizeof(struct in6_addr));
125 rt0->ip6r0_len += sizeof(struct in6_addr) >> 3;
126 cmsg->cmsg_len = CMSG_LEN((rt0->ip6r0_len + 1) << 3);
127 break;
128 }
129 default:
130 return (-1);
131 }
132
133 return (0);
134 }
135
136 /* ARGSUSED */
137 int
inet6_rthdr_lasthop(struct cmsghdr * cmsg,unsigned int flags)138 inet6_rthdr_lasthop(struct cmsghdr *cmsg, unsigned int flags)
139 {
140 struct ip6_rthdr *rthdr;
141
142 rthdr = (struct ip6_rthdr *)CMSG_DATA(cmsg);
143
144 switch (rthdr->ip6r_type) {
145 case IPV6_RTHDR_TYPE_0:
146 {
147 struct ip6_rthdr0 *rt0 = (struct ip6_rthdr0 *)rthdr;
148 #ifdef COMPAT_RFC1883 /* XXX */
149 if (flags != IPV6_RTHDR_LOOSE && flags != IPV6_RTHDR_STRICT)
150 return (-1);
151 #endif /* COMPAT_RFC1883 */
152 if (rt0->ip6r0_segleft > 23)
153 return (-1);
154 #ifdef COMPAT_RFC1883 /* XXX */
155 if (flags == IPV6_RTHDR_STRICT) {
156 int c, b;
157 c = rt0->ip6r0_segleft / 8;
158 b = rt0->ip6r0_segleft % 8;
159 rt0->ip6r0_slmap[c] |= (1 << (7 - b));
160 }
161 #else
162 if (flags != IPV6_RTHDR_LOOSE)
163 return (-1);
164 #endif /* COMPAT_RFC1883 */
165 break;
166 }
167 default:
168 return (-1);
169 }
170
171 return (0);
172 }
173
174 #if 0
175 int
176 inet6_rthdr_reverse(const struct cmsghdr *in, struct cmsghdr *out)
177 {
178
179 return (-1);
180 }
181 #endif
182
183 int
inet6_rthdr_segments(const struct cmsghdr * cmsg)184 inet6_rthdr_segments(const struct cmsghdr *cmsg)
185 {
186 struct ip6_rthdr *rthdr;
187
188 rthdr = (struct ip6_rthdr *)CMSG_DATA(cmsg);
189
190 switch (rthdr->ip6r_type) {
191 case IPV6_RTHDR_TYPE_0:
192 {
193 struct ip6_rthdr0 *rt0 = (struct ip6_rthdr0 *)rthdr;
194
195 if (rt0->ip6r0_len % 2 || 46 < rt0->ip6r0_len)
196 return (-1);
197
198 return (rt0->ip6r0_len * 8) / sizeof(struct in6_addr);
199 }
200
201 default:
202 return (-1);
203 }
204 }
205
206 struct in6_addr *
inet6_rthdr_getaddr(struct cmsghdr * cmsg,int idx)207 inet6_rthdr_getaddr(struct cmsghdr *cmsg, int idx)
208 {
209 struct ip6_rthdr *rthdr;
210
211 rthdr = (struct ip6_rthdr *)CMSG_DATA(cmsg);
212
213 switch (rthdr->ip6r_type) {
214 case IPV6_RTHDR_TYPE_0:
215 {
216 struct ip6_rthdr0 *rt0 = (struct ip6_rthdr0 *)rthdr;
217 int naddr;
218
219 if (rt0->ip6r0_len % 2 || 46 < rt0->ip6r0_len)
220 return NULL;
221 naddr = (rt0->ip6r0_len * 8) / sizeof(struct in6_addr);
222 if (idx <= 0 || naddr < idx)
223 return NULL;
224 #ifdef COMPAT_RFC2292
225 return (((struct in6_addr *)(rt0 + 1)) + idx - 1);
226 #else
227 return (((struct in6_addr *)(rt0 + 1)) + idx);
228 #endif
229 }
230
231 default:
232 return NULL;
233 }
234 }
235
236 int
inet6_rthdr_getflags(const struct cmsghdr * cmsg,int idx)237 inet6_rthdr_getflags(const struct cmsghdr *cmsg, int idx)
238 {
239 struct ip6_rthdr *rthdr;
240
241 rthdr = (struct ip6_rthdr *)CMSG_DATA(cmsg);
242
243 switch (rthdr->ip6r_type) {
244 case IPV6_RTHDR_TYPE_0:
245 {
246 struct ip6_rthdr0 *rt0 = (struct ip6_rthdr0 *)rthdr;
247 int naddr;
248
249 if (rt0->ip6r0_len % 2 || 46 < rt0->ip6r0_len)
250 return (-1);
251 naddr = (rt0->ip6r0_len * 8) / sizeof(struct in6_addr);
252 if (idx < 0 || naddr < idx)
253 return (-1);
254 #ifdef COMPAT_RFC1883 /* XXX */
255 if (rt0->ip6r0_slmap[idx / 8] & (0x80 >> (idx % 8)))
256 return IPV6_RTHDR_STRICT;
257 else
258 return IPV6_RTHDR_LOOSE;
259 #else
260 return IPV6_RTHDR_LOOSE;
261 #endif /* COMPAT_RFC1883 */
262 }
263
264 default:
265 return (-1);
266 }
267 }
268
269 /*
270 * RFC3542 API
271 */
272
273 socklen_t
inet6_rth_space(int type,int segments)274 inet6_rth_space(int type, int segments)
275 {
276 switch (type) {
277 case IPV6_RTHDR_TYPE_0:
278 if ((segments >= 0) && (segments <= 127))
279 return (((segments * 2) + 1) << 3);
280 /* FALLTHROUGH */
281 default:
282 return (0); /* type not supported */
283 }
284 }
285
286 void *
inet6_rth_init(void * bp,socklen_t bp_len,int type,int segments)287 inet6_rth_init(void *bp, socklen_t bp_len, int type, int segments)
288 {
289 struct ip6_rthdr *rth = (struct ip6_rthdr *)bp;
290 struct ip6_rthdr0 *rth0;
291
292 switch (type) {
293 case IPV6_RTHDR_TYPE_0:
294 /* length validation */
295 if (bp_len < inet6_rth_space(IPV6_RTHDR_TYPE_0, segments))
296 return (NULL);
297 /* segment validation */
298 if ((segments < 0) || (segments > 127))
299 return (NULL);
300
301 memset(bp, 0, bp_len);
302 rth0 = (struct ip6_rthdr0 *)rth;
303 rth0->ip6r0_len = segments * 2;
304 rth0->ip6r0_type = IPV6_RTHDR_TYPE_0;
305 rth0->ip6r0_segleft = 0;
306 rth0->ip6r0_reserved = 0;
307 break;
308 default:
309 return (NULL); /* type not supported */
310 }
311
312 return (bp);
313 }
314
315 int
inet6_rth_add(void * bp,const struct in6_addr * addr)316 inet6_rth_add(void *bp, const struct in6_addr *addr)
317 {
318 struct ip6_rthdr *rth = (struct ip6_rthdr *)bp;
319 struct ip6_rthdr0 *rth0;
320 struct in6_addr *nextaddr;
321
322 switch (rth->ip6r_type) {
323 case IPV6_RTHDR_TYPE_0:
324 rth0 = (struct ip6_rthdr0 *)rth;
325 /* Don't exceed the number of stated segments */
326 if (rth0->ip6r0_segleft == (rth0->ip6r0_len / 2))
327 return (-1);
328 nextaddr = (struct in6_addr *)(rth0 + 1) + rth0->ip6r0_segleft;
329 *nextaddr = *addr;
330 rth0->ip6r0_segleft++;
331 break;
332 default:
333 return (-1); /* type not supported */
334 }
335
336 return (0);
337 }
338
339 int
inet6_rth_reverse(const void * in,void * out)340 inet6_rth_reverse(const void *in, void *out)
341 {
342 struct ip6_rthdr *rth_in = (struct ip6_rthdr *)in;
343 struct ip6_rthdr0 *rth0_in, *rth0_out;
344 int i, segments;
345
346 switch (rth_in->ip6r_type) {
347 case IPV6_RTHDR_TYPE_0:
348 rth0_in = (struct ip6_rthdr0 *)in;
349 rth0_out = (struct ip6_rthdr0 *)out;
350
351 /* parameter validation XXX too paranoid? */
352 if (rth0_in->ip6r0_len % 2)
353 return (-1);
354 segments = rth0_in->ip6r0_len / 2;
355
356 /* we can't use memcpy here, since in and out may overlap */
357 memmove((void *)rth0_out, (void *)rth0_in,
358 ((rth0_in->ip6r0_len) + 1) << 3);
359 rth0_out->ip6r0_segleft = segments;
360
361 /* reverse the addresses */
362 for (i = 0; i < segments / 2; i++) {
363 struct in6_addr addr_tmp, *addr1, *addr2;
364
365 addr1 = (struct in6_addr *)(rth0_out + 1) + i;
366 addr2 = (struct in6_addr *)(rth0_out + 1) +
367 (segments - i - 1);
368 addr_tmp = *addr1;
369 *addr1 = *addr2;
370 *addr2 = addr_tmp;
371 }
372
373 break;
374 default:
375 return (-1); /* type not supported */
376 }
377
378 return (0);
379 }
380
381 int
inet6_rth_segments(const void * bp)382 inet6_rth_segments(const void *bp)
383 {
384 struct ip6_rthdr *rh = (struct ip6_rthdr *)bp;
385 struct ip6_rthdr0 *rh0;
386 int addrs;
387
388 switch (rh->ip6r_type) {
389 case IPV6_RTHDR_TYPE_0:
390 rh0 = (struct ip6_rthdr0 *)bp;
391
392 /*
393 * Validation for a type-0 routing header.
394 * Is this too strict?
395 */
396 if ((rh0->ip6r0_len % 2) != 0 ||
397 (addrs = (rh0->ip6r0_len >> 1)) < rh0->ip6r0_segleft)
398 return (-1);
399
400 return (addrs);
401 default:
402 return (-1); /* unknown type */
403 }
404 }
405
406 struct in6_addr *
inet6_rth_getaddr(const void * bp,int idx)407 inet6_rth_getaddr(const void *bp, int idx)
408 {
409 struct ip6_rthdr *rh = (struct ip6_rthdr *)bp;
410 struct ip6_rthdr0 *rh0;
411 int addrs;
412
413 switch (rh->ip6r_type) {
414 case IPV6_RTHDR_TYPE_0:
415 rh0 = (struct ip6_rthdr0 *)bp;
416
417 /*
418 * Validation for a type-0 routing header.
419 * Is this too strict?
420 */
421 if ((rh0->ip6r0_len % 2) != 0 ||
422 (addrs = (rh0->ip6r0_len >> 1)) < rh0->ip6r0_segleft)
423 return (NULL);
424
425 if (idx < 0 || addrs <= idx)
426 return (NULL);
427
428 return (((struct in6_addr *)(rh0 + 1)) + idx);
429 default:
430 return (NULL); /* unknown type */
431 break;
432 }
433 }
434