xref: /openbsd-src/lib/libc/net/rthdr.c (revision a28daedfc357b214be5c701aa8ba8adb29a7f1c2)
1 /*	$OpenBSD: rthdr.c,v 1.8 2006/12/09 01:12:28 itojun Exp $	*/
2 /*	$KAME: rthdr.c,v 1.22 2006/02/09 08:18:58 keiichi Exp $	*/
3 
4 /*
5  * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project.
6  * All rights reserved.
7  *
8  * Redistribution and use in source and binary forms, with or without
9  * modification, are permitted provided that the following conditions
10  * are met:
11  * 1. Redistributions of source code must retain the above copyright
12  *    notice, this list of conditions and the following disclaimer.
13  * 2. Redistributions in binary form must reproduce the above copyright
14  *    notice, this list of conditions and the following disclaimer in the
15  *    documentation and/or other materials provided with the distribution.
16  * 3. Neither the name of the project nor the names of its contributors
17  *    may be used to endorse or promote products derived from this software
18  *    without specific prior written permission.
19  *
20  * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND
21  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
23  * ARE DISCLAIMED.  IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE
24  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
25  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
26  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
27  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
28  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
29  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
30  * SUCH DAMAGE.
31  */
32 
33 #include <sys/param.h>
34 #include <sys/types.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
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 		return (CMSG_SPACE(sizeof(struct in6_addr) * seg +
55 		    sizeof(struct ip6_rthdr0)));
56 	default:
57 		return (0);
58 	}
59 }
60 
61 struct cmsghdr *
62 inet6_rthdr_init(void *bp, int type)
63 {
64 	struct cmsghdr *ch = (struct cmsghdr *)bp;
65 	struct ip6_rthdr *rthdr;
66 
67 	rthdr = (struct ip6_rthdr *)CMSG_DATA(ch);
68 
69 	ch->cmsg_level = IPPROTO_IPV6;
70 	ch->cmsg_type = IPV6_RTHDR;
71 
72 	switch (type) {
73 	case IPV6_RTHDR_TYPE_0:
74 		ch->cmsg_len = CMSG_LEN(sizeof(struct ip6_rthdr0));
75 		bzero(rthdr, sizeof(struct ip6_rthdr0));
76 		rthdr->ip6r_type = IPV6_RTHDR_TYPE_0;
77 		return (ch);
78 	default:
79 		return (NULL);
80 	}
81 }
82 
83 int
84 inet6_rthdr_add(struct cmsghdr *cmsg, const struct in6_addr *addr, u_int flags)
85 {
86 	struct ip6_rthdr *rthdr;
87 
88 	rthdr = (struct ip6_rthdr *)CMSG_DATA(cmsg);
89 
90 	switch (rthdr->ip6r_type) {
91 	case IPV6_RTHDR_TYPE_0:
92 	{
93 		struct ip6_rthdr0 *rt0 = (struct ip6_rthdr0 *)rthdr;
94 		if (flags != IPV6_RTHDR_LOOSE)
95 			return (-1);
96 		if (rt0->ip6r0_segleft == 23)
97 			return (-1);
98 		rt0->ip6r0_segleft++;
99 		bcopy(addr, (caddr_t)rt0 + ((rt0->ip6r0_len + 1) << 3),
100 		    sizeof(struct in6_addr));
101 		rt0->ip6r0_len += sizeof(struct in6_addr) >> 3;
102 		cmsg->cmsg_len = CMSG_LEN((rt0->ip6r0_len + 1) << 3);
103 		break;
104 	}
105 	default:
106 		return (-1);
107 	}
108 
109 	return (0);
110 }
111 
112 int
113 inet6_rthdr_lasthop(struct cmsghdr *cmsg, unsigned int flags)
114 {
115 	struct ip6_rthdr *rthdr;
116 
117 	rthdr = (struct ip6_rthdr *)CMSG_DATA(cmsg);
118 
119 	switch (rthdr->ip6r_type) {
120 	case IPV6_RTHDR_TYPE_0:
121 	{
122 		struct ip6_rthdr0 *rt0 = (struct ip6_rthdr0 *)rthdr;
123 		if (flags != IPV6_RTHDR_LOOSE)
124 			return (-1);
125 		if (rt0->ip6r0_segleft > 23)
126 			return (-1);
127 		break;
128 	}
129 	default:
130 		return (-1);
131 	}
132 
133 	return (0);
134 }
135 
136 #if 0
137 int
138 inet6_rthdr_reverse(in, out)
139 	const struct cmsghdr *in;
140 	struct cmsghdr *out;
141 {
142 
143 	return (-1);
144 }
145 #endif
146 
147 int
148 inet6_rthdr_segments(const struct cmsghdr *cmsg)
149 {
150 	struct ip6_rthdr *rthdr;
151 
152 	rthdr = (struct ip6_rthdr *)CMSG_DATA(cmsg);
153 
154 	switch (rthdr->ip6r_type) {
155 	case IPV6_RTHDR_TYPE_0:
156 	{
157 		struct ip6_rthdr0 *rt0 = (struct ip6_rthdr0 *)rthdr;
158 
159 		if (rt0->ip6r0_len % 2 || 46 < rt0->ip6r0_len)
160 			return (-1);
161 
162 		return (rt0->ip6r0_len * 8) / sizeof(struct in6_addr);
163 	}
164 
165 	default:
166 		return (-1);
167 	}
168 }
169 
170 struct in6_addr *
171 inet6_rthdr_getaddr(struct cmsghdr *cmsg, int index)
172 {
173 	struct ip6_rthdr *rthdr;
174 
175 	rthdr = (struct ip6_rthdr *)CMSG_DATA(cmsg);
176 
177 	switch (rthdr->ip6r_type) {
178 	case IPV6_RTHDR_TYPE_0:
179 	{
180 		struct ip6_rthdr0 *rt0 = (struct ip6_rthdr0 *)rthdr;
181 		int naddr;
182 
183 		if (rt0->ip6r0_len % 2 || 46 < rt0->ip6r0_len)
184 			return NULL;
185 		naddr = (rt0->ip6r0_len * 8) / sizeof(struct in6_addr);
186 		if (index <= 0 || naddr < index)
187 			return NULL;
188 		return ((struct in6_addr *)(rt0 + 1)) + index;
189 	}
190 
191 	default:
192 		return NULL;
193 	}
194 }
195 
196 int
197 inet6_rthdr_getflags(const struct cmsghdr *cmsg, int index)
198 {
199 	struct ip6_rthdr *rthdr;
200 
201 	rthdr = (struct ip6_rthdr *)CMSG_DATA(cmsg);
202 
203 	switch (rthdr->ip6r_type) {
204 	case IPV6_RTHDR_TYPE_0:
205 	{
206 		struct ip6_rthdr0 *rt0 = (struct ip6_rthdr0 *)rthdr;
207 		int naddr;
208 
209 		if (rt0->ip6r0_len % 2 || 46 < rt0->ip6r0_len)
210 			return (-1);
211 		naddr = (rt0->ip6r0_len * 8) / sizeof(struct in6_addr);
212 		if (index < 0 || naddr < index)
213 			return (-1);
214 		return IPV6_RTHDR_LOOSE;
215 	}
216 
217 	default:
218 		return (-1);
219 	}
220 }
221 
222 /*
223  * RFC3542 (2292bis) API
224  */
225 
226 socklen_t
227 inet6_rth_space(int type, int segments)
228 {
229 	switch (type) {
230 	case IPV6_RTHDR_TYPE_0:
231 		return (((segments * 2) + 1) << 3);
232 	default:
233 		return (0);	/* type not suppported */
234 	}
235 }
236 
237 void *
238 inet6_rth_init(void *bp, socklen_t bp_len, int type, int segments)
239 {
240 	struct ip6_rthdr *rth = (struct ip6_rthdr *)bp;
241 	struct ip6_rthdr0 *rth0;
242 
243 	switch (type) {
244 	case IPV6_RTHDR_TYPE_0:
245 		/* length validation */
246 		if (bp_len < inet6_rth_space(IPV6_RTHDR_TYPE_0, segments))
247 			return (NULL);
248 
249 		memset(bp, 0, bp_len);
250 		rth0 = (struct ip6_rthdr0 *)rth;
251 		rth0->ip6r0_len = segments * 2;
252 		rth0->ip6r0_type = IPV6_RTHDR_TYPE_0;
253 		rth0->ip6r0_segleft = 0;
254 		rth0->ip6r0_reserved = 0;
255 		break;
256 	default:
257 		return (NULL);	/* type not supported */
258 	}
259 
260 	return (bp);
261 }
262 
263 int
264 inet6_rth_add(void *bp, const struct in6_addr *addr)
265 {
266 	struct ip6_rthdr *rth = (struct ip6_rthdr *)bp;
267 	struct ip6_rthdr0 *rth0;
268 	struct in6_addr *nextaddr;
269 
270 	switch (rth->ip6r_type) {
271 	case IPV6_RTHDR_TYPE_0:
272 		rth0 = (struct ip6_rthdr0 *)rth;
273 		nextaddr = (struct in6_addr *)(rth0 + 1) + rth0->ip6r0_segleft;
274 		*nextaddr = *addr;
275 		rth0->ip6r0_segleft++;
276 		break;
277 	default:
278 		return (-1);	/* type not supported */
279 	}
280 
281 	return (0);
282 }
283 
284 int
285 inet6_rth_reverse(const void *in, void *out)
286 {
287 	struct ip6_rthdr *rth_in = (struct ip6_rthdr *)in;
288 	struct ip6_rthdr0 *rth0_in, *rth0_out;
289 	int i, segments;
290 
291 	switch (rth_in->ip6r_type) {
292 	case IPV6_RTHDR_TYPE_0:
293 		rth0_in = (struct ip6_rthdr0 *)in;
294 		rth0_out = (struct ip6_rthdr0 *)out;
295 
296 		/* parameter validation XXX too paranoid? */
297 		if (rth0_in->ip6r0_len % 2)
298 			return (-1);
299 		segments = rth0_in->ip6r0_len / 2;
300 
301 		/* we can't use memcpy here, since in and out may overlap */
302 		memmove((void *)rth0_out, (void *)rth0_in,
303 			((rth0_in->ip6r0_len) + 1) << 3);
304 		rth0_out->ip6r0_segleft = segments;
305 
306 		/* reverse the addresses */
307 		for (i = 0; i < segments / 2; i++) {
308 			struct in6_addr addr_tmp, *addr1, *addr2;
309 
310 			addr1 = (struct in6_addr *)(rth0_out + 1) + i;
311 			addr2 = (struct in6_addr *)(rth0_out + 1) +
312 			    (segments - i - 1);
313 			addr_tmp = *addr1;
314 			*addr1 = *addr2;
315 			*addr2 = addr_tmp;
316 		}
317 
318 		break;
319 	default:
320 		return (-1);	/* type not supported */
321 	}
322 
323 	return (0);
324 }
325 
326 int
327 inet6_rth_segments(const void *bp)
328 {
329 	struct ip6_rthdr *rh = (struct ip6_rthdr *)bp;
330 	struct ip6_rthdr0 *rh0;
331 	int addrs;
332 
333 	switch (rh->ip6r_type) {
334 	case IPV6_RTHDR_TYPE_0:
335 		rh0 = (struct ip6_rthdr0 *)bp;
336 
337 		/*
338 		 * Validation for a type-0 routing header.
339 		 * Is this too strict?
340 		 */
341 		if ((rh0->ip6r0_len % 2) != 0 ||
342 		    (addrs = (rh0->ip6r0_len >> 1)) < rh0->ip6r0_segleft)
343 			return (-1);
344 
345 		return (addrs);
346 	default:
347 		return (-1);	/* unknown type */
348 	}
349 }
350 
351 struct in6_addr *
352 inet6_rth_getaddr(const void *bp, int idx)
353 {
354 	struct ip6_rthdr *rh = (struct ip6_rthdr *)bp;
355 	struct ip6_rthdr0 *rh0;
356 	int addrs;
357 
358 	switch (rh->ip6r_type) {
359 	case IPV6_RTHDR_TYPE_0:
360 		 rh0 = (struct ip6_rthdr0 *)bp;
361 
362 		/*
363 		 * Validation for a type-0 routing header.
364 		 * Is this too strict?
365 		 */
366 		if ((rh0->ip6r0_len % 2) != 0 ||
367 		    (addrs = (rh0->ip6r0_len >> 1)) < rh0->ip6r0_segleft)
368 			return (NULL);
369 
370 		if (idx < 0 || addrs <= idx)
371 			return (NULL);
372 
373 		return (((struct in6_addr *)(rh0 + 1)) + idx);
374 	default:
375 		return (NULL);	/* unknown type */
376 		break;
377 	}
378 }
379