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