xref: /netbsd-src/lib/libc/net/rthdr.c (revision 21e37cc72a480a47828990a439cde7ac9ffaf0c6)
1 /*	$NetBSD: rthdr.c,v 1.14 2003/06/06 08:13:45 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 #if defined(LIBC_SCCS) && !defined(lint)
34 __RCSID("$NetBSD: rthdr.c,v 1.14 2003/06/06 08:13:45 itojun Exp $");
35 #endif /* LIBC_SCCS and not lint */
36 
37 #include "namespace.h"
38 #include <sys/param.h>
39 #include <sys/types.h>
40 #include <sys/socket.h>
41 
42 #include <netinet/in.h>
43 #include <netinet/ip6.h>
44 
45 #include <assert.h>
46 #include <string.h>
47 #include <stdio.h>
48 
49 #ifdef __weak_alias
50 __weak_alias(inet6_rthdr_add,_inet6_rthdr_add)
51 __weak_alias(inet6_rthdr_getaddr,_inet6_rthdr_getaddr)
52 __weak_alias(inet6_rthdr_getflags,_inet6_rthdr_getflags)
53 __weak_alias(inet6_rthdr_init,_inet6_rthdr_init)
54 __weak_alias(inet6_rthdr_lasthop,_inet6_rthdr_lasthop)
55 __weak_alias(inet6_rthdr_segments,_inet6_rthdr_segments)
56 __weak_alias(inet6_rthdr_space,_inet6_rthdr_space)
57 #endif
58 
59 size_t
60 inet6_rthdr_space(type, seg)
61 	int type, seg;
62 {
63 	switch (type) {
64 	case IPV6_RTHDR_TYPE_0:
65 		if (seg < 1 || seg > 23)
66 			return (0);
67 		return (CMSG_SPACE(sizeof(struct in6_addr) * seg +
68 		    sizeof(struct ip6_rthdr0)));
69 	default:
70 		return (0);
71 	}
72 }
73 
74 struct cmsghdr *
75 inet6_rthdr_init(bp, type)
76 	void *bp;
77 	int type;
78 {
79 	struct cmsghdr *ch;
80 	struct ip6_rthdr *rthdr;
81 
82 	_DIAGASSERT(bp != NULL);
83 
84 	ch = (struct cmsghdr *)bp;
85 	rthdr = (struct ip6_rthdr *)(void *)CMSG_DATA(ch);
86 
87 	ch->cmsg_level = IPPROTO_IPV6;
88 	ch->cmsg_type = IPV6_RTHDR;
89 
90 	switch (type) {
91 	case IPV6_RTHDR_TYPE_0:
92 		ch->cmsg_len = CMSG_LEN(sizeof(struct ip6_rthdr0));
93 		(void)memset(rthdr, 0, sizeof(struct ip6_rthdr0));
94 		rthdr->ip6r_type = IPV6_RTHDR_TYPE_0;
95 		return (ch);
96 	default:
97 		return (NULL);
98 	}
99 }
100 
101 int
102 inet6_rthdr_add(cmsg, addr, flags)
103 	struct cmsghdr *cmsg;
104 	const struct in6_addr *addr;
105 	u_int flags;
106 {
107 	struct ip6_rthdr *rthdr;
108 
109 	_DIAGASSERT(cmsg != NULL);
110 	_DIAGASSERT(addr != NULL);
111 
112 	rthdr = (struct ip6_rthdr *)(void *)CMSG_DATA(cmsg);
113 
114 	switch (rthdr->ip6r_type) {
115 	case IPV6_RTHDR_TYPE_0:
116 	{
117 		struct ip6_rthdr0 *rt0 = (struct ip6_rthdr0 *)(void *)rthdr;
118 		if (flags != IPV6_RTHDR_LOOSE)
119 			return (-1);
120 		if (rt0->ip6r0_segleft == 23)
121 			return (-1);
122 		rt0->ip6r0_segleft++;
123 		(void)memcpy(((caddr_t)(void *)rt0) +
124 		    ((rt0->ip6r0_len + 1) << 3), addr, sizeof(struct in6_addr));
125 		rt0->ip6r0_len += sizeof(struct in6_addr) >> 3;
126 		cmsg->cmsg_len = CMSG_LEN((rt0->ip6r0_len + 1) << 3);
127 		break;
128 	}
129 	default:
130 		return (-1);
131 	}
132 
133 	return (0);
134 }
135 
136 int
137 inet6_rthdr_lasthop(cmsg, flags)
138 	struct cmsghdr *cmsg;
139 	unsigned int flags;
140 {
141 	struct ip6_rthdr *rthdr;
142 
143 	_DIAGASSERT(cmsg != NULL);
144 
145 	rthdr = (struct ip6_rthdr *)(void *)CMSG_DATA(cmsg);
146 
147 	switch (rthdr->ip6r_type) {
148 	case IPV6_RTHDR_TYPE_0:
149 	{
150 		struct ip6_rthdr0 *rt0 = (struct ip6_rthdr0 *)(void *)rthdr;
151 		if (flags != IPV6_RTHDR_LOOSE)
152 			return (-1);
153 		if (rt0->ip6r0_segleft > 23)
154 			return (-1);
155 		break;
156 	}
157 	default:
158 		return (-1);
159 	}
160 
161 	return (0);
162 }
163 
164 #if 0
165 int
166 inet6_rthdr_reverse(in, out)
167 	const struct cmsghdr *in;
168 	struct cmsghdr *out;
169 {
170 
171 	return (-1);
172 }
173 #endif
174 
175 int
176 inet6_rthdr_segments(cmsg)
177 	const struct cmsghdr *cmsg;
178 {
179 	const struct ip6_rthdr *rthdr;
180 
181 	_DIAGASSERT(cmsg != NULL);
182 
183 	/*LINTED const castaway*/
184 	rthdr = (const struct ip6_rthdr *)(const void *)CMSG_DATA(cmsg);
185 
186 	switch (rthdr->ip6r_type) {
187 	case IPV6_RTHDR_TYPE_0:
188 	{
189 		const struct ip6_rthdr0 *rt0 =
190 		    (const struct ip6_rthdr0 *)(const void *)rthdr;
191 
192 		if (rt0->ip6r0_len % 2 || 46 < rt0->ip6r0_len)
193 			return (-1);
194 
195 		return (rt0->ip6r0_len * 8) / sizeof(struct in6_addr);
196 	}
197 
198 	default:
199 		return (-1);
200 	}
201 }
202 
203 struct in6_addr *
204 inet6_rthdr_getaddr(cmsg, idx)
205 	struct cmsghdr *cmsg;
206 	int idx;
207 {
208 	struct ip6_rthdr *rthdr;
209 
210 	_DIAGASSERT(cmsg != NULL);
211 
212 	rthdr = (struct ip6_rthdr *)(void *)CMSG_DATA(cmsg);
213 
214 	switch (rthdr->ip6r_type) {
215 	case IPV6_RTHDR_TYPE_0:
216 	{
217 		struct ip6_rthdr0 *rt0 = (struct ip6_rthdr0 *)(void *)rthdr;
218 		int naddr;
219 
220 		if (rt0->ip6r0_len % 2 || 46 < rt0->ip6r0_len)
221 			return NULL;
222 		naddr = (rt0->ip6r0_len * 8) / sizeof(struct in6_addr);
223 		if (idx <= 0 || naddr < idx)
224 			return NULL;
225 		return ((struct in6_addr *)(void *)(rt0 + 1)) + idx;
226 	}
227 
228 	default:
229 		return NULL;
230 	}
231 }
232 
233 int
234 inet6_rthdr_getflags(cmsg, idx)
235 	const struct cmsghdr *cmsg;
236 	int idx;
237 {
238 	const struct ip6_rthdr *rthdr;
239 
240 	_DIAGASSERT(cmsg != NULL);
241 
242 	/*LINTED const castaway*/
243 	rthdr = (const struct ip6_rthdr *)(const void *)CMSG_DATA(cmsg);
244 
245 	switch (rthdr->ip6r_type) {
246 	case IPV6_RTHDR_TYPE_0:
247 	{
248 		const struct ip6_rthdr0 *rt0 = (const struct ip6_rthdr0 *)
249 		(const void *)rthdr;
250 		int naddr;
251 
252 		if (rt0->ip6r0_len % 2 || 46 < rt0->ip6r0_len)
253 			return (-1);
254 		naddr = (rt0->ip6r0_len * 8) / sizeof(struct in6_addr);
255 		if (idx < 0 || naddr < idx)
256 			return (-1);
257 		return IPV6_RTHDR_LOOSE;
258 	}
259 
260 	default:
261 		return (-1);
262 	}
263 }
264