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