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