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