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