]> git.saurik.com Git - apple/xnu.git/blob - bsd/netinet6/in6_gif.c
xnu-201.tar.gz
[apple/xnu.git] / bsd / netinet6 / in6_gif.c
1 /* $KAME: in6_gif.c,v 1.27 2000/03/25 07:23:43 sumikawa 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 /*
33 * in6_gif.c
34 */
35
36 #if defined(__FreeBSD__) && __FreeBSD__ >= 3
37 #include "opt_inet.h"
38 #endif
39
40 #include <sys/param.h>
41 #include <sys/systm.h>
42 #include <sys/socket.h>
43 #include <sys/sockio.h>
44 #include <sys/mbuf.h>
45 #include <sys/errno.h>
46 #if !defined(__FreeBSD__) || __FreeBSD__ < 3
47 #include <sys/ioctl.h>
48 #endif
49 #include <sys/malloc.h>
50 #include <sys/protosw.h>
51
52 #include <net/if.h>
53 #include <net/route.h>
54
55 #include <netinet/in.h>
56 #include <netinet/in_systm.h>
57 #if INET
58 #include <netinet/ip.h>
59 #endif
60 #include <netinet/ip_encap.h>
61 #if INET6
62 #include <netinet/ip6.h>
63 #include <netinet6/ip6_var.h>
64 #include <netinet6/in6_gif.h>
65 #include <netinet6/in6_var.h>
66 #include <netinet6/ip6protosw.h>
67 #endif
68 #include <netinet/ip_ecn.h>
69
70 #include <net/if_gif.h>
71
72 #include <net/net_osdep.h>
73
74 #if INET6
75 extern struct ip6protosw in6_gif_protosw;
76 #endif
77
78 int
79 in6_gif_output(ifp, family, m, rt)
80 struct ifnet *ifp;
81 int family; /* family of the packet to be encapsulate. */
82 struct mbuf *m;
83 struct rtentry *rt;
84 {
85 struct gif_softc *sc = (struct gif_softc*)ifp;
86 struct sockaddr_in6 *dst = (struct sockaddr_in6 *)&sc->gif_ro6.ro_dst;
87 struct sockaddr_in6 *sin6_src = (struct sockaddr_in6 *)sc->gif_psrc;
88 struct sockaddr_in6 *sin6_dst = (struct sockaddr_in6 *)sc->gif_pdst;
89 struct ip6_hdr *ip6;
90 int proto;
91 u_int8_t itos, otos;
92
93 if (sin6_src == NULL || sin6_dst == NULL ||
94 sin6_src->sin6_family != AF_INET6 ||
95 sin6_dst->sin6_family != AF_INET6) {
96 m_freem(m);
97 return EAFNOSUPPORT;
98 }
99
100 switch (family) {
101 #if INET
102 case AF_INET:
103 {
104 struct ip *ip;
105
106 proto = IPPROTO_IPV4;
107 if (m->m_len < sizeof(*ip)) {
108 m = m_pullup(m, sizeof(*ip));
109 if (!m)
110 return ENOBUFS;
111 }
112 ip = mtod(m, struct ip *);
113 itos = ip->ip_tos;
114 break;
115 }
116 #endif
117 #if INET6
118 case AF_INET6:
119 {
120 struct ip6_hdr *ip6;
121 proto = IPPROTO_IPV6;
122 if (m->m_len < sizeof(*ip6)) {
123 m = m_pullup(m, sizeof(*ip6));
124 if (!m)
125 return ENOBUFS;
126 }
127 ip6 = mtod(m, struct ip6_hdr *);
128 itos = (ntohl(ip6->ip6_flow) >> 20) & 0xff;
129 break;
130 }
131 #endif
132 default:
133 #if DEBUG
134 printf("in6_gif_output: warning: unknown family %d passed\n",
135 family);
136 #endif
137 m_freem(m);
138 return EAFNOSUPPORT;
139 }
140
141 /* prepend new IP header */
142 M_PREPEND(m, sizeof(struct ip6_hdr), M_DONTWAIT);
143 if (m && m->m_len < sizeof(struct ip6_hdr))
144 m = m_pullup(m, sizeof(struct ip6_hdr));
145 if (m == NULL) {
146 printf("ENOBUFS in in6_gif_output %d\n", __LINE__);
147 return ENOBUFS;
148 }
149
150 ip6 = mtod(m, struct ip6_hdr *);
151 ip6->ip6_flow = 0;
152 ip6->ip6_vfc &= ~IPV6_VERSION_MASK;
153 ip6->ip6_vfc |= IPV6_VERSION;
154 ip6->ip6_plen = htons((u_short)m->m_pkthdr.len);
155 ip6->ip6_nxt = proto;
156 ip6->ip6_hlim = ip6_gif_hlim;
157 ip6->ip6_src = sin6_src->sin6_addr;
158 if (ifp->if_flags & IFF_LINK0) {
159 /* multi-destination mode */
160 if (!IN6_IS_ADDR_UNSPECIFIED(&sin6_dst->sin6_addr))
161 ip6->ip6_dst = sin6_dst->sin6_addr;
162 else if (rt) {
163 if (family != AF_INET6) {
164 m_freem(m);
165 return EINVAL; /*XXX*/
166 }
167 ip6->ip6_dst = ((struct sockaddr_in6 *)(rt->rt_gateway))->sin6_addr;
168 } else {
169 m_freem(m);
170 return ENETUNREACH;
171 }
172 } else {
173 /* bidirectional configured tunnel mode */
174 if (!IN6_IS_ADDR_UNSPECIFIED(&sin6_dst->sin6_addr))
175 ip6->ip6_dst = sin6_dst->sin6_addr;
176 else {
177 m_freem(m);
178 return ENETUNREACH;
179 }
180 }
181 if (ifp->if_flags & IFF_LINK1) {
182 otos = 0;
183 ip_ecn_ingress(ECN_ALLOWED, &otos, &itos);
184 ip6->ip6_flow |= htonl((u_int32_t)otos << 20);
185 }
186
187 if (dst->sin6_family != sin6_dst->sin6_family ||
188 !IN6_ARE_ADDR_EQUAL(&dst->sin6_addr, &sin6_dst->sin6_addr)) {
189 /* cache route doesn't match */
190 bzero(dst, sizeof(*dst));
191 dst->sin6_family = sin6_dst->sin6_family;
192 dst->sin6_len = sizeof(struct sockaddr_in6);
193 dst->sin6_addr = sin6_dst->sin6_addr;
194 if (sc->gif_ro6.ro_rt) {
195 RTFREE(sc->gif_ro6.ro_rt);
196 sc->gif_ro6.ro_rt = NULL;
197 }
198 #if 0
199 sc->gif_if.if_mtu = GIF_MTU;
200 #endif
201 }
202
203 if (sc->gif_ro6.ro_rt == NULL) {
204 rtalloc((struct route *)&sc->gif_ro6);
205 if (sc->gif_ro6.ro_rt == NULL) {
206 m_freem(m);
207 return ENETUNREACH;
208 }
209 #if 0
210 ifp->if_mtu = sc->gif_ro6.ro_rt->rt_ifp->if_mtu
211 - sizeof(struct ip6_hdr);
212 #endif
213 }
214
215 return(ip6_output(m, 0, &sc->gif_ro6, 0, 0, NULL));
216 }
217
218 int in6_gif_input(mp, offp, proto)
219 struct mbuf **mp;
220 int *offp, proto;
221 {
222 struct mbuf *m = *mp;
223 #if 0
224 struct gif_softc *sc;
225 #endif
226 struct ifnet *gifp = NULL;
227 struct ip6_hdr *ip6;
228 #if 0
229 int i;
230 #endif
231 int af = 0;
232 u_int32_t otos;
233
234 ip6 = mtod(m, struct ip6_hdr *);
235
236 #if 0
237 #define satoin6(sa) (((struct sockaddr_in6 *)(sa))->sin6_addr)
238 for (i = 0, sc = gif; i < ngif; i++, sc++) {
239 if (sc->gif_psrc == NULL ||
240 sc->gif_pdst == NULL ||
241 sc->gif_psrc->sa_family != AF_INET6 ||
242 sc->gif_pdst->sa_family != AF_INET6) {
243 continue;
244 }
245 if ((sc->gif_if.if_flags & IFF_UP) == 0)
246 continue;
247 if ((sc->gif_if.if_flags & IFF_LINK0) &&
248 IN6_ARE_ADDR_EQUAL(&satoin6(sc->gif_psrc), &ip6->ip6_dst) &&
249 IN6_IS_ADDR_UNSPECIFIED(&satoin6(sc->gif_pdst))) {
250 gifp = &sc->gif_if;
251 continue;
252 }
253 if (IN6_ARE_ADDR_EQUAL(&satoin6(sc->gif_psrc), &ip6->ip6_dst) &&
254 IN6_ARE_ADDR_EQUAL(&satoin6(sc->gif_pdst), &ip6->ip6_src)) {
255 gifp = &sc->gif_if;
256 break;
257 }
258 }
259 #else
260 gifp = (struct ifnet *)encap_getarg(m);
261 #endif
262
263 if (gifp == NULL) {
264 m_freem(m);
265 ip6stat.ip6s_nogif++;
266 return IPPROTO_DONE;
267 }
268
269 if ((gifp->if_flags & IFF_UP) == 0) {
270 m_freem(m);
271 ip6stat.ip6s_nogif++;
272 return IPPROTO_DONE;
273 }
274
275 otos = ip6->ip6_flow;
276 m_adj(m, *offp);
277
278 switch (proto) {
279 #if INET
280 case IPPROTO_IPV4:
281 {
282 struct ip *ip;
283 u_int8_t otos8;
284 af = AF_INET;
285 otos8 = (ntohl(otos) >> 20) & 0xff;
286 if (m->m_len < sizeof(*ip)) {
287 m = m_pullup(m, sizeof(*ip));
288 if (!m)
289 return IPPROTO_DONE;
290 }
291 ip = mtod(m, struct ip *);
292 if (gifp->if_flags & IFF_LINK1)
293 ip_ecn_egress(ECN_ALLOWED, &otos8, &ip->ip_tos);
294 break;
295 }
296 #endif /* INET */
297 #if INET6
298 case IPPROTO_IPV6:
299 {
300 struct ip6_hdr *ip6;
301 af = AF_INET6;
302 if (m->m_len < sizeof(*ip6)) {
303 m = m_pullup(m, sizeof(*ip6));
304 if (!m)
305 return IPPROTO_DONE;
306 }
307 ip6 = mtod(m, struct ip6_hdr *);
308 if (gifp->if_flags & IFF_LINK1)
309 ip6_ecn_egress(ECN_ALLOWED, &otos, &ip6->ip6_flow);
310 break;
311 }
312 #endif
313 default:
314 ip6stat.ip6s_nogif++;
315 m_freem(m);
316 return IPPROTO_DONE;
317 }
318
319 gif_input(m, af, gifp);
320 return IPPROTO_DONE;
321 }
322
323 int
324 in6_gif_ioctl(ifp, cmd, data)
325 struct ifnet *ifp;
326 #if defined(__FreeBSD__) && __FreeBSD__ < 3
327 int cmd;
328 #else
329 u_long cmd;
330 #endif
331 caddr_t data;
332 {
333 struct gif_softc *sc = (struct gif_softc*)ifp;
334 struct ifreq *ifr = (struct ifreq*)data;
335 int error = 0, size;
336 struct sockaddr *sa, *dst, *src;
337 const struct encaptab *p;
338 struct sockaddr_in6 smask6, dmask6;
339
340 switch (cmd) {
341 case SIOCSIFFLAGS:
342 /*
343 * whenever we change our idea about multi-destination mode
344 * we need to update encap attachment.
345 */
346 if (((ifp->if_flags ^ sc->gif_oflags) & IFF_LINK0) == 0)
347 break;
348 if (sc->gif_psrc == NULL || sc->gif_pdst == NULL ||
349 sc->gif_psrc->sa_family != sc->gif_pdst->sa_family)
350 break;
351 bzero(&smask6, sizeof(smask6));
352 smask6.sin6_addr.s6_addr32[0] = ~0;
353 smask6.sin6_addr.s6_addr32[1] = ~0;
354 smask6.sin6_addr.s6_addr32[2] = ~0;
355 smask6.sin6_addr.s6_addr32[3] = ~0;
356 #if 0 /* we'll need to do this soon */
357 smask6.sin6_scope_id = ~0;
358 #endif
359 dmask6 = smask6;
360 if ((ifp->if_flags & IFF_LINK0) == 0 &&
361 IN6_IS_ADDR_UNSPECIFIED(&((struct sockaddr_in6 *)dst)->sin6_addr)) {
362 bzero(&dmask6, sizeof(dmask6));
363 #if 0 /* we'll need to do this soon */
364 dmask6.sin6_scope_id = ~0;
365 #endif
366 }
367 p = encap_attach(sc->gif_psrc->sa_family, -1, sc->gif_psrc,
368 (struct sockaddr *)&smask6, sc->gif_pdst,
369 (struct sockaddr *)&dmask6,
370 (struct protosw *)&in6_gif_protosw, &sc->gif_if);
371 if (p == NULL) {
372 error = EINVAL;
373 goto bad;
374 }
375 if (sc->encap_cookie != NULL)
376 (void)encap_detach(sc->encap_cookie);
377 sc->encap_cookie = p;
378 sc->gif_oflags = ifp->if_flags;
379
380 break;
381
382 #if INET6
383 case SIOCSIFPHYADDR_IN6:
384 #endif
385 switch (ifr->ifr_addr.sa_family) {
386 #if INET6
387 case AF_INET6:
388 src = (struct sockaddr *)
389 &(((struct in6_aliasreq *)data)->ifra_addr);
390 dst = (struct sockaddr *)
391 &(((struct in6_aliasreq *)data)->ifra_dstaddr);
392
393 bzero(&smask6, sizeof(smask6));
394 smask6.sin6_addr.s6_addr32[0] = ~0;
395 smask6.sin6_addr.s6_addr32[1] = ~0;
396 smask6.sin6_addr.s6_addr32[2] = ~0;
397 smask6.sin6_addr.s6_addr32[3] = ~0;
398 #if 0 /* we'll need to do this soon */
399 smask6.sin6_scope_id = ~0;
400 #endif
401 dmask6 = smask6;
402 if ((ifp->if_flags & IFF_LINK0) == 0 &&
403 IN6_IS_ADDR_UNSPECIFIED(&((struct sockaddr_in6 *)dst)->sin6_addr)) {
404 bzero(&dmask6, sizeof(dmask6));
405 #if 0 /* we'll need to do this soon */
406 dmask6.sin6_scope_id = ~0;
407 #endif
408 }
409 size = sizeof(struct sockaddr_in6);
410 break;
411 #endif /* INET6 */
412 default:
413 error = EAFNOSUPPORT;
414 goto bad;
415 }
416
417 if (sc->encap_cookie != NULL)
418 (void)encap_detach(sc->encap_cookie);
419 if (sc->gif_psrc != NULL) {
420 _FREE((caddr_t)sc->gif_psrc, M_IFADDR);
421 sc->gif_psrc = NULL;
422 }
423 if (sc->gif_pdst != NULL) {
424 _FREE((caddr_t)sc->gif_pdst, M_IFADDR);
425 sc->gif_pdst = NULL;
426 }
427
428 p = encap_attach(ifr->ifr_addr.sa_family, -1, src,
429 (struct sockaddr *)&smask6, dst,
430 (struct sockaddr *)&dmask6,
431 (struct protosw *)&in6_gif_protosw, &sc->gif_if);
432 if (p == NULL) {
433 error = EINVAL;
434 goto bad;
435 }
436 sc->encap_cookie = p;
437 sc->gif_oflags = ifp->if_flags;
438
439 sa = (struct sockaddr *)_MALLOC(size, M_IFADDR, M_WAITOK);
440 bcopy((caddr_t)src, (caddr_t)sa, size);
441 sc->gif_psrc = sa;
442
443 sa = (struct sockaddr *)_MALLOC(size, M_IFADDR, M_WAITOK);
444 bcopy((caddr_t)dst, (caddr_t)sa, size);
445 sc->gif_pdst = sa;
446
447 ifp->if_flags |= IFF_UP;
448 if_up(ifp); /* send up RTM_IFINFO */
449
450 error = 0;
451 break;
452 default:
453 error = EINVAL;
454 goto bad;
455 }
456
457 bad:
458 return error;
459 }