diff options
Diffstat (limited to 'sys/net/if_stf.c')
-rw-r--r-- | sys/net/if_stf.c | 648 |
1 files changed, 535 insertions, 113 deletions
diff --git a/sys/net/if_stf.c b/sys/net/if_stf.c index 5209bc8..4f6dfd9 100644 --- a/sys/net/if_stf.c +++ b/sys/net/if_stf.c @@ -3,6 +3,9 @@ /*- * Copyright (C) 2000 WIDE Project. + * Copyright (c) 2010 Hiroki Sato <hrs@FreeBSD.org> + * Copyright (c) 2013 Ermal Luçi <eri@FreeBSD.org> + * Copyright (c) 2017 Rubicon Communications, LLC (Netgate) * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -31,7 +34,7 @@ */ /* - * 6to4 interface, based on RFC3056. + * 6to4 interface, based on RFC3056 + 6rd (RFC5569) support. * * 6to4 interface is NOT capable of link-layer (I mean, IPv4) multicasting. * There is no address mapping defined from IPv6 multicast address to IPv4 @@ -60,7 +63,7 @@ * ICMPv6: * - Redirects cannot be used due to the lack of link-local address. * - * stf interface does not have, and will not need, a link-local address. + * stf interface does not have, and will not need, a link-local address. * It seems to have no real benefit and does not help the above symptoms much. * Even if we assign link-locals to interface, we cannot really * use link-local unicast/multicast on top of 6to4 cloud (since there's no @@ -72,6 +75,12 @@ * http://playground.iijlab.net/i-d/draft-itojun-ipv6-transition-abuse-00.txt * for details. The code tries to filter out some of malicious packets. * Note that there is no way to be 100% secure. + * + * 6rd (RFC5569 & RFC5969) extension is enabled when an IPv6 GUA other than + * 2002::/16 is assigned. The stf(4) recognizes a 32-bit just after + * prefixlen as the IPv4 address of the 6rd customer site. The + * prefixlen must be shorter than 32. + * */ #include <sys/param.h> @@ -83,6 +92,7 @@ #include <sys/kernel.h> #include <sys/lock.h> #include <sys/module.h> +#include <sys/priv.h> #include <sys/protosw.h> #include <sys/proc.h> #include <sys/queue.h> @@ -106,8 +116,10 @@ #include <netinet/ip.h> #include <netinet/ip_var.h> #include <netinet/in_var.h> +#include <net/if_stf.h> #include <netinet/ip6.h> +#include <netinet6/in6_fib.h> #include <netinet6/ip6_var.h> #include <netinet6/in6_var.h> #include <netinet/ip_ecn.h> @@ -120,15 +132,41 @@ #include <security/mac/mac_framework.h> +#define STF_DEBUG 1 +#if STF_DEBUG > 3 +#define ip_sprintf(buf, a) \ + sprintf(buf, "%u.%u.%u.%u", \ + (ntohl((a)->s_addr)>>24)&0xFF, \ + (ntohl((a)->s_addr)>>16)&0xFF, \ + (ntohl((a)->s_addr)>>8)&0xFF, \ + (ntohl((a)->s_addr))&0xFF); +#endif + +#if STF_DEBUG +#define DEBUG_PRINTF(a, ...) \ + do { \ + if (V_stf_debug >= a) \ + printf(__VA_ARGS__); \ + } while (0) +#else +#define DEBUG_PRINTF(a, ...) +#endif + SYSCTL_DECL(_net_link); static SYSCTL_NODE(_net_link, IFT_STF, stf, CTLFLAG_RW, 0, "6to4 Interface"); +#if STF_DEBUG +static VNET_DEFINE(int, stf_debug) = 0; +#define V_stf_debug VNET(stf_debug) +SYSCTL_INT(_net_link_stf, OID_AUTO, stf_debug, CTLFLAG_VNET | CTLFLAG_RW, + &VNET_NAME(stf_debug), 0, + "Enable displaying debug messages of stf interfaces"); +#endif + static int stf_permit_rfc1918 = 0; SYSCTL_INT(_net_link_stf, OID_AUTO, permit_rfc1918, CTLFLAG_RWTUN, &stf_permit_rfc1918, 0, "Permit the use of private IPv4 addresses"); -#define STFUNIT 0 - #define IN6_IS_ADDR_6TO4(x) (ntohs((x)->s6_addr16[0]) == 0x2002) /* @@ -139,9 +177,13 @@ SYSCTL_INT(_net_link_stf, OID_AUTO, permit_rfc1918, CTLFLAG_RWTUN, struct stf_softc { struct ifnet *sc_ifp; - struct mtx sc_ro_mtx; + in_addr_t inaddr; + in_addr_t dstv4_addr; + in_addr_t srcv4_addr; + u_int v4prefixlen; u_int sc_fibnum; const struct encaptab *encap_cookie; + LIST_ENTRY(stf_softc) stf_list; }; #define STF2IFP(sc) ((sc)->sc_ifp) @@ -151,7 +193,11 @@ static const char stfname[] = "stf"; * Note that mutable fields in the softc are not currently locked. * We do lock sc_ro in stf_output though. */ +static struct mtx stf_mtx; static MALLOC_DEFINE(M_STF, stfname, "6to4 Tunnel Interface"); +static VNET_DEFINE(LIST_HEAD(, stf_softc), stf_softc_list); +#define V_stf_softc_list VNET(stf_softc_list) + static const int ip_stf_ttl = 40; extern struct domain inetdomain; @@ -167,8 +213,6 @@ static struct protosw in_stf_protosw = { .pr_usrreqs = &rip_usrreqs }; -static char *stfnames[] = {"stf0", "stf", "6to4", NULL}; - static int stfmodevent(module_t, int, void *); static int stf_encapcheck(const struct mbuf *, int, int, void *); static int stf_getsrcifa6(struct ifnet *, struct in6_addr *, struct in6_addr *); @@ -179,68 +223,39 @@ static int stf_checkaddr4(struct stf_softc *, struct in_addr *, struct ifnet *); static int stf_checkaddr6(struct stf_softc *, struct in6_addr *, struct ifnet *); +static struct sockaddr_in *stf_getin4addr_in6(struct stf_softc *, + struct sockaddr_in *, struct in6_addr, struct in6_addr, + struct in6_addr); +static struct sockaddr_in *stf_getin4addr(struct stf_softc *, + struct sockaddr_in *, struct in6_addr, struct in6_addr); static int stf_ioctl(struct ifnet *, u_long, caddr_t); -static int stf_clone_match(struct if_clone *, const char *); -static int stf_clone_create(struct if_clone *, char *, size_t, caddr_t); -static int stf_clone_destroy(struct if_clone *, struct ifnet *); +static int stf_clone_create(struct if_clone *, int, caddr_t); +static void stf_clone_destroy(struct ifnet *); static struct if_clone *stf_cloner; static int -stf_clone_match(struct if_clone *ifc, const char *name) +stf_clone_create(struct if_clone *ifc, int unit, caddr_t params) { - int i; - - for(i = 0; stfnames[i] != NULL; i++) { - if (strcmp(stfnames[i], name) == 0) - return (1); - } - - return (0); -} - -static int -stf_clone_create(struct if_clone *ifc, char *name, size_t len, caddr_t params) -{ - int err, unit; struct stf_softc *sc; struct ifnet *ifp; - /* - * We can only have one unit, but since unit allocation is - * already locked, we use it to keep from allocating extra - * interfaces. - */ - unit = STFUNIT; - err = ifc_alloc_unit(ifc, &unit); - if (err != 0) - return (err); - sc = malloc(sizeof(struct stf_softc), M_STF, M_WAITOK | M_ZERO); ifp = STF2IFP(sc) = if_alloc(IFT_STF); if (ifp == NULL) { free(sc, M_STF); - ifc_free_unit(ifc, unit); return (ENOSPC); } ifp->if_softc = sc; sc->sc_fibnum = curthread->td_proc->p_fibnum; + if_initname(ifp, stfname, unit); - /* - * Set the name manually rather then using if_initname because - * we don't conform to the default naming convention for interfaces. - */ - strlcpy(ifp->if_xname, name, IFNAMSIZ); - ifp->if_dname = stfname; - ifp->if_dunit = IF_DUNIT_NONE; - - mtx_init(&(sc)->sc_ro_mtx, "stf ro", NULL, MTX_DEF); sc->encap_cookie = encap_attach_func(AF_INET, IPPROTO_IPV6, stf_encapcheck, &in_stf_protosw, sc); if (sc->encap_cookie == NULL) { if_printf(ifp, "attach failed\n"); + if_free(ifp); free(sc, M_STF); - ifc_free_unit(ifc, unit); return (ENOMEM); } @@ -250,39 +265,56 @@ stf_clone_create(struct if_clone *ifc, char *name, size_t len, caddr_t params) ifp->if_snd.ifq_maxlen = ifqmaxlen; if_attach(ifp); bpfattach(ifp, DLT_NULL, sizeof(u_int32_t)); + + mtx_lock(&stf_mtx); + LIST_INSERT_HEAD(&V_stf_softc_list, sc, stf_list); + mtx_unlock(&stf_mtx); + return (0); } -static int -stf_clone_destroy(struct if_clone *ifc, struct ifnet *ifp) +static void +stf_clone_destroy(struct ifnet *ifp) { struct stf_softc *sc = ifp->if_softc; int err; + mtx_lock(&stf_mtx); + LIST_REMOVE(sc, stf_list); + mtx_unlock(&stf_mtx); + err = encap_detach(sc->encap_cookie); KASSERT(err == 0, ("Unexpected error detaching encap_cookie")); - mtx_destroy(&(sc)->sc_ro_mtx); bpfdetach(ifp); if_detach(ifp); if_free(ifp); free(sc, M_STF); - ifc_free_unit(ifc, STFUNIT); +} - return (0); +static void +vnet_stf_init(const void *unused __unused) +{ + + LIST_INIT(&V_stf_softc_list); } +VNET_SYSINIT(vnet_stf_init, SI_SUB_PSEUDO, SI_ORDER_MIDDLE, vnet_stf_init, + NULL); + static int stfmodevent(module_t mod, int type, void *data) { switch (type) { case MOD_LOAD: - stf_cloner = if_clone_advanced(stfname, 0, stf_clone_match, - stf_clone_create, stf_clone_destroy); + mtx_init(&stf_mtx, "stf_mtx", NULL, MTX_DEF); + stf_cloner = if_clone_simple(stfname, + stf_clone_create, stf_clone_destroy, 0); break; case MOD_UNLOAD: if_clone_detach(stf_cloner); + mtx_destroy(&stf_mtx); break; default: return (EOPNOTSUPP); @@ -298,60 +330,125 @@ static moduledata_t stf_mod = { }; DECLARE_MODULE(if_stf, stf_mod, SI_SUB_PSEUDO, SI_ORDER_ANY); +MODULE_VERSION(if_stf, 1); static int stf_encapcheck(const struct mbuf *m, int off, int proto, void *arg) { struct ip ip; struct stf_softc *sc; - struct in_addr a, b, mask; struct in6_addr addr6, mask6; + struct sockaddr_in sin4addr, sin4mask; + DEBUG_PRINTF(1, "%s: enter\n", __func__); sc = (struct stf_softc *)arg; if (sc == NULL) - return 0; + return (0); if ((STF2IFP(sc)->if_flags & IFF_UP) == 0) - return 0; + return (0); /* IFF_LINK0 means "no decapsulation" */ if ((STF2IFP(sc)->if_flags & IFF_LINK0) != 0) - return 0; + return (0); if (proto != IPPROTO_IPV6) - return 0; + return (0); /* LINTED const cast */ m_copydata((struct mbuf *)(uintptr_t)m, 0, sizeof(ip), (caddr_t)&ip); if (ip.ip_v != 4) - return 0; + return (0); if (stf_getsrcifa6(STF2IFP(sc), &addr6, &mask6) != 0) return (0); + if (sc->srcv4_addr != INADDR_ANY) { + sin4addr.sin_addr.s_addr = sc->srcv4_addr; + sin4addr.sin_family = AF_INET; + } else + if (stf_getin4addr(sc, &sin4addr, addr6, mask6) == NULL) + return (0); + +#if STF_DEBUG > 3 + { + char buf[INET6_ADDRSTRLEN + 1]; + memset(&buf, 0, sizeof(buf)); + + ip6_sprintf(buf, &addr6); + DEBUG_PRINTF(1, "%s: addr6 = %s\n", __func__, buf); + ip6_sprintf(buf, &mask6); + DEBUG_PRINTF(1, "%s: mask6 = %s\n", __func__, buf); + + ip_sprintf(buf, &sin4addr.sin_addr); + DEBUG_PRINTF(1, "%s: sin4addr.sin_addr = %s\n", __func__, buf); + ip_sprintf(buf, &ip.ip_src); + DEBUG_PRINTF(1, "%s: ip.ip_src = %s\n", __func__, buf); + ip_sprintf(buf, &ip.ip_dst); + DEBUG_PRINTF(1, "%s: ip.ip_dst = %s\n", __func__, buf); + } +#endif + /* * check if IPv4 dst matches the IPv4 address derived from the * local 6to4 address. * success on: dst = 10.1.1.1, ia6->ia_addr = 2002:0a01:0101:... */ - if (bcmp(GET_V4(&addr6), &ip.ip_dst, sizeof(ip.ip_dst)) != 0) - return 0; + if (sin4addr.sin_addr.s_addr != ip.ip_dst.s_addr) { + DEBUG_PRINTF(1, + "%s: IPv4 dst address do not match the encoded address. " + "Ignore this packet.\n", __func__); + return (0); + } - /* - * check if IPv4 src matches the IPv4 address derived from the - * local 6to4 address masked by prefixmask. - * success on: src = 10.1.1.1, ia6->ia_addr = 2002:0a00:.../24 - * fail on: src = 10.1.1.1, ia6->ia_addr = 2002:0b00:.../24 - */ - bzero(&a, sizeof(a)); - bcopy(GET_V4(&addr6), &a, sizeof(a)); - bcopy(GET_V4(&mask6), &mask, sizeof(mask)); - a.s_addr &= mask.s_addr; - b = ip.ip_src; - b.s_addr &= mask.s_addr; - if (a.s_addr != b.s_addr) - return 0; + if (IN6_IS_ADDR_6TO4(&addr6)) { + /* + * 6to4 (RFC 3056). + * Check if IPv4 src matches the IPv4 address derived + * from the local 6to4 address masked by prefixmask. + * success on: src = 10.1.1.1, ia6->ia_addr = 2002:0a00:.../24 + * fail on: src = 10.1.1.1, ia6->ia_addr = 2002:0b00:.../24 + */ + + memcpy(&sin4mask.sin_addr, GET_V4(&mask6), sizeof(sin4mask)); +#if STF_DEBUG > 3 + { + char buf[INET6_ADDRSTRLEN + 1]; + memset(&buf, 0, sizeof(buf)); + + ip_sprintf(buf, &sin4addr.sin_addr); + DEBUG_PRINTF(1, "%s: sin4addr = %s\n", + __func__, buf); + ip_sprintf(buf, &ip.ip_src); + DEBUG_PRINTF(1, "%s: ip.ip_src = %s\n", + __func__, buf); + ip_sprintf(buf, &sin4mask.sin_addr); + DEBUG_PRINTF(1, "%s: sin4mask = %s\n", + __func__, buf); + } +#endif + + if ((sin4addr.sin_addr.s_addr & sin4mask.sin_addr.s_addr) != + (ip.ip_src.s_addr & sin4mask.sin_addr.s_addr)) { + DEBUG_PRINTF(1, + "%s: v4 address do not match expected address. " + "Ignore this packet.\n", __func__); + return (0); + } + } else { + /* 6rd (RFC 5569) */ + /* + * No restriction on the src address in the case of + * 6rd because the stf(4) interface always has a + * prefix which covers whole of IPv4 src address + * range. So, stf_output() will catch all of + * 6rd-capsuled IPv4 traffic with suspicious inner dst + * IPv4 address (i.e. the IPv6 destination address is + * one the admin does not like to route to outside), + * and then it discard them silently. + */ + } /* stf interface makes single side match only */ return 32; @@ -362,29 +459,50 @@ stf_getsrcifa6(struct ifnet *ifp, struct in6_addr *addr, struct in6_addr *mask) { struct ifaddr *ia; struct in_ifaddr *ia4; + struct in6_addr addr6, mask6; struct in6_ifaddr *ia6; - struct sockaddr_in6 *sin6; + struct sockaddr_in sin4; + struct stf_softc *sc; struct in_addr in; + sc = ifp->if_softc; if_addr_rlock(ifp); TAILQ_FOREACH(ia, &ifp->if_addrhead, ifa_link) { if (ia->ifa_addr->sa_family != AF_INET6) continue; - sin6 = (struct sockaddr_in6 *)ia->ifa_addr; - if (!IN6_IS_ADDR_6TO4(&sin6->sin6_addr)) - continue; + ia6 = (struct in6_ifaddr *)ia; + *&addr6 = ((struct sockaddr_in6 *)ia->ifa_addr)->sin6_addr; + *&mask6 = ia6->ia_prefixmask.sin6_addr; + if (sc->srcv4_addr != INADDR_ANY) + bcopy(&sc->srcv4_addr, &in, sizeof(in)); + else { + if (stf_getin4addr(sc, &sin4, addr6, mask6) == NULL) + continue; + bcopy(&sin4.sin_addr, &in, sizeof(in)); + } - bcopy(GET_V4(&sin6->sin6_addr), &in, sizeof(in)); LIST_FOREACH(ia4, INADDR_HASH(in.s_addr), ia_hash) if (ia4->ia_addr.sin_addr.s_addr == in.s_addr) break; if (ia4 == NULL) continue; - ia6 = (struct in6_ifaddr *)ia; +#if STF_DEBUG > 3 + { + char buf[INET6_ADDRSTRLEN + 1]; + memset(&buf, 0, sizeof(buf)); + + ip6_sprintf(buf, &((struct sockaddr_in6 *)ia->ifa_addr)->sin6_addr); + DEBUG_PRINTF(1, "%s: ia->ifa_addr->sin6_addr = %s\n", + __func__, buf); + ip_sprintf(buf, &ia4->ia_addr.sin_addr); + DEBUG_PRINTF(1, "%s: ia4->ia_addr.sin_addr = %s\n", + __func__, buf); + } +#endif - *addr = sin6->sin6_addr; - *mask = ia6->ia_prefixmask.sin6_addr; + *addr = addr6; + *mask = mask6; if_addr_runlock(ifp); return (0); } @@ -399,8 +517,7 @@ stf_output(struct ifnet *ifp, struct mbuf *m, const struct sockaddr *dst, { struct stf_softc *sc; const struct sockaddr_in6 *dst6; - struct in_addr in4; - const void *ptr; + struct sockaddr_in dst4, src4; u_int8_t tos; struct ip *ip; struct ip6_hdr *ip6; @@ -450,18 +567,27 @@ stf_output(struct ifnet *ifp, struct mbuf *m, const struct sockaddr *dst, * Pickup the right outer dst addr from the list of candidates. * ip6_dst has priority as it may be able to give us shorter IPv4 hops. */ - ptr = NULL; - if (IN6_IS_ADDR_6TO4(&ip6->ip6_dst)) - ptr = GET_V4(&ip6->ip6_dst); - else if (IN6_IS_ADDR_6TO4(&dst6->sin6_addr)) - ptr = GET_V4(&dst6->sin6_addr); - else { - m_freem(m); - if_inc_counter(ifp, IFCOUNTER_OERRORS, 1); - return ENETUNREACH; + DEBUG_PRINTF(1, "%s: dst addr selection\n", __func__); + if (stf_getin4addr_in6(sc, &dst4, addr6, mask6, + ip6->ip6_dst) == NULL) { + if (sc->dstv4_addr != INADDR_ANY) + dst4.sin_addr.s_addr = sc->dstv4_addr; + else if (stf_getin4addr_in6(sc, &dst4, addr6, mask6, + dst6->sin6_addr) == NULL) { + m_freem(m); + if_inc_counter(ifp, IFCOUNTER_OERRORS, 1); + return (ENETUNREACH); + } } - bcopy(ptr, &in4, sizeof(in4)); +#if STF_DEBUG > 3 + { + char buf[INET6_ADDRSTRLEN + 1]; + memset(&buf, 0, sizeof(buf)); + ip_sprintf(buf, &dst4.sin_addr); + DEBUG_PRINTF(1, "%s: ip_dst = %s\n", __func__, buf); + } +#endif if (bpf_peers_present(ifp->if_bpf)) { /* * We need to prepend the address family as @@ -483,8 +609,24 @@ stf_output(struct ifnet *ifp, struct mbuf *m, const struct sockaddr *dst, bzero(ip, sizeof(*ip)); - bcopy(GET_V4(&addr6), &ip->ip_src, sizeof(ip->ip_src)); - bcopy(&in4, &ip->ip_dst, sizeof(ip->ip_dst)); + if (sc->srcv4_addr != INADDR_ANY) + src4.sin_addr.s_addr = sc->srcv4_addr; + else if (stf_getin4addr(sc, &src4, addr6, mask6) == NULL) { + m_freem(m); + if_inc_counter(ifp, IFCOUNTER_OERRORS, 1); + return (ENETUNREACH); + } + bcopy(&src4.sin_addr, &ip->ip_src, sizeof(ip->ip_src)); +#if STF_DEBUG > 3 + { + char buf[INET6_ADDRSTRLEN + 1]; + memset(&buf, 0, sizeof(buf)); + + ip_sprintf(buf, &ip->ip_src); + DEBUG_PRINTF(1, "%s: ip_src = %s\n", __func__, buf); + } +#endif + bcopy(&dst4.sin_addr, &ip->ip_dst, sizeof(ip->ip_dst)); ip->ip_p = IPPROTO_IPV6; ip->ip_ttl = ip_stf_ttl; ip->ip_len = htons(m->m_pkthdr.len); @@ -534,13 +676,6 @@ stf_checkaddr4(struct stf_softc *sc, struct in_addr *in, struct ifnet *inifp) } /* - * reject packets with private address range. - * (requirement from RFC3056 section 2 1st paragraph) - */ - if (isrfc1918addr(in)) - return -1; - - /* * reject packets with broadcast */ IN_IFADDR_RLOCK(&in_ifa_tracker); @@ -603,6 +738,7 @@ in_stf_input(struct mbuf **mp, int *offp, int proto) struct mbuf *m; u_int8_t otos, itos; struct ifnet *ifp; + struct nhop6_basic nh6; int off; m = *mp; @@ -628,6 +764,17 @@ in_stf_input(struct mbuf **mp, int *offp, int proto) mac_ifnet_create_mbuf(ifp, m); #endif +#if STF_DEBUG > 3 + { + char buf[INET6_ADDRSTRLEN + 1]; + memset(&buf, 0, sizeof(buf)); + + ip_sprintf(buf, &ip->ip_dst); + DEBUG_PRINTF(1, "%s: ip->ip_dst = %s\n", __func__, buf); + ip_sprintf(buf, &ip->ip_src); + DEBUG_PRINTF(1, "%s: ip->ip_src = %s\n", __func__, buf); + } +#endif /* * perform sanity check against outer src/dst. * for source, perform ingress filter as well. @@ -648,6 +795,17 @@ in_stf_input(struct mbuf **mp, int *offp, int proto) } ip6 = mtod(m, struct ip6_hdr *); +#if STF_DEBUG > 3 + { + char buf[INET6_ADDRSTRLEN + 1]; + memset(&buf, 0, sizeof(buf)); + + ip6_sprintf(buf, &ip6->ip6_dst); + DEBUG_PRINTF(1, "%s: ip6->ip6_dst = %s\n", __func__, buf); + ip6_sprintf(buf, &ip6->ip6_src); + DEBUG_PRINTF(1, "%s: ip6->ip6_src = %s\n", __func__, buf); + } +#endif /* * perform sanity check against inner src/dst. * for source, perform ingress filter as well. @@ -658,6 +816,35 @@ in_stf_input(struct mbuf **mp, int *offp, int proto) return (IPPROTO_DONE); } + /* + * reject packets with private address range. + * (requirement from RFC3056 section 2 1st paragraph) + */ + if ((IN6_IS_ADDR_6TO4(&ip6->ip6_src) && isrfc1918addr(&ip->ip_src)) || + (IN6_IS_ADDR_6TO4(&ip6->ip6_dst) && isrfc1918addr(&ip->ip_dst))) { + m_freem(m); + return (IPPROTO_DONE); + } + + /* + * Ignore if the destination is the same stf interface because + * all of valid IPv6 outgoing traffic should go interfaces + * except for it. + */ + if (fib6_lookup_nh_basic(sc->sc_fibnum, + &ip6->ip6_dst, 0, 0, 0, &nh6) != 0) { + DEBUG_PRINTF(1, "%s: no IPv6 dst. Ignored.\n", __func__); + m_free(m); + return (IPPROTO_DONE); + } + if ((nh6.nh_ifp == ifp) && + (!IN6_ARE_ADDR_EQUAL(&ip6->ip6_src, &nh6.nh_addr))) { + DEBUG_PRINTF(1, "%s: IPv6 dst is the same stf. Ignored.\n", + __func__); + m_free(m); + return (IPPROTO_DONE); + } + itos = (ntohl(ip6->ip6_flow) >> 20) & 0xff; if ((ifp->if_flags & IFF_LINK1) != 0) ip_ecn_egress(ECN_ALLOWED, &otos, &itos); @@ -667,7 +854,7 @@ in_stf_input(struct mbuf **mp, int *offp, int proto) ip6->ip6_flow |= htonl((u_int32_t)itos << 20); m->m_pkthdr.rcvif = ifp; - + if (bpf_peers_present(ifp->if_bpf)) { /* * We need to prepend the address family as @@ -680,6 +867,7 @@ in_stf_input(struct mbuf **mp, int *offp, int proto) bpf_mtap2(ifp->if_bpf, &af, sizeof(af), m); } + DEBUG_PRINTF(1, "%s: netisr_dispatch(NETISR_IPV6)\n", __func__); /* * Put the packet to the network layer input queue according to the * specified address family. @@ -693,35 +881,262 @@ in_stf_input(struct mbuf **mp, int *offp, int proto) return (IPPROTO_DONE); } +static struct sockaddr_in * +stf_getin4addr_in6(struct stf_softc *sc, struct sockaddr_in *sin, + struct in6_addr addr6, struct in6_addr mask6, struct in6_addr in6) +{ + int i; + +#if STF_DEBUG > 3 + { + char buf[INET6_ADDRSTRLEN + 1]; + memset(&buf, 0, sizeof(buf)); + + ip6_sprintf(buf, &in6); + DEBUG_PRINTF(1, "%s: in6 = %s\n", __func__, buf); + ip6_sprintf(buf, &addr6); + DEBUG_PRINTF(1, "%s: addr6 = %s\n", __func__, buf); + ip6_sprintf(buf, &mask6); + DEBUG_PRINTF(1, "%s: mask6 = %s\n", __func__, buf); + } +#endif + + /* + * When (src addr & src mask) != (in6 & src mask), + * the dst is not in the 6rd domain. The IPv4 address must + * not be used. + */ + for (i = 0; i < sizeof(addr6); i++) { + if ((((u_char *)&addr6)[i] & ((u_char *)&mask6)[i]) != + (((u_char *)&in6)[i] & ((u_char *)&mask6)[i])) + return (NULL); + } + + /* After the mask check, use in6 instead of addr6. */ + return (stf_getin4addr(sc, sin, in6, mask6)); +} + +static struct sockaddr_in * +stf_getin4addr(struct stf_softc *sc, struct sockaddr_in *sin, + struct in6_addr addr6, struct in6_addr mask6) +{ + struct in_addr *in; + + DEBUG_PRINTF(1, "%s: enter.\n", __func__); +#if STF_DEBUG > 3 + { + char tmpbuf[INET6_ADDRSTRLEN + 1]; + memset(&tmpbuf, 0, INET6_ADDRSTRLEN); + + ip6_sprintf(tmpbuf, &addr6); + DEBUG_PRINTF(1, "%s: addr6 = %s\n", __func__, tmpbuf); + } +#endif + memset(sin, 0, sizeof(*sin)); + in = &sin->sin_addr; + if (IN6_IS_ADDR_6TO4(&addr6)) { + /* 6to4 (RFC 3056) */ + bcopy(GET_V4(&addr6), in, sizeof(*in)); + if (isrfc1918addr(in)) + return (NULL); + } else { + /* 6rd (RFC 5569) */ + struct in6_addr buf; + u_char *p = (u_char *)&buf; + u_char *q = (u_char *)&in->s_addr; + u_int residue = 0, v4residue = 0; + u_char mask, v4mask = 0; + int i, j; + u_int plen, loop; + + /* + * 6rd-relays IPv6 prefix is located at a 32-bit just + * after the prefix edge. + */ + plen = in6_mask2len(&mask6, NULL); + if (64 < plen) { + DEBUG_PRINTF(1, "prefixlen is %d\n", plen); + return (NULL); + } + + loop = 4; /* Normal 6rd operation */ + memcpy(&buf, &addr6, sizeof(buf)); + if (sc->v4prefixlen != 0 && sc->v4prefixlen != 32) + v4residue = sc->v4prefixlen % 8; + p += plen / 8; + //plen -= 32; + + residue = plen % 8; + mask = ((u_char)(-1) >> (8 - residue)); + if (v4residue) { + loop++; + v4mask = ((u_char)(-1) << v4residue); + } + /* + * The p points head of the IPv4 address part in + * bytes. The residue is a bit-shift factor when + * prefixlen is not a multiple of 8. + */ + DEBUG_PRINTF(2, "residue = %d 0x%x\n", residue, mask); + for (j = (loop - (sc->v4prefixlen / 8)), + i = (loop - (sc->v4prefixlen / 8)); + i < loop; j++, i++) { + if (residue) { + q[i] = ((p[j] & mask) << (8 - residue)); + q[i] |= ((p[j + 1] >> residue) & mask); + DEBUG_PRINTF(2, + "FINAL i = %d q[%d] - p[%d/%d] %x\n", + i, q[i], p[j], p[j + 1] >> residue, q[i]); + } else { + q[i] = p[j]; + DEBUG_PRINTF(2, + "FINAL i = %d q[%d] - p[%d] %x\n", + i, q[i], p[j], q[i]); + } + } + if (v4residue) + q[loop - (sc->v4prefixlen / 8)] &= v4mask; + + if (sc->v4prefixlen > 0 && sc->v4prefixlen < 32) + in->s_addr |= sc->inaddr & + ((uint32_t)(-1) >> (32 - sc->v4prefixlen)); + } + +#if STF_DEBUG > 3 + { + char tmpbuf[INET6_ADDRSTRLEN + 1]; + memset(&tmpbuf, 0, INET_ADDRSTRLEN); + + ip_sprintf(tmpbuf, in); + DEBUG_PRINTF(1, "%s: in->in_addr = %s\n", __func__, tmpbuf); + } +#endif + + return (sin); +} + static int stf_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data) { struct ifaddr *ifa; + struct ifdrv *ifd; struct ifreq *ifr; - struct sockaddr_in6 *sin6; - struct in_addr addr; + struct sockaddr_in sin4; + struct stf_softc *sc, *sc_cur; + struct stfv4args args; + struct in6_addr addr6, mask6; int error, mtu; error = 0; + sc_cur = ifp->if_softc; + switch (cmd) { + case SIOCSDRVSPEC: + ifd = (struct ifdrv *)data; + error = priv_check(curthread, PRIV_NET_ADDIFADDR); + if (error) + break; + if (ifd->ifd_cmd == STF_SV4NET) { + if (ifd->ifd_len != sizeof(args)) { + error = EINVAL; + break; + } + mtx_lock(&stf_mtx); + LIST_FOREACH(sc, &V_stf_softc_list, stf_list) { + if (sc == sc_cur) + continue; + if (sc->inaddr == 0 || sc->v4prefixlen == 0) + continue; + + if ((ntohl(sc->inaddr) & + ((uint32_t)(-1) << sc_cur->v4prefixlen)) == + ntohl(sc_cur->inaddr)) { + error = EEXIST; + mtx_unlock(&stf_mtx); + return (error); + } + if ((ntohl(sc_cur->inaddr) & + ((uint32_t)(-1) << sc->v4prefixlen)) == + ntohl(sc->inaddr)) { + error = EEXIST; + mtx_unlock(&stf_mtx); + return (error); + } + } + mtx_unlock(&stf_mtx); + bzero(&args, sizeof args); + error = copyin(ifd->ifd_data, &args, ifd->ifd_len); + if (error) + break; + + sc_cur->srcv4_addr = args.inaddr.s_addr; + sc_cur->inaddr = ntohl(args.inaddr.s_addr); + sc_cur->inaddr &= ((uint32_t)(-1) << args.prefix); + sc_cur->inaddr = htonl(sc_cur->inaddr); + sc_cur->v4prefixlen = args.prefix; + if (sc_cur->v4prefixlen == 0) + sc_cur->v4prefixlen = 32; + } else if (ifd->ifd_cmd == STF_SDSTV4) { + if (ifd->ifd_len != sizeof(args)) { + error = EINVAL; + break; + } + bzero(&args, sizeof args); + error = copyin(ifd->ifd_data, &args, ifd->ifd_len); + if (error) + break; + sc_cur->dstv4_addr = args.dstv4_addr.s_addr; + } else + error = EINVAL; + break; + case SIOCGDRVSPEC: + ifd = (struct ifdrv *)data; + if (ifd->ifd_len != sizeof(args)) { + error = EINVAL; + break; + } + if (ifd->ifd_cmd != STF_GV4NET) { + error = EINVAL; + break; + } + bzero(&args, sizeof args); + args.inaddr.s_addr = sc_cur->srcv4_addr; + args.dstv4_addr.s_addr = sc_cur->dstv4_addr; + args.prefix = sc_cur->v4prefixlen; + error = copyout(&args, ifd->ifd_data, ifd->ifd_len); + break; case SIOCSIFADDR: ifa = (struct ifaddr *)data; if (ifa == NULL || ifa->ifa_addr->sa_family != AF_INET6) { error = EAFNOSUPPORT; break; } - sin6 = (struct sockaddr_in6 *)ifa->ifa_addr; - if (!IN6_IS_ADDR_6TO4(&sin6->sin6_addr)) { + if (stf_getin4addr(sc_cur, &sin4, + satosin6(ifa->ifa_addr)->sin6_addr, + satosin6(ifa->ifa_netmask)->sin6_addr) == NULL) { error = EINVAL; break; } - bcopy(GET_V4(&sin6->sin6_addr), &addr, sizeof(addr)); - if (isrfc1918addr(&addr)) { - error = EINVAL; - break; + /* + * Sanity check: if more than two interfaces have IFF_UP, do + * if_down() for all of them except for the specified one. + */ + mtx_lock(&stf_mtx); + LIST_FOREACH(sc, &V_stf_softc_list, stf_list) { + if (sc == sc_cur) + continue; + if (stf_getsrcifa6(ifp, &addr6, &mask6) != 0) + continue; + if (IN6_ARE_ADDR_EQUAL(&addr6, + &ifatoia6(ifa)->ia_addr.sin6_addr)) { + error = EEXIST; + break; + } } + mtx_unlock(&stf_mtx); ifp->if_flags |= IFF_UP; + ifp->if_drv_flags |= IFF_DRV_RUNNING; break; case SIOCADDMULTI: @@ -733,6 +1148,13 @@ stf_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data) error = EAFNOSUPPORT; break; + case SIOCSIFFLAGS: + if (ifp->if_flags & IFF_UP) + ifp->if_drv_flags |= IFF_DRV_RUNNING; + else + ifp->if_drv_flags &= ~IFF_DRV_RUNNING; + break; + case SIOCGIFMTU: break; |