Merge from vendor branch FILE:
[dragonfly.git] / lib / libc / net / rthdr.c
1 /*      $KAME: rthdr.c,v 1.8 2001/08/20 02:32:40 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  * $FreeBSD: src/lib/libc/net/rthdr.c,v 1.2.2.1 2002/04/28 05:40:24 suz Exp $
32  * $DragonFly: src/lib/libc/net/rthdr.c,v 1.4 2005/11/13 02:04:47 swildner Exp $
33  */
34
35 #include <sys/param.h>
36 #include <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 size_t
46 inet6_rthdr_space(int type, int seg)
47 {
48     switch(type) {
49      case IPV6_RTHDR_TYPE_0:
50          if (seg < 1 || seg > 23)
51              return(0);
52          return(CMSG_SPACE(sizeof(struct in6_addr) * (seg - 1)
53                            + sizeof(struct ip6_rthdr0)));
54      default:
55 #ifdef DEBUG
56          fprintf(stderr, "inet6_rthdr_space: unknown type(%d)\n", type);
57 #endif 
58          return(0);
59     }
60 }
61
62 struct cmsghdr *
63 inet6_rthdr_init(void *bp, int type)
64 {
65     struct cmsghdr *ch = (struct cmsghdr *)bp;
66     struct ip6_rthdr *rthdr;
67
68     rthdr = (struct ip6_rthdr *)CMSG_DATA(ch);
69
70     ch->cmsg_level = IPPROTO_IPV6;
71     ch->cmsg_type = IPV6_RTHDR;
72
73     switch(type) {
74      case IPV6_RTHDR_TYPE_0:
75          ch->cmsg_len = CMSG_LEN(sizeof(struct ip6_rthdr0) - sizeof(struct in6_addr));
76          bzero(rthdr, sizeof(struct ip6_rthdr0));
77          rthdr->ip6r_type = IPV6_RTHDR_TYPE_0;
78          return(ch);
79      default:
80 #ifdef DEBUG
81          fprintf(stderr, "inet6_rthdr_init: unknown type(%d)\n", type);
82 #endif 
83          return(NULL);
84     }
85 }
86
87 int
88 inet6_rthdr_add(struct cmsghdr *cmsg, const struct in6_addr *addr, u_int flags)
89 {
90     struct ip6_rthdr *rthdr;
91
92     rthdr = (struct ip6_rthdr *)CMSG_DATA(cmsg);
93
94     switch(rthdr->ip6r_type) {
95      case IPV6_RTHDR_TYPE_0:
96      {
97          struct ip6_rthdr0 *rt0 = (struct ip6_rthdr0 *)rthdr;
98          if (flags != IPV6_RTHDR_LOOSE && flags != IPV6_RTHDR_STRICT) {
99 #ifdef DEBUG
100              fprintf(stderr, "inet6_rthdr_add: unsupported flag(%d)\n", flags);
101 #endif 
102              return(-1);
103          }
104          if (rt0->ip6r0_segleft == 23) {
105 #ifdef DEBUG
106              fprintf(stderr, "inet6_rthdr_add: segment overflow\n");
107 #endif 
108              return(-1);
109          }
110          if (flags == IPV6_RTHDR_STRICT) {
111              int c, b;
112              c = rt0->ip6r0_segleft / 8;
113              b = rt0->ip6r0_segleft % 8;
114              rt0->ip6r0_slmap[c] |= (1 << (7 - b));
115          }
116          rt0->ip6r0_segleft++;
117          bcopy(addr, (caddr_t)rt0 + ((rt0->ip6r0_len + 1) << 3),
118                sizeof(struct in6_addr));
119          rt0->ip6r0_len += sizeof(struct in6_addr) >> 3;
120          cmsg->cmsg_len = CMSG_LEN((rt0->ip6r0_len + 1) << 3);
121          break;
122      }
123      default:
124 #ifdef DEBUG
125          fprintf(stderr, "inet6_rthdr_add: unknown type(%d)\n",
126                  rthdr->ip6r_type);
127 #endif 
128          return(-1);
129     }
130
131     return(0);
132 }
133
134 int
135 inet6_rthdr_lasthop(struct cmsghdr *cmsg, unsigned int flags)
136 {
137     struct ip6_rthdr *rthdr;
138
139     rthdr = (struct ip6_rthdr *)CMSG_DATA(cmsg);
140
141     switch(rthdr->ip6r_type) {
142      case IPV6_RTHDR_TYPE_0:
143      {
144          struct ip6_rthdr0 *rt0 = (struct ip6_rthdr0 *)rthdr;
145          if (flags != IPV6_RTHDR_LOOSE && flags != IPV6_RTHDR_STRICT) {
146 #ifdef DEBUG
147              fprintf(stderr, "inet6_rthdr_lasthop: unsupported flag(%d)\n", flags);
148 #endif 
149              return(-1);
150          }
151          if (rt0->ip6r0_segleft > 23) {
152 #ifdef DEBUG
153              fprintf(stderr, "inet6_rthdr_add: segment overflow\n");
154 #endif 
155              return(-1);
156          }
157          if (flags == IPV6_RTHDR_STRICT) {
158              int c, b;
159              c = rt0->ip6r0_segleft / 8;
160              b = rt0->ip6r0_segleft % 8;
161              rt0->ip6r0_slmap[c] |= (1 << (7 - b));
162          }
163          break;
164      }
165      default:
166 #ifdef DEBUG
167          fprintf(stderr, "inet6_rthdr_lasthop: unknown type(%d)\n",
168                  rthdr->ip6r_type);
169 #endif 
170          return(-1);
171     }
172
173     return(0);
174 }
175
176 #if 0
177 int
178 inet6_rthdr_reverse(const struct cmsghdr *in, struct cmsghdr *out)
179 {
180 #ifdef DEBUG
181     fprintf(stderr, "inet6_rthdr_reverse: not implemented yet\n");
182 #endif 
183     return -1;
184 }
185 #endif
186
187 int
188 inet6_rthdr_segments(const struct cmsghdr *cmsg)
189 {
190     struct ip6_rthdr *rthdr;
191
192     rthdr = (struct ip6_rthdr *)CMSG_DATA(cmsg);
193
194     switch(rthdr->ip6r_type) {
195     case IPV6_RTHDR_TYPE_0:
196       {
197         struct ip6_rthdr0 *rt0 = (struct ip6_rthdr0 *)rthdr;
198
199         if (rt0->ip6r0_len % 2 || 46 < rt0->ip6r0_len) {
200 #ifdef DEBUG
201             fprintf(stderr, "inet6_rthdr_segments: invalid size(%d)\n",
202                 rt0->ip6r0_len);
203 #endif 
204             return -1;
205         }
206
207         return (rt0->ip6r0_len * 8) / sizeof(struct in6_addr);
208       }
209
210     default:
211 #ifdef DEBUG
212         fprintf(stderr, "inet6_rthdr_segments: unknown type(%d)\n",
213             rthdr->ip6r_type);
214 #endif 
215         return -1;
216     }
217 }
218
219 struct in6_addr *
220 inet6_rthdr_getaddr(struct cmsghdr *cmsg, int idx)
221 {
222     struct ip6_rthdr *rthdr;
223
224     rthdr = (struct ip6_rthdr *)CMSG_DATA(cmsg);
225
226     switch(rthdr->ip6r_type) {
227     case IPV6_RTHDR_TYPE_0:
228       {
229         struct ip6_rthdr0 *rt0 = (struct ip6_rthdr0 *)rthdr;
230         int naddr;
231
232         if (rt0->ip6r0_len % 2 || 46 < rt0->ip6r0_len) {
233 #ifdef DEBUG
234             fprintf(stderr, "inet6_rthdr_getaddr: invalid size(%d)\n",
235                 rt0->ip6r0_len);
236 #endif 
237             return NULL;
238         }
239         naddr = (rt0->ip6r0_len * 8) / sizeof(struct in6_addr);
240         if (idx <= 0 || naddr < idx) {
241 #ifdef DEBUG
242             fprintf(stderr, "inet6_rthdr_getaddr: invalid idx(%d)\n", idx);
243 #endif 
244             return NULL;
245         }
246         return &rt0->ip6r0_addr[idx - 1];
247       }
248
249     default:
250 #ifdef DEBUG
251         fprintf(stderr, "inet6_rthdr_getaddr: unknown type(%d)\n",
252             rthdr->ip6r_type);
253 #endif 
254         return NULL;
255     }
256 }
257
258 int
259 inet6_rthdr_getflags(const struct cmsghdr *cmsg, int idx)
260 {
261     struct ip6_rthdr *rthdr;
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_getflags: invalid size(%d)\n",
274                 rt0->ip6r0_len);
275 #endif 
276             return -1;
277         }
278         naddr = (rt0->ip6r0_len * 8) / sizeof(struct in6_addr);
279         if (idx < 0 || naddr < idx) {
280 #ifdef DEBUG
281             fprintf(stderr, "inet6_rthdr_getflags: invalid idx(%d)\n", idx);
282 #endif 
283             return -1;
284         }
285         if (rt0->ip6r0_slmap[idx / 8] & (0x80 >> (idx % 8)))
286             return IPV6_RTHDR_STRICT;
287         else
288             return IPV6_RTHDR_LOOSE;
289       }
290
291     default:
292 #ifdef DEBUG
293         fprintf(stderr, "inet6_rthdr_getflags: unknown type(%d)\n",
294             rthdr->ip6r_type);
295 #endif 
296         return -1;
297     }
298 }