rthdr.c revision 1.9 1 /* $NetBSD: rthdr.c,v 1.9 2000/04/24 10:40:25 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.9 2000/04/24 10:40:25 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 - 1)
68 + sizeof(struct ip6_rthdr0)));
69 default:
70 #ifdef DEBUG
71 fprintf(stderr, "inet6_rthdr_space: unknown type(%d)\n", type);
72 #endif
73 return(0);
74 }
75 }
76
77 struct cmsghdr *
78 inet6_rthdr_init(bp, type)
79 void *bp;
80 int type;
81 {
82 register struct cmsghdr *ch;
83 register struct ip6_rthdr *rthdr;
84
85 _DIAGASSERT(bp != NULL);
86
87 ch = (struct cmsghdr *)bp;
88 rthdr = (struct ip6_rthdr *)CMSG_DATA(ch);
89
90 ch->cmsg_level = IPPROTO_IPV6;
91 ch->cmsg_type = IPV6_RTHDR;
92
93 switch(type) {
94 case IPV6_RTHDR_TYPE_0:
95 ch->cmsg_len = CMSG_LEN(sizeof(struct ip6_rthdr0) - sizeof(struct in6_addr));
96 (void)memset(rthdr, 0, sizeof(struct ip6_rthdr0));
97 rthdr->ip6r_type = IPV6_RTHDR_TYPE_0;
98 return(ch);
99 default:
100 #ifdef DEBUG
101 fprintf(stderr, "inet6_rthdr_init: unknown type(%d)\n", type);
102 #endif
103 return(NULL);
104 }
105 }
106
107 int
108 inet6_rthdr_add(cmsg, addr, flags)
109 struct cmsghdr *cmsg;
110 const struct in6_addr *addr;
111 u_int flags;
112 {
113 register struct ip6_rthdr *rthdr;
114
115 _DIAGASSERT(cmsg != NULL);
116 _DIAGASSERT(addr != NULL);
117
118 rthdr = (struct ip6_rthdr *)CMSG_DATA(cmsg);
119
120 switch(rthdr->ip6r_type) {
121 case IPV6_RTHDR_TYPE_0:
122 {
123 struct ip6_rthdr0 *rt0 = (struct ip6_rthdr0 *)rthdr;
124 if (flags != IPV6_RTHDR_LOOSE && flags != IPV6_RTHDR_STRICT) {
125 #ifdef DEBUG
126 fprintf(stderr, "inet6_rthdr_add: unsupported flag(%d)\n", flags);
127 #endif
128 return(-1);
129 }
130 if (rt0->ip6r0_segleft == 23) {
131 #ifdef DEBUG
132 fprintf(stderr, "inet6_rthdr_add: segment overflow\n");
133 #endif
134 return(-1);
135 }
136 if (flags == IPV6_RTHDR_STRICT) {
137 int c, b;
138 c = rt0->ip6r0_segleft / 8;
139 b = rt0->ip6r0_segleft % 8;
140 rt0->ip6r0_slmap[c] |= (1 << (7 - b));
141 }
142 rt0->ip6r0_segleft++;
143 (void)memcpy((caddr_t)rt0 + ((rt0->ip6r0_len + 1) << 3), addr,
144 sizeof(struct in6_addr));
145 rt0->ip6r0_len += sizeof(struct in6_addr) >> 3;
146 cmsg->cmsg_len = CMSG_LEN((rt0->ip6r0_len + 1) << 3);
147 break;
148 }
149 default:
150 #ifdef DEBUG
151 fprintf(stderr, "inet6_rthdr_add: unknown type(%d)\n",
152 rthdr->ip6r_type);
153 #endif
154 return(-1);
155 }
156
157 return(0);
158 }
159
160 int
161 inet6_rthdr_lasthop(cmsg, flags)
162 struct cmsghdr *cmsg;
163 unsigned int flags;
164 {
165 register struct ip6_rthdr *rthdr;
166
167 _DIAGASSERT(cmsg != NULL);
168
169 rthdr = (struct ip6_rthdr *)CMSG_DATA(cmsg);
170
171 switch(rthdr->ip6r_type) {
172 case IPV6_RTHDR_TYPE_0:
173 {
174 struct ip6_rthdr0 *rt0 = (struct ip6_rthdr0 *)rthdr;
175 if (flags != IPV6_RTHDR_LOOSE && flags != IPV6_RTHDR_STRICT) {
176 #ifdef DEBUG
177 fprintf(stderr, "inet6_rthdr_lasthop: unsupported flag(%d)\n", flags);
178 #endif
179 return(-1);
180 }
181 if (rt0->ip6r0_segleft > 23) {
182 #ifdef DEBUG
183 fprintf(stderr, "inet6_rthdr_add: segment overflow\n");
184 #endif
185 return(-1);
186 }
187 if (flags == IPV6_RTHDR_STRICT) {
188 int c, b;
189 c = rt0->ip6r0_segleft / 8;
190 b = rt0->ip6r0_segleft % 8;
191 rt0->ip6r0_slmap[c] |= (1 << (7 - b));
192 }
193 break;
194 }
195 default:
196 #ifdef DEBUG
197 fprintf(stderr, "inet6_rthdr_lasthop: unknown type(%d)\n",
198 rthdr->ip6r_type);
199 #endif
200 return(-1);
201 }
202
203 return(0);
204 }
205
206 #if 0
207 int
208 inet6_rthdr_reverse(in, out)
209 const struct cmsghdr *in;
210 struct cmsghdr *out;
211 {
212 #ifdef DEBUG
213 fprintf(stderr, "inet6_rthdr_reverse: not implemented yet\n");
214 #endif
215 return -1;
216 }
217 #endif
218
219 int
220 inet6_rthdr_segments(cmsg)
221 const struct cmsghdr *cmsg;
222 {
223 const register struct ip6_rthdr *rthdr;
224
225 _DIAGASSERT(cmsg != NULL);
226
227 rthdr = (const struct ip6_rthdr *)CMSG_DATA(cmsg);
228
229 switch(rthdr->ip6r_type) {
230 case IPV6_RTHDR_TYPE_0:
231 {
232 const struct ip6_rthdr0 *rt0 = (const struct ip6_rthdr0 *)rthdr;
233
234 if (rt0->ip6r0_len % 2 || 46 < rt0->ip6r0_len) {
235 #ifdef DEBUG
236 fprintf(stderr, "inet6_rthdr_segments: invalid size(%d)\n",
237 rt0->ip6r0_len);
238 #endif
239 return -1;
240 }
241
242 return (rt0->ip6r0_len * 8) / sizeof(struct in6_addr);
243 }
244
245 default:
246 #ifdef DEBUG
247 fprintf(stderr, "inet6_rthdr_segments: unknown type(%d)\n",
248 rthdr->ip6r_type);
249 #endif
250 return -1;
251 }
252 }
253
254 struct in6_addr *
255 inet6_rthdr_getaddr(cmsg, index)
256 struct cmsghdr *cmsg;
257 int index;
258 {
259 register struct ip6_rthdr *rthdr;
260
261 _DIAGASSERT(cmsg != NULL);
262
263 rthdr = (struct ip6_rthdr *)CMSG_DATA(cmsg);
264
265 switch(rthdr->ip6r_type) {
266 case IPV6_RTHDR_TYPE_0:
267 {
268 struct ip6_rthdr0 *rt0 = (struct ip6_rthdr0 *)rthdr;
269 int naddr;
270
271 if (rt0->ip6r0_len % 2 || 46 < rt0->ip6r0_len) {
272 #ifdef DEBUG
273 fprintf(stderr, "inet6_rthdr_getaddr: invalid size(%d)\n",
274 rt0->ip6r0_len);
275 #endif
276 return NULL;
277 }
278 naddr = (rt0->ip6r0_len * 8) / sizeof(struct in6_addr);
279 if (index <= 0 || naddr < index) {
280 #ifdef DEBUG
281 fprintf(stderr, "inet6_rthdr_getaddr: invalid index(%d)\n", index);
282 #endif
283 return NULL;
284 }
285 return &rt0->ip6r0_addr[index - 1];
286 }
287
288 default:
289 #ifdef DEBUG
290 fprintf(stderr, "inet6_rthdr_getaddr: unknown type(%d)\n",
291 rthdr->ip6r_type);
292 #endif
293 return NULL;
294 }
295 }
296
297 int
298 inet6_rthdr_getflags(cmsg, index)
299 const struct cmsghdr *cmsg;
300 int index;
301 {
302 const register struct ip6_rthdr *rthdr;
303
304 _DIAGASSERT(cmsg != NULL);
305
306 rthdr = (const struct ip6_rthdr *)CMSG_DATA(cmsg);
307
308 switch(rthdr->ip6r_type) {
309 case IPV6_RTHDR_TYPE_0:
310 {
311 const struct ip6_rthdr0 *rt0 = (const struct ip6_rthdr0 *)rthdr;
312 int naddr;
313
314 if (rt0->ip6r0_len % 2 || 46 < rt0->ip6r0_len) {
315 #ifdef DEBUG
316 fprintf(stderr, "inet6_rthdr_getflags: invalid size(%d)\n",
317 rt0->ip6r0_len);
318 #endif
319 return -1;
320 }
321 naddr = (rt0->ip6r0_len * 8) / sizeof(struct in6_addr);
322 if (index < 0 || naddr < index) {
323 #ifdef DEBUG
324 fprintf(stderr, "inet6_rthdr_getflags: invalid index(%d)\n", index);
325 #endif
326 return -1;
327 }
328 if (rt0->ip6r0_slmap[index / 8] & (0x80 >> (index % 8)))
329 return IPV6_RTHDR_STRICT;
330 else
331 return IPV6_RTHDR_LOOSE;
332 }
333
334 default:
335 #ifdef DEBUG
336 fprintf(stderr, "inet6_rthdr_getflags: unknown type(%d)\n",
337 rthdr->ip6r_type);
338 #endif
339 return -1;
340 }
341 }
342