]>
Commit | Line | Data |
---|---|---|
b0d623f7 | 1 | /* |
39037602 | 2 | * Copyright (c) 2009-2016 Apple Inc. All rights reserved. |
b0d623f7 A |
3 | * |
4 | * @APPLE_OSREFERENCE_LICENSE_HEADER_START@ | |
39236c6e | 5 | * |
b0d623f7 A |
6 | * This file contains Original Code and/or Modifications of Original Code |
7 | * as defined in and that are subject to the Apple Public Source License | |
8 | * Version 2.0 (the 'License'). You may not use this file except in | |
9 | * compliance with the License. The rights granted to you under the License | |
10 | * may not be used to create, or enable the creation or redistribution of, | |
11 | * unlawful or unlicensed copies of an Apple operating system, or to | |
12 | * circumvent, violate, or enable the circumvention or violation of, any | |
13 | * terms of an Apple operating system software license agreement. | |
39236c6e | 14 | * |
b0d623f7 A |
15 | * Please obtain a copy of the License at |
16 | * http://www.opensource.apple.com/apsl/ and read it before using this file. | |
39236c6e | 17 | * |
b0d623f7 A |
18 | * The Original Code and all software distributed under the License are |
19 | * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER | |
20 | * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, | |
21 | * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, | |
22 | * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. | |
23 | * Please see the License for the specific language governing rights and | |
24 | * limitations under the License. | |
39236c6e | 25 | * |
b0d623f7 A |
26 | * @APPLE_OSREFERENCE_LICENSE_HEADER_END@ |
27 | */ | |
28 | ||
39236c6e A |
29 | /* $FreeBSD: src/sys/netinet6/in6_gif.c,v 1.2.2.3 2001/07/03 11:01:52 ume Exp $ */ |
30 | /* $KAME: in6_gif.c,v 1.49 2001/05/14 14:02:17 itojun Exp $ */ | |
1c79356b A |
31 | |
32 | /* | |
33 | * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project. | |
34 | * All rights reserved. | |
35 | * | |
36 | * Redistribution and use in source and binary forms, with or without | |
37 | * modification, are permitted provided that the following conditions | |
38 | * are met: | |
39 | * 1. Redistributions of source code must retain the above copyright | |
40 | * notice, this list of conditions and the following disclaimer. | |
41 | * 2. Redistributions in binary form must reproduce the above copyright | |
42 | * notice, this list of conditions and the following disclaimer in the | |
43 | * documentation and/or other materials provided with the distribution. | |
44 | * 3. Neither the name of the project nor the names of its contributors | |
45 | * may be used to endorse or promote products derived from this software | |
46 | * without specific prior written permission. | |
47 | * | |
48 | * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND | |
49 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE | |
50 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE | |
51 | * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE | |
52 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL | |
53 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS | |
54 | * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) | |
55 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT | |
56 | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY | |
57 | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF | |
58 | * SUCH DAMAGE. | |
59 | */ | |
60 | ||
1c79356b A |
61 | |
62 | #include <sys/param.h> | |
63 | #include <sys/systm.h> | |
64 | #include <sys/socket.h> | |
65 | #include <sys/sockio.h> | |
66 | #include <sys/mbuf.h> | |
67 | #include <sys/errno.h> | |
9bccf70c A |
68 | #include <sys/queue.h> |
69 | #include <sys/syslog.h> | |
70 | ||
1c79356b A |
71 | #include <sys/malloc.h> |
72 | #include <sys/protosw.h> | |
73 | ||
74 | #include <net/if.h> | |
75 | #include <net/route.h> | |
76 | ||
77 | #include <netinet/in.h> | |
78 | #include <netinet/in_systm.h> | |
79 | #if INET | |
80 | #include <netinet/ip.h> | |
81 | #endif | |
82 | #include <netinet/ip_encap.h> | |
1c79356b A |
83 | #include <netinet/ip6.h> |
84 | #include <netinet6/ip6_var.h> | |
85 | #include <netinet6/in6_gif.h> | |
86 | #include <netinet6/in6_var.h> | |
1c79356b | 87 | #include <netinet/ip_ecn.h> |
9bccf70c | 88 | #include <netinet6/ip6_ecn.h> |
1c79356b A |
89 | |
90 | #include <net/if_gif.h> | |
91 | ||
92 | #include <net/net_osdep.h> | |
93 | ||
1c79356b | 94 | int |
91447636 A |
95 | in6_gif_output( |
96 | struct ifnet *ifp, | |
97 | int family, /* family of the packet to be encapsulate. */ | |
98 | struct mbuf *m, | |
2d21ac55 | 99 | __unused struct rtentry *rt) |
1c79356b | 100 | { |
2d21ac55 | 101 | struct gif_softc *sc = ifnet_softc(ifp); |
1c79356b | 102 | struct sockaddr_in6 *dst = (struct sockaddr_in6 *)&sc->gif_ro6.ro_dst; |
39236c6e A |
103 | struct sockaddr_in6 *sin6_src = (struct sockaddr_in6 *) |
104 | (void *)sc->gif_psrc; | |
105 | struct sockaddr_in6 *sin6_dst = (struct sockaddr_in6 *) | |
106 | (void *)sc->gif_pdst; | |
1c79356b A |
107 | struct ip6_hdr *ip6; |
108 | int proto; | |
109 | u_int8_t itos, otos; | |
110 | ||
39236c6e A |
111 | GIF_LOCK_ASSERT(sc); |
112 | ||
1c79356b A |
113 | if (sin6_src == NULL || sin6_dst == NULL || |
114 | sin6_src->sin6_family != AF_INET6 || | |
115 | sin6_dst->sin6_family != AF_INET6) { | |
116 | m_freem(m); | |
0a7de745 | 117 | return EAFNOSUPPORT; |
1c79356b A |
118 | } |
119 | ||
120 | switch (family) { | |
121 | #if INET | |
122 | case AF_INET: | |
0a7de745 | 123 | { |
1c79356b A |
124 | struct ip *ip; |
125 | ||
126 | proto = IPPROTO_IPV4; | |
0a7de745 A |
127 | if (mbuf_len(m) < sizeof(*ip)) { |
128 | m = m_pullup(m, sizeof(*ip)); | |
129 | if (!m) { | |
130 | return ENOBUFS; | |
131 | } | |
1c79356b A |
132 | } |
133 | ip = mtod(m, struct ip *); | |
134 | itos = ip->ip_tos; | |
135 | break; | |
0a7de745 | 136 | } |
1c79356b | 137 | #endif |
1c79356b | 138 | case AF_INET6: |
0a7de745 | 139 | { |
1c79356b | 140 | proto = IPPROTO_IPV6; |
0a7de745 A |
141 | if (mbuf_len(m) < sizeof(*ip6)) { |
142 | m = m_pullup(m, sizeof(*ip6)); | |
143 | if (!m) { | |
144 | return ENOBUFS; | |
145 | } | |
1c79356b A |
146 | } |
147 | ip6 = mtod(m, struct ip6_hdr *); | |
148 | itos = (ntohl(ip6->ip6_flow) >> 20) & 0xff; | |
149 | break; | |
0a7de745 | 150 | } |
1c79356b A |
151 | default: |
152 | #if DEBUG | |
153 | printf("in6_gif_output: warning: unknown family %d passed\n", | |
0a7de745 | 154 | family); |
1c79356b A |
155 | #endif |
156 | m_freem(m); | |
0a7de745 | 157 | return EAFNOSUPPORT; |
1c79356b | 158 | } |
39236c6e | 159 | |
1c79356b | 160 | /* prepend new IP header */ |
0a7de745 A |
161 | M_PREPEND(m, sizeof(struct ip6_hdr), M_DONTWAIT, 1); |
162 | if (m && mbuf_len(m) < sizeof(struct ip6_hdr)) { | |
163 | m = m_pullup(m, sizeof(struct ip6_hdr)); | |
164 | } | |
1c79356b A |
165 | if (m == NULL) { |
166 | printf("ENOBUFS in in6_gif_output %d\n", __LINE__); | |
0a7de745 | 167 | return ENOBUFS; |
1c79356b A |
168 | } |
169 | ||
170 | ip6 = mtod(m, struct ip6_hdr *); | |
0a7de745 A |
171 | ip6->ip6_flow = 0; |
172 | ip6->ip6_vfc &= ~IPV6_VERSION_MASK; | |
173 | ip6->ip6_vfc |= IPV6_VERSION; | |
174 | ip6->ip6_plen = htons((u_short)m->m_pkthdr.len); | |
175 | ip6->ip6_nxt = proto; | |
176 | ip6->ip6_hlim = ip6_gif_hlim; | |
177 | ip6->ip6_src = sin6_src->sin6_addr; | |
9bccf70c | 178 | /* bidirectional configured tunnel mode */ |
0a7de745 | 179 | if (!IN6_IS_ADDR_UNSPECIFIED(&sin6_dst->sin6_addr)) { |
9bccf70c | 180 | ip6->ip6_dst = sin6_dst->sin6_addr; |
0a7de745 | 181 | } else { |
9bccf70c | 182 | m_freem(m); |
0a7de745 | 183 | return ENETUNREACH; |
1c79356b | 184 | } |
3e170ce0 | 185 | ip_ecn_ingress((ifp->if_flags & IFF_LINK1) ? ECN_NORMAL : ECN_NOCARE, |
39236c6e | 186 | &otos, &itos); |
6d2010ae | 187 | ip6->ip6_flow &= ~htonl(0xff << 20); |
9bccf70c | 188 | ip6->ip6_flow |= htonl((u_int32_t)otos << 20); |
1c79356b | 189 | |
39236c6e A |
190 | if (ROUTE_UNUSABLE(&sc->gif_ro6) || |
191 | dst->sin6_family != sin6_dst->sin6_family || | |
935ed37a | 192 | !IN6_ARE_ADDR_EQUAL(&dst->sin6_addr, &sin6_dst->sin6_addr) || |
39236c6e | 193 | (sc->gif_ro6.ro_rt != NULL && sc->gif_ro6.ro_rt->rt_ifp == ifp)) { |
b0d623f7 | 194 | /* cache route doesn't match or recursive route */ |
0a7de745 | 195 | bzero(dst, sizeof(*dst)); |
1c79356b | 196 | dst->sin6_family = sin6_dst->sin6_family; |
0a7de745 | 197 | dst->sin6_len = sizeof(struct sockaddr_in6); |
1c79356b | 198 | dst->sin6_addr = sin6_dst->sin6_addr; |
39236c6e | 199 | ROUTE_RELEASE(&sc->gif_ro6); |
1c79356b A |
200 | #if 0 |
201 | sc->gif_if.if_mtu = GIF_MTU; | |
202 | #endif | |
203 | } | |
204 | ||
205 | if (sc->gif_ro6.ro_rt == NULL) { | |
206 | rtalloc((struct route *)&sc->gif_ro6); | |
207 | if (sc->gif_ro6.ro_rt == NULL) { | |
208 | m_freem(m); | |
0a7de745 | 209 | return ENETUNREACH; |
1c79356b | 210 | } |
b0d623f7 | 211 | RT_LOCK(sc->gif_ro6.ro_rt); |
9bccf70c | 212 | /* if it constitutes infinite encapsulation, punt. */ |
935ed37a | 213 | if (sc->gif_ro6.ro_rt->rt_ifp == ifp) { |
b0d623f7 | 214 | RT_UNLOCK(sc->gif_ro6.ro_rt); |
9bccf70c | 215 | m_freem(m); |
0a7de745 | 216 | return ENETUNREACH; /* XXX */ |
9bccf70c | 217 | } |
1c79356b A |
218 | #if 0 |
219 | ifp->if_mtu = sc->gif_ro6.ro_rt->rt_ifp->if_mtu | |
0a7de745 | 220 | - sizeof(struct ip6_hdr); |
1c79356b | 221 | #endif |
b0d623f7 | 222 | RT_UNLOCK(sc->gif_ro6.ro_rt); |
1c79356b | 223 | } |
39236c6e | 224 | |
9bccf70c A |
225 | #if IPV6_MINMTU |
226 | /* | |
227 | * force fragmentation to minimum MTU, to avoid path MTU discovery. | |
228 | * it is too painful to ask for resend of inner packet, to achieve | |
229 | * path MTU discovery for encapsulated packets. | |
230 | */ | |
0a7de745 | 231 | return ip6_output(m, 0, &sc->gif_ro6, IPV6_MINMTU, 0, NULL, NULL); |
9bccf70c | 232 | #else |
0a7de745 | 233 | return ip6_output(m, 0, &sc->gif_ro6, 0, 0, NULL, NULL); |
9bccf70c | 234 | #endif |
1c79356b A |
235 | } |
236 | ||
39236c6e A |
237 | int |
238 | in6_gif_input(struct mbuf **mp, int *offp, int proto) | |
1c79356b A |
239 | { |
240 | struct mbuf *m = *mp; | |
1c79356b A |
241 | struct ifnet *gifp = NULL; |
242 | struct ip6_hdr *ip6; | |
1c79356b A |
243 | int af = 0; |
244 | u_int32_t otos; | |
3e170ce0 | 245 | int egress_success = 0; |
1c79356b A |
246 | |
247 | ip6 = mtod(m, struct ip6_hdr *); | |
248 | ||
39236c6e | 249 | gifp = ((struct gif_softc *)encap_getarg(m))->gif_if; |
1c79356b | 250 | |
9bccf70c | 251 | if (gifp == NULL || (gifp->if_flags & IFF_UP) == 0) { |
1c79356b A |
252 | m_freem(m); |
253 | ip6stat.ip6s_nogif++; | |
0a7de745 | 254 | return IPPROTO_DONE; |
1c79356b A |
255 | } |
256 | ||
257 | otos = ip6->ip6_flow; | |
258 | m_adj(m, *offp); | |
259 | ||
260 | switch (proto) { | |
1c79356b | 261 | case IPPROTO_IPV4: |
0a7de745 | 262 | { |
1c79356b | 263 | struct ip *ip; |
4bd07ac2 A |
264 | u_int8_t otos8, old_tos; |
265 | int sum; | |
266 | ||
1c79356b A |
267 | af = AF_INET; |
268 | otos8 = (ntohl(otos) >> 20) & 0xff; | |
0a7de745 A |
269 | if (mbuf_len(m) < sizeof(*ip)) { |
270 | m = m_pullup(m, sizeof(*ip)); | |
271 | if (!m) { | |
272 | return IPPROTO_DONE; | |
273 | } | |
1c79356b A |
274 | } |
275 | ip = mtod(m, struct ip *); | |
4bd07ac2 A |
276 | if (gifp->if_flags & IFF_LINK1) { |
277 | old_tos = ip->ip_tos; | |
3e170ce0 | 278 | egress_success = ip_ecn_egress(ECN_NORMAL, &otos8, &ip->ip_tos); |
4bd07ac2 | 279 | if (old_tos != ip->ip_tos) { |
0a7de745 A |
280 | sum = ~ntohs(ip->ip_sum) & 0xffff; |
281 | sum += (~old_tos & 0xffff) + ip->ip_tos; | |
282 | sum = (sum >> 16) + (sum & 0xffff); | |
283 | sum += (sum >> 16); /* add carry */ | |
284 | ip->ip_sum = htons(~sum & 0xffff); | |
4bd07ac2 | 285 | } |
0a7de745 | 286 | } else { |
3e170ce0 | 287 | egress_success = ip_ecn_egress(ECN_NOCARE, &otos8, &ip->ip_tos); |
0a7de745 | 288 | } |
1c79356b | 289 | break; |
0a7de745 | 290 | } |
1c79356b | 291 | case IPPROTO_IPV6: |
0a7de745 | 292 | { |
1c79356b | 293 | af = AF_INET6; |
0a7de745 A |
294 | if (mbuf_len(m) < sizeof(*ip6)) { |
295 | m = m_pullup(m, sizeof(*ip6)); | |
296 | if (!m) { | |
297 | return IPPROTO_DONE; | |
298 | } | |
1c79356b A |
299 | } |
300 | ip6 = mtod(m, struct ip6_hdr *); | |
0a7de745 | 301 | if (gifp->if_flags & IFF_LINK1) { |
3e170ce0 | 302 | egress_success = ip6_ecn_egress(ECN_NORMAL, &otos, &ip6->ip6_flow); |
0a7de745 | 303 | } else { |
3e170ce0 | 304 | egress_success = ip6_ecn_egress(ECN_NOCARE, &otos, &ip6->ip6_flow); |
0a7de745 | 305 | } |
1c79356b | 306 | break; |
0a7de745 | 307 | } |
1c79356b A |
308 | default: |
309 | ip6stat.ip6s_nogif++; | |
310 | m_freem(m); | |
0a7de745 | 311 | return IPPROTO_DONE; |
1c79356b | 312 | } |
2d21ac55 | 313 | |
3e170ce0 A |
314 | if (egress_success == 0) { |
315 | ip6stat.ip6s_nogif++; | |
316 | m_freem(m); | |
0a7de745 | 317 | return IPPROTO_DONE; |
3e170ce0 A |
318 | } |
319 | ||
39236c6e | 320 | /* Replace the rcvif by gifp for ifnet_input to route it correctly */ |
0a7de745 | 321 | if (m->m_pkthdr.rcvif) { |
2d21ac55 | 322 | m->m_pkthdr.rcvif = gifp; |
0a7de745 | 323 | } |
2d21ac55 A |
324 | |
325 | ifnet_input(gifp, m, NULL); | |
0a7de745 | 326 | return IPPROTO_DONE; |
1c79356b A |
327 | } |
328 | ||
9bccf70c | 329 | /* |
55e303ae | 330 | * validate outer address. |
9bccf70c | 331 | */ |
55e303ae | 332 | static int |
2d21ac55 A |
333 | gif_validate6( |
334 | const struct ip6_hdr *ip6, | |
335 | struct gif_softc *sc, | |
336 | struct ifnet *ifp) | |
55e303ae | 337 | { |
9bccf70c | 338 | struct sockaddr_in6 *src, *dst; |
9bccf70c | 339 | |
316670eb A |
340 | src = (struct sockaddr_in6 *)(void *)sc->gif_psrc; |
341 | dst = (struct sockaddr_in6 *)(void *)sc->gif_pdst; | |
9bccf70c | 342 | |
55e303ae A |
343 | /* |
344 | * Check for address match. Note that the check is for an incoming | |
345 | * packet. We should compare the *source* address in our configuration | |
346 | * and the *destination* address of the packet, and vice versa. | |
347 | */ | |
348 | if (!IN6_ARE_ADDR_EQUAL(&src->sin6_addr, &ip6->ip6_dst) || | |
0a7de745 A |
349 | !IN6_ARE_ADDR_EQUAL(&dst->sin6_addr, &ip6->ip6_src)) { |
350 | return 0; | |
351 | } | |
9bccf70c A |
352 | |
353 | /* martian filters on outer source - done in ip6_input */ | |
354 | ||
355 | /* ingress filters on outer source */ | |
2d21ac55 | 356 | if ((ifnet_flags(sc->gif_if) & IFF_LINK2) == 0 && ifp) { |
9bccf70c A |
357 | struct sockaddr_in6 sin6; |
358 | struct rtentry *rt; | |
359 | ||
0a7de745 | 360 | bzero(&sin6, sizeof(sin6)); |
9bccf70c | 361 | sin6.sin6_family = AF_INET6; |
0a7de745 | 362 | sin6.sin6_len = sizeof(struct sockaddr_in6); |
55e303ae | 363 | sin6.sin6_addr = ip6->ip6_src; |
55e303ae | 364 | |
b0d623f7 | 365 | rt = rtalloc1((struct sockaddr *)&sin6, 0, 0); |
0a7de745 | 366 | if (rt != NULL) { |
b0d623f7 | 367 | RT_LOCK(rt); |
0a7de745 | 368 | } |
55e303ae | 369 | if (!rt || rt->rt_ifp != ifp) { |
9bccf70c A |
370 | #if 0 |
371 | log(LOG_WARNING, "%s: packet from %s dropped " | |
372 | "due to ingress filter\n", if_name(&sc->gif_if), | |
373 | ip6_sprintf(&sin6.sin6_addr)); | |
1c79356b | 374 | #endif |
b0d623f7 A |
375 | if (rt != NULL) { |
376 | RT_UNLOCK(rt); | |
9bccf70c | 377 | rtfree(rt); |
b0d623f7 | 378 | } |
0a7de745 | 379 | return 0; |
1c79356b | 380 | } |
b0d623f7 | 381 | RT_UNLOCK(rt); |
9bccf70c | 382 | rtfree(rt); |
1c79356b A |
383 | } |
384 | ||
0a7de745 | 385 | return 128 * 2; |
1c79356b | 386 | } |
55e303ae A |
387 | |
388 | /* | |
389 | * we know that we are in IFF_UP, outer address available, and outer family | |
390 | * matched the physical addr family. see gif_encapcheck(). | |
391 | * sanity check for arg should have been done in the caller. | |
392 | */ | |
393 | int | |
2d21ac55 A |
394 | gif_encapcheck6( |
395 | const struct mbuf *m, | |
396 | __unused int off, | |
397 | __unused int proto, | |
398 | void *arg) | |
55e303ae A |
399 | { |
400 | struct ip6_hdr ip6; | |
401 | struct gif_softc *sc; | |
402 | struct ifnet *ifp; | |
403 | ||
404 | /* sanity check done in caller */ | |
405 | sc = (struct gif_softc *)arg; | |
406 | ||
39236c6e A |
407 | GIF_LOCK_ASSERT(sc); |
408 | ||
0a7de745 | 409 | mbuf_copydata((struct mbuf *)(size_t)m, 0, sizeof(ip6), &ip6); |
55e303ae A |
410 | ifp = ((m->m_flags & M_PKTHDR) != 0) ? m->m_pkthdr.rcvif : NULL; |
411 | ||
0a7de745 | 412 | return gif_validate6(&ip6, sc, ifp); |
55e303ae | 413 | } |