1121472Sume/*	$KAME: rthdr.c,v 1.19 2003/06/06 10:48:51 itojun Exp $	*/
295023Ssuz
3331722Seadler/*
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/socket.h>
3754696Sshin
3854696Sshin#include <netinet/in.h>
3954696Sshin#include <netinet/ip6.h>
4054696Sshin
4154696Sshin#include <string.h>
4254696Sshin#include <stdio.h>
4354696Sshin
44121472Sume/*
45121472Sume * RFC2292 API
46121472Sume */
47121472Sume
4854696Sshinsize_t
49288045Srodrigcinet6_rthdr_space(int type, int seg)
5054696Sshin{
51121472Sume	switch (type) {
52121472Sume	case IPV6_RTHDR_TYPE_0:
53121472Sume		if (seg < 1 || seg > 23)
54121472Sume			return (0);
55121472Sume#ifdef COMPAT_RFC2292
56121472Sume		return (CMSG_SPACE(sizeof(struct in6_addr) * (seg - 1) +
57121472Sume		    sizeof(struct ip6_rthdr0)));
58121472Sume#else
59121472Sume		return (CMSG_SPACE(sizeof(struct in6_addr) * seg +
60121472Sume		    sizeof(struct ip6_rthdr0)));
6154696Sshin#endif
62121472Sume	default:
63121472Sume		return (0);
64121472Sume	}
6554696Sshin}
6654696Sshin
6754696Sshinstruct cmsghdr *
68288045Srodrigcinet6_rthdr_init(void *bp, int type)
6954696Sshin{
70121472Sume	struct cmsghdr *ch = (struct cmsghdr *)bp;
71121472Sume	struct ip6_rthdr *rthdr;
7254696Sshin
73121472Sume	rthdr = (struct ip6_rthdr *)CMSG_DATA(ch);
7457719Sshin
75121472Sume	ch->cmsg_level = IPPROTO_IPV6;
76121472Sume	ch->cmsg_type = IPV6_RTHDR;
7754696Sshin
78121472Sume	switch (type) {
79121472Sume	case IPV6_RTHDR_TYPE_0:
80121472Sume#ifdef COMPAT_RFC2292
81121472Sume		ch->cmsg_len = CMSG_LEN(sizeof(struct ip6_rthdr0) -
82121472Sume		    sizeof(struct in6_addr));
83121472Sume#else
84121472Sume		ch->cmsg_len = CMSG_LEN(sizeof(struct ip6_rthdr0));
8554696Sshin#endif
86121472Sume
87121472Sume		bzero(rthdr, sizeof(struct ip6_rthdr0));
88121472Sume		rthdr->ip6r_type = IPV6_RTHDR_TYPE_0;
89121472Sume		return (ch);
90121472Sume	default:
91121472Sume		return (NULL);
92121472Sume	}
9354696Sshin}
9454696Sshin
95121472Sume/* ARGSUSED */
9654696Sshinint
97288045Srodrigcinet6_rthdr_add(struct cmsghdr *cmsg, const struct in6_addr *addr, u_int flags)
9854696Sshin{
99121472Sume	struct ip6_rthdr *rthdr;
10054696Sshin
101121472Sume	rthdr = (struct ip6_rthdr *)CMSG_DATA(cmsg);
10257719Sshin
103121472Sume	switch (rthdr->ip6r_type) {
104121472Sume	case IPV6_RTHDR_TYPE_0:
105121472Sume	{
106121472Sume		struct ip6_rthdr0 *rt0 = (struct ip6_rthdr0 *)rthdr;
107121472Sume		if (flags != IPV6_RTHDR_LOOSE && flags != IPV6_RTHDR_STRICT)
108121472Sume			return (-1);
109121472Sume		if (rt0->ip6r0_segleft == 23)
110121472Sume			return (-1);
111121472Sume
112121472Sume#ifdef COMPAT_RFC1883		/* XXX */
113121472Sume		if (flags == IPV6_RTHDR_STRICT) {
114121472Sume			int c, b;
115121472Sume			c = rt0->ip6r0_segleft / 8;
116121472Sume			b = rt0->ip6r0_segleft % 8;
117121472Sume			rt0->ip6r0_slmap[c] |= (1 << (7 - b));
118121472Sume		}
119121472Sume#else
120121472Sume		if (flags != IPV6_RTHDR_LOOSE)
121121472Sume			return (-1);
12254696Sshin#endif
123121472Sume		rt0->ip6r0_segleft++;
124121472Sume		bcopy(addr, (caddr_t)rt0 + ((rt0->ip6r0_len + 1) << 3),
125121472Sume		    sizeof(struct in6_addr));
126121472Sume		rt0->ip6r0_len += sizeof(struct in6_addr) >> 3;
127121472Sume		cmsg->cmsg_len = CMSG_LEN((rt0->ip6r0_len + 1) << 3);
128121472Sume		break;
129121472Sume	}
130121472Sume	default:
131121472Sume		return (-1);
132121472Sume	}
13354696Sshin
134121472Sume	return (0);
13554696Sshin}
13654696Sshin
137121472Sume/* ARGSUSED */
13854696Sshinint
139288045Srodrigcinet6_rthdr_lasthop(struct cmsghdr *cmsg, unsigned int flags)
14054696Sshin{
141121472Sume	struct ip6_rthdr *rthdr;
14254696Sshin
143121472Sume	rthdr = (struct ip6_rthdr *)CMSG_DATA(cmsg);
14457719Sshin
145121472Sume	switch (rthdr->ip6r_type) {
146121472Sume	case IPV6_RTHDR_TYPE_0:
147121472Sume	{
148121472Sume		struct ip6_rthdr0 *rt0 = (struct ip6_rthdr0 *)rthdr;
149121472Sume#ifdef COMPAT_RFC1883		/* XXX */
150121472Sume		if (flags != IPV6_RTHDR_LOOSE && flags != IPV6_RTHDR_STRICT)
151121472Sume			return (-1);
152121472Sume#endif /* COMPAT_RFC1883 */
153121472Sume		if (rt0->ip6r0_segleft > 23)
154121472Sume			return (-1);
155121472Sume#ifdef COMPAT_RFC1883		/* XXX */
156121472Sume		if (flags == IPV6_RTHDR_STRICT) {
157121472Sume			int c, b;
158121472Sume			c = rt0->ip6r0_segleft / 8;
159121472Sume			b = rt0->ip6r0_segleft % 8;
160121472Sume			rt0->ip6r0_slmap[c] |= (1 << (7 - b));
161121472Sume		}
162121472Sume#else
163121472Sume		if (flags != IPV6_RTHDR_LOOSE)
164121472Sume			return (-1);
165121472Sume#endif /* COMPAT_RFC1883 */
166121472Sume		break;
167121472Sume	}
168121472Sume	default:
169121472Sume		return (-1);
170121472Sume	}
17154696Sshin
172121472Sume	return (0);
17354696Sshin}
17454696Sshin
17554696Sshin#if 0
17654696Sshinint
177288045Srodrigcinet6_rthdr_reverse(const struct cmsghdr *in, struct cmsghdr *out)
17854696Sshin{
179121472Sume
180121472Sume	return (-1);
18154696Sshin}
18254696Sshin#endif
18354696Sshin
18454696Sshinint
185288045Srodrigcinet6_rthdr_segments(const struct cmsghdr *cmsg)
18654696Sshin{
187121472Sume	struct ip6_rthdr *rthdr;
18854696Sshin
189121472Sume	rthdr = (struct ip6_rthdr *)CMSG_DATA(cmsg);
19057719Sshin
191121472Sume	switch (rthdr->ip6r_type) {
192121472Sume	case IPV6_RTHDR_TYPE_0:
193121472Sume	{
194121472Sume		struct ip6_rthdr0 *rt0 = (struct ip6_rthdr0 *)rthdr;
19554696Sshin
196121472Sume		if (rt0->ip6r0_len % 2 || 46 < rt0->ip6r0_len)
197121472Sume			return (-1);
198121472Sume
199121472Sume		return (rt0->ip6r0_len * 8) / sizeof(struct in6_addr);
20054696Sshin	}
20154696Sshin
202121472Sume	default:
203121472Sume		return (-1);
204121472Sume	}
20554696Sshin}
20654696Sshin
20754696Sshinstruct in6_addr *
208288045Srodrigcinet6_rthdr_getaddr(struct cmsghdr *cmsg, int idx)
20954696Sshin{
210121472Sume	struct ip6_rthdr *rthdr;
21154696Sshin
212121472Sume	rthdr = (struct ip6_rthdr *)CMSG_DATA(cmsg);
21357719Sshin
214121472Sume	switch (rthdr->ip6r_type) {
215121472Sume	case IPV6_RTHDR_TYPE_0:
216121472Sume	{
217121472Sume		struct ip6_rthdr0 *rt0 = (struct ip6_rthdr0 *)rthdr;
218121472Sume		int naddr;
21954696Sshin
220121472Sume		if (rt0->ip6r0_len % 2 || 46 < rt0->ip6r0_len)
221121472Sume			return NULL;
222121472Sume		naddr = (rt0->ip6r0_len * 8) / sizeof(struct in6_addr);
223121472Sume		if (idx <= 0 || naddr < idx)
224121472Sume			return NULL;
225121472Sume#ifdef COMPAT_RFC2292
226121472Sume		return (((struct in6_addr *)(rt0 + 1)) + idx - 1);
227121472Sume#else
228121472Sume		return (((struct in6_addr *)(rt0 + 1)) + idx);
229121472Sume#endif
23054696Sshin	}
231121472Sume
232121472Sume	default:
233121472Sume		return NULL;
23454696Sshin	}
23554696Sshin}
23654696Sshin
23754696Sshinint
238288045Srodrigcinet6_rthdr_getflags(const struct cmsghdr *cmsg, int idx)
23954696Sshin{
240121472Sume	struct ip6_rthdr *rthdr;
24154696Sshin
242121472Sume	rthdr = (struct ip6_rthdr *)CMSG_DATA(cmsg);
24357719Sshin
244121472Sume	switch (rthdr->ip6r_type) {
245121472Sume	case IPV6_RTHDR_TYPE_0:
246121472Sume	{
247121472Sume		struct ip6_rthdr0 *rt0 = (struct ip6_rthdr0 *)rthdr;
248121472Sume		int naddr;
24954696Sshin
250121472Sume		if (rt0->ip6r0_len % 2 || 46 < rt0->ip6r0_len)
251121472Sume			return (-1);
252121472Sume		naddr = (rt0->ip6r0_len * 8) / sizeof(struct in6_addr);
253121472Sume		if (idx < 0 || naddr < idx)
254121472Sume			return (-1);
255121472Sume#ifdef COMPAT_RFC1883		/* XXX */
256121472Sume		if (rt0->ip6r0_slmap[idx / 8] & (0x80 >> (idx % 8)))
257121472Sume			return IPV6_RTHDR_STRICT;
258121472Sume		else
259121472Sume			return IPV6_RTHDR_LOOSE;
260121472Sume#else
261121472Sume		return IPV6_RTHDR_LOOSE;
262121472Sume#endif /* COMPAT_RFC1883 */
26354696Sshin	}
264121472Sume
265121472Sume	default:
266121472Sume		return (-1);
26754696Sshin	}
268121472Sume}
26954696Sshin
270121472Sume/*
271148160Sume * RFC3542 API
272121472Sume */
273121472Sume
274121472Sumesocklen_t
275121472Sumeinet6_rth_space(int type, int segments)
276121472Sume{
277121472Sume	switch (type) {
278121472Sume	case IPV6_RTHDR_TYPE_0:
279168867Smtm		if ((segments >= 0) && (segments <= 127))
280168867Smtm			return (((segments * 2) + 1) << 3);
281168867Smtm		/* FALLTHROUGH */
282121472Sume	default:
283121472Sume		return (0);	/* type not suppported */
284121472Sume	}
28554696Sshin}
286121472Sume
287121472Sumevoid *
288121472Sumeinet6_rth_init(void *bp, socklen_t bp_len, int type, int segments)
289121472Sume{
290121472Sume	struct ip6_rthdr *rth = (struct ip6_rthdr *)bp;
291121472Sume	struct ip6_rthdr0 *rth0;
292121472Sume
293121472Sume	switch (type) {
294121472Sume	case IPV6_RTHDR_TYPE_0:
295121472Sume		/* length validation */
296121472Sume		if (bp_len < inet6_rth_space(IPV6_RTHDR_TYPE_0, segments))
297121472Sume			return (NULL);
298168867Smtm		/* segment validation */
299168867Smtm		if ((segments < 0) || (segments > 127))
300168867Smtm			return (NULL);
301121472Sume
302121472Sume		memset(bp, 0, bp_len);
303121472Sume		rth0 = (struct ip6_rthdr0 *)rth;
304121472Sume		rth0->ip6r0_len = segments * 2;
305121472Sume		rth0->ip6r0_type = IPV6_RTHDR_TYPE_0;
306121472Sume		rth0->ip6r0_segleft = 0;
307121472Sume		rth0->ip6r0_reserved = 0;
308121472Sume		break;
309121472Sume	default:
310121472Sume		return (NULL);	/* type not supported */
311121472Sume	}
312121472Sume
313121472Sume	return (bp);
314121472Sume}
315121472Sume
316121472Sumeint
317121472Sumeinet6_rth_add(void *bp, const struct in6_addr *addr)
318121472Sume{
319121472Sume	struct ip6_rthdr *rth = (struct ip6_rthdr *)bp;
320121472Sume	struct ip6_rthdr0 *rth0;
321121472Sume	struct in6_addr *nextaddr;
322121472Sume
323121472Sume	switch (rth->ip6r_type) {
324121472Sume	case IPV6_RTHDR_TYPE_0:
325121472Sume		rth0 = (struct ip6_rthdr0 *)rth;
326168867Smtm		/* Don't exceed the number of stated segments */
327168867Smtm		if (rth0->ip6r0_segleft == (rth0->ip6r0_len / 2))
328168867Smtm			return (-1);
329121472Sume		nextaddr = (struct in6_addr *)(rth0 + 1) + rth0->ip6r0_segleft;
330121472Sume		*nextaddr = *addr;
331121472Sume		rth0->ip6r0_segleft++;
332121472Sume		break;
333121472Sume	default:
334121472Sume		return (-1);	/* type not supported */
335121472Sume	}
336121472Sume
337121472Sume	return (0);
338121472Sume}
339121472Sume
340121472Sumeint
341121472Sumeinet6_rth_reverse(const void *in, void *out)
342121472Sume{
343121472Sume	struct ip6_rthdr *rth_in = (struct ip6_rthdr *)in;
344121472Sume	struct ip6_rthdr0 *rth0_in, *rth0_out;
345121472Sume	int i, segments;
346121472Sume
347121472Sume	switch (rth_in->ip6r_type) {
348121472Sume	case IPV6_RTHDR_TYPE_0:
349121472Sume		rth0_in = (struct ip6_rthdr0 *)in;
350121472Sume		rth0_out = (struct ip6_rthdr0 *)out;
351121472Sume
352121472Sume		/* parameter validation XXX too paranoid? */
353121472Sume		if (rth0_in->ip6r0_len % 2)
354121472Sume			return (-1);
355121472Sume		segments = rth0_in->ip6r0_len / 2;
356121472Sume
357121472Sume		/* we can't use memcpy here, since in and out may overlap */
358121472Sume		memmove((void *)rth0_out, (void *)rth0_in,
359121472Sume			((rth0_in->ip6r0_len) + 1) << 3);
360121472Sume		rth0_out->ip6r0_segleft = segments;
361121472Sume
362121472Sume		/* reverse the addresses */
363121472Sume		for (i = 0; i < segments / 2; i++) {
364121472Sume			struct in6_addr addr_tmp, *addr1, *addr2;
365121472Sume
366121472Sume			addr1 = (struct in6_addr *)(rth0_out + 1) + i;
367121472Sume			addr2 = (struct in6_addr *)(rth0_out + 1) +
368121472Sume				(segments - i - 1);
369121472Sume			addr_tmp = *addr1;
370121472Sume			*addr1 = *addr2;
371121472Sume			*addr2 = addr_tmp;
372121472Sume		}
373121472Sume
374121472Sume		break;
375121472Sume	default:
376121472Sume		return (-1);	/* type not supported */
377121472Sume	}
378121472Sume
379121472Sume	return (0);
380121472Sume}
381121472Sume
382121472Sumeint
383121472Sumeinet6_rth_segments(const void *bp)
384121472Sume{
385121472Sume	struct ip6_rthdr *rh = (struct ip6_rthdr *)bp;
386121472Sume	struct ip6_rthdr0 *rh0;
387121472Sume	int addrs;
388121472Sume
389121472Sume	switch (rh->ip6r_type) {
390121472Sume	case IPV6_RTHDR_TYPE_0:
391121472Sume		rh0 = (struct ip6_rthdr0 *)bp;
392121472Sume
393121472Sume		/*
394121472Sume		 * Validation for a type-0 routing header.
395121472Sume		 * Is this too strict?
396121472Sume		 */
397121472Sume		if ((rh0->ip6r0_len % 2) != 0 ||
398121472Sume		    (addrs = (rh0->ip6r0_len >> 1)) < rh0->ip6r0_segleft)
399121472Sume			return (-1);
400121472Sume
401121472Sume		return (addrs);
402121472Sume	default:
403121472Sume		return (-1);	/* unknown type */
404121472Sume	}
405121472Sume}
406121472Sume
407121472Sumestruct in6_addr *
408121472Sumeinet6_rth_getaddr(const void *bp, int idx)
409121472Sume{
410121472Sume	struct ip6_rthdr *rh = (struct ip6_rthdr *)bp;
411121472Sume	struct ip6_rthdr0 *rh0;
412148159Sume	int addrs;
413121472Sume
414121472Sume	switch (rh->ip6r_type) {
415121472Sume	case IPV6_RTHDR_TYPE_0:
416121472Sume		 rh0 = (struct ip6_rthdr0 *)bp;
417121472Sume
418121472Sume		/*
419121472Sume		 * Validation for a type-0 routing header.
420121472Sume		 * Is this too strict?
421121472Sume		 */
422148159Sume		if ((rh0->ip6r0_len % 2) != 0 ||
423148159Sume		    (addrs = (rh0->ip6r0_len >> 1)) < rh0->ip6r0_segleft)
424121472Sume			return (NULL);
425121472Sume
426121472Sume		if (idx < 0 || addrs <= idx)
427121472Sume			return (NULL);
428121472Sume
429121472Sume		return (((struct in6_addr *)(rh0 + 1)) + idx);
430121472Sume	default:
431121472Sume		return (NULL);	/* unknown type */
432121472Sume		break;
433121472Sume	}
434121472Sume}
435