Changeset View
Changeset View
Standalone View
Standalone View
sys/net/if_stf.c
/* $FreeBSD$ */ | /* $FreeBSD$ */ | ||||
/* $KAME: if_stf.c,v 1.73 2001/12/03 11:08:30 keiichi Exp $ */ | /* $KAME: if_stf.c,v 1.73 2001/12/03 11:08:30 keiichi Exp $ */ | ||||
/*- | /*- | ||||
* SPDX-License-Identifier: BSD-3-Clause | * SPDX-License-Identifier: BSD-3-Clause | ||||
* | * | ||||
* Copyright (C) 2000 WIDE Project. | * Copyright (C) 2000 WIDE Project. | ||||
* Copyright (c) 2010 Hiroki Sato <hrs@FreeBSD.org> | |||||
* Copyright (c) 2013 Ermal Luci <eri@FreeBSD.org> | |||||
* Copyright (c) 2017-2021 Rubicon Communications, LLC (Netgate) | |||||
* All rights reserved. | * All rights reserved. | ||||
* | * | ||||
* Redistribution and use in source and binary forms, with or without | * Redistribution and use in source and binary forms, with or without | ||||
* modification, are permitted provided that the following conditions | * modification, are permitted provided that the following conditions | ||||
* are met: | * are met: | ||||
* 1. Redistributions of source code must retain the above copyright | * 1. Redistributions of source code must retain the above copyright | ||||
* notice, this list of conditions and the following disclaimer. | * notice, this list of conditions and the following disclaimer. | ||||
* 2. Redistributions in binary form must reproduce the above copyright | * 2. Redistributions in binary form must reproduce the above copyright | ||||
▲ Show 20 Lines • Show All 60 Lines • ▼ Show 20 Lines | |||||
* Note that there is no way to be 100% secure. | * Note that there is no way to be 100% secure. | ||||
*/ | */ | ||||
#include <sys/param.h> | #include <sys/param.h> | ||||
#include <sys/systm.h> | #include <sys/systm.h> | ||||
#include <sys/socket.h> | #include <sys/socket.h> | ||||
#include <sys/sockio.h> | #include <sys/sockio.h> | ||||
#include <sys/mbuf.h> | #include <sys/mbuf.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/proc.h> | #include <sys/proc.h> | ||||
#include <sys/queue.h> | #include <sys/queue.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> | ||||
#include <net/route.h> | #include <net/route.h> | ||||
#include <net/route/nhop.h> | #include <net/route/nhop.h> | ||||
#include <net/netisr.h> | #include <net/netisr.h> | ||||
#include <net/if_stf.h> | |||||
#include <net/if_types.h> | #include <net/if_types.h> | ||||
#include <net/vnet.h> | #include <net/vnet.h> | ||||
#include <netinet/in.h> | #include <netinet/in.h> | ||||
#include <netinet/in_fib.h> | #include <netinet/in_fib.h> | ||||
#include <netinet/in_systm.h> | #include <netinet/in_systm.h> | ||||
#include <netinet/ip.h> | #include <netinet/ip.h> | ||||
#include <netinet/ip_var.h> | #include <netinet/ip_var.h> | ||||
#include <netinet/in_var.h> | #include <netinet/in_var.h> | ||||
#include <netinet/ip6.h> | #include <netinet/ip6.h> | ||||
#include <netinet6/in6_fib.h> | |||||
#include <netinet6/ip6_var.h> | #include <netinet6/ip6_var.h> | ||||
#include <netinet6/in6_var.h> | #include <netinet6/in6_var.h> | ||||
#include <netinet/ip_ecn.h> | #include <netinet/ip_ecn.h> | ||||
#include <netinet/ip_encap.h> | #include <netinet/ip_encap.h> | ||||
#include <machine/stdarg.h> | #include <machine/stdarg.h> | ||||
Show All 16 Lines | |||||
/* | /* | ||||
* XXX: Return a pointer with 16-bit aligned. Don't cast it to | * XXX: Return a pointer with 16-bit aligned. Don't cast it to | ||||
* struct in_addr *; use bcopy() instead. | * struct in_addr *; use bcopy() instead. | ||||
*/ | */ | ||||
#define GET_V4(x) (&(x)->s6_addr16[1]) | #define GET_V4(x) (&(x)->s6_addr16[1]) | ||||
struct stf_softc { | struct stf_softc { | ||||
struct ifnet *sc_ifp; | struct ifnet *sc_ifp; | ||||
in_addr_t braddr; /* Border relay IPv4 address */ | |||||
in_addr_t srcv4_addr; /* Our IPv4 WAN address */ | |||||
u_int v4prefixlen; /* How much of the v4 address to include in our address. */ | |||||
u_int sc_fibnum; | u_int sc_fibnum; | ||||
const struct encaptab *encap_cookie; | const struct encaptab *encap_cookie; | ||||
}; | }; | ||||
#define STF2IFP(sc) ((sc)->sc_ifp) | #define STF2IFP(sc) ((sc)->sc_ifp) | ||||
static const char stfname[] = "stf"; | static const char stfname[] = "stf"; | ||||
static MALLOC_DEFINE(M_STF, stfname, "6to4 Tunnel Interface"); | static MALLOC_DEFINE(M_STF, stfname, "6to4 Tunnel Interface"); | ||||
static const int ip_stf_ttl = 40; | static const int ip_stf_ttl = 40; | ||||
static int in_stf_input(struct mbuf *, int, int, void *); | static int in_stf_input(struct mbuf *, int, int, void *); | ||||
static char *stfnames[] = {"stf0", "stf", "6to4", NULL}; | static char *stfnames[] = {"stf0", "stf", "6to4", NULL}; | ||||
static int stfmodevent(module_t, int, void *); | static int stfmodevent(module_t, int, void *); | ||||
static int stf_encapcheck(const struct mbuf *, int, int, void *); | static int stf_encapcheck(const struct mbuf *, int, int, void *); | ||||
static int stf_getsrcifa6(struct ifnet *, struct in6_addr *, struct in6_addr *); | static int stf_getsrcifa6(struct ifnet *, struct in6_addr *, struct in6_addr *); | ||||
static int stf_output(struct ifnet *, struct mbuf *, const struct sockaddr *, | static int stf_output(struct ifnet *, struct mbuf *, const struct sockaddr *, | ||||
struct route *); | struct route *); | ||||
static int isrfc1918addr(struct in_addr *); | static int isrfc1918addr(struct in_addr *); | ||||
static int stf_checkaddr4(struct stf_softc *, struct in_addr *, | static int stf_checkaddr4(struct stf_softc *, struct in_addr *, | ||||
struct ifnet *); | struct ifnet *); | ||||
static int stf_checkaddr6(struct stf_softc *, struct in6_addr *, | static int stf_checkaddr6(struct stf_softc *, struct in6_addr *, | ||||
struct ifnet *); | 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_ioctl(struct ifnet *, u_long, caddr_t); | ||||
static int stf_clone_match(struct if_clone *, const char *); | 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_create(struct if_clone *, char *, size_t, caddr_t); | ||||
static int stf_clone_destroy(struct if_clone *, struct ifnet *); | static int stf_clone_destroy(struct if_clone *, struct ifnet *); | ||||
VNET_DEFINE_STATIC(struct if_clone *, stf_cloner); | VNET_DEFINE_STATIC(struct if_clone *, stf_cloner); | ||||
#define V_stf_cloner VNET(stf_cloner) | #define V_stf_cloner VNET(stf_cloner) | ||||
▲ Show 20 Lines • Show All 144 Lines • ▼ Show 20 Lines | |||||
static moduledata_t stf_mod = { | static moduledata_t stf_mod = { | ||||
"if_stf", | "if_stf", | ||||
stfmodevent, | stfmodevent, | ||||
0 | 0 | ||||
}; | }; | ||||
DECLARE_MODULE(if_stf, stf_mod, SI_SUB_PSEUDO, SI_ORDER_ANY); | DECLARE_MODULE(if_stf, stf_mod, SI_SUB_PSEUDO, SI_ORDER_ANY); | ||||
MODULE_VERSION(if_stf, 2); | |||||
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 in_addr a, b, mask; | |||||
struct in6_addr addr6, mask6; | struct in6_addr addr6, mask6; | ||||
struct sockaddr_in sin4addr, sin4mask; | |||||
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" */ | ||||
if ((STF2IFP(sc)->if_flags & IFF_LINK0) != 0) | if ((STF2IFP(sc)->if_flags & IFF_LINK0) != 0) | ||||
return (0); | return (0); | ||||
if (proto != IPPROTO_IPV6) | if (proto != IPPROTO_IPV6) | ||||
return (0); | return (0); | ||||
m_copydata(m, 0, sizeof(ip), (caddr_t)&ip); | m_copydata(m, 0, sizeof(ip), (caddr_t)&ip); | ||||
if (ip.ip_v != 4) | if (ip.ip_v != 4) | ||||
return (0); | return (0); | ||||
if (stf_getsrcifa6(STF2IFP(sc), &addr6, &mask6) != 0) | if (stf_getsrcifa6(STF2IFP(sc), &addr6, &mask6) != 0) | ||||
return (0); | return (0); | ||||
/* | if (sc->srcv4_addr != INADDR_ANY) { | ||||
* check if IPv4 dst matches the IPv4 address derived from the | sin4addr.sin_addr.s_addr = sc->srcv4_addr; | ||||
* local 6to4 address. | sin4addr.sin_family = AF_INET; | ||||
* success on: dst = 10.1.1.1, ia6->ia_addr = 2002:0a01:0101:... | } else | ||||
*/ | if (stf_getin4addr(sc, &sin4addr, addr6, mask6) == NULL) | ||||
if (bcmp(GET_V4(&addr6), &ip.ip_dst, sizeof(ip.ip_dst)) != 0) | |||||
return (0); | return (0); | ||||
if (sin4addr.sin_addr.s_addr != ip.ip_dst.s_addr) | |||||
return (0); | |||||
if (IN6_IS_ADDR_6TO4(&addr6)) { | |||||
/* | /* | ||||
* check if IPv4 src matches the IPv4 address derived from the | * 6to4 (RFC 3056). | ||||
* local 6to4 address masked by prefixmask. | * 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 | * 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 | * fail on: src = 10.1.1.1, ia6->ia_addr = 2002:0b00:.../24 | ||||
*/ | */ | ||||
bzero(&a, sizeof(a)); | memcpy(&sin4mask.sin_addr, GET_V4(&mask6), | ||||
bcopy(GET_V4(&addr6), &a, sizeof(a)); | sizeof(sin4mask.sin_addr)); | ||||
bcopy(GET_V4(&mask6), &mask, sizeof(mask)); | if ((sin4addr.sin_addr.s_addr & sin4mask.sin_addr.s_addr) != | ||||
a.s_addr &= mask.s_addr; | (ip.ip_src.s_addr & sin4mask.sin_addr.s_addr)) | ||||
b = ip.ip_src; | |||||
b.s_addr &= mask.s_addr; | |||||
if (a.s_addr != b.s_addr) | |||||
return (0); | 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 */ | /* 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_ifaddr *ia6; | struct in6_ifaddr *ia6; | ||||
struct sockaddr_in6 *sin6; | struct sockaddr_in sin4; | ||||
struct stf_softc *sc; | |||||
struct in_addr in; | struct in_addr in; | ||||
NET_EPOCH_ASSERT(); | NET_EPOCH_ASSERT(); | ||||
sc = ifp->if_softc; | |||||
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; | ||||
sin6 = (struct sockaddr_in6 *)ia->ifa_addr; | |||||
if (!IN6_IS_ADDR_6TO4(&sin6->sin6_addr)) | ia6 = (struct in6_ifaddr *)ia; | ||||
hrs: I think there is no need to define ia6 after fixing the following two lines.
| |||||
*&addr6 = ((struct sockaddr_in6 *)ia->ifa_addr)->sin6_addr; | |||||
hrsUnsubmitted Done Inline Actions
hrs: - LHS should be simply addr6.
- RHS can be simplified by using IFA_IN6(is).
- memcpy() or… | |||||
*&mask6 = ia6->ia_prefixmask.sin6_addr; | |||||
hrsUnsubmitted Done Inline Actions
hrs: - IA6_MASKIN6(ia) | |||||
if (sc->srcv4_addr != INADDR_ANY) | |||||
bcopy(&sc->srcv4_addr, &in, sizeof(in)); | |||||
else { | |||||
if (stf_getin4addr(sc, &sin4, addr6, mask6) == NULL) | |||||
continue; | continue; | ||||
bcopy(&sin4.sin_addr, &in, sizeof(in)); | |||||
} | |||||
bcopy(GET_V4(&sin6->sin6_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; | ||||
ia6 = (struct in6_ifaddr *)ia; | *addr = addr6; | ||||
*mask = mask6; | |||||
*addr = sin6->sin6_addr; | |||||
*mask = ia6->ia_prefixmask.sin6_addr; | |||||
return (0); | return (0); | ||||
} | } | ||||
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 in_addr in4; | struct sockaddr_in dst4, src4; | ||||
const void *ptr; | |||||
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; | ||||
#ifdef MAC | #ifdef MAC | ||||
error = mac_ifnet_check_transmit(ifp, m); | error = mac_ifnet_check_transmit(ifp, m); | ||||
Show All 33 Lines | #endif | ||||
} | } | ||||
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. | ||||
*/ | */ | ||||
ptr = NULL; | if (stf_getin4addr_in6(sc, &dst4, addr6, mask6, | ||||
if (IN6_IS_ADDR_6TO4(&ip6->ip6_dst)) | ip6->ip6_dst) == NULL) { | ||||
ptr = GET_V4(&ip6->ip6_dst); | if (sc->braddr != INADDR_ANY) | ||||
hrsUnsubmitted Not Done Inline Actionsin_nullhost()? hrs: in_nullhost()? | |||||
kpAuthorUnsubmitted Done Inline ActionsThat expects a struct in_addr, and we use in_addr_t here. kp: That expects a struct in_addr, and we use in_addr_t here. | |||||
else if (IN6_IS_ADDR_6TO4(&dst6->sin6_addr)) | dst4.sin_addr.s_addr = sc->braddr; | ||||
ptr = GET_V4(&dst6->sin6_addr); | else if (stf_getin4addr_in6(sc, &dst4, addr6, mask6, | ||||
else { | dst6->sin6_addr) == NULL) { | ||||
m_freem(m); | m_freem(m); | ||||
if_inc_counter(ifp, IFCOUNTER_OERRORS, 1); | if_inc_counter(ifp, IFCOUNTER_OERRORS, 1); | ||||
return (ENETUNREACH); | return (ENETUNREACH); | ||||
} | } | ||||
bcopy(ptr, &in4, sizeof(in4)); | } | ||||
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); | ||||
return (ENOBUFS); | return (ENOBUFS); | ||||
} | } | ||||
ip = mtod(m, struct ip *); | ip = mtod(m, struct ip *); | ||||
bzero(ip, sizeof(*ip)); | bzero(ip, sizeof(*ip)); | ||||
bcopy(GET_V4(&addr6), &ip->ip_src, sizeof(ip->ip_src)); | if (sc->srcv4_addr != INADDR_ANY) | ||||
bcopy(&in4, &ip->ip_dst, sizeof(ip->ip_dst)); | 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)); | |||||
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); | ||||
Show All 32 Lines | stf_checkaddr4(struct stf_softc *sc, struct in_addr *in, struct ifnet *inifp) | ||||
if (IN_MULTICAST(ntohl(in->s_addr))) | if (IN_MULTICAST(ntohl(in->s_addr))) | ||||
return (-1); | return (-1); | ||||
switch ((ntohl(in->s_addr) & 0xff000000) >> 24) { | switch ((ntohl(in->s_addr) & 0xff000000) >> 24) { | ||||
case 0: case 127: case 255: | case 0: case 127: case 255: | ||||
return (-1); | return (-1); | ||||
} | } | ||||
/* | /* | ||||
* reject packets with private address range. | |||||
* (requirement from RFC3056 section 2 1st paragraph) | |||||
*/ | |||||
if (isrfc1918addr(in)) | |||||
return (-1); | |||||
/* | |||||
* reject packets with broadcast | * reject packets with broadcast | ||||
*/ | */ | ||||
CK_STAILQ_FOREACH(ia4, &V_in_ifaddrhead, ia_link) { | CK_STAILQ_FOREACH(ia4, &V_in_ifaddrhead, ia_link) { | ||||
if ((ia4->ia_ifa.ifa_ifp->if_flags & IFF_BROADCAST) == 0) | if ((ia4->ia_ifa.ifa_ifp->if_flags & IFF_BROADCAST) == 0) | ||||
continue; | continue; | ||||
if (in->s_addr == ia4->ia_broadaddr.sin_addr.s_addr) { | if (in->s_addr == ia4->ia_broadaddr.sin_addr.s_addr) { | ||||
return (-1); | return (-1); | ||||
} | } | ||||
▲ Show 20 Lines • Show All 44 Lines • ▼ Show 20 Lines | |||||
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; | |||||
NET_EPOCH_ASSERT(); | NET_EPOCH_ASSERT(); | ||||
if (proto != IPPROTO_IPV6) { | if (proto != IPPROTO_IPV6) { | ||||
m_freem(m); | m_freem(m); | ||||
return (IPPROTO_DONE); | return (IPPROTO_DONE); | ||||
} | } | ||||
Show All 34 Lines | #endif | ||||
* 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); | ||||
return (IPPROTO_DONE); | 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. | |||||
*/ | |||||
nh = fib6_lookup(sc->sc_fibnum, &ip6->ip6_dst, 0, 0, 0); | |||||
if (nh == NULL) { | |||||
m_free(m); | |||||
return (IPPROTO_DONE); | |||||
} | |||||
if ((nh->nh_ifp == ifp) && | |||||
(!IN6_ARE_ADDR_EQUAL(&ip6->ip6_src, &nh->gw6_sa.sin6_addr))) { | |||||
m_free(m); | |||||
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); | ||||
ip6->ip6_flow &= ~htonl(0xff << 20); | ip6->ip6_flow &= ~htonl(0xff << 20); | ||||
ip6->ip6_flow |= htonl((u_int32_t)itos << 20); | ip6->ip6_flow |= htonl((u_int32_t)itos << 20); | ||||
Show All 19 Lines | #endif | ||||
*/ | */ | ||||
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); | ||||
return (IPPROTO_DONE); | 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; | |||||
/* | |||||
* 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; | |||||
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) */ | |||||
in_addr_t v4prefix; | |||||
uint8_t *v6 = (uint8_t*)&addr6; | |||||
uint64_t v6prefix; | |||||
u_int plen; | |||||
u_int v4suffixlen; | |||||
v4prefix = 0; | |||||
v4suffixlen = 32 - sc->v4prefixlen; | |||||
hrsUnsubmitted Done Inline ActionsThis should be inside the next if-conditional. hrs: This should be inside the next if-conditional. | |||||
if (sc->v4prefixlen < 32) { | |||||
v4prefix = sc->srcv4_addr & htonl((((uint32_t)-1) << v4suffixlen)); | |||||
v4prefix = ntohl(v4prefix); | |||||
hrsUnsubmitted Done Inline ActionsThese two lines are confusing. How about v4prefix = ntohl(sc->srcv4_addr & (0xffffffffU << v4suffixlen))? hrs: These two lines are confusing. How about v4prefix = ntohl(sc->srcv4_addr & (0xffffffffU <<… | |||||
} else { | |||||
hrsUnsubmitted Done Inline Actions(sc->v4prefixlen > 32) must be rejected and return with NULL. It never happens if SIOCSDRVSPEC guarantees it is between 1 to 32, but having a sanity check is useful. hrs: (sc->v4prefixlen > 32) must be rejected and return with NULL. It never happens if SIOCSDRVSPEC… | |||||
v4suffixlen = 32; | |||||
} | |||||
plen = in6_mask2len(&mask6, NULL); | |||||
/* To make this simple we do not support prefixes longer than | |||||
* 64 bits. RFC5969 says "a 6rd delegated prefix SHOULD be /64 | |||||
* or shorter." so this is a moderately safe assumption. */ | |||||
v6prefix = be64toh(*(uint64_t *)v6); | |||||
hrsUnsubmitted Done Inline Actions(plen > 64) must be rejected and return with NULL before this. It is still possible to happen. hrs: (plen > 64) must be rejected and return with NULL before this. It is still possible to happen.
| |||||
/* Shift away the v6 prefix itself. */ | |||||
v6prefix <<= plen; | |||||
v6prefix >>= plen; | |||||
/* Now shift away everything after the v4 address. */ | |||||
v6prefix >>= 64 - plen - v4suffixlen; | |||||
sin->sin_addr.s_addr = htonl(v4prefix | (uint32_t)v6prefix); | |||||
} | |||||
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 ifreq *ifr; | struct ifreq *ifr; | ||||
struct sockaddr_in6 *sin6; | struct sockaddr_in sin4; | ||||
struct in_addr addr; | struct stf_softc *sc_cur; | ||||
struct stfv4args args; | |||||
int error, mtu; | int error, mtu; | ||||
error = 0; | error = 0; | ||||
sc_cur = ifp->if_softc; | |||||
switch (cmd) { | switch (cmd) { | ||||
case SIOCSDRVSPEC: | |||||
ifd = (struct ifdrv *)data; | |||||
error = priv_check(curthread, PRIV_NET_ADDIFADDR); | |||||
if (error) | |||||
break; | |||||
if (ifd->ifd_cmd == STF6RD_SV4NET) { | |||||
if (ifd->ifd_len != sizeof(args)) { | |||||
error = EINVAL; | |||||
break; | |||||
} | |||||
bzero(&args, sizeof args); | |||||
hrsUnsubmitted Done Inline Actionssizeof(args) hrs: sizeof(args) | |||||
error = copyin(ifd->ifd_data, &args, ifd->ifd_len); | |||||
if (error) | |||||
break; | |||||
sc_cur->srcv4_addr = args.srcv4_addr.s_addr; | |||||
hrsUnsubmitted Done Inline Actionsbcopy or memcpy. hrs: bcopy or memcpy. | |||||
sc_cur->v4prefixlen = args.v4_prefixlen; | |||||
hrsUnsubmitted Done Inline ActionsThe value must be checked to guarantee that it is within 1 to 32. hrs: The value must be checked to guarantee that it is within 1 to 32. | |||||
if (sc_cur->v4prefixlen == 0) | |||||
sc_cur->v4prefixlen = 32; | |||||
} else if (ifd->ifd_cmd == STF6RD_SBR) { | |||||
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->braddr = args.braddr.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 != STF6RD_GV4NET) { | |||||
hrsUnsubmitted Done Inline ActionsConsistency. SIOCSDRVSPEC checks ifd_cmd first, and this checks ifd_len first. hrs: Consistency. SIOCSDRVSPEC checks ifd_cmd first, and this checks ifd_len first. | |||||
error = EINVAL; | |||||
break; | |||||
} | |||||
bzero(&args, sizeof args); | |||||
args.srcv4_addr.s_addr = sc_cur->srcv4_addr; | |||||
args.braddr.s_addr = sc_cur->braddr; | |||||
args.v4_prefixlen = sc_cur->v4prefixlen; | |||||
error = copyout(&args, ifd->ifd_data, ifd->ifd_len); | |||||
break; | |||||
case SIOCSIFADDR: | case SIOCSIFADDR: | ||||
ifa = (struct ifaddr *)data; | ifa = (struct ifaddr *)data; | ||||
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; | ||||
} | } | ||||
sin6 = (struct sockaddr_in6 *)ifa->ifa_addr; | if (stf_getin4addr(sc_cur, &sin4, | ||||
if (!IN6_IS_ADDR_6TO4(&sin6->sin6_addr)) { | satosin6(ifa->ifa_addr)->sin6_addr, | ||||
satosin6(ifa->ifa_netmask)->sin6_addr) == NULL) { | |||||
error = EINVAL; | error = EINVAL; | ||||
break; | break; | ||||
} | } | ||||
bcopy(GET_V4(&sin6->sin6_addr), &addr, sizeof(addr)); | |||||
if (isrfc1918addr(&addr)) { | |||||
error = EINVAL; | |||||
break; | |||||
} | |||||
ifp->if_flags |= IFF_UP; | ifp->if_flags |= IFF_UP; | ||||
ifp->if_drv_flags |= IFF_DRV_RUNNING; | ifp->if_drv_flags |= IFF_DRV_RUNNING; | ||||
break; | break; | ||||
case SIOCADDMULTI: | case SIOCADDMULTI: | ||||
case SIOCDELMULTI: | case SIOCDELMULTI: | ||||
ifr = (struct ifreq *)data; | ifr = (struct ifreq *)data; | ||||
if (ifr && ifr->ifr_addr.sa_family == AF_INET6) | if (ifr && ifr->ifr_addr.sa_family == AF_INET6) | ||||
Show All 24 Lines |
I think there is no need to define ia6 after fixing the following two lines.