source: rtems-libbsd/freebsd/lib/libc/net/rthdr.c @ 8a5d1da

4.1155-freebsd-126-freebsd-12freebsd-9.3
Last change on this file since 8a5d1da was 8a5d1da, checked in by Sebastian Huber <sebastian.huber@…>, on 10/31/13 at 09:46:08

lib/libc/net/rthdr.c: Add from FreeBSD

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