X-Git-Url: https://git.saurik.com/apple/xnu.git/blobdiff_plain/9bccf70c0258c7cac2dcb80011b2a964d884c552..d190cdc3f5544636abb56dc1874be391d3e1b148:/bsd/net/if_stf.c diff --git a/bsd/net/if_stf.c b/bsd/net/if_stf.c index 7bdf6faa5..62f662115 100644 --- a/bsd/net/if_stf.c +++ b/bsd/net/if_stf.c @@ -1,3 +1,31 @@ +/* + * Copyright (c) 2000-2016 Apple Inc. All rights reserved. + * + * @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, 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@ + */ + /* $FreeBSD: src/sys/net/if_stf.c,v 1.1.2.6 2001/07/24 19:10:18 brooks Exp $ */ /* $KAME: if_stf.c,v 1.62 2001/06/07 22:32:16 itojun Exp $ */ @@ -29,6 +57,12 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ +/* + * NOTICE: This file was modified by SPARTA, Inc. in 2006 to introduce + * support for mandatory and extensible security protections. This notice + * is included in support of clause 2.2 (b) of the Apple Public License, + * Version 2.0. + */ /* * 6to4 interface, based on RFC3056. @@ -83,15 +117,14 @@ #include #include #include -#include #include +#include + #include #include -#include #include -#include #include #include @@ -105,236 +138,258 @@ #include #include -#include +#include +#include #include #include -#define IN6_IS_ADDR_6TO4(x) (ntohs((x)->s6_addr16[0]) == 0x2002) -#define GET_V4(x) ((struct in_addr *)(&(x)->s6_addr16[1])) +#if CONFIG_MACF_NET +#include +#endif + +#define GET_V4(x) ((const struct in_addr *)(const void *)(&(x)->s6_addr16[1])) + +static lck_grp_t *stf_mtx_grp; struct stf_softc { - struct ifnet sc_if; /* common area */ -#ifdef __APPLE__ - struct if_proto *stf_proto; /* dlil protocol attached */ -#endif + ifnet_t sc_if; /* common area */ + u_int32_t sc_protocol_family; /* dlil protocol attached */ union { struct route __sc_ro4; struct route_in6 __sc_ro6; /* just for safety */ } __sc_ro46; #define sc_ro __sc_ro46.__sc_ro4 + decl_lck_mtx_data(, sc_ro_mtx); const struct encaptab *encap_cookie; + bpf_tap_mode tap_mode; + bpf_packet_func tap_callback; }; -static struct stf_softc *stf; +void stfattach (void); -#ifdef __APPLE__ -void stfattach __P((void)); -int stf_pre_output __P((struct ifnet *, register struct mbuf **, struct sockaddr *, - caddr_t, char *, char *, u_long)); -static u_long stf_dl_tag=0; -#endif - -#ifndef __APPLE__ -static MALLOC_DEFINE(M_STF, "stf", "6to4 Tunnel Interface"); -#endif static int ip_stf_ttl = 40; +static int stf_init_done; -extern struct domain inetdomain; -struct protosw in_stf_protosw = -{ SOCK_RAW, &inetdomain, IPPROTO_IPV6, PR_ATOMIC|PR_ADDR, - in_stf_input, rip_output, 0, rip_ctloutput, - 0, - 0, 0, 0, 0, - 0, - &rip_usrreqs -}; +static void in_stf_input(struct mbuf *, int); +static void stfinit(void); -static int stf_encapcheck __P((const struct mbuf *, int, int, void *)); -static struct in6_ifaddr *stf_getsrcifa6 __P((struct ifnet *)); -int stf_pre_output __P((struct ifnet *, register struct mbuf **, struct sockaddr *, - caddr_t, char *, char *, u_long)); -static int stf_checkaddr4 __P((struct stf_softc *, struct in_addr *, - struct ifnet *)); -static int stf_checkaddr6 __P((struct stf_softc *, struct in6_addr *, - struct ifnet *)); -static void stf_rtrequest __P((int, struct rtentry *, struct sockaddr *)); -int stf_ioctl __P((struct ifnet *, u_long, void *)); +static struct protosw in_stf_protosw = +{ + .pr_type = SOCK_RAW, + .pr_protocol = IPPROTO_IPV6, + .pr_flags = PR_ATOMIC|PR_ADDR, + .pr_input = in_stf_input, + .pr_ctloutput = rip_ctloutput, + .pr_usrreqs = &rip_usrreqs, + .pr_unlock = rip_unlock, +}; +static int stf_encapcheck(const struct mbuf *, int, int, void *); +static struct in6_ifaddr *stf_getsrcifa6(struct ifnet *); +int stf_pre_output(struct ifnet *, protocol_family_t, struct mbuf **, + const struct sockaddr *, void *, char *, char *); +static int stf_checkaddr4(struct stf_softc *, const struct in_addr *, + struct ifnet *); +static int stf_checkaddr6(struct stf_softc *, struct in6_addr *, + struct ifnet *); +static void stf_rtrequest(int, struct rtentry *, struct sockaddr *); +static errno_t stf_ioctl(ifnet_t ifp, u_long cmd, void *data); +static errno_t stf_output(ifnet_t ifp, mbuf_t m); -static -int stf_add_if(struct ifnet *ifp) +static void +stfinit(void) { - ifp->if_demux = 0; - ifp->if_framer = 0; - return 0; + if (!stf_init_done) { + stf_mtx_grp = lck_grp_alloc_init("stf", LCK_GRP_ATTR_NULL); + stf_init_done = 1; + } } -static -int stf_del_if(struct ifnet *ifp) +/* + * gif_input is the input handler for IP and IPv6 attached to gif + */ +static errno_t +stf_media_input( + __unused ifnet_t ifp, + protocol_family_t protocol_family, + mbuf_t m, + __unused char *frame_header) { - return 0; + if (proto_input(protocol_family, m) != 0) + m_freem(m); + + return (0); } -static -int stf_add_proto(struct ddesc_head_str *desc_head, struct if_proto *proto, u_long dl_tag) -{ + + +static errno_t +stf_add_proto( + ifnet_t ifp, + protocol_family_t protocol_family, + __unused const struct ifnet_demux_desc *demux_array, + __unused u_int32_t demux_count) +{ /* Only one protocol may be attached at a time */ - struct stf_softc* stf = (struct stf_softc*)proto->ifp; - if (stf->stf_proto == NULL) - stf->stf_proto = proto; + struct stf_softc* stf = ifnet_softc(ifp); + if (stf->sc_protocol_family == 0) + stf->sc_protocol_family = protocol_family; else { printf("stf_add_proto: stf already has a proto\n"); - return (EBUSY); + return EBUSY; } - - return (0); -} - -static -int stf_del_proto(struct if_proto *proto, u_long dl_tag) -{ - if (((struct stf_softc*)proto->ifp)->stf_proto == proto) - ((struct stf_softc*)proto->ifp)->stf_proto = NULL; - else - return ENOENT; - + return 0; } -int stf_shutdown() +static errno_t +stf_del_proto( + ifnet_t ifp, + protocol_family_t protocol_family) { + if (((struct stf_softc*)ifnet_softc(ifp))->sc_protocol_family == protocol_family) + ((struct stf_softc*)ifnet_softc(ifp))->sc_protocol_family = 0; + return 0; } -void stf_reg_if_mods() -{ - struct dlil_ifmod_reg_str stf_ifmod; +static errno_t +stf_attach_inet6( + ifnet_t ifp, + protocol_family_t protocol_family) +{ + struct ifnet_attach_proto_param reg; + errno_t stat; + + if (protocol_family != PF_INET6) + return EPROTONOSUPPORT; - bzero(&stf_ifmod, sizeof(stf_ifmod)); - stf_ifmod.add_if = stf_add_if; - stf_ifmod.del_if = stf_del_if; - stf_ifmod.add_proto = stf_add_proto; - stf_ifmod.del_proto = stf_del_proto; - stf_ifmod.ifmod_ioctl = 0; - stf_ifmod.shutdown = stf_shutdown; + bzero(®, sizeof(reg)); + reg.input = stf_media_input; + reg.pre_output = stf_pre_output; - - if (dlil_reg_if_modules(APPLE_IF_FAM_STF, &stf_ifmod)) - panic("Couldn't register stf modules\n"); - -} - -u_long stf_attach_inet6(struct ifnet *ifp) -{ - struct dlil_proto_reg_str reg; - struct dlil_demux_desc desc; - short native=0; - int stat, i; - - if (stf_dl_tag != 0) - return stf_dl_tag; - - TAILQ_INIT(®.demux_desc_head); - desc.type = DLIL_DESC_RAW; - desc.variants.bitmask.proto_id_length = 0; - desc.variants.bitmask.proto_id = 0; - desc.variants.bitmask.proto_id_mask = 0; - desc.native_type = (char *) &native; - TAILQ_INSERT_TAIL(®.demux_desc_head, &desc, next); - reg.interface_family = ifp->if_family; - reg.unit_number = ifp->if_unit; - reg.input = 0; - reg.pre_output = stf_pre_output; - reg.event = 0; - reg.offer = 0; - reg.ioctl = 0; - reg.default_proto = 0; - reg.protocol_family = PF_INET6; - - stat = dlil_attach_protocol(®, &stf_dl_tag); - if (stat) { - panic("stf_attach_inet6 can't attach interface\n"); + stat = ifnet_attach_protocol(ifp, protocol_family, ®); + if (stat && stat != EEXIST) { + printf("stf_attach_proto_family can't attach interface fam=%d\n", + protocol_family); } - return stf_dl_tag; + return stat; } -u_long stf_detach_inet6(struct ifnet *ifp) +static errno_t +stf_demux( + ifnet_t ifp, + __unused mbuf_t m, + __unused char *frame_ptr, + protocol_family_t *protocol_family) { - u_long ip_dl_tag = 0; - int stat; - - stat = dlil_find_dltag(ifp->if_family, ifp->if_unit, AF_INET6, &ip_dl_tag); - if (stat == 0) { - stat = dlil_detach_protocol(ip_dl_tag); - if (stat) { - printf("WARNING: stf_detach can't detach IP AF_INET6 from interface\n"); - } - } - return (stat); + struct stf_softc* stf = ifnet_softc(ifp); + *protocol_family = stf->sc_protocol_family; + return 0; } +static errno_t +stf_set_bpf_tap( + ifnet_t ifp, + bpf_tap_mode mode, + bpf_packet_func callback) +{ + struct stf_softc *sc = ifnet_softc(ifp); + + sc->tap_mode = mode; + sc->tap_callback = callback; + + return 0; +} void stfattach(void) { - struct ifnet *ifp; struct stf_softc *sc; - int i, error; - - - int err; + int error; const struct encaptab *p; + struct ifnet_init_params stf_init; + + stfinit(); - stf_reg_if_mods(); /* DLIL modules */ + error = proto_register_plumber(PF_INET6, APPLE_IF_FAM_STF, + stf_attach_inet6, NULL); + if (error != 0) + printf("proto_register_plumber failed for AF_INET6 error=%d\n", error); - sc = _MALLOC(sizeof(struct stf_softc), M_DEVBUF, M_WAITOK); + sc = _MALLOC(sizeof(struct stf_softc), M_DEVBUF, M_WAITOK | M_ZERO); if (sc == 0) { printf("stf softc attach failed\n" ); return; } - - bzero(sc, sizeof(*sc)); - sc->sc_if.if_name = "stf"; - sc->sc_if.if_unit = 0; - + p = encap_attach_func(AF_INET, IPPROTO_IPV6, stf_encapcheck, &in_stf_protosw, sc); if (p == NULL) { - printf("%s: attach failed\n", if_name(&sc->sc_if)); + printf("sftattach encap_attach_func failed\n"); FREE(sc, M_DEVBUF); return; } sc->encap_cookie = p; - sc->sc_if.if_mtu = IPV6_MMTU; - sc->sc_if.if_flags = 0; - sc->sc_if.if_ioctl = stf_ioctl; - sc->sc_if.if_output = NULL; /* processing done in pre_output */ - sc->sc_if.if_type = IFT_STF; - sc->sc_if.if_family= APPLE_IF_FAM_STF; + lck_mtx_init(&sc->sc_ro_mtx, stf_mtx_grp, LCK_ATTR_NULL); + + bzero(&stf_init, sizeof(stf_init)); + stf_init.name = "stf"; + stf_init.unit = 0; + stf_init.type = IFT_STF; + stf_init.family = IFNET_FAMILY_STF; + stf_init.output = stf_output; + stf_init.demux = stf_demux; + stf_init.add_proto = stf_add_proto; + stf_init.del_proto = stf_del_proto; + stf_init.softc = sc; + stf_init.ioctl = stf_ioctl; + stf_init.set_bpf_tap = stf_set_bpf_tap; + + error = ifnet_allocate(&stf_init, &sc->sc_if); + if (error != 0) { + printf("stfattach, ifnet_allocate failed - %d\n", error); + encap_detach(sc->encap_cookie); + lck_mtx_destroy(&sc->sc_ro_mtx, stf_mtx_grp); + FREE(sc, M_DEVBUF); + return; + } + ifnet_set_mtu(sc->sc_if, IPV6_MMTU); + ifnet_set_flags(sc->sc_if, 0, 0xffff); /* clear all flags */ #if 0 /* turn off ingress filter */ - sc->sc_if.if_flags |= IFF_LINK2; + ifnet_set_flags(sc->sc_if, IFF_LINK2, IFF_LINK2); #endif - sc->sc_if.if_snd.ifq_maxlen = IFQ_MAXLEN; - if (error = dlil_if_attach(&sc->sc_if)) - printf("stfattach: can't dlil_if_attach error=%d\n"); - else - bpfattach(&sc->sc_if, DLT_NULL, sizeof(u_int)); +#if CONFIG_MACF_NET + mac_ifnet_label_init(&sc->sc_if); +#endif + + error = ifnet_attach(sc->sc_if, NULL); + if (error != 0) { + printf("stfattach: ifnet_attach returned error=%d\n", error); + encap_detach(sc->encap_cookie); + ifnet_release(sc->sc_if); + lck_mtx_destroy(&sc->sc_ro_mtx, stf_mtx_grp); + FREE(sc, M_DEVBUF); + return; + } + + bpfattach(sc->sc_if, DLT_NULL, sizeof(u_int)); - return ; + return; } static int -stf_encapcheck(m, off, proto, arg) - const struct mbuf *m; - int off; - int proto; - void *arg; +stf_encapcheck( + const struct mbuf *m, + __unused int off, + int proto, + void *arg) { struct ip ip; struct in6_ifaddr *ia6; @@ -345,23 +400,22 @@ stf_encapcheck(m, off, proto, arg) if (sc == NULL) return 0; - if ((sc->sc_if.if_flags & IFF_UP) == 0) + if ((ifnet_flags(sc->sc_if) & IFF_UP) == 0) return 0; /* IFF_LINK0 means "no decapsulation" */ - if ((sc->sc_if.if_flags & IFF_LINK0) != 0) + if ((ifnet_flags(sc->sc_if) & IFF_LINK0) != 0) return 0; if (proto != IPPROTO_IPV6) return 0; - /* LINTED const cast */ - m_copydata((struct mbuf *)m, 0, sizeof(ip), (caddr_t)&ip); + mbuf_copydata((struct mbuf *)(size_t)m, 0, sizeof(ip), &ip); if (ip.ip_v != 4) return 0; - ia6 = stf_getsrcifa6(&sc->sc_if); + ia6 = stf_getsrcifa6(sc->sc_if); if (ia6 == NULL) return 0; @@ -370,10 +424,13 @@ stf_encapcheck(m, off, proto, arg) * local 6to4 address. * success on: dst = 10.1.1.1, ia6->ia_addr = 2002:0a01:0101:... */ + IFA_LOCK(&ia6->ia_ifa); if (bcmp(GET_V4(&ia6->ia_addr.sin6_addr), &ip.ip_dst, - sizeof(ip.ip_dst)) != 0) + sizeof(ip.ip_dst)) != 0) { + IFA_UNLOCK(&ia6->ia_ifa); + IFA_REMREF(&ia6->ia_ifa); return 0; - + } /* * check if IPv4 src matches the IPv4 address derived from the * local 6to4 address masked by prefixmask. @@ -385,77 +442,97 @@ stf_encapcheck(m, off, proto, arg) a.s_addr &= GET_V4(&ia6->ia_prefixmask.sin6_addr)->s_addr; b = ip.ip_src; b.s_addr &= GET_V4(&ia6->ia_prefixmask.sin6_addr)->s_addr; - if (a.s_addr != b.s_addr) + if (a.s_addr != b.s_addr) { + IFA_UNLOCK(&ia6->ia_ifa); + IFA_REMREF(&ia6->ia_ifa); return 0; - + } /* stf interface makes single side match only */ + IFA_UNLOCK(&ia6->ia_ifa); + IFA_REMREF(&ia6->ia_ifa); return 32; } static struct in6_ifaddr * -stf_getsrcifa6(ifp) - struct ifnet *ifp; +stf_getsrcifa6(struct ifnet *ifp) { struct ifaddr *ia; struct in_ifaddr *ia4; struct sockaddr_in6 *sin6; struct in_addr in; - for (ia = ifp->if_addrlist.tqh_first; - ia; - ia = ia->ifa_list.tqe_next) - { - if (ia->ifa_addr == NULL) + ifnet_lock_shared(ifp); + for (ia = ifp->if_addrlist.tqh_first; ia; ia = ia->ifa_list.tqe_next) { + IFA_LOCK(ia); + if (ia->ifa_addr == NULL) { + IFA_UNLOCK(ia); continue; - if (ia->ifa_addr->sa_family != AF_INET6) + } + if (ia->ifa_addr->sa_family != AF_INET6) { + IFA_UNLOCK(ia); continue; - sin6 = (struct sockaddr_in6 *)ia->ifa_addr; - if (!IN6_IS_ADDR_6TO4(&sin6->sin6_addr)) + } + sin6 = (struct sockaddr_in6 *)(void *)ia->ifa_addr; + if (!IN6_IS_ADDR_6TO4(&sin6->sin6_addr)) { + IFA_UNLOCK(ia); continue; - + } bcopy(GET_V4(&sin6->sin6_addr), &in, sizeof(in)); + IFA_UNLOCK(ia); + lck_rw_lock_shared(in_ifaddr_rwlock); for (ia4 = TAILQ_FIRST(&in_ifaddrhead); ia4; ia4 = TAILQ_NEXT(ia4, ia_link)) { - if (ia4->ia_addr.sin_addr.s_addr == in.s_addr) + IFA_LOCK(&ia4->ia_ifa); + if (ia4->ia_addr.sin_addr.s_addr == in.s_addr) { + IFA_UNLOCK(&ia4->ia_ifa); break; + } + IFA_UNLOCK(&ia4->ia_ifa); } + lck_rw_done(in_ifaddr_rwlock); if (ia4 == NULL) continue; - return (struct in6_ifaddr *)ia; + IFA_ADDREF(ia); /* for caller */ + ifnet_lock_done(ifp); + return ((struct in6_ifaddr *)ia); } + ifnet_lock_done(ifp); - return NULL; + return (NULL); } int -stf_pre_output(ifp, m0, dst, rt, frame_type, address, dl_tag) - struct ifnet *ifp; - register struct mbuf **m0; - struct sockaddr *dst; - caddr_t rt; - char *frame_type; - char *address; - u_long dl_tag; +stf_pre_output( + struct ifnet *ifp, + __unused protocol_family_t protocol_family, + struct mbuf **m0, + const struct sockaddr *dst, + __unused void *route, + __unused char *desk_linkaddr, + __unused char *frame_type) { - register struct mbuf *m = *m0; + struct mbuf *m = *m0; struct stf_softc *sc; - struct sockaddr_in6 *dst6; - struct in_addr *in4; - struct sockaddr_in *dst4; + const struct sockaddr_in6 *dst6; + const struct in_addr *in4; u_int8_t tos; struct ip *ip; struct ip6_hdr *ip6; struct in6_ifaddr *ia6; - int error = 0 ; + struct sockaddr_in *dst4; + struct ip_out_args ipoa = + { IFSCOPE_NONE, { 0 }, IPOAF_SELECT_SRCIF, 0, + SO_TC_UNSPEC, _NET_SERVICE_TYPE_UNSPEC }; + errno_t result = 0; - sc = (struct stf_softc*)ifp; - dst6 = (struct sockaddr_in6 *)dst; + sc = ifnet_softc(ifp); + dst6 = (const struct sockaddr_in6 *)(const void *)dst; /* just in case */ - if ((ifp->if_flags & IFF_UP) == 0) { + if ((ifnet_flags(ifp) & IFF_UP) == 0) { printf("stf: IFF_DOWN\n"); return ENETDOWN; } @@ -470,10 +547,13 @@ stf_pre_output(ifp, m0, dst, rt, frame_type, address, dl_tag) return ENETDOWN; } - if (m->m_len < sizeof(*ip6)) { + if (mbuf_len(m) < sizeof(*ip6)) { m = m_pullup(m, sizeof(*ip6)); - if (!m) + if (!m) { + *m0 = NULL; /* makes sure this won't be double freed */ + IFA_REMREF(&ia6->ia_ifa); return ENOBUFS; + } } ip6 = mtod(m, struct ip6_hdr *); tos = (ntohl(ip6->ip6_flow) >> 20) & 0xff; @@ -487,77 +567,80 @@ stf_pre_output(ifp, m0, dst, rt, frame_type, address, dl_tag) else if (IN6_IS_ADDR_6TO4(&dst6->sin6_addr)) in4 = GET_V4(&dst6->sin6_addr); else { + IFA_REMREF(&ia6->ia_ifa); return ENETUNREACH; } if (ifp->if_bpf) { - /* - * We need to prepend the address family as - * a four byte field. Cons up a dummy header - * to pacify bpf. This is safe because bpf - * will only read from the mbuf (i.e., it won't - * try to free it or keep a pointer a to it). - */ - struct mbuf m0; + /* We need to prepend the address family as a four byte field. */ u_int32_t af = AF_INET6; - m0.m_next = m; - m0.m_len = 4; - m0.m_data = (char *)⁡ - - bpf_mtap(ifp, &m0); + bpf_tap_out(ifp, 0, m, &af, sizeof(af)); } - M_PREPEND(m, sizeof(struct ip), M_DONTWAIT); - if (m && m->m_len < sizeof(struct ip)) + M_PREPEND(m, sizeof(struct ip), M_DONTWAIT, 1); + if (m && mbuf_len(m) < sizeof(struct ip)) m = m_pullup(m, sizeof(struct ip)); - if (m == NULL) + if (m == NULL) { + *m0 = NULL; + IFA_REMREF(&ia6->ia_ifa); return ENOBUFS; + } ip = mtod(m, struct ip *); bzero(ip, sizeof(*ip)); + IFA_LOCK_SPIN(&ia6->ia_ifa); bcopy(GET_V4(&((struct sockaddr_in6 *)&ia6->ia_addr)->sin6_addr), &ip->ip_src, sizeof(ip->ip_src)); + IFA_UNLOCK(&ia6->ia_ifa); bcopy(in4, &ip->ip_dst, sizeof(ip->ip_dst)); ip->ip_p = IPPROTO_IPV6; ip->ip_ttl = ip_stf_ttl; ip->ip_len = m->m_pkthdr.len; /*host order*/ if (ifp->if_flags & IFF_LINK1) - ip_ecn_ingress(ECN_ALLOWED, &ip->ip_tos, &tos); + ip_ecn_ingress(ECN_NORMAL, &ip->ip_tos, &tos); else ip_ecn_ingress(ECN_NOCARE, &ip->ip_tos, &tos); - dst4 = (struct sockaddr_in *)&sc->sc_ro.ro_dst; - if (dst4->sin_family != AF_INET || + lck_mtx_lock(&sc->sc_ro_mtx); + dst4 = (struct sockaddr_in *)(void *)&sc->sc_ro.ro_dst; + if (ROUTE_UNUSABLE(&sc->sc_ro) || dst4->sin_family != AF_INET || bcmp(&dst4->sin_addr, &ip->ip_dst, sizeof(ip->ip_dst)) != 0) { - /* cache route doesn't match */ + ROUTE_RELEASE(&sc->sc_ro); + /* cache route doesn't match: always the case during the first use */ dst4->sin_family = AF_INET; dst4->sin_len = sizeof(struct sockaddr_in); bcopy(&ip->ip_dst, &dst4->sin_addr, sizeof(dst4->sin_addr)); - if (sc->sc_ro.ro_rt) { - RTFREE(sc->sc_ro.ro_rt); - sc->sc_ro.ro_rt = NULL; - } } - if (sc->sc_ro.ro_rt == NULL) { - rtalloc(&sc->sc_ro); - if (sc->sc_ro.ro_rt == NULL) { - return ENETUNREACH; - } - } + result = ip_output(m, NULL, &sc->sc_ro, IP_OUTARGS, NULL, &ipoa); + lck_mtx_unlock(&sc->sc_ro_mtx); - error = ip_output(m, NULL, &sc->sc_ro, 0, NULL); - if (error == 0) - return EJUSTRETURN; + /* Assumption: ip_output will free mbuf on errors */ + /* All the output processing is done here, don't let stf_output be called */ + if (result == 0) + result = EJUSTRETURN; + *m0 = NULL; + IFA_REMREF(&ia6->ia_ifa); + return result; } +static errno_t +stf_output( + __unused ifnet_t ifp, + __unused mbuf_t m) +{ + /* All processing is done in stf_pre_output + * this shouldn't be called as the pre_output returns "EJUSTRETURN" + */ + return 0; +} static int -stf_checkaddr4(sc, in, inifp) - struct stf_softc *sc; - struct in_addr *in; - struct ifnet *inifp; /* incoming interface */ +stf_checkaddr4( + struct stf_softc *sc, + const struct in_addr *in, + struct ifnet *inifp) /* incoming interface */ { struct in_ifaddr *ia4; @@ -575,20 +658,29 @@ stf_checkaddr4(sc, in, inifp) /* * reject packets with broadcast */ + lck_rw_lock_shared(in_ifaddr_rwlock); for (ia4 = TAILQ_FIRST(&in_ifaddrhead); ia4; ia4 = TAILQ_NEXT(ia4, ia_link)) { - if ((ia4->ia_ifa.ifa_ifp->if_flags & IFF_BROADCAST) == 0) + IFA_LOCK(&ia4->ia_ifa); + if ((ia4->ia_ifa.ifa_ifp->if_flags & IFF_BROADCAST) == 0) { + IFA_UNLOCK(&ia4->ia_ifa); continue; - if (in->s_addr == ia4->ia_broadaddr.sin_addr.s_addr) + } + if (in->s_addr == ia4->ia_broadaddr.sin_addr.s_addr) { + IFA_UNLOCK(&ia4->ia_ifa); + lck_rw_done(in_ifaddr_rwlock); return -1; + } + IFA_UNLOCK(&ia4->ia_ifa); } + lck_rw_done(in_ifaddr_rwlock); /* * perform ingress filter */ - if (sc && (sc->sc_if.if_flags & IFF_LINK2) == 0 && inifp) { + if (sc && (ifnet_flags(sc->sc_if) & IFF_LINK2) == 0 && inifp) { struct sockaddr_in sin; struct rtentry *rt; @@ -596,17 +688,22 @@ stf_checkaddr4(sc, in, inifp) sin.sin_family = AF_INET; sin.sin_len = sizeof(struct sockaddr_in); sin.sin_addr = *in; - rt = rtalloc1((struct sockaddr *)&sin, 0, 0UL); - if (!rt || rt->rt_ifp != inifp) { + rt = rtalloc1((struct sockaddr *)&sin, 0, 0); + if (rt != NULL) + RT_LOCK(rt); + if (rt == NULL || rt->rt_ifp != inifp) { #if 1 log(LOG_WARNING, "%s: packet from 0x%x dropped " - "due to ingress filter\n", if_name(&sc->sc_if), + "due to ingress filter\n", if_name(sc->sc_if), (u_int32_t)ntohl(sin.sin_addr.s_addr)); #endif - if (rt) + if (rt != NULL) { + RT_UNLOCK(rt); rtfree(rt); + } return -1; } + RT_UNLOCK(rt); rtfree(rt); } @@ -614,10 +711,10 @@ stf_checkaddr4(sc, in, inifp) } static int -stf_checkaddr6(sc, in6, inifp) - struct stf_softc *sc; - struct in6_addr *in6; - struct ifnet *inifp; /* incoming interface */ +stf_checkaddr6( + struct stf_softc *sc, + struct in6_addr *in6, + struct ifnet *inifp) /* incoming interface */ { /* * check 6to4 addresses @@ -637,23 +734,22 @@ stf_checkaddr6(sc, in6, inifp) return 0; } -void -in_stf_input(m, off) - struct mbuf *m; - int off; +static void +in_stf_input( + struct mbuf *m, + int off) { struct stf_softc *sc; struct ip *ip; - struct ip6_hdr *ip6; + struct ip6_hdr ip6; u_int8_t otos, itos; - int s, isr, proto; - struct ifqueue *ifq = NULL; + int proto; struct ifnet *ifp; + struct ifnet_stat_increment_param stats; ip = mtod(m, struct ip *); proto = ip->ip_p; - if (proto != IPPROTO_IPV6) { m_freem(m); return; @@ -663,12 +759,16 @@ in_stf_input(m, off) sc = (struct stf_softc *)encap_getarg(m); - if (sc == NULL || (sc->sc_if.if_flags & IFF_UP) == 0) { + if (sc == NULL || (ifnet_flags(sc->sc_if) & IFF_UP) == 0) { m_freem(m); return; } - ifp = &sc->sc_if; + ifp = sc->sc_if; + +#if MAC_LABEL + mac_mbuf_label_associate_ifnet(ifp, m); +#endif /* * perform sanity check against outer src/dst. @@ -681,55 +781,34 @@ in_stf_input(m, off) } otos = ip->ip_tos; - m_adj(m, off); - - if (m->m_len < sizeof(*ip6)) { - m = m_pullup(m, sizeof(*ip6)); - if (!m) - return; - } - ip6 = mtod(m, struct ip6_hdr *); + mbuf_copydata(m, off, sizeof(ip6), &ip6); /* * perform sanity check against inner src/dst. * for source, perform ingress filter as well. */ - if (stf_checkaddr6(sc, &ip6->ip6_dst, NULL) < 0 || - stf_checkaddr6(sc, &ip6->ip6_src, m->m_pkthdr.rcvif) < 0) { + if (stf_checkaddr6(sc, &ip6.ip6_dst, NULL) < 0 || + stf_checkaddr6(sc, &ip6.ip6_src, m->m_pkthdr.rcvif) < 0) { m_freem(m); return; } - itos = (ntohl(ip6->ip6_flow) >> 20) & 0xff; - if ((ifp->if_flags & IFF_LINK1) != 0) - ip_ecn_egress(ECN_ALLOWED, &otos, &itos); + itos = (ntohl(ip6.ip6_flow) >> 20) & 0xff; + if ((ifnet_flags(ifp) & IFF_LINK1) != 0) + ip_ecn_egress(ECN_NORMAL, &otos, &itos); else ip_ecn_egress(ECN_NOCARE, &otos, &itos); - ip6->ip6_flow &= ~htonl(0xff << 20); - ip6->ip6_flow |= htonl((u_int32_t)itos << 20); + ip6.ip6_flow &= ~htonl(0xff << 20); + ip6.ip6_flow |= htonl((u_int32_t)itos << 20); m->m_pkthdr.rcvif = ifp; + mbuf_pkthdr_setheader(m, mbuf_data(m)); + mbuf_adj(m, off); if (ifp->if_bpf) { - /* - * We need to prepend the address family as - * a four byte field. Cons up a dummy header - * to pacify bpf. This is safe because bpf - * will only read from the mbuf (i.e., it won't - * try to free it or keep a pointer a to it). - */ - struct mbuf m0; + /* We need to prepend the address family as a four byte field. */ u_int32_t af = AF_INET6; - - m0.m_next = m; - m0.m_len = 4; - m0.m_data = (char *)⁡ - -#ifdef HAVE_OLD_BPF - bpf_mtap(ifp, &m0); -#else - bpf_mtap(ifp->if_bpf, &m0); -#endif + bpf_tap_in(ifp, 0, m, &af, sizeof(af)); } /* @@ -738,40 +817,32 @@ in_stf_input(m, off) * See net/if_gif.c for possible issues with packet processing * reorder due to extra queueing. */ - ifq = &ip6intrq; - isr = NETISR_IPV6; - - s = splimp(); - if (IF_QFULL(ifq)) { - IF_DROP(ifq); /* update statistics */ - m_freem(m); - splx(s); - return; - } - IF_ENQUEUE(ifq, m); - schednetisr(isr); - ifp->if_ipackets++; - ifp->if_ibytes += m->m_pkthdr.len; - splx(s); + bzero(&stats, sizeof(stats)); + stats.packets_in = 1; + stats.bytes_in = mbuf_pkthdr_len(m); + mbuf_pkthdr_setrcvif(m, ifp); + ifnet_input(ifp, m, &stats); + + return; } -/* ARGSUSED */ static void -stf_rtrequest(cmd, rt, sa) - int cmd; - struct rtentry *rt; - struct sockaddr *sa; +stf_rtrequest( + __unused int cmd, + struct rtentry *rt, + __unused struct sockaddr *sa) { - - if (rt) + if (rt != NULL) { + RT_LOCK_ASSERT_HELD(rt); rt->rt_rmx.rmx_mtu = IPV6_MMTU; + } } -int -stf_ioctl(ifp, cmd, data) - struct ifnet *ifp; - u_long cmd; - void *data; +static errno_t +stf_ioctl( + ifnet_t ifp, + u_long cmd, + void *data) { struct ifaddr *ifa; struct ifreq *ifr; @@ -782,16 +853,31 @@ stf_ioctl(ifp, cmd, data) switch (cmd) { case SIOCSIFADDR: ifa = (struct ifaddr *)data; - if (ifa == NULL || ifa->ifa_addr->sa_family != AF_INET6) { + if (ifa == NULL) { error = EAFNOSUPPORT; break; } - sin6 = (struct sockaddr_in6 *)ifa->ifa_addr; + IFA_LOCK(ifa); + if (ifa->ifa_addr->sa_family != AF_INET6) { + IFA_UNLOCK(ifa); + error = EAFNOSUPPORT; + break; + } + sin6 = (struct sockaddr_in6 *)(void *)ifa->ifa_addr; if (IN6_IS_ADDR_6TO4(&sin6->sin6_addr)) { - ifa->ifa_rtrequest = stf_rtrequest; - ifp->if_flags |= IFF_UP; - } else + if ( !(ifnet_flags( ifp ) & IFF_UP) ) { + /* do this only if the interface is not already up */ + ifa->ifa_rtrequest = stf_rtrequest; + IFA_UNLOCK(ifa); + ifnet_set_flags(ifp, IFF_UP, IFF_UP); + } else { + IFA_UNLOCK(ifa); + } + } else { + IFA_UNLOCK(ifa); error = EINVAL; + } + IFA_LOCK_ASSERT_NOTHELD(ifa); break; case SIOCADDMULTI: @@ -804,7 +890,7 @@ stf_ioctl(ifp, cmd, data) break; default: - error = EINVAL; + error = EOPNOTSUPP; break; }