xref: /netbsd-src/lib/libc/net/rthdr.c (revision 27527e67bbdf8d9ec84fd58803048ed6d181ece2)
1 /*	$NetBSD: rthdr.c,v 1.15 2005/11/29 03:12:00 christos 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.15 2005/11/29 03:12:00 christos 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 	rthdr = __UNCONST(CCMSG_DATA(cmsg));
184 
185 	switch (rthdr->ip6r_type) {
186 	case IPV6_RTHDR_TYPE_0:
187 	{
188 		const struct ip6_rthdr0 *rt0 =
189 		    (const struct ip6_rthdr0 *)(const void *)rthdr;
190 
191 		if (rt0->ip6r0_len % 2 || 46 < rt0->ip6r0_len)
192 			return (-1);
193 
194 		return (rt0->ip6r0_len * 8) / sizeof(struct in6_addr);
195 	}
196 
197 	default:
198 		return (-1);
199 	}
200 }
201 
202 struct in6_addr *
203 inet6_rthdr_getaddr(cmsg, idx)
204 	struct cmsghdr *cmsg;
205 	int idx;
206 {
207 	struct ip6_rthdr *rthdr;
208 
209 	_DIAGASSERT(cmsg != NULL);
210 
211 	rthdr = (struct ip6_rthdr *)(void *)CMSG_DATA(cmsg);
212 
213 	switch (rthdr->ip6r_type) {
214 	case IPV6_RTHDR_TYPE_0:
215 	{
216 		struct ip6_rthdr0 *rt0 = (struct ip6_rthdr0 *)(void *)rthdr;
217 		int naddr;
218 
219 		if (rt0->ip6r0_len % 2 || 46 < rt0->ip6r0_len)
220 			return NULL;
221 		naddr = (rt0->ip6r0_len * 8) / sizeof(struct in6_addr);
222 		if (idx <= 0 || naddr < idx)
223 			return NULL;
224 		return ((struct in6_addr *)(void *)(rt0 + 1)) + idx;
225 	}
226 
227 	default:
228 		return NULL;
229 	}
230 }
231 
232 int
233 inet6_rthdr_getflags(cmsg, idx)
234 	const struct cmsghdr *cmsg;
235 	int idx;
236 {
237 	const struct ip6_rthdr *rthdr;
238 
239 	_DIAGASSERT(cmsg != NULL);
240 
241 	rthdr = __UNCONST(CCMSG_DATA(cmsg));
242 
243 	switch (rthdr->ip6r_type) {
244 	case IPV6_RTHDR_TYPE_0:
245 	{
246 		const struct ip6_rthdr0 *rt0 = (const struct ip6_rthdr0 *)
247 		(const void *)rthdr;
248 		int naddr;
249 
250 		if (rt0->ip6r0_len % 2 || 46 < rt0->ip6r0_len)
251 			return (-1);
252 		naddr = (rt0->ip6r0_len * 8) / sizeof(struct in6_addr);
253 		if (idx < 0 || naddr < idx)
254 			return (-1);
255 		return IPV6_RTHDR_LOOSE;
256 	}
257 
258 	default:
259 		return (-1);
260 	}
261 }
262