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