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