Changeset View
Changeset View
Standalone View
Standalone View
sys/net/if_stf.c
Show First 20 Lines • Show All 86 Lines • ▼ Show 20 Lines | |||||
#include <sys/endian.h> | #include <sys/endian.h> | ||||
#include <sys/errno.h> | #include <sys/errno.h> | ||||
#include <sys/kernel.h> | #include <sys/kernel.h> | ||||
#include <sys/lock.h> | #include <sys/lock.h> | ||||
#include <sys/module.h> | #include <sys/module.h> | ||||
#include <sys/priv.h> | #include <sys/priv.h> | ||||
#include <sys/proc.h> | #include <sys/proc.h> | ||||
#include <sys/queue.h> | #include <sys/queue.h> | ||||
#include <sys/sdt.h> | |||||
#include <sys/sysctl.h> | #include <sys/sysctl.h> | ||||
#include <machine/cpu.h> | #include <machine/cpu.h> | ||||
#include <sys/malloc.h> | #include <sys/malloc.h> | ||||
#include <net/if.h> | #include <net/if.h> | ||||
#include <net/if_var.h> | #include <net/if_var.h> | ||||
#include <net/if_clone.h> | #include <net/if_clone.h> | ||||
Show All 20 Lines | |||||
#include <netinet/ip_encap.h> | #include <netinet/ip_encap.h> | ||||
#include <machine/stdarg.h> | #include <machine/stdarg.h> | ||||
#include <net/bpf.h> | #include <net/bpf.h> | ||||
#include <security/mac/mac_framework.h> | #include <security/mac/mac_framework.h> | ||||
SDT_PROVIDER_DEFINE(if_stf); | |||||
SDT_PROBE_DEFINE3(if_stf, , encapcheck, in, "struct mbuf *", "int", "int"); | |||||
SDT_PROBE_DEFINE0(if_stf, , encapcheck, accept); | |||||
SDT_PROBE_DEFINE3(if_stf, , getsrcifa6, in, "struct ifnet *", | |||||
"struct in6_addr *", "struct in6_addr *"); | |||||
SDT_PROBE_DEFINE2(if_stf, , getsrcifa6, found, "struct in6_addr *", | |||||
"struct in6_addr *"); | |||||
SDT_PROBE_DEFINE0(if_stf, , getsrcifa6, notfound); | |||||
SDT_PROBE_DEFINE4(if_stf, , stf_output, in, "struct ifnet *", "struct mbuf *", | |||||
"struct sockaddr *", "struct route *"); | |||||
SDT_PROBE_DEFINE2(if_stf, , stf_output, error, "int", "int"); | |||||
SDT_PROBE_DEFINE1(if_stf, , stf_output, out, "int"); | |||||
SDT_PROBE_DEFINE3(if_stf, , checkaddr6, in, "struct stf_softc *", | |||||
"struct in6_addr *", "struct ifnet *"); | |||||
SDT_PROBE_DEFINE2(if_stf, , checkaddr6, out, "int", "int"); | |||||
SDT_PROBE_DEFINE3(if_stf, , stf_input, in, "struct mbuf *", "int", "int"); | |||||
SDT_PROBE_DEFINE2(if_stf, , stf_input, out, "int", "int"); | |||||
SDT_PROBE_DEFINE3(if_stf, , ioctl, sv4net, "struct in_addr *", | |||||
"struct in_addr *", "int"); | |||||
SDT_PROBE_DEFINE1(if_stf, , ioctl, sdstv4, "struct in_addr *"); | |||||
SDT_PROBE_DEFINE1(if_stf, , ioctl, ifaddr, "struct ifaddr *"); | |||||
SDT_PROBE_DEFINE4(if_stf, , getin4addr_in6, out, "struct in6_addr *", | |||||
"struct in6_addr *", "struct in6_addr *", "struct sockaddr_in *"); | |||||
SDT_PROBE_DEFINE2(if_stf, , getin4addr, in, "struct in6_addr *", "struct in6_addr *"); | |||||
SDT_PROBE_DEFINE1(if_stf, , getin4addr, out, "struct sockaddr_in *"); | |||||
SYSCTL_DECL(_net_link); | SYSCTL_DECL(_net_link); | ||||
static SYSCTL_NODE(_net_link, IFT_STF, stf, CTLFLAG_RW | CTLFLAG_MPSAFE, 0, | static SYSCTL_NODE(_net_link, IFT_STF, stf, CTLFLAG_RW | CTLFLAG_MPSAFE, 0, | ||||
"6to4 Interface"); | "6to4 Interface"); | ||||
static int stf_permit_rfc1918 = 0; | static int stf_permit_rfc1918 = 0; | ||||
SYSCTL_INT(_net_link_stf, OID_AUTO, permit_rfc1918, CTLFLAG_RWTUN, | SYSCTL_INT(_net_link_stf, OID_AUTO, permit_rfc1918, CTLFLAG_RWTUN, | ||||
&stf_permit_rfc1918, 0, "Permit the use of private IPv4 addresses"); | &stf_permit_rfc1918, 0, "Permit the use of private IPv4 addresses"); | ||||
▲ Show 20 Lines • Show All 205 Lines • ▼ Show 20 Lines | |||||
static int | static int | ||||
stf_encapcheck(const struct mbuf *m, int off, int proto, void *arg) | stf_encapcheck(const struct mbuf *m, int off, int proto, void *arg) | ||||
{ | { | ||||
struct ip ip; | struct ip ip; | ||||
struct stf_softc *sc; | struct stf_softc *sc; | ||||
struct in6_addr addr6, mask6; | struct in6_addr addr6, mask6; | ||||
struct sockaddr_in sin4addr, sin4mask; | struct sockaddr_in sin4addr, sin4mask; | ||||
SDT_PROBE3(if_stf, , encapcheck, in, m, off, proto); | |||||
sc = (struct stf_softc *)arg; | sc = (struct stf_softc *)arg; | ||||
if (sc == NULL) | if (sc == NULL) | ||||
return (0); | return (0); | ||||
if ((STF2IFP(sc)->if_flags & IFF_UP) == 0) | if ((STF2IFP(sc)->if_flags & IFF_UP) == 0) | ||||
return (0); | return (0); | ||||
/* IFF_LINK0 means "no decapsulation" */ | /* IFF_LINK0 means "no decapsulation" */ | ||||
▲ Show 20 Lines • Show All 43 Lines • ▼ Show 20 Lines | if (IN6_IS_ADDR_6TO4(&addr6)) { | ||||
* range. So, stf_output() will catch all of | * range. So, stf_output() will catch all of | ||||
* 6rd-capsuled IPv4 traffic with suspicious inner dst | * 6rd-capsuled IPv4 traffic with suspicious inner dst | ||||
* IPv4 address (i.e. the IPv6 destination address is | * IPv4 address (i.e. the IPv6 destination address is | ||||
* one the admin does not like to route to outside), | * one the admin does not like to route to outside), | ||||
* and then it discard them silently. | * and then it discard them silently. | ||||
*/ | */ | ||||
} | } | ||||
SDT_PROBE0(if_stf, , encapcheck, accept); | |||||
/* stf interface makes single side match only */ | /* stf interface makes single side match only */ | ||||
return (32); | return (32); | ||||
} | } | ||||
static int | static int | ||||
stf_getsrcifa6(struct ifnet *ifp, struct in6_addr *addr, struct in6_addr *mask) | stf_getsrcifa6(struct ifnet *ifp, struct in6_addr *addr, struct in6_addr *mask) | ||||
{ | { | ||||
struct ifaddr *ia; | struct ifaddr *ia; | ||||
struct in_ifaddr *ia4; | struct in_ifaddr *ia4; | ||||
struct in6_addr addr6, mask6; | struct in6_addr addr6, mask6; | ||||
struct sockaddr_in sin4; | struct sockaddr_in sin4; | ||||
struct stf_softc *sc; | struct stf_softc *sc; | ||||
struct in_addr in; | struct in_addr in; | ||||
NET_EPOCH_ASSERT(); | NET_EPOCH_ASSERT(); | ||||
sc = ifp->if_softc; | sc = ifp->if_softc; | ||||
SDT_PROBE3(if_stf, , getsrcifa6, in, ifp, addr, mask); | |||||
CK_STAILQ_FOREACH(ia, &ifp->if_addrhead, ifa_link) { | CK_STAILQ_FOREACH(ia, &ifp->if_addrhead, ifa_link) { | ||||
if (ia->ifa_addr->sa_family != AF_INET6) | if (ia->ifa_addr->sa_family != AF_INET6) | ||||
continue; | continue; | ||||
addr6 = *IFA_IN6(ia); | addr6 = *IFA_IN6(ia); | ||||
mask6 = *IFA_MASKIN6(ia); | mask6 = *IFA_MASKIN6(ia); | ||||
if (sc->srcv4_addr != INADDR_ANY) | if (sc->srcv4_addr != INADDR_ANY) | ||||
bcopy(&sc->srcv4_addr, &in, sizeof(in)); | bcopy(&sc->srcv4_addr, &in, sizeof(in)); | ||||
else { | else { | ||||
if (stf_getin4addr(sc, &sin4, addr6, mask6) == NULL) | if (stf_getin4addr(sc, &sin4, addr6, mask6) == NULL) | ||||
continue; | continue; | ||||
bcopy(&sin4.sin_addr, &in, sizeof(in)); | bcopy(&sin4.sin_addr, &in, sizeof(in)); | ||||
} | } | ||||
CK_LIST_FOREACH(ia4, INADDR_HASH(in.s_addr), ia_hash) | CK_LIST_FOREACH(ia4, INADDR_HASH(in.s_addr), ia_hash) | ||||
if (ia4->ia_addr.sin_addr.s_addr == in.s_addr) | if (ia4->ia_addr.sin_addr.s_addr == in.s_addr) | ||||
break; | break; | ||||
if (ia4 == NULL) | if (ia4 == NULL) | ||||
continue; | continue; | ||||
*addr = addr6; | *addr = addr6; | ||||
*mask = mask6; | *mask = mask6; | ||||
SDT_PROBE2(if_stf, , getsrcifa6, found, addr, mask); | |||||
return (0); | return (0); | ||||
} | } | ||||
SDT_PROBE0(if_stf, , getsrcifa6, notfound); | |||||
return (ENOENT); | return (ENOENT); | ||||
} | } | ||||
static int | static int | ||||
stf_output(struct ifnet *ifp, struct mbuf *m, const struct sockaddr *dst, | stf_output(struct ifnet *ifp, struct mbuf *m, const struct sockaddr *dst, | ||||
struct route *ro) | struct route *ro) | ||||
{ | { | ||||
struct stf_softc *sc; | struct stf_softc *sc; | ||||
const struct sockaddr_in6 *dst6; | const struct sockaddr_in6 *dst6; | ||||
struct sockaddr_in dst4, src4; | struct sockaddr_in dst4, src4; | ||||
u_int8_t tos; | u_int8_t tos; | ||||
struct ip *ip; | struct ip *ip; | ||||
struct ip6_hdr *ip6; | struct ip6_hdr *ip6; | ||||
struct in6_addr addr6, mask6; | struct in6_addr addr6, mask6; | ||||
int error; | int error; | ||||
SDT_PROBE4(if_stf, , stf_output, in, ifp, m, dst, ro); | |||||
#ifdef MAC | #ifdef MAC | ||||
error = mac_ifnet_check_transmit(ifp, m); | error = mac_ifnet_check_transmit(ifp, m); | ||||
if (error) { | if (error) { | ||||
m_freem(m); | m_freem(m); | ||||
SDT_PROBE2(if_stf, , stf_output, error, error, __LINE__); | |||||
return (error); | return (error); | ||||
} | } | ||||
#endif | #endif | ||||
sc = ifp->if_softc; | sc = ifp->if_softc; | ||||
dst6 = (const struct sockaddr_in6 *)dst; | dst6 = (const struct sockaddr_in6 *)dst; | ||||
/* just in case */ | /* just in case */ | ||||
if ((ifp->if_flags & IFF_UP) == 0) { | if ((ifp->if_flags & IFF_UP) == 0) { | ||||
m_freem(m); | m_freem(m); | ||||
if_inc_counter(ifp, IFCOUNTER_OERRORS, 1); | if_inc_counter(ifp, IFCOUNTER_OERRORS, 1); | ||||
SDT_PROBE2(if_stf, , stf_output, error, ENETDOWN, __LINE__); | |||||
return (ENETDOWN); | return (ENETDOWN); | ||||
} | } | ||||
/* | /* | ||||
* If we don't have an ip4 address that match my inner ip6 address, | * If we don't have an ip4 address that match my inner ip6 address, | ||||
* we shouldn't generate output. Without this check, we'll end up | * we shouldn't generate output. Without this check, we'll end up | ||||
* using wrong IPv4 source. | * using wrong IPv4 source. | ||||
*/ | */ | ||||
if (stf_getsrcifa6(ifp, &addr6, &mask6) != 0) { | if (stf_getsrcifa6(ifp, &addr6, &mask6) != 0) { | ||||
m_freem(m); | m_freem(m); | ||||
if_inc_counter(ifp, IFCOUNTER_OERRORS, 1); | if_inc_counter(ifp, IFCOUNTER_OERRORS, 1); | ||||
SDT_PROBE2(if_stf, , stf_output, error, ENETDOWN, __LINE__); | |||||
return (ENETDOWN); | return (ENETDOWN); | ||||
} | } | ||||
if (m->m_len < sizeof(*ip6)) { | if (m->m_len < sizeof(*ip6)) { | ||||
m = m_pullup(m, sizeof(*ip6)); | m = m_pullup(m, sizeof(*ip6)); | ||||
if (!m) { | if (!m) { | ||||
if_inc_counter(ifp, IFCOUNTER_OERRORS, 1); | if_inc_counter(ifp, IFCOUNTER_OERRORS, 1); | ||||
SDT_PROBE2(if_stf, , stf_output, error, ENOBUFS, | |||||
__LINE__); | |||||
return (ENOBUFS); | return (ENOBUFS); | ||||
} | } | ||||
} | } | ||||
ip6 = mtod(m, struct ip6_hdr *); | ip6 = mtod(m, struct ip6_hdr *); | ||||
tos = IPV6_TRAFFIC_CLASS(ip6); | tos = IPV6_TRAFFIC_CLASS(ip6); | ||||
/* | /* | ||||
* Pickup the right outer dst addr from the list of candidates. | * 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. | * ip6_dst has priority as it may be able to give us shorter IPv4 hops. | ||||
*/ | */ | ||||
if (stf_getin4addr_in6(sc, &dst4, addr6, mask6, | if (stf_getin4addr_in6(sc, &dst4, addr6, mask6, | ||||
ip6->ip6_dst) == NULL) { | ip6->ip6_dst) == NULL) { | ||||
if (sc->braddr != INADDR_ANY) | if (sc->braddr != INADDR_ANY) | ||||
dst4.sin_addr.s_addr = sc->braddr; | dst4.sin_addr.s_addr = sc->braddr; | ||||
else if (stf_getin4addr_in6(sc, &dst4, addr6, mask6, | else if (stf_getin4addr_in6(sc, &dst4, addr6, mask6, | ||||
dst6->sin6_addr) == NULL) { | dst6->sin6_addr) == NULL) { | ||||
m_freem(m); | m_freem(m); | ||||
if_inc_counter(ifp, IFCOUNTER_OERRORS, 1); | if_inc_counter(ifp, IFCOUNTER_OERRORS, 1); | ||||
SDT_PROBE2(if_stf, , stf_output, error, ENETUNREACH, | |||||
__LINE__); | |||||
return (ENETUNREACH); | return (ENETUNREACH); | ||||
} | } | ||||
} | } | ||||
if (bpf_peers_present(ifp->if_bpf)) { | if (bpf_peers_present(ifp->if_bpf)) { | ||||
/* | /* | ||||
* We need to prepend the address family as | * We need to prepend the address family as | ||||
* a four byte field. Cons up a dummy header | * a four byte field. Cons up a dummy header | ||||
* to pacify bpf. This is safe because bpf | * to pacify bpf. This is safe because bpf | ||||
* will only read from the mbuf (i.e., it won't | * will only read from the mbuf (i.e., it won't | ||||
* try to free it or keep a pointer a to it). | * try to free it or keep a pointer a to it). | ||||
*/ | */ | ||||
u_int af = AF_INET6; | u_int af = AF_INET6; | ||||
bpf_mtap2(ifp->if_bpf, &af, sizeof(af), m); | bpf_mtap2(ifp->if_bpf, &af, sizeof(af), m); | ||||
} | } | ||||
M_PREPEND(m, sizeof(struct ip), M_NOWAIT); | M_PREPEND(m, sizeof(struct ip), M_NOWAIT); | ||||
if (m == NULL) { | if (m == NULL) { | ||||
if_inc_counter(ifp, IFCOUNTER_OERRORS, 1); | if_inc_counter(ifp, IFCOUNTER_OERRORS, 1); | ||||
SDT_PROBE2(if_stf, , stf_output, error, ENOBUFS, __LINE__); | |||||
return (ENOBUFS); | return (ENOBUFS); | ||||
} | } | ||||
ip = mtod(m, struct ip *); | ip = mtod(m, struct ip *); | ||||
bzero(ip, sizeof(*ip)); | bzero(ip, sizeof(*ip)); | ||||
if (sc->srcv4_addr != INADDR_ANY) | if (sc->srcv4_addr != INADDR_ANY) | ||||
src4.sin_addr.s_addr = sc->srcv4_addr; | src4.sin_addr.s_addr = sc->srcv4_addr; | ||||
else if (stf_getin4addr(sc, &src4, addr6, mask6) == NULL) { | else if (stf_getin4addr(sc, &src4, addr6, mask6) == NULL) { | ||||
m_freem(m); | m_freem(m); | ||||
if_inc_counter(ifp, IFCOUNTER_OERRORS, 1); | if_inc_counter(ifp, IFCOUNTER_OERRORS, 1); | ||||
SDT_PROBE2(if_stf, , stf_output, error, ENETUNREACH, __LINE__); | |||||
return (ENETUNREACH); | return (ENETUNREACH); | ||||
} | } | ||||
bcopy(&src4.sin_addr, &ip->ip_src, sizeof(ip->ip_src)); | bcopy(&src4.sin_addr, &ip->ip_src, sizeof(ip->ip_src)); | ||||
bcopy(&dst4.sin_addr, &ip->ip_dst, sizeof(ip->ip_dst)); | bcopy(&dst4.sin_addr, &ip->ip_dst, sizeof(ip->ip_dst)); | ||||
ip->ip_p = IPPROTO_IPV6; | ip->ip_p = IPPROTO_IPV6; | ||||
ip->ip_ttl = ip_stf_ttl; | ip->ip_ttl = ip_stf_ttl; | ||||
ip->ip_len = htons(m->m_pkthdr.len); | ip->ip_len = htons(m->m_pkthdr.len); | ||||
if (ifp->if_flags & IFF_LINK1) | if (ifp->if_flags & IFF_LINK1) | ||||
ip_ecn_ingress(ECN_ALLOWED, &ip->ip_tos, &tos); | ip_ecn_ingress(ECN_ALLOWED, &ip->ip_tos, &tos); | ||||
else | else | ||||
ip_ecn_ingress(ECN_NOCARE, &ip->ip_tos, &tos); | ip_ecn_ingress(ECN_NOCARE, &ip->ip_tos, &tos); | ||||
M_SETFIB(m, sc->sc_fibnum); | M_SETFIB(m, sc->sc_fibnum); | ||||
if_inc_counter(ifp, IFCOUNTER_OPACKETS, 1); | if_inc_counter(ifp, IFCOUNTER_OPACKETS, 1); | ||||
error = ip_output(m, NULL, NULL, 0, NULL, NULL); | error = ip_output(m, NULL, NULL, 0, NULL, NULL); | ||||
SDT_PROBE1(if_stf, , stf_output, out, error); | |||||
return (error); | return (error); | ||||
} | } | ||||
static int | static int | ||||
isrfc1918addr(struct in_addr *in) | isrfc1918addr(struct in_addr *in) | ||||
{ | { | ||||
/* | /* | ||||
* returns 1 if private address range: | * returns 1 if private address range: | ||||
▲ Show 20 Lines • Show All 51 Lines • ▼ Show 20 Lines | stf_checkaddr4(struct stf_softc *sc, struct in_addr *in, struct ifnet *inifp) | ||||
} | } | ||||
return (0); | return (0); | ||||
} | } | ||||
static int | static int | ||||
stf_checkaddr6(struct stf_softc *sc, struct in6_addr *in6, struct ifnet *inifp) | stf_checkaddr6(struct stf_softc *sc, struct in6_addr *in6, struct ifnet *inifp) | ||||
{ | { | ||||
SDT_PROBE3(if_stf, , checkaddr6, in, sc, in6, inifp); | |||||
/* | /* | ||||
* check 6to4 addresses | * check 6to4 addresses | ||||
*/ | */ | ||||
if (IN6_IS_ADDR_6TO4(in6)) { | if (IN6_IS_ADDR_6TO4(in6)) { | ||||
struct in_addr in4; | struct in_addr in4; | ||||
int ret; | |||||
bcopy(GET_V4(in6), &in4, sizeof(in4)); | bcopy(GET_V4(in6), &in4, sizeof(in4)); | ||||
return (stf_checkaddr4(sc, &in4, inifp)); | ret = stf_checkaddr4(sc, &in4, inifp); | ||||
SDT_PROBE2(if_stf, , checkaddr6, out, ret, __LINE__); | |||||
return (ret); | |||||
} | } | ||||
/* | /* | ||||
* reject anything that look suspicious. the test is implemented | * reject anything that look suspicious. the test is implemented | ||||
* in ip6_input too, but we check here as well to | * in ip6_input too, but we check here as well to | ||||
* (1) reject bad packets earlier, and | * (1) reject bad packets earlier, and | ||||
* (2) to be safe against future ip6_input change. | * (2) to be safe against future ip6_input change. | ||||
*/ | */ | ||||
if (IN6_IS_ADDR_V4COMPAT(in6) || IN6_IS_ADDR_V4MAPPED(in6)) | if (IN6_IS_ADDR_V4COMPAT(in6)) { | ||||
SDT_PROBE2(if_stf, , checkaddr6, out, -1, __LINE__); | |||||
return (-1); | return (-1); | ||||
} | |||||
if (IN6_IS_ADDR_V4MAPPED(in6)) { | |||||
SDT_PROBE2(if_stf, , checkaddr6, out, -1, __LINE__); | |||||
return (-1); | |||||
} | |||||
SDT_PROBE2(if_stf, , checkaddr6, out, 0, __LINE__); | |||||
return (0); | return (0); | ||||
} | } | ||||
static int | static int | ||||
in_stf_input(struct mbuf *m, int off, int proto, void *arg) | in_stf_input(struct mbuf *m, int off, int proto, void *arg) | ||||
{ | { | ||||
struct stf_softc *sc = arg; | struct stf_softc *sc = arg; | ||||
struct ip *ip; | struct ip *ip; | ||||
struct ip6_hdr *ip6; | struct ip6_hdr *ip6; | ||||
u_int8_t otos, itos; | u_int8_t otos, itos; | ||||
struct ifnet *ifp; | struct ifnet *ifp; | ||||
struct nhop_object *nh; | struct nhop_object *nh; | ||||
NET_EPOCH_ASSERT(); | NET_EPOCH_ASSERT(); | ||||
SDT_PROBE3(if_stf, , stf_input, in, m, off, proto); | |||||
if (proto != IPPROTO_IPV6) { | if (proto != IPPROTO_IPV6) { | ||||
m_freem(m); | m_freem(m); | ||||
SDT_PROBE2(if_stf, , stf_input, out, IPPROTO_DONE, __LINE__); | |||||
return (IPPROTO_DONE); | return (IPPROTO_DONE); | ||||
} | } | ||||
ip = mtod(m, struct ip *); | ip = mtod(m, struct ip *); | ||||
if (sc == NULL || (STF2IFP(sc)->if_flags & IFF_UP) == 0) { | if (sc == NULL || (STF2IFP(sc)->if_flags & IFF_UP) == 0) { | ||||
m_freem(m); | m_freem(m); | ||||
SDT_PROBE2(if_stf, , stf_input, out, IPPROTO_DONE, __LINE__); | |||||
return (IPPROTO_DONE); | return (IPPROTO_DONE); | ||||
} | } | ||||
ifp = STF2IFP(sc); | ifp = STF2IFP(sc); | ||||
#ifdef MAC | #ifdef MAC | ||||
mac_ifnet_create_mbuf(ifp, m); | mac_ifnet_create_mbuf(ifp, m); | ||||
#endif | #endif | ||||
/* | /* | ||||
* perform sanity check against outer src/dst. | * perform sanity check against outer src/dst. | ||||
* for source, perform ingress filter as well. | * for source, perform ingress filter as well. | ||||
*/ | */ | ||||
if (stf_checkaddr4(sc, &ip->ip_dst, NULL) < 0 || | if (stf_checkaddr4(sc, &ip->ip_dst, NULL) < 0 || | ||||
stf_checkaddr4(sc, &ip->ip_src, m->m_pkthdr.rcvif) < 0) { | stf_checkaddr4(sc, &ip->ip_src, m->m_pkthdr.rcvif) < 0) { | ||||
m_freem(m); | m_freem(m); | ||||
SDT_PROBE2(if_stf, , stf_input, out, IPPROTO_DONE, __LINE__); | |||||
return (IPPROTO_DONE); | return (IPPROTO_DONE); | ||||
} | } | ||||
otos = ip->ip_tos; | otos = ip->ip_tos; | ||||
m_adj(m, off); | m_adj(m, off); | ||||
if (m->m_len < sizeof(*ip6)) { | if (m->m_len < sizeof(*ip6)) { | ||||
m = m_pullup(m, sizeof(*ip6)); | m = m_pullup(m, sizeof(*ip6)); | ||||
if (!m) | if (!m) { | ||||
SDT_PROBE2(if_stf, , stf_input, out, IPPROTO_DONE, | |||||
__LINE__); | |||||
return (IPPROTO_DONE); | return (IPPROTO_DONE); | ||||
} | } | ||||
} | |||||
ip6 = mtod(m, struct ip6_hdr *); | ip6 = mtod(m, struct ip6_hdr *); | ||||
/* | /* | ||||
* perform sanity check against inner src/dst. | * perform sanity check against inner src/dst. | ||||
* for source, perform ingress filter as well. | * for source, perform ingress filter as well. | ||||
*/ | */ | ||||
if (stf_checkaddr6(sc, &ip6->ip6_dst, NULL) < 0 || | if (stf_checkaddr6(sc, &ip6->ip6_dst, NULL) < 0 || | ||||
stf_checkaddr6(sc, &ip6->ip6_src, m->m_pkthdr.rcvif) < 0) { | stf_checkaddr6(sc, &ip6->ip6_src, m->m_pkthdr.rcvif) < 0) { | ||||
m_freem(m); | m_freem(m); | ||||
SDT_PROBE2(if_stf, , stf_input, out, IPPROTO_DONE, __LINE__); | |||||
return (IPPROTO_DONE); | return (IPPROTO_DONE); | ||||
} | } | ||||
/* | /* | ||||
* reject packets with private address range. | * reject packets with private address range. | ||||
* (requirement from RFC3056 section 2 1st paragraph) | * (requirement from RFC3056 section 2 1st paragraph) | ||||
*/ | */ | ||||
if ((IN6_IS_ADDR_6TO4(&ip6->ip6_src) && isrfc1918addr(&ip->ip_src)) || | if ((IN6_IS_ADDR_6TO4(&ip6->ip6_src) && isrfc1918addr(&ip->ip_src)) || | ||||
(IN6_IS_ADDR_6TO4(&ip6->ip6_dst) && isrfc1918addr(&ip->ip_dst))) { | (IN6_IS_ADDR_6TO4(&ip6->ip6_dst) && isrfc1918addr(&ip->ip_dst))) { | ||||
m_freem(m); | m_freem(m); | ||||
SDT_PROBE2(if_stf, , stf_input, out, IPPROTO_DONE, __LINE__); | |||||
return (IPPROTO_DONE); | return (IPPROTO_DONE); | ||||
} | } | ||||
/* | /* | ||||
* Ignore if the destination is the same stf interface because | * Ignore if the destination is the same stf interface because | ||||
* all of valid IPv6 outgoing traffic should go interfaces | * all of valid IPv6 outgoing traffic should go interfaces | ||||
* except for it. | * except for it. | ||||
*/ | */ | ||||
nh = fib6_lookup(sc->sc_fibnum, &ip6->ip6_dst, 0, 0, 0); | nh = fib6_lookup(sc->sc_fibnum, &ip6->ip6_dst, 0, 0, 0); | ||||
if (nh == NULL) { | if (nh == NULL) { | ||||
m_free(m); | m_free(m); | ||||
SDT_PROBE2(if_stf, , stf_input, out, IPPROTO_DONE, __LINE__); | |||||
return (IPPROTO_DONE); | return (IPPROTO_DONE); | ||||
} | } | ||||
if ((nh->nh_ifp == ifp) && | if ((nh->nh_ifp == ifp) && | ||||
(!IN6_ARE_ADDR_EQUAL(&ip6->ip6_src, &nh->gw6_sa.sin6_addr))) { | (!IN6_ARE_ADDR_EQUAL(&ip6->ip6_src, &nh->gw6_sa.sin6_addr))) { | ||||
m_free(m); | m_free(m); | ||||
SDT_PROBE2(if_stf, , stf_input, out, IPPROTO_DONE, __LINE__); | |||||
return (IPPROTO_DONE); | return (IPPROTO_DONE); | ||||
} | } | ||||
itos = IPV6_TRAFFIC_CLASS(ip6); | itos = IPV6_TRAFFIC_CLASS(ip6); | ||||
if ((ifp->if_flags & IFF_LINK1) != 0) | if ((ifp->if_flags & IFF_LINK1) != 0) | ||||
ip_ecn_egress(ECN_ALLOWED, &otos, &itos); | ip_ecn_egress(ECN_ALLOWED, &otos, &itos); | ||||
else | else | ||||
ip_ecn_egress(ECN_NOCARE, &otos, &itos); | ip_ecn_egress(ECN_NOCARE, &otos, &itos); | ||||
Show All 19 Lines | #endif | ||||
* specified address family. | * specified address family. | ||||
* See net/if_gif.c for possible issues with packet processing | * See net/if_gif.c for possible issues with packet processing | ||||
* reorder due to extra queueing. | * reorder due to extra queueing. | ||||
*/ | */ | ||||
if_inc_counter(ifp, IFCOUNTER_IPACKETS, 1); | if_inc_counter(ifp, IFCOUNTER_IPACKETS, 1); | ||||
if_inc_counter(ifp, IFCOUNTER_IBYTES, m->m_pkthdr.len); | if_inc_counter(ifp, IFCOUNTER_IBYTES, m->m_pkthdr.len); | ||||
M_SETFIB(m, ifp->if_fib); | M_SETFIB(m, ifp->if_fib); | ||||
netisr_dispatch(NETISR_IPV6, m); | netisr_dispatch(NETISR_IPV6, m); | ||||
SDT_PROBE2(if_stf, , stf_input, out, IPPROTO_DONE, __LINE__); | |||||
return (IPPROTO_DONE); | return (IPPROTO_DONE); | ||||
} | } | ||||
static struct sockaddr_in * | static struct sockaddr_in * | ||||
stf_getin4addr_in6(struct stf_softc *sc, struct sockaddr_in *sin, | stf_getin4addr_in6(struct stf_softc *sc, struct sockaddr_in *sin, | ||||
struct in6_addr addr6, struct in6_addr mask6, struct in6_addr in6) | struct in6_addr addr6, struct in6_addr mask6, struct in6_addr in6) | ||||
{ | { | ||||
int i; | int i; | ||||
struct sockaddr_in *out; | |||||
/* | /* | ||||
* When (src addr & src mask) != (in6 & src mask), | * When (src addr & src mask) != (in6 & src mask), | ||||
* the dst is not in the 6rd domain. The IPv4 address must | * the dst is not in the 6rd domain. The IPv4 address must | ||||
* not be used. | * not be used. | ||||
*/ | */ | ||||
for (i = 0; i < sizeof(addr6); i++) { | for (i = 0; i < sizeof(addr6); i++) { | ||||
if ((((u_char *)&addr6)[i] & ((u_char *)&mask6)[i]) != | if ((((u_char *)&addr6)[i] & ((u_char *)&mask6)[i]) != | ||||
(((u_char *)&in6)[i] & ((u_char *)&mask6)[i])) | (((u_char *)&in6)[i] & ((u_char *)&mask6)[i])) { | ||||
SDT_PROBE4(if_stf, , getin4addr_in6, out, &addr6, | |||||
&mask6, &in6, NULL); | |||||
return (NULL); | return (NULL); | ||||
} | } | ||||
} | |||||
/* After the mask check, use in6 instead of addr6. */ | /* After the mask check, use in6 instead of addr6. */ | ||||
return (stf_getin4addr(sc, sin, in6, mask6)); | out = stf_getin4addr(sc, sin, in6, mask6); | ||||
SDT_PROBE4(if_stf, , getin4addr_in6, out, &addr6, &mask6, &in6, out); | |||||
return (out); | |||||
} | } | ||||
static struct sockaddr_in * | static struct sockaddr_in * | ||||
stf_getin4addr(struct stf_softc *sc, struct sockaddr_in *sin, | stf_getin4addr(struct stf_softc *sc, struct sockaddr_in *sin, | ||||
struct in6_addr addr6, struct in6_addr mask6) | struct in6_addr addr6, struct in6_addr mask6) | ||||
{ | { | ||||
struct in_addr *in; | struct in_addr *in; | ||||
SDT_PROBE2(if_stf, , getin4addr, in, &addr6, &mask6); | |||||
memset(sin, 0, sizeof(*sin)); | memset(sin, 0, sizeof(*sin)); | ||||
in = &sin->sin_addr; | in = &sin->sin_addr; | ||||
if (IN6_IS_ADDR_6TO4(&addr6)) { | if (IN6_IS_ADDR_6TO4(&addr6)) { | ||||
/* 6to4 (RFC 3056) */ | /* 6to4 (RFC 3056) */ | ||||
bcopy(GET_V4(&addr6), in, sizeof(*in)); | bcopy(GET_V4(&addr6), in, sizeof(*in)); | ||||
if (isrfc1918addr(in)) | if (isrfc1918addr(in)) | ||||
return (NULL); | return (NULL); | ||||
} else { | } else { | ||||
Show All 28 Lines | if (IN6_IS_ADDR_6TO4(&addr6)) { | ||||
v6prefix >>= plen; | v6prefix >>= plen; | ||||
/* Now shift away everything after the v4 address. */ | /* Now shift away everything after the v4 address. */ | ||||
v6prefix >>= 64 - plen - v4suffixlen; | v6prefix >>= 64 - plen - v4suffixlen; | ||||
sin->sin_addr.s_addr = htonl(v4prefix | (uint32_t)v6prefix); | sin->sin_addr.s_addr = htonl(v4prefix | (uint32_t)v6prefix); | ||||
} | } | ||||
SDT_PROBE1(if_stf, , getin4addr, out, sin); | |||||
return (sin); | return (sin); | ||||
} | } | ||||
static int | static int | ||||
stf_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data) | stf_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data) | ||||
{ | { | ||||
struct ifaddr *ifa; | struct ifaddr *ifa; | ||||
struct ifdrv *ifd; | struct ifdrv *ifd; | ||||
Show All 25 Lines | if (ifd->ifd_cmd == STF6RD_SV4NET) { | ||||
if (args.v4_prefixlen < 1 || args.v4_prefixlen > 32) { | if (args.v4_prefixlen < 1 || args.v4_prefixlen > 32) { | ||||
error = EINVAL; | error = EINVAL; | ||||
break; | break; | ||||
} | } | ||||
bcopy(&args.srcv4_addr, &sc_cur->srcv4_addr, | bcopy(&args.srcv4_addr, &sc_cur->srcv4_addr, | ||||
sizeof(sc_cur->srcv4_addr)); | sizeof(sc_cur->srcv4_addr)); | ||||
sc_cur->v4prefixlen = args.v4_prefixlen; | sc_cur->v4prefixlen = args.v4_prefixlen; | ||||
SDT_PROBE3(if_stf, , ioctl, sv4net, sc_cur->srcv4_addr, | |||||
sc_cur->srcv4_addr, sc_cur->v4prefixlen); | |||||
} else if (ifd->ifd_cmd == STF6RD_SBR) { | } else if (ifd->ifd_cmd == STF6RD_SBR) { | ||||
if (ifd->ifd_len != sizeof(args)) { | if (ifd->ifd_len != sizeof(args)) { | ||||
error = EINVAL; | error = EINVAL; | ||||
break; | break; | ||||
} | } | ||||
bzero(&args, sizeof(args)); | bzero(&args, sizeof(args)); | ||||
error = copyin(ifd->ifd_data, &args, ifd->ifd_len); | error = copyin(ifd->ifd_data, &args, ifd->ifd_len); | ||||
if (error) | if (error) | ||||
break; | break; | ||||
sc_cur->braddr = args.braddr.s_addr; | sc_cur->braddr = args.braddr.s_addr; | ||||
SDT_PROBE1(if_stf, , ioctl, sdstv4, | |||||
sc_cur->braddr); | |||||
} else | } else | ||||
error = EINVAL; | error = EINVAL; | ||||
break; | break; | ||||
case SIOCGDRVSPEC: | case SIOCGDRVSPEC: | ||||
ifd = (struct ifdrv *)data; | ifd = (struct ifdrv *)data; | ||||
if (ifd->ifd_cmd != STF6RD_GV4NET) { | if (ifd->ifd_cmd != STF6RD_GV4NET) { | ||||
error = EINVAL; | error = EINVAL; | ||||
break; | break; | ||||
} | } | ||||
if (ifd->ifd_len != sizeof(args)) { | if (ifd->ifd_len != sizeof(args)) { | ||||
error = EINVAL; | error = EINVAL; | ||||
break; | break; | ||||
} | } | ||||
bzero(&args, sizeof(args)); | bzero(&args, sizeof(args)); | ||||
args.srcv4_addr.s_addr = sc_cur->srcv4_addr; | args.srcv4_addr.s_addr = sc_cur->srcv4_addr; | ||||
args.braddr.s_addr = sc_cur->braddr; | args.braddr.s_addr = sc_cur->braddr; | ||||
args.v4_prefixlen = sc_cur->v4prefixlen; | args.v4_prefixlen = sc_cur->v4prefixlen; | ||||
error = copyout(&args, ifd->ifd_data, ifd->ifd_len); | error = copyout(&args, ifd->ifd_data, ifd->ifd_len); | ||||
break; | break; | ||||
case SIOCSIFADDR: | case SIOCSIFADDR: | ||||
ifa = (struct ifaddr *)data; | ifa = (struct ifaddr *)data; | ||||
SDT_PROBE1(if_stf, , ioctl, ifaddr, ifa); | |||||
if (ifa == NULL || ifa->ifa_addr->sa_family != AF_INET6) { | if (ifa == NULL || ifa->ifa_addr->sa_family != AF_INET6) { | ||||
error = EAFNOSUPPORT; | error = EAFNOSUPPORT; | ||||
break; | break; | ||||
} | } | ||||
if (stf_getin4addr(sc_cur, &sin4, | if (stf_getin4addr(sc_cur, &sin4, | ||||
satosin6(ifa->ifa_addr)->sin6_addr, | satosin6(ifa->ifa_addr)->sin6_addr, | ||||
satosin6(ifa->ifa_netmask)->sin6_addr) == NULL) { | satosin6(ifa->ifa_netmask)->sin6_addr) == NULL) { | ||||
error = EINVAL; | error = EINVAL; | ||||
Show All 34 Lines |