xref: /freebsd/lib/libc/net/rthdr.c (revision b2d48be1bc7df45ddd13b143a160d0acb5a383c5)
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
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 *
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
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
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
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 *
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
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
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 *
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
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
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
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 *
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