X-Git-Url: https://git.saurik.com/apple/xnu.git/blobdiff_plain/1c79356b52d46aa6b508fb032f5ae709b1f2897b..490019cf9519204c5fb36b2fba54ceb983bb6b72:/bsd/netinet/in_gif.c diff --git a/bsd/netinet/in_gif.c b/bsd/netinet/in_gif.c index f57a3ea28..0cd7a2287 100644 --- a/bsd/netinet/in_gif.c +++ b/bsd/netinet/in_gif.c @@ -1,25 +1,31 @@ /* - * Copyright (c) 2000 Apple Computer, Inc. All rights reserved. + * Copyright (c) 2000-2013 Apple Inc. All rights reserved. * - * @APPLE_LICENSE_HEADER_START@ - * - * The contents of this file constitute Original Code as defined in and - * are subject to the Apple Public Source License Version 1.1 (the - * "License"). You may not use this file except in compliance with the - * License. Please obtain a copy of the License at - * http://www.apple.com/publicsource and read it before using this file. - * - * This Original Code and all software distributed under the License are - * distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * @APPLE_OSREFERENCE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. The rights granted to you under the License + * may not be used to create, or enable the creation or redistribution of, + * unlawful or unlicensed copies of an Apple operating system, or to + * circumvent, violate, or enable the circumvention or violation of, any + * terms of an Apple operating system software license agreement. + * + * Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT. Please see the - * License for the specific language governing rights and limitations - * under the License. - * - * @APPLE_LICENSE_HEADER_END@ + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_OSREFERENCE_LICENSE_HEADER_END@ */ -/* $KAME: in_gif.c,v 1.27 2000/03/30 01:29:05 jinmei Exp $ */ +/* $KAME: in_gif.c,v 1.54 2001/05/14 14:02:16 itojun Exp $ */ /* * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project. @@ -50,39 +56,21 @@ * SUCH DAMAGE. */ -/* - * in_gif.c - */ -#if BSD310 -#include "opt_mrouting.h" -#if defined(__FreeBSD__) && __FreeBSD__ >= 3 -#include "opt_inet.h" -#endif -#endif #include #include #include #include -#include #include #include -#ifdef __FreeBSD__ #include #include -#endif -#if !defined(__FreeBSD__) || __FreeBSD__ < 3 -#include -#endif -#include -#if defined(__FreeBSD__) && __FreeBSD__ >= 3 || defined (__APPLE__) #include -#endif +#include #include #include -#include #include #include @@ -97,48 +85,41 @@ #include #endif -#if MROUTING -#include -#endif /* MROUTING */ - -#include - -#include "gif.h" +#include #include -#if NGIF > 0 -int ip_gif_ttl = GIF_TTL; -#else -int ip_gif_ttl = 0; -#endif - -extern struct protosw in_gif_protosw; - -SYSCTL_INT(_net_inet_ip, IPCTL_GIF_TTL, gifttl, - CTLFLAG_RW, &ip_gif_ttl , 0, ""); +int ip_gif_ttl = GIF_TTL; +SYSCTL_INT(_net_inet_ip, IPCTL_GIF_TTL, gifttl, CTLFLAG_RW | CTLFLAG_LOCKED, + &ip_gif_ttl, 0, ""); int -in_gif_output(ifp, family, m, rt) - struct ifnet *ifp; - int family; - struct mbuf *m; - struct rtentry *rt; +in_gif_output( + struct ifnet *ifp, + int family, + struct mbuf *m, + __unused struct rtentry *rt) { - register struct gif_softc *sc = (struct gif_softc*)ifp; - struct sockaddr_in *dst = (struct sockaddr_in *)&sc->gif_ro.ro_dst; - struct sockaddr_in *sin_src = (struct sockaddr_in *)sc->gif_psrc; - struct sockaddr_in *sin_dst = (struct sockaddr_in *)sc->gif_pdst; + struct gif_softc *sc = ifnet_softc(ifp); + struct sockaddr_in *dst = (struct sockaddr_in *) + (void *)&sc->gif_ro.ro_dst; + struct sockaddr_in *sin_src = (struct sockaddr_in *) + (void *)sc->gif_psrc; + struct sockaddr_in *sin_dst = (struct sockaddr_in *) + (void *)sc->gif_pdst; struct ip iphdr; /* capsule IP header, host byte ordered */ int proto, error; u_int8_t tos; + struct ip_out_args ipoa = + { IFSCOPE_NONE, { 0 }, IPOAF_SELECT_SRCIF, 0 }; + + GIF_LOCK_ASSERT(sc); if (sin_src == NULL || sin_dst == NULL || sin_src->sin_family != AF_INET || sin_dst->sin_family != AF_INET) { - printf("in_gif_output: unknown family src=%x dst=%x\n", sin_src->sin_family, sin_dst->sin_family); m_freem(m); - return EAFNOSUPPORT; + return (EAFNOSUPPORT); } switch (family) { @@ -148,94 +129,77 @@ in_gif_output(ifp, family, m, rt) struct ip *ip; proto = IPPROTO_IPV4; - if (m->m_len < sizeof(*ip)) { - m = m_pullup(m, sizeof(*ip)); + if (mbuf_len(m) < sizeof (*ip)) { + m = m_pullup(m, sizeof (*ip)); if (!m) - return ENOBUFS; + return (ENOBUFS); } ip = mtod(m, struct ip *); tos = ip->ip_tos; break; } -#endif /*INET*/ +#endif /* INET */ #if INET6 case AF_INET6: { struct ip6_hdr *ip6; proto = IPPROTO_IPV6; - if (m->m_len < sizeof(*ip6)) { - m = m_pullup(m, sizeof(*ip6)); + if (mbuf_len(m) < sizeof (*ip6)) { + m = m_pullup(m, sizeof (*ip6)); if (!m) - return ENOBUFS; + return (ENOBUFS); } ip6 = mtod(m, struct ip6_hdr *); tos = (ntohl(ip6->ip6_flow) >> 20) & 0xff; break; } -#endif /*INET6*/ +#endif /* INET6 */ default: #if DEBUG printf("in_gif_output: warning: unknown family %d passed\n", family); #endif m_freem(m); - return EAFNOSUPPORT; + return (EAFNOSUPPORT); } - bzero(&iphdr, sizeof(iphdr)); + bzero(&iphdr, sizeof (iphdr)); iphdr.ip_src = sin_src->sin_addr; - if (ifp->if_flags & IFF_LINK0) { - /* multi-destination mode */ - if (sin_dst->sin_addr.s_addr != INADDR_ANY) - iphdr.ip_dst = sin_dst->sin_addr; - else if (rt) { - if (family != AF_INET) { - m_freem(m); - return EINVAL; /*XXX*/ - } - iphdr.ip_dst = ((struct sockaddr_in *) - (rt->rt_gateway))->sin_addr; - } else { - m_freem(m); - return ENETUNREACH; - } - } else { - /* bidirectional configured tunnel mode */ - if (sin_dst->sin_addr.s_addr != INADDR_ANY) - iphdr.ip_dst = sin_dst->sin_addr; - else { - m_freem(m); - return ENETUNREACH; - } + /* bidirectional configured tunnel mode */ + if (sin_dst->sin_addr.s_addr != INADDR_ANY) + iphdr.ip_dst = sin_dst->sin_addr; + else { + m_freem(m); + return (ENETUNREACH); } iphdr.ip_p = proto; /* version will be set in ip_output() */ iphdr.ip_ttl = ip_gif_ttl; - iphdr.ip_len = m->m_pkthdr.len + sizeof(struct ip); + iphdr.ip_len = m->m_pkthdr.len + sizeof (struct ip); if (ifp->if_flags & IFF_LINK1) - ip_ecn_ingress(ECN_ALLOWED, &iphdr.ip_tos, &tos); + ip_ecn_ingress(ECN_NORMAL, &iphdr.ip_tos, &tos); + else + ip_ecn_ingress(ECN_NOCARE, &iphdr.ip_tos, &tos); /* prepend new IP header */ - M_PREPEND(m, sizeof(struct ip), M_DONTWAIT); - if (m && m->m_len < sizeof(struct ip)) - m = m_pullup(m, sizeof(struct ip)); + M_PREPEND(m, sizeof (struct ip), M_DONTWAIT, 0); + if (m && mbuf_len(m) < sizeof (struct ip)) + m = m_pullup(m, sizeof (struct ip)); if (m == NULL) { printf("ENOBUFS in in_gif_output %d\n", __LINE__); - return ENOBUFS; + return (ENOBUFS); } + bcopy(&iphdr, mtod(m, struct ip *), sizeof (struct ip)); - *(mtod(m, struct ip *)) = iphdr; - - if (dst->sin_family != sin_dst->sin_family || - dst->sin_addr.s_addr != sin_dst->sin_addr.s_addr) { - /* cache route doesn't match */ + if (ROUTE_UNUSABLE(&sc->gif_ro) || + dst->sin_family != sin_dst->sin_family || + dst->sin_addr.s_addr != sin_dst->sin_addr.s_addr || + (sc->gif_ro.ro_rt != NULL && sc->gif_ro.ro_rt->rt_ifp == ifp)) { + /* cache route doesn't match or recursive route */ dst->sin_family = sin_dst->sin_family; - dst->sin_len = sizeof(struct sockaddr_in); + dst->sin_len = sizeof (struct sockaddr_in); dst->sin_addr = sin_dst->sin_addr; - if (sc->gif_ro.ro_rt) { - RTFREE(sc->gif_ro.ro_rt); - sc->gif_ro.ro_rt = NULL; - } + ROUTE_RELEASE(&sc->gif_ro); #if 0 sc->gif_if.if_mtu = GIF_MTU; #endif @@ -245,20 +209,26 @@ in_gif_output(ifp, family, m, rt) rtalloc(&sc->gif_ro); if (sc->gif_ro.ro_rt == NULL) { m_freem(m); - return ENETUNREACH; + return (ENETUNREACH); + } + + /* if it constitutes infinite encapsulation, punt. */ + RT_LOCK(sc->gif_ro.ro_rt); + if (sc->gif_ro.ro_rt->rt_ifp == ifp) { + RT_UNLOCK(sc->gif_ro.ro_rt); + m_freem(m); + return (ENETUNREACH); /* XXX */ } #if 0 ifp->if_mtu = sc->gif_ro.ro_rt->rt_ifp->if_mtu - - sizeof(struct ip); + - sizeof (struct ip); #endif + RT_UNLOCK(sc->gif_ro.ro_rt); } -#ifndef __OpenBSD__ - error = ip_output(m, NULL, &sc->gif_ro, 0, NULL); -#else - error = ip_output(m, NULL, &sc->gif_ro, 0, NULL, NULL); -#endif - return(error); + error = ip_output(m, NULL, &sc->gif_ro, IP_OUTARGS, NULL, &ipoa); + + return (error); } void @@ -266,75 +236,22 @@ in_gif_input(m, off) struct mbuf *m; int off; { - struct gif_softc *sc; struct ifnet *gifp = NULL; struct ip *ip; - int i, af, proto; - u_int8_t otos; - - if (gif == NULL) { - m_freem(m); - return; - } + int af, proto; + u_int8_t otos, old_tos; + int egress_success = 0; + int sum; ip = mtod(m, struct ip *); - proto = ip->ip_p; + proto = ip->ip_p; -#if 0 - /* this code will be soon improved. */ -#define satosin(sa) ((struct sockaddr_in *)(sa)) - for (i = 0, sc = gif; i < ngif; i++, sc++) { - if (sc->gif_psrc == NULL - || sc->gif_pdst == NULL - || sc->gif_psrc->sa_family != AF_INET - || sc->gif_pdst->sa_family != AF_INET) { - continue; - } - if ((sc->gif_if.if_flags & IFF_UP) == 0) - continue; + gifp = ((struct gif_softc *)encap_getarg(m))->gif_if; - if ((sc->gif_if.if_flags & IFF_LINK0) - && satosin(sc->gif_psrc)->sin_addr.s_addr == ip->ip_dst.s_addr - && satosin(sc->gif_pdst)->sin_addr.s_addr == INADDR_ANY) { - gifp = &sc->gif_if; - continue; - } - - if (satosin(sc->gif_psrc)->sin_addr.s_addr == ip->ip_dst.s_addr - && satosin(sc->gif_pdst)->sin_addr.s_addr == ip->ip_src.s_addr) - { - gifp = &sc->gif_if; - break; - } - } -#else - gifp = (struct ifnet *)encap_getarg(m); -#endif - - if (gifp == NULL) { - /* for backward compatibility */ - if (proto == IPPROTO_IPV4) { -#ifdef __OpenBSD__ -#if defined(MROUTING) || defined(IPSEC) - ip4_input(m, off, proto); - return; -#endif -#else -#if MROUTING - ipip_input(m, off); - return; -#endif /*MROUTING*/ -#endif - } + if (gifp == NULL || (gifp->if_flags & IFF_UP) == 0) { m_freem(m); - ipstat.ips_nogif++; - return; - } - - if ((gifp->if_flags & IFF_UP) == 0) { - m_freem(m); - ipstat.ips_nogif++; + OSAddAtomic(1, &ipstat.ips_nogif); return; } @@ -345,16 +262,25 @@ in_gif_input(m, off) #if INET case IPPROTO_IPV4: { - struct ip *ip; af = AF_INET; - if (m->m_len < sizeof(*ip)) { - m = m_pullup(m, sizeof(*ip)); + if (mbuf_len(m) < sizeof (*ip)) { + m = m_pullup(m, sizeof (*ip)); if (!m) return; } ip = mtod(m, struct ip *); - if (gifp->if_flags & IFF_LINK1) - ip_ecn_egress(ECN_ALLOWED, &otos, &ip->ip_tos); + if (gifp->if_flags & IFF_LINK1) { + old_tos = ip->ip_tos; + egress_success = ip_ecn_egress(ECN_NORMAL, &otos, &ip->ip_tos); + if (old_tos != ip->ip_tos) { + sum = ~ntohs(ip->ip_sum) & 0xffff; + sum += (~otos & 0xffff) + ip->ip_tos; + sum = (sum >> 16) + (sum & 0xffff); + sum += (sum >> 16); /* add carry */ + ip->ip_sum = htons(~sum & 0xffff); + } + } else + egress_success = ip_ecn_egress(ECN_NOCARE, &otos, &ip->ip_tos); break; } #endif @@ -364,143 +290,126 @@ in_gif_input(m, off) struct ip6_hdr *ip6; u_int8_t itos; af = AF_INET6; - if (m->m_len < sizeof(*ip6)) { - m = m_pullup(m, sizeof(*ip6)); + if (mbuf_len(m) < sizeof (*ip6)) { + m = m_pullup(m, sizeof (*ip6)); if (!m) return; } ip6 = mtod(m, struct ip6_hdr *); itos = (ntohl(ip6->ip6_flow) >> 20) & 0xff; if (gifp->if_flags & IFF_LINK1) - ip_ecn_egress(ECN_ALLOWED, &otos, &itos); + egress_success = ip_ecn_egress(ECN_NORMAL, &otos, &itos); + else + egress_success = ip_ecn_egress(ECN_NOCARE, &otos, &itos); ip6->ip6_flow &= ~htonl(0xff << 20); ip6->ip6_flow |= htonl((u_int32_t)itos << 20); break; } #endif /* INET6 */ default: - ipstat.ips_nogif++; + OSAddAtomic(1, &ipstat.ips_nogif); + m_freem(m); + return; + } + + if (egress_success == 0) { + OSAddAtomic(1, &ipstat.ips_nogif); m_freem(m); return; } + +#ifdef __APPLE__ + /* Replace the rcvif by gifp for dlil to route it correctly */ + if (m->m_pkthdr.rcvif) + m->m_pkthdr.rcvif = gifp; + ifnet_input(gifp, m, NULL); +#else gif_input(m, af, gifp); - return; +#endif } +/* + * We know that we are in IFF_UP, outer address available, and outer family + * matched the physical addr family. see gif_encapcheck(). + */ int -in_gif_ioctl(ifp, cmd, data) - struct ifnet *ifp; -#if defined(__FreeBSD__) && __FreeBSD__ < 3 - int cmd; -#else - u_long cmd; -#endif - caddr_t data; +gif_encapcheck4( + const struct mbuf *m, + __unused int off, + __unused int proto, + void *arg) { - struct gif_softc *sc = (struct gif_softc*)ifp; - struct ifreq *ifr = (struct ifreq*)data; - int error = 0, size; - struct sockaddr *sa, *dst, *src; - const struct encaptab *p; - struct sockaddr_in smask4, dmask4; - - switch (cmd) { - case SIOCSIFFLAGS: - /* - * whenever we change our idea about multi-destination mode - * we need to update encap attachment. - */ - if (((ifp->if_flags ^ sc->gif_oflags) & IFF_LINK0) == 0) - break; - if (sc->gif_psrc == NULL || sc->gif_pdst == NULL || - sc->gif_psrc->sa_family != sc->gif_pdst->sa_family) - break; - bzero(&smask4, sizeof(smask4)); - smask4.sin_addr.s_addr = ~0; - dmask4 = smask4; - if ((ifp->if_flags & IFF_LINK0) != 0 && - ((struct sockaddr_in *)dst)->sin_addr.s_addr == - INADDR_ANY) { - bzero(&dmask4, sizeof(dmask4)); - } - p = encap_attach(sc->gif_psrc->sa_family, -1, sc->gif_psrc, - (struct sockaddr *)&smask4, sc->gif_pdst, - (struct sockaddr *)&dmask4, - (struct protosw *)&in_gif_protosw, &sc->gif_if); - if (p == NULL) { - error = EINVAL; - goto bad; + struct ip ip; + struct gif_softc *sc; + struct sockaddr_in *src, *dst; + int addrmatch; + struct in_ifaddr *ia4; + + /* sanity check done in caller */ + sc = (struct gif_softc *)arg; + src = (struct sockaddr_in *)(void *)sc->gif_psrc; + dst = (struct sockaddr_in *)(void *)sc->gif_pdst; + + GIF_LOCK_ASSERT(sc); + + mbuf_copydata((struct mbuf *)(size_t)m, 0, sizeof (ip), &ip); + + /* check for address match */ + addrmatch = 0; + if (src->sin_addr.s_addr == ip.ip_dst.s_addr) + addrmatch |= 1; + if (dst->sin_addr.s_addr == ip.ip_src.s_addr) + addrmatch |= 2; + if (addrmatch != 3) + return (0); + + /* martian filters on outer source - NOT done in ip_input! */ + if (IN_MULTICAST(ntohl(ip.ip_src.s_addr))) + return (0); + switch ((ntohl(ip.ip_src.s_addr) & 0xff000000) >> 24) { + case 0: case 127: case 255: + return (0); + } + /* reject packets with broadcast on source */ + lck_rw_lock_shared(in_ifaddr_rwlock); + for (ia4 = TAILQ_FIRST(&in_ifaddrhead); ia4; + ia4 = TAILQ_NEXT(ia4, ia_link)) { + if ((ifnet_flags(ia4->ia_ifa.ifa_ifp) & IFF_BROADCAST) == 0) + continue; + IFA_LOCK(&ia4->ia_ifa); + if (ip.ip_src.s_addr == ia4->ia_broadaddr.sin_addr.s_addr) { + IFA_UNLOCK(&ia4->ia_ifa); + lck_rw_done(in_ifaddr_rwlock); + return (0); } - if (sc->encap_cookie != NULL) - (void)encap_detach(sc->encap_cookie); - sc->encap_cookie = p; - sc->gif_oflags = ifp->if_flags; - - break; - - case SIOCSIFPHYADDR: - switch (ifr->ifr_addr.sa_family) { - case AF_INET: - src = (struct sockaddr *) - &(((struct in_aliasreq *)data)->ifra_addr); - dst = (struct sockaddr *) - &(((struct in_aliasreq *)data)->ifra_dstaddr); - - bzero(&smask4, sizeof(smask4)); - smask4.sin_addr.s_addr = ~0; - dmask4 = smask4; - if ((ifp->if_flags & IFF_LINK0) != 0 && - ((struct sockaddr_in *)dst)->sin_addr.s_addr == - INADDR_ANY) { - bzero(&dmask4, sizeof(dmask4)); + IFA_UNLOCK(&ia4->ia_ifa); + } + lck_rw_done(in_ifaddr_rwlock); + + /* ingress filters on outer source */ + if ((ifnet_flags(sc->gif_if) & IFF_LINK2) == 0 && + (m->m_flags & M_PKTHDR) != 0 && m->m_pkthdr.rcvif) { + struct sockaddr_in sin; + struct rtentry *rt; + + bzero(&sin, sizeof (sin)); + sin.sin_family = AF_INET; + sin.sin_len = sizeof (struct sockaddr_in); + sin.sin_addr = ip.ip_src; + rt = rtalloc1_scoped((struct sockaddr *)&sin, 0, 0, + m->m_pkthdr.rcvif->if_index); + if (rt != NULL) + RT_LOCK(rt); + if (rt == NULL || rt->rt_ifp != m->m_pkthdr.rcvif) { + if (rt != NULL) { + RT_UNLOCK(rt); + rtfree(rt); } - size = sizeof(struct sockaddr_in); - break; - default: - error = EAFNOSUPPORT; - goto bad; - } - - if (sc->encap_cookie) - (void)encap_detach(sc->encap_cookie); - if (sc->gif_psrc != NULL) { - _FREE((caddr_t)sc->gif_psrc, M_IFADDR); - sc->gif_psrc = NULL; + return (0); } - if (sc->gif_pdst != NULL) { - _FREE((caddr_t)sc->gif_pdst, M_IFADDR); - sc->gif_pdst = NULL; - } - - p = encap_attach(ifr->ifr_addr.sa_family, -1, src, - (struct sockaddr *)&smask4, dst, - (struct sockaddr *)&dmask4, - (struct protosw *)&in_gif_protosw, &sc->gif_if); - if (p == NULL) { - error = EINVAL; - goto bad; - } - sc->encap_cookie = p; - sc->gif_oflags = ifp->if_flags; - - sa = (struct sockaddr *)_MALLOC(size, M_IFADDR, M_WAITOK); - bcopy((caddr_t)src, (caddr_t)sa, size); - sc->gif_psrc = sa; - - sa = (struct sockaddr *)_MALLOC(size, M_IFADDR, M_WAITOK); - bcopy((caddr_t)dst, (caddr_t)sa, size); - sc->gif_pdst = sa; - - ifp->if_flags |= IFF_UP; - if_up(ifp); /* send up RTM_IFINFO */ - - error = 0; - break; - default: - error = EINVAL; - goto bad; + RT_UNLOCK(rt); + rtfree(rt); } - bad: - return error; + return (32 * 2); }