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