xref: /dragonfly/lib/libc/net/rthdr.c (revision 68b2c890)
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.5 2007/05/29 10:58:11 hasso 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 }
299 
300 /*
301  * RFC3542 (2292bis) API
302  */
303 
304 socklen_t
305 inet6_rth_space(int type __unused, int segments __unused)
306 {
307 	return (0);	/* type not suppported */
308 }
309 
310 void *
311 inet6_rth_init(void *bp __unused, socklen_t bp_len __unused, int type __unused,
312 	       int segments __unused)
313 {
314 	return (NULL);	/* type not supported */
315 }
316 
317 int
318 inet6_rth_add(void *bp __unused, const struct in6_addr *addr __unused)
319 {
320 	return (-1);	/* type not supported */
321 }
322 
323 int
324 inet6_rth_reverse(const void *in __unused, void *out __unused)
325 {
326 	return (-1);	/* type not supported */
327 }
328 
329 int
330 inet6_rth_segments(const void *bp __unused)
331 {
332 	return (-1);	/* type not supported */
333 }
334 
335 struct in6_addr *
336 inet6_rth_getaddr(const void *bp __unused, int idx __unused)
337 {
338 	return (NULL);	/* type not supported */
339 }
340