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