xref: /freebsd/lib/libc/net/rthdr.c (revision c17d43407fe04133a94055b0dbc7ea8965654a9f)
1 /*
2  * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project.
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  * 1. Redistributions of source code must retain the above copyright
9  *    notice, this list of conditions and the following disclaimer.
10  * 2. Redistributions in binary form must reproduce the above copyright
11  *    notice, this list of conditions and the following disclaimer in the
12  *    documentation and/or other materials provided with the distribution.
13  * 3. Neither the name of the project nor the names of its contributors
14  *    may be used to endorse or promote products derived from this software
15  *    without specific prior written permission.
16  *
17  * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND
18  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20  * ARE DISCLAIMED.  IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE
21  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27  * SUCH DAMAGE.
28  */
29 
30 #include <sys/cdefs.h>
31 __FBSDID("$FreeBSD$");
32 
33 #include <sys/param.h>
34 #include <sys/types.h>
35 #include <sys/socket.h>
36 
37 #include <netinet/in.h>
38 #include <netinet/ip6.h>
39 
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     struct cmsghdr *ch = (struct cmsghdr *)bp;
67     struct ip6_rthdr *rthdr;
68 
69     rthdr = (struct ip6_rthdr *)CMSG_DATA(ch);
70 
71     ch->cmsg_level = IPPROTO_IPV6;
72     ch->cmsg_type = IPV6_RTHDR;
73 
74     switch(type) {
75      case IPV6_RTHDR_TYPE_0:
76 	 ch->cmsg_len = CMSG_LEN(sizeof(struct ip6_rthdr0) - sizeof(struct in6_addr));
77 	 bzero(rthdr, sizeof(struct ip6_rthdr0));
78 	 rthdr->ip6r_type = IPV6_RTHDR_TYPE_0;
79 	 return(ch);
80      default:
81 #ifdef DEBUG
82 	 fprintf(stderr, "inet6_rthdr_init: unknown type(%d)\n", type);
83 #endif
84 	 return(NULL);
85     }
86 }
87 
88 int
89 inet6_rthdr_add(cmsg, addr, flags)
90     struct cmsghdr *cmsg;
91     const struct in6_addr *addr;
92     u_int flags;
93 {
94     struct ip6_rthdr *rthdr;
95 
96     rthdr = (struct ip6_rthdr *)CMSG_DATA(cmsg);
97 
98     switch(rthdr->ip6r_type) {
99      case IPV6_RTHDR_TYPE_0:
100      {
101 	 struct ip6_rthdr0 *rt0 = (struct ip6_rthdr0 *)rthdr;
102 	 if (flags != IPV6_RTHDR_LOOSE && flags != IPV6_RTHDR_STRICT) {
103 #ifdef DEBUG
104 	     fprintf(stderr, "inet6_rthdr_add: unsupported flag(%d)\n", flags);
105 #endif
106 	     return(-1);
107 	 }
108 	 if (rt0->ip6r0_segleft == 23) {
109 #ifdef DEBUG
110 	     fprintf(stderr, "inet6_rthdr_add: segment overflow\n");
111 #endif
112 	     return(-1);
113 	 }
114 	 if (flags == IPV6_RTHDR_STRICT) {
115 	     int c, b;
116 	     c = rt0->ip6r0_segleft / 8;
117 	     b = rt0->ip6r0_segleft % 8;
118 	     rt0->ip6r0_slmap[c] |= (1 << (7 - b));
119 	 }
120 	 rt0->ip6r0_segleft++;
121 	 bcopy(addr, (caddr_t)rt0 + ((rt0->ip6r0_len + 1) << 3),
122 	       sizeof(struct in6_addr));
123 	 rt0->ip6r0_len += sizeof(struct in6_addr) >> 3;
124 	 cmsg->cmsg_len = CMSG_LEN((rt0->ip6r0_len + 1) << 3);
125 	 break;
126      }
127      default:
128 #ifdef DEBUG
129 	 fprintf(stderr, "inet6_rthdr_add: unknown type(%d)\n",
130 		 rthdr->ip6r_type);
131 #endif
132 	 return(-1);
133     }
134 
135     return(0);
136 }
137 
138 int
139 inet6_rthdr_lasthop(cmsg, flags)
140     struct cmsghdr *cmsg;
141     unsigned int flags;
142 {
143     struct ip6_rthdr *rthdr;
144 
145     rthdr = (struct ip6_rthdr *)CMSG_DATA(cmsg);
146 
147     switch(rthdr->ip6r_type) {
148      case IPV6_RTHDR_TYPE_0:
149      {
150 	 struct ip6_rthdr0 *rt0 = (struct ip6_rthdr0 *)rthdr;
151 	 if (flags != IPV6_RTHDR_LOOSE && flags != IPV6_RTHDR_STRICT) {
152 #ifdef DEBUG
153 	     fprintf(stderr, "inet6_rthdr_lasthop: unsupported flag(%d)\n", flags);
154 #endif
155 	     return(-1);
156 	 }
157 	 if (rt0->ip6r0_segleft > 23) {
158 #ifdef DEBUG
159 	     fprintf(stderr, "inet6_rthdr_add: segment overflow\n");
160 #endif
161 	     return(-1);
162 	 }
163 	 if (flags == IPV6_RTHDR_STRICT) {
164 	     int c, b;
165 	     c = rt0->ip6r0_segleft / 8;
166 	     b = rt0->ip6r0_segleft % 8;
167 	     rt0->ip6r0_slmap[c] |= (1 << (7 - b));
168 	 }
169 	 break;
170      }
171      default:
172 #ifdef DEBUG
173 	 fprintf(stderr, "inet6_rthdr_lasthop: unknown type(%d)\n",
174 		 rthdr->ip6r_type);
175 #endif
176 	 return(-1);
177     }
178 
179     return(0);
180 }
181 
182 #if 0
183 int
184 inet6_rthdr_reverse(in, out)
185     const struct cmsghdr *in;
186     struct cmsghdr *out;
187 {
188 #ifdef DEBUG
189     fprintf(stderr, "inet6_rthdr_reverse: not implemented yet\n");
190 #endif
191     return -1;
192 }
193 #endif
194 
195 int
196 inet6_rthdr_segments(cmsg)
197     const struct cmsghdr *cmsg;
198 {
199     struct ip6_rthdr *rthdr;
200 
201     rthdr = (struct ip6_rthdr *)CMSG_DATA(cmsg);
202 
203     switch(rthdr->ip6r_type) {
204     case IPV6_RTHDR_TYPE_0:
205       {
206 	struct ip6_rthdr0 *rt0 = (struct ip6_rthdr0 *)rthdr;
207 
208 	if (rt0->ip6r0_len % 2 || 46 < rt0->ip6r0_len) {
209 #ifdef DEBUG
210 	    fprintf(stderr, "inet6_rthdr_segments: invalid size(%d)\n",
211 		rt0->ip6r0_len);
212 #endif
213 	    return -1;
214 	}
215 
216 	return (rt0->ip6r0_len * 8) / sizeof(struct in6_addr);
217       }
218 
219     default:
220 #ifdef DEBUG
221 	fprintf(stderr, "inet6_rthdr_segments: unknown type(%d)\n",
222 	    rthdr->ip6r_type);
223 #endif
224 	return -1;
225     }
226 }
227 
228 struct in6_addr *
229 inet6_rthdr_getaddr(cmsg, index)
230     struct cmsghdr *cmsg;
231     int index;
232 {
233     struct ip6_rthdr *rthdr;
234 
235     rthdr = (struct ip6_rthdr *)CMSG_DATA(cmsg);
236 
237     switch(rthdr->ip6r_type) {
238     case IPV6_RTHDR_TYPE_0:
239       {
240 	struct ip6_rthdr0 *rt0 = (struct ip6_rthdr0 *)rthdr;
241 	int naddr;
242 
243 	if (rt0->ip6r0_len % 2 || 46 < rt0->ip6r0_len) {
244 #ifdef DEBUG
245 	    fprintf(stderr, "inet6_rthdr_getaddr: invalid size(%d)\n",
246 		rt0->ip6r0_len);
247 #endif
248 	    return NULL;
249 	}
250 	naddr = (rt0->ip6r0_len * 8) / sizeof(struct in6_addr);
251 	if (index <= 0 || naddr < index) {
252 #ifdef DEBUG
253 	    fprintf(stderr, "inet6_rthdr_getaddr: invalid index(%d)\n", index);
254 #endif
255 	    return NULL;
256 	}
257 	return &rt0->ip6r0_addr[index - 1];
258       }
259 
260     default:
261 #ifdef DEBUG
262 	fprintf(stderr, "inet6_rthdr_getaddr: unknown type(%d)\n",
263 	    rthdr->ip6r_type);
264 #endif
265 	return NULL;
266     }
267 }
268 
269 int
270 inet6_rthdr_getflags(cmsg, index)
271     const struct cmsghdr *cmsg;
272     int index;
273 {
274     struct ip6_rthdr *rthdr;
275 
276     rthdr = (struct ip6_rthdr *)CMSG_DATA(cmsg);
277 
278     switch(rthdr->ip6r_type) {
279     case IPV6_RTHDR_TYPE_0:
280       {
281 	struct ip6_rthdr0 *rt0 = (struct ip6_rthdr0 *)rthdr;
282 	int naddr;
283 
284 	if (rt0->ip6r0_len % 2 || 46 < rt0->ip6r0_len) {
285 #ifdef DEBUG
286 	    fprintf(stderr, "inet6_rthdr_getflags: invalid size(%d)\n",
287 		rt0->ip6r0_len);
288 #endif
289 	    return -1;
290 	}
291 	naddr = (rt0->ip6r0_len * 8) / sizeof(struct in6_addr);
292 	if (index < 0 || naddr < index) {
293 #ifdef DEBUG
294 	    fprintf(stderr, "inet6_rthdr_getflags: invalid index(%d)\n", index);
295 #endif
296 	    return -1;
297 	}
298 	if (rt0->ip6r0_slmap[index / 8] & (0x80 >> (index % 8)))
299 	    return IPV6_RTHDR_STRICT;
300 	else
301 	    return IPV6_RTHDR_LOOSE;
302       }
303 
304     default:
305 #ifdef DEBUG
306 	fprintf(stderr, "inet6_rthdr_getflags: unknown type(%d)\n",
307 	    rthdr->ip6r_type);
308 #endif
309 	return -1;
310     }
311 }
312