1121472Sume/*	$KAME: rthdr.c,v 1.19 2003/06/06 10:48:51 itojun Exp $	*/
295023Ssuz
354696Sshin/*
454696Sshin * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project.
554696Sshin * All rights reserved.
654696Sshin *
754696Sshin * Redistribution and use in source and binary forms, with or without
854696Sshin * modification, are permitted provided that the following conditions
954696Sshin * are met:
1054696Sshin * 1. Redistributions of source code must retain the above copyright
1154696Sshin *    notice, this list of conditions and the following disclaimer.
1254696Sshin * 2. Redistributions in binary form must reproduce the above copyright
1354696Sshin *    notice, this list of conditions and the following disclaimer in the
1454696Sshin *    documentation and/or other materials provided with the distribution.
1554696Sshin * 3. Neither the name of the project nor the names of its contributors
1654696Sshin *    may be used to endorse or promote products derived from this software
1754696Sshin *    without specific prior written permission.
1854696Sshin *
1954696Sshin * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND
2054696Sshin * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
2154696Sshin * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
2254696Sshin * ARE DISCLAIMED.  IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE
2354696Sshin * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
2454696Sshin * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
2554696Sshin * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
2654696Sshin * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
2754696Sshin * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
2854696Sshin * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
2954696Sshin * SUCH DAMAGE.
3054696Sshin */
3154696Sshin
3292986Sobrien#include <sys/cdefs.h>
3392986Sobrien__FBSDID("$FreeBSD$");
3492986Sobrien
3554696Sshin#include <sys/param.h>
3654696Sshin#include <sys/types.h>
3754696Sshin#include <sys/socket.h>
3854696Sshin
3954696Sshin#include <netinet/in.h>
4054696Sshin#include <netinet/ip6.h>
4154696Sshin
4254696Sshin#include <string.h>
4354696Sshin#include <stdio.h>
4454696Sshin
45121472Sume/*
46121472Sume * RFC2292 API
47121472Sume */
48121472Sume
4954696Sshinsize_t
5054696Sshininet6_rthdr_space(type, seg)
51121472Sume	int type, seg;
5254696Sshin{
53121472Sume	switch (type) {
54121472Sume	case IPV6_RTHDR_TYPE_0:
55121472Sume		if (seg < 1 || seg > 23)
56121472Sume			return (0);
57121472Sume#ifdef COMPAT_RFC2292
58121472Sume		return (CMSG_SPACE(sizeof(struct in6_addr) * (seg - 1) +
59121472Sume		    sizeof(struct ip6_rthdr0)));
60121472Sume#else
61121472Sume		return (CMSG_SPACE(sizeof(struct in6_addr) * seg +
62121472Sume		    sizeof(struct ip6_rthdr0)));
6354696Sshin#endif
64121472Sume	default:
65121472Sume		return (0);
66121472Sume	}
6754696Sshin}
6854696Sshin
6954696Sshinstruct cmsghdr *
7054696Sshininet6_rthdr_init(bp, type)
71121472Sume	void *bp;
72121472Sume	int type;
7354696Sshin{
74121472Sume	struct cmsghdr *ch = (struct cmsghdr *)bp;
75121472Sume	struct ip6_rthdr *rthdr;
7654696Sshin
77121472Sume	rthdr = (struct ip6_rthdr *)CMSG_DATA(ch);
7857719Sshin
79121472Sume	ch->cmsg_level = IPPROTO_IPV6;
80121472Sume	ch->cmsg_type = IPV6_RTHDR;
8154696Sshin
82121472Sume	switch (type) {
83121472Sume	case IPV6_RTHDR_TYPE_0:
84121472Sume#ifdef COMPAT_RFC2292
85121472Sume		ch->cmsg_len = CMSG_LEN(sizeof(struct ip6_rthdr0) -
86121472Sume		    sizeof(struct in6_addr));
87121472Sume#else
88121472Sume		ch->cmsg_len = CMSG_LEN(sizeof(struct ip6_rthdr0));
8954696Sshin#endif
90121472Sume
91121472Sume		bzero(rthdr, sizeof(struct ip6_rthdr0));
92121472Sume		rthdr->ip6r_type = IPV6_RTHDR_TYPE_0;
93121472Sume		return (ch);
94121472Sume	default:
95121472Sume		return (NULL);
96121472Sume	}
9754696Sshin}
9854696Sshin
99121472Sume/* ARGSUSED */
10054696Sshinint
10154696Sshininet6_rthdr_add(cmsg, addr, flags)
102121472Sume	struct cmsghdr *cmsg;
103121472Sume	const struct in6_addr *addr;
104121472Sume	u_int flags;
10554696Sshin{
106121472Sume	struct ip6_rthdr *rthdr;
10754696Sshin
108121472Sume	rthdr = (struct ip6_rthdr *)CMSG_DATA(cmsg);
10957719Sshin
110121472Sume	switch (rthdr->ip6r_type) {
111121472Sume	case IPV6_RTHDR_TYPE_0:
112121472Sume	{
113121472Sume		struct ip6_rthdr0 *rt0 = (struct ip6_rthdr0 *)rthdr;
114121472Sume		if (flags != IPV6_RTHDR_LOOSE && flags != IPV6_RTHDR_STRICT)
115121472Sume			return (-1);
116121472Sume		if (rt0->ip6r0_segleft == 23)
117121472Sume			return (-1);
118121472Sume
119121472Sume#ifdef COMPAT_RFC1883		/* XXX */
120121472Sume		if (flags == IPV6_RTHDR_STRICT) {
121121472Sume			int c, b;
122121472Sume			c = rt0->ip6r0_segleft / 8;
123121472Sume			b = rt0->ip6r0_segleft % 8;
124121472Sume			rt0->ip6r0_slmap[c] |= (1 << (7 - b));
125121472Sume		}
126121472Sume#else
127121472Sume		if (flags != IPV6_RTHDR_LOOSE)
128121472Sume			return (-1);
12954696Sshin#endif
130121472Sume		rt0->ip6r0_segleft++;
131121472Sume		bcopy(addr, (caddr_t)rt0 + ((rt0->ip6r0_len + 1) << 3),
132121472Sume		    sizeof(struct in6_addr));
133121472Sume		rt0->ip6r0_len += sizeof(struct in6_addr) >> 3;
134121472Sume		cmsg->cmsg_len = CMSG_LEN((rt0->ip6r0_len + 1) << 3);
135121472Sume		break;
136121472Sume	}
137121472Sume	default:
138121472Sume		return (-1);
139121472Sume	}
14054696Sshin
141121472Sume	return (0);
14254696Sshin}
14354696Sshin
144121472Sume/* ARGSUSED */
14554696Sshinint
14654696Sshininet6_rthdr_lasthop(cmsg, flags)
147121472Sume	struct cmsghdr *cmsg;
148121472Sume	unsigned int flags;
14954696Sshin{
150121472Sume	struct ip6_rthdr *rthdr;
15154696Sshin
152121472Sume	rthdr = (struct ip6_rthdr *)CMSG_DATA(cmsg);
15357719Sshin
154121472Sume	switch (rthdr->ip6r_type) {
155121472Sume	case IPV6_RTHDR_TYPE_0:
156121472Sume	{
157121472Sume		struct ip6_rthdr0 *rt0 = (struct ip6_rthdr0 *)rthdr;
158121472Sume#ifdef COMPAT_RFC1883		/* XXX */
159121472Sume		if (flags != IPV6_RTHDR_LOOSE && flags != IPV6_RTHDR_STRICT)
160121472Sume			return (-1);
161121472Sume#endif /* COMPAT_RFC1883 */
162121472Sume		if (rt0->ip6r0_segleft > 23)
163121472Sume			return (-1);
164121472Sume#ifdef COMPAT_RFC1883		/* XXX */
165121472Sume		if (flags == IPV6_RTHDR_STRICT) {
166121472Sume			int c, b;
167121472Sume			c = rt0->ip6r0_segleft / 8;
168121472Sume			b = rt0->ip6r0_segleft % 8;
169121472Sume			rt0->ip6r0_slmap[c] |= (1 << (7 - b));
170121472Sume		}
171121472Sume#else
172121472Sume		if (flags != IPV6_RTHDR_LOOSE)
173121472Sume			return (-1);
174121472Sume#endif /* COMPAT_RFC1883 */
175121472Sume		break;
176121472Sume	}
177121472Sume	default:
178121472Sume		return (-1);
179121472Sume	}
18054696Sshin
181121472Sume	return (0);
18254696Sshin}
18354696Sshin
18454696Sshin#if 0
18554696Sshinint
18654696Sshininet6_rthdr_reverse(in, out)
187121472Sume	const struct cmsghdr *in;
188121472Sume	struct cmsghdr *out;
18954696Sshin{
190121472Sume
191121472Sume	return (-1);
19254696Sshin}
19354696Sshin#endif
19454696Sshin
19554696Sshinint
19654696Sshininet6_rthdr_segments(cmsg)
197121472Sume	const struct cmsghdr *cmsg;
19854696Sshin{
199121472Sume	struct ip6_rthdr *rthdr;
20054696Sshin
201121472Sume	rthdr = (struct ip6_rthdr *)CMSG_DATA(cmsg);
20257719Sshin
203121472Sume	switch (rthdr->ip6r_type) {
204121472Sume	case IPV6_RTHDR_TYPE_0:
205121472Sume	{
206121472Sume		struct ip6_rthdr0 *rt0 = (struct ip6_rthdr0 *)rthdr;
20754696Sshin
208121472Sume		if (rt0->ip6r0_len % 2 || 46 < rt0->ip6r0_len)
209121472Sume			return (-1);
210121472Sume
211121472Sume		return (rt0->ip6r0_len * 8) / sizeof(struct in6_addr);
21254696Sshin	}
21354696Sshin
214121472Sume	default:
215121472Sume		return (-1);
216121472Sume	}
21754696Sshin}
21854696Sshin
21954696Sshinstruct in6_addr *
22095023Ssuzinet6_rthdr_getaddr(cmsg, idx)
221121472Sume	struct cmsghdr *cmsg;
222121472Sume	int idx;
22354696Sshin{
224121472Sume	struct ip6_rthdr *rthdr;
22554696Sshin
226121472Sume	rthdr = (struct ip6_rthdr *)CMSG_DATA(cmsg);
22757719Sshin
228121472Sume	switch (rthdr->ip6r_type) {
229121472Sume	case IPV6_RTHDR_TYPE_0:
230121472Sume	{
231121472Sume		struct ip6_rthdr0 *rt0 = (struct ip6_rthdr0 *)rthdr;
232121472Sume		int naddr;
23354696Sshin
234121472Sume		if (rt0->ip6r0_len % 2 || 46 < rt0->ip6r0_len)
235121472Sume			return NULL;
236121472Sume		naddr = (rt0->ip6r0_len * 8) / sizeof(struct in6_addr);
237121472Sume		if (idx <= 0 || naddr < idx)
238121472Sume			return NULL;
239121472Sume#ifdef COMPAT_RFC2292
240121472Sume		return (((struct in6_addr *)(rt0 + 1)) + idx - 1);
241121472Sume#else
242121472Sume		return (((struct in6_addr *)(rt0 + 1)) + idx);
243121472Sume#endif
24454696Sshin	}
245121472Sume
246121472Sume	default:
247121472Sume		return NULL;
24854696Sshin	}
24954696Sshin}
25054696Sshin
25154696Sshinint
25295023Ssuzinet6_rthdr_getflags(cmsg, idx)
253121472Sume	const struct cmsghdr *cmsg;
254121472Sume	int idx;
25554696Sshin{
256121472Sume	struct ip6_rthdr *rthdr;
25754696Sshin
258121472Sume	rthdr = (struct ip6_rthdr *)CMSG_DATA(cmsg);
25957719Sshin
260121472Sume	switch (rthdr->ip6r_type) {
261121472Sume	case IPV6_RTHDR_TYPE_0:
262121472Sume	{
263121472Sume		struct ip6_rthdr0 *rt0 = (struct ip6_rthdr0 *)rthdr;
264121472Sume		int naddr;
26554696Sshin
266121472Sume		if (rt0->ip6r0_len % 2 || 46 < rt0->ip6r0_len)
267121472Sume			return (-1);
268121472Sume		naddr = (rt0->ip6r0_len * 8) / sizeof(struct in6_addr);
269121472Sume		if (idx < 0 || naddr < idx)
270121472Sume			return (-1);
271121472Sume#ifdef COMPAT_RFC1883		/* XXX */
272121472Sume		if (rt0->ip6r0_slmap[idx / 8] & (0x80 >> (idx % 8)))
273121472Sume			return IPV6_RTHDR_STRICT;
274121472Sume		else
275121472Sume			return IPV6_RTHDR_LOOSE;
276121472Sume#else
277121472Sume		return IPV6_RTHDR_LOOSE;
278121472Sume#endif /* COMPAT_RFC1883 */
27954696Sshin	}
280121472Sume
281121472Sume	default:
282121472Sume		return (-1);
28354696Sshin	}
284121472Sume}
28554696Sshin
286121472Sume/*
287148160Sume * RFC3542 API
288121472Sume */
289121472Sume
290121472Sumesocklen_t
291121472Sumeinet6_rth_space(int type, int segments)
292121472Sume{
293121472Sume	switch (type) {
294121472Sume	case IPV6_RTHDR_TYPE_0:
295168867Smtm		if ((segments >= 0) && (segments <= 127))
296168867Smtm			return (((segments * 2) + 1) << 3);
297168867Smtm		/* FALLTHROUGH */
298121472Sume	default:
299121472Sume		return (0);	/* type not suppported */
300121472Sume	}
30154696Sshin}
302121472Sume
303121472Sumevoid *
304121472Sumeinet6_rth_init(void *bp, socklen_t bp_len, int type, int segments)
305121472Sume{
306121472Sume	struct ip6_rthdr *rth = (struct ip6_rthdr *)bp;
307121472Sume	struct ip6_rthdr0 *rth0;
308121472Sume
309121472Sume	switch (type) {
310121472Sume	case IPV6_RTHDR_TYPE_0:
311121472Sume		/* length validation */
312121472Sume		if (bp_len < inet6_rth_space(IPV6_RTHDR_TYPE_0, segments))
313121472Sume			return (NULL);
314168867Smtm		/* segment validation */
315168867Smtm		if ((segments < 0) || (segments > 127))
316168867Smtm			return (NULL);
317121472Sume
318121472Sume		memset(bp, 0, bp_len);
319121472Sume		rth0 = (struct ip6_rthdr0 *)rth;
320121472Sume		rth0->ip6r0_len = segments * 2;
321121472Sume		rth0->ip6r0_type = IPV6_RTHDR_TYPE_0;
322121472Sume		rth0->ip6r0_segleft = 0;
323121472Sume		rth0->ip6r0_reserved = 0;
324121472Sume		break;
325121472Sume	default:
326121472Sume		return (NULL);	/* type not supported */
327121472Sume	}
328121472Sume
329121472Sume	return (bp);
330121472Sume}
331121472Sume
332121472Sumeint
333121472Sumeinet6_rth_add(void *bp, const struct in6_addr *addr)
334121472Sume{
335121472Sume	struct ip6_rthdr *rth = (struct ip6_rthdr *)bp;
336121472Sume	struct ip6_rthdr0 *rth0;
337121472Sume	struct in6_addr *nextaddr;
338121472Sume
339121472Sume	switch (rth->ip6r_type) {
340121472Sume	case IPV6_RTHDR_TYPE_0:
341121472Sume		rth0 = (struct ip6_rthdr0 *)rth;
342168867Smtm		/* Don't exceed the number of stated segments */
343168867Smtm		if (rth0->ip6r0_segleft == (rth0->ip6r0_len / 2))
344168867Smtm			return (-1);
345121472Sume		nextaddr = (struct in6_addr *)(rth0 + 1) + rth0->ip6r0_segleft;
346121472Sume		*nextaddr = *addr;
347121472Sume		rth0->ip6r0_segleft++;
348121472Sume		break;
349121472Sume	default:
350121472Sume		return (-1);	/* type not supported */
351121472Sume	}
352121472Sume
353121472Sume	return (0);
354121472Sume}
355121472Sume
356121472Sumeint
357121472Sumeinet6_rth_reverse(const void *in, void *out)
358121472Sume{
359121472Sume	struct ip6_rthdr *rth_in = (struct ip6_rthdr *)in;
360121472Sume	struct ip6_rthdr0 *rth0_in, *rth0_out;
361121472Sume	int i, segments;
362121472Sume
363121472Sume	switch (rth_in->ip6r_type) {
364121472Sume	case IPV6_RTHDR_TYPE_0:
365121472Sume		rth0_in = (struct ip6_rthdr0 *)in;
366121472Sume		rth0_out = (struct ip6_rthdr0 *)out;
367121472Sume
368121472Sume		/* parameter validation XXX too paranoid? */
369121472Sume		if (rth0_in->ip6r0_len % 2)
370121472Sume			return (-1);
371121472Sume		segments = rth0_in->ip6r0_len / 2;
372121472Sume
373121472Sume		/* we can't use memcpy here, since in and out may overlap */
374121472Sume		memmove((void *)rth0_out, (void *)rth0_in,
375121472Sume			((rth0_in->ip6r0_len) + 1) << 3);
376121472Sume		rth0_out->ip6r0_segleft = segments;
377121472Sume
378121472Sume		/* reverse the addresses */
379121472Sume		for (i = 0; i < segments / 2; i++) {
380121472Sume			struct in6_addr addr_tmp, *addr1, *addr2;
381121472Sume
382121472Sume			addr1 = (struct in6_addr *)(rth0_out + 1) + i;
383121472Sume			addr2 = (struct in6_addr *)(rth0_out + 1) +
384121472Sume				(segments - i - 1);
385121472Sume			addr_tmp = *addr1;
386121472Sume			*addr1 = *addr2;
387121472Sume			*addr2 = addr_tmp;
388121472Sume		}
389121472Sume
390121472Sume		break;
391121472Sume	default:
392121472Sume		return (-1);	/* type not supported */
393121472Sume	}
394121472Sume
395121472Sume	return (0);
396121472Sume}
397121472Sume
398121472Sumeint
399121472Sumeinet6_rth_segments(const void *bp)
400121472Sume{
401121472Sume	struct ip6_rthdr *rh = (struct ip6_rthdr *)bp;
402121472Sume	struct ip6_rthdr0 *rh0;
403121472Sume	int addrs;
404121472Sume
405121472Sume	switch (rh->ip6r_type) {
406121472Sume	case IPV6_RTHDR_TYPE_0:
407121472Sume		rh0 = (struct ip6_rthdr0 *)bp;
408121472Sume
409121472Sume		/*
410121472Sume		 * Validation for a type-0 routing header.
411121472Sume		 * Is this too strict?
412121472Sume		 */
413121472Sume		if ((rh0->ip6r0_len % 2) != 0 ||
414121472Sume		    (addrs = (rh0->ip6r0_len >> 1)) < rh0->ip6r0_segleft)
415121472Sume			return (-1);
416121472Sume
417121472Sume		return (addrs);
418121472Sume	default:
419121472Sume		return (-1);	/* unknown type */
420121472Sume	}
421121472Sume}
422121472Sume
423121472Sumestruct in6_addr *
424121472Sumeinet6_rth_getaddr(const void *bp, int idx)
425121472Sume{
426121472Sume	struct ip6_rthdr *rh = (struct ip6_rthdr *)bp;
427121472Sume	struct ip6_rthdr0 *rh0;
428148159Sume	int addrs;
429121472Sume
430121472Sume	switch (rh->ip6r_type) {
431121472Sume	case IPV6_RTHDR_TYPE_0:
432121472Sume		 rh0 = (struct ip6_rthdr0 *)bp;
433121472Sume
434121472Sume		/*
435121472Sume		 * Validation for a type-0 routing header.
436121472Sume		 * Is this too strict?
437121472Sume		 */
438148159Sume		if ((rh0->ip6r0_len % 2) != 0 ||
439148159Sume		    (addrs = (rh0->ip6r0_len >> 1)) < rh0->ip6r0_segleft)
440121472Sume			return (NULL);
441121472Sume
442121472Sume		if (idx < 0 || addrs <= idx)
443121472Sume			return (NULL);
444121472Sume
445121472Sume		return (((struct in6_addr *)(rh0 + 1)) + idx);
446121472Sume	default:
447121472Sume		return (NULL);	/* unknown type */
448121472Sume		break;
449121472Sume	}
450121472Sume}
451