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