rthdr.c revision 1.13 1 /* $NetBSD: rthdr.c,v 1.13 2003/06/06 06:43:18 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/cdefs.h>
33 #if defined(LIBC_SCCS) && !defined(lint)
34 __RCSID("$NetBSD: rthdr.c,v 1.13 2003/06/06 06:43:18 itojun Exp $");
35 #endif /* LIBC_SCCS and not lint */
36
37 #include "namespace.h"
38 #include <sys/param.h>
39 #include <sys/types.h>
40 #include <sys/socket.h>
41
42 #include <netinet/in.h>
43 #include <netinet/ip6.h>
44
45 #include <assert.h>
46 #include <string.h>
47 #include <stdio.h>
48
49 #ifdef __weak_alias
50 __weak_alias(inet6_rthdr_add,_inet6_rthdr_add)
51 __weak_alias(inet6_rthdr_getaddr,_inet6_rthdr_getaddr)
52 __weak_alias(inet6_rthdr_getflags,_inet6_rthdr_getflags)
53 __weak_alias(inet6_rthdr_init,_inet6_rthdr_init)
54 __weak_alias(inet6_rthdr_lasthop,_inet6_rthdr_lasthop)
55 __weak_alias(inet6_rthdr_segments,_inet6_rthdr_segments)
56 __weak_alias(inet6_rthdr_space,_inet6_rthdr_space)
57 #endif
58
59 size_t
60 inet6_rthdr_space(type, seg)
61 int type, seg;
62 {
63 switch (type) {
64 case IPV6_RTHDR_TYPE_0:
65 if (seg < 1 || seg > 23)
66 return (0);
67 return (CMSG_SPACE(sizeof(struct in6_addr) * (seg - 1) +
68 sizeof(struct ip6_rthdr0)));
69 default:
70 return (0);
71 }
72 }
73
74 struct cmsghdr *
75 inet6_rthdr_init(bp, type)
76 void *bp;
77 int type;
78 {
79 struct cmsghdr *ch;
80 struct ip6_rthdr *rthdr;
81
82 _DIAGASSERT(bp != NULL);
83
84 ch = (struct cmsghdr *)bp;
85 rthdr = (struct ip6_rthdr *)(void *)CMSG_DATA(ch);
86
87 ch->cmsg_level = IPPROTO_IPV6;
88 ch->cmsg_type = IPV6_RTHDR;
89
90 switch (type) {
91 case IPV6_RTHDR_TYPE_0:
92 ch->cmsg_len = CMSG_LEN(sizeof(struct ip6_rthdr0) -
93 sizeof(struct in6_addr));
94 (void)memset(rthdr, 0, sizeof(struct ip6_rthdr0));
95 rthdr->ip6r_type = IPV6_RTHDR_TYPE_0;
96 return (ch);
97 default:
98 return (NULL);
99 }
100 }
101
102 int
103 inet6_rthdr_add(cmsg, addr, flags)
104 struct cmsghdr *cmsg;
105 const struct in6_addr *addr;
106 u_int flags;
107 {
108 struct ip6_rthdr *rthdr;
109
110 _DIAGASSERT(cmsg != NULL);
111 _DIAGASSERT(addr != NULL);
112
113 rthdr = (struct ip6_rthdr *)(void *)CMSG_DATA(cmsg);
114
115 switch (rthdr->ip6r_type) {
116 case IPV6_RTHDR_TYPE_0:
117 {
118 struct ip6_rthdr0 *rt0 = (struct ip6_rthdr0 *)(void *)rthdr;
119 if (flags != IPV6_RTHDR_LOOSE && flags != IPV6_RTHDR_STRICT)
120 return (-1);
121 if (rt0->ip6r0_segleft == 23)
122 return (-1);
123 if (flags == IPV6_RTHDR_STRICT) {
124 int c, b;
125 c = rt0->ip6r0_segleft / 8;
126 b = rt0->ip6r0_segleft % 8;
127 rt0->ip6r0_slmap[c] |= (1 << (7 - b));
128 }
129 rt0->ip6r0_segleft++;
130 (void)memcpy(((caddr_t)(void *)rt0) +
131 ((rt0->ip6r0_len + 1) << 3), addr, sizeof(struct in6_addr));
132 rt0->ip6r0_len += sizeof(struct in6_addr) >> 3;
133 cmsg->cmsg_len = CMSG_LEN((rt0->ip6r0_len + 1) << 3);
134 break;
135 }
136 default:
137 return (-1);
138 }
139
140 return (0);
141 }
142
143 int
144 inet6_rthdr_lasthop(cmsg, flags)
145 struct cmsghdr *cmsg;
146 unsigned int flags;
147 {
148 struct ip6_rthdr *rthdr;
149
150 _DIAGASSERT(cmsg != NULL);
151
152 rthdr = (struct ip6_rthdr *)(void *)CMSG_DATA(cmsg);
153
154 switch (rthdr->ip6r_type) {
155 case IPV6_RTHDR_TYPE_0:
156 {
157 struct ip6_rthdr0 *rt0 = (struct ip6_rthdr0 *)(void *)rthdr;
158 if (flags != IPV6_RTHDR_LOOSE && flags != IPV6_RTHDR_STRICT)
159 return (-1);
160 if (rt0->ip6r0_segleft > 23)
161 return (-1);
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 return (-1);
172 }
173
174 return (0);
175 }
176
177 #if 0
178 int
179 inet6_rthdr_reverse(in, out)
180 const struct cmsghdr *in;
181 struct cmsghdr *out;
182 {
183
184 return (-1);
185 }
186 #endif
187
188 int
189 inet6_rthdr_segments(cmsg)
190 const struct cmsghdr *cmsg;
191 {
192 const struct ip6_rthdr *rthdr;
193
194 _DIAGASSERT(cmsg != NULL);
195
196 /*LINTED const castaway*/
197 rthdr = (const struct ip6_rthdr *)(const void *)CMSG_DATA(cmsg);
198
199 switch (rthdr->ip6r_type) {
200 case IPV6_RTHDR_TYPE_0:
201 {
202 const struct ip6_rthdr0 *rt0 =
203 (const struct ip6_rthdr0 *)(const void *)rthdr;
204
205 if (rt0->ip6r0_len % 2 || 46 < rt0->ip6r0_len)
206 return (-1);
207
208 return (rt0->ip6r0_len * 8) / sizeof(struct in6_addr);
209 }
210
211 default:
212 return (-1);
213 }
214 }
215
216 struct in6_addr *
217 inet6_rthdr_getaddr(cmsg, idx)
218 struct cmsghdr *cmsg;
219 int idx;
220 {
221 struct ip6_rthdr *rthdr;
222
223 _DIAGASSERT(cmsg != NULL);
224
225 rthdr = (struct ip6_rthdr *)(void *)CMSG_DATA(cmsg);
226
227 switch (rthdr->ip6r_type) {
228 case IPV6_RTHDR_TYPE_0:
229 {
230 struct ip6_rthdr0 *rt0 = (struct ip6_rthdr0 *)(void *)rthdr;
231 int naddr;
232
233 if (rt0->ip6r0_len % 2 || 46 < rt0->ip6r0_len)
234 return NULL;
235 naddr = (rt0->ip6r0_len * 8) / sizeof(struct in6_addr);
236 if (idx <= 0 || naddr < idx)
237 return NULL;
238 return &rt0->ip6r0_addr[idx - 1];
239 }
240
241 default:
242 return NULL;
243 }
244 }
245
246 int
247 inet6_rthdr_getflags(cmsg, idx)
248 const struct cmsghdr *cmsg;
249 int idx;
250 {
251 const struct ip6_rthdr *rthdr;
252
253 _DIAGASSERT(cmsg != NULL);
254
255 /*LINTED const castaway*/
256 rthdr = (const struct ip6_rthdr *)(const void *)CMSG_DATA(cmsg);
257
258 switch (rthdr->ip6r_type) {
259 case IPV6_RTHDR_TYPE_0:
260 {
261 const struct ip6_rthdr0 *rt0 = (const struct ip6_rthdr0 *)
262 (const void *)rthdr;
263 int naddr;
264
265 if (rt0->ip6r0_len % 2 || 46 < rt0->ip6r0_len)
266 return (-1);
267 naddr = (rt0->ip6r0_len * 8) / sizeof(struct in6_addr);
268 if (idx < 0 || naddr < idx)
269 return (-1);
270 if (rt0->ip6r0_slmap[idx / 8] & (0x80 >> (idx % 8)))
271 return IPV6_RTHDR_STRICT;
272 else
273 return IPV6_RTHDR_LOOSE;
274 }
275
276 default:
277 return (-1);
278 }
279 }
280