Changeset View
Changeset View
Standalone View
Standalone View
head/sys/netinet6/ip6_gre.c
Show First 20 Lines • Show All 60 Lines • ▼ Show 20 Lines | |||||
VNET_DEFINE(int, ip6_gre_hlim) = IPV6_DEFHLIM; | VNET_DEFINE(int, ip6_gre_hlim) = IPV6_DEFHLIM; | ||||
#define V_ip6_gre_hlim VNET(ip6_gre_hlim) | #define V_ip6_gre_hlim VNET(ip6_gre_hlim) | ||||
SYSCTL_DECL(_net_inet6_ip6); | SYSCTL_DECL(_net_inet6_ip6); | ||||
SYSCTL_INT(_net_inet6_ip6, OID_AUTO, grehlim, CTLFLAG_VNET | CTLFLAG_RW, | SYSCTL_INT(_net_inet6_ip6, OID_AUTO, grehlim, CTLFLAG_VNET | CTLFLAG_RW, | ||||
&VNET_NAME(ip6_gre_hlim), 0, "Default hop limit for encapsulated packets"); | &VNET_NAME(ip6_gre_hlim), 0, "Default hop limit for encapsulated packets"); | ||||
VNET_DEFINE_STATIC(struct gre_list *, ipv6_hashtbl) = NULL; | VNET_DEFINE_STATIC(struct gre_list *, ipv6_hashtbl) = NULL; | ||||
VNET_DEFINE_STATIC(struct gre_list *, ipv6_srchashtbl) = NULL; | |||||
#define V_ipv6_hashtbl VNET(ipv6_hashtbl) | #define V_ipv6_hashtbl VNET(ipv6_hashtbl) | ||||
#define V_ipv6_srchashtbl VNET(ipv6_srchashtbl) | |||||
#define GRE_HASH(src, dst) (V_ipv6_hashtbl[\ | #define GRE_HASH(src, dst) (V_ipv6_hashtbl[\ | ||||
in6_gre_hashval((src), (dst)) & (GRE_HASH_SIZE - 1)]) | in6_gre_hashval((src), (dst)) & (GRE_HASH_SIZE - 1)]) | ||||
#define GRE_SRCHASH(src) (V_ipv6_srchashtbl[\ | |||||
fnv_32_buf((src), sizeof(*src), FNV1_32_INIT) & (GRE_HASH_SIZE - 1)]) | |||||
#define GRE_HASH_SC(sc) GRE_HASH(&(sc)->gre_oip6.ip6_src,\ | #define GRE_HASH_SC(sc) GRE_HASH(&(sc)->gre_oip6.ip6_src,\ | ||||
&(sc)->gre_oip6.ip6_dst) | &(sc)->gre_oip6.ip6_dst) | ||||
static uint32_t | static uint32_t | ||||
in6_gre_hashval(const struct in6_addr *src, const struct in6_addr *dst) | in6_gre_hashval(const struct in6_addr *src, const struct in6_addr *dst) | ||||
{ | { | ||||
uint32_t ret; | uint32_t ret; | ||||
▲ Show 20 Lines • Show All 46 Lines • ▼ Show 20 Lines | if (IN6_ARE_ADDR_EQUAL(&sc->gre_oip6.ip6_src, | ||||
return (0); | return (0); | ||||
*arg = sc; | *arg = sc; | ||||
return (ENCAP_DRV_LOOKUP); | return (ENCAP_DRV_LOOKUP); | ||||
} | } | ||||
} | } | ||||
return (0); | return (0); | ||||
} | } | ||||
/* | |||||
* Check that ingress address belongs to local host. | |||||
*/ | |||||
static void | static void | ||||
in6_gre_set_running(struct gre_softc *sc) | |||||
{ | |||||
if (in6_localip(&sc->gre_oip6.ip6_src)) | |||||
GRE2IFP(sc)->if_drv_flags |= IFF_DRV_RUNNING; | |||||
else | |||||
GRE2IFP(sc)->if_drv_flags &= ~IFF_DRV_RUNNING; | |||||
} | |||||
/* | |||||
* ifaddr_event handler. | |||||
* Clear IFF_DRV_RUNNING flag when ingress address disappears to prevent | |||||
* source address spoofing. | |||||
*/ | |||||
static void | |||||
in6_gre_srcaddr(void *arg __unused, const struct sockaddr *sa, | |||||
int event __unused) | |||||
{ | |||||
const struct sockaddr_in6 *sin; | |||||
struct gre_softc *sc; | |||||
if (V_ipv6_srchashtbl == NULL) | |||||
return; | |||||
MPASS(in_epoch(net_epoch_preempt)); | |||||
sin = (const struct sockaddr_in6 *)sa; | |||||
CK_LIST_FOREACH(sc, &GRE_SRCHASH(&sin->sin6_addr), srchash) { | |||||
if (IN6_ARE_ADDR_EQUAL(&sc->gre_oip6.ip6_src, | |||||
&sin->sin6_addr) == 0) | |||||
continue; | |||||
in6_gre_set_running(sc); | |||||
} | |||||
} | |||||
static void | |||||
in6_gre_attach(struct gre_softc *sc) | in6_gre_attach(struct gre_softc *sc) | ||||
{ | { | ||||
sc->gre_hlen = sizeof(struct greip6); | sc->gre_hlen = sizeof(struct greip6); | ||||
sc->gre_oip6.ip6_vfc = IPV6_VERSION; | sc->gre_oip6.ip6_vfc = IPV6_VERSION; | ||||
sc->gre_oip6.ip6_nxt = IPPROTO_GRE; | sc->gre_oip6.ip6_nxt = IPPROTO_GRE; | ||||
gre_updatehdr(sc, &sc->gre_gi6hdr->gi6_gre); | gre_updatehdr(sc, &sc->gre_gi6hdr->gi6_gre); | ||||
CK_LIST_INSERT_HEAD(&GRE_HASH_SC(sc), sc, chain); | CK_LIST_INSERT_HEAD(&GRE_HASH_SC(sc), sc, chain); | ||||
CK_LIST_INSERT_HEAD(&GRE_SRCHASH(&sc->gre_oip6.ip6_src), sc, srchash); | |||||
} | } | ||||
void | void | ||||
in6_gre_setopts(struct gre_softc *sc, u_long cmd, uint32_t value) | in6_gre_setopts(struct gre_softc *sc, u_long cmd, uint32_t value) | ||||
{ | { | ||||
MPASS(cmd == GRESKEY || cmd == GRESOPTS); | MPASS(cmd == GRESKEY || cmd == GRESOPTS); | ||||
/* NOTE: we are protected with gre_ioctl_sx lock */ | /* NOTE: we are protected with gre_ioctl_sx lock */ | ||||
MPASS(sc->gre_family == AF_INET6); | MPASS(sc->gre_family == AF_INET6); | ||||
CK_LIST_REMOVE(sc, chain); | CK_LIST_REMOVE(sc, chain); | ||||
CK_LIST_REMOVE(sc, srchash); | |||||
GRE_WAIT(); | GRE_WAIT(); | ||||
if (cmd == GRESKEY) | if (cmd == GRESKEY) | ||||
sc->gre_key = value; | sc->gre_key = value; | ||||
else | else | ||||
sc->gre_options = value; | sc->gre_options = value; | ||||
in6_gre_attach(sc); | in6_gre_attach(sc); | ||||
} | } | ||||
Show All 27 Lines | case SIOCSIFPHYADDR_IN6: | ||||
* Check validity of the scope zone ID of the | * Check validity of the scope zone ID of the | ||||
* addresses, and convert it into the kernel | * addresses, and convert it into the kernel | ||||
* internal form if necessary. | * internal form if necessary. | ||||
*/ | */ | ||||
if ((error = sa6_embedscope(src, 0)) != 0 || | if ((error = sa6_embedscope(src, 0)) != 0 || | ||||
(error = sa6_embedscope(dst, 0)) != 0) | (error = sa6_embedscope(dst, 0)) != 0) | ||||
break; | break; | ||||
if (V_ipv6_hashtbl == NULL) | if (V_ipv6_hashtbl == NULL) { | ||||
V_ipv6_hashtbl = gre_hashinit(); | V_ipv6_hashtbl = gre_hashinit(); | ||||
V_ipv6_srchashtbl = gre_hashinit(); | |||||
} | |||||
error = in6_gre_checkdup(sc, &src->sin6_addr, | error = in6_gre_checkdup(sc, &src->sin6_addr, | ||||
&dst->sin6_addr); | &dst->sin6_addr); | ||||
if (error == EADDRNOTAVAIL) | if (error == EADDRNOTAVAIL) | ||||
break; | break; | ||||
if (error == EEXIST) { | if (error == EEXIST) { | ||||
/* Addresses are the same. Just return. */ | /* Addresses are the same. Just return. */ | ||||
error = 0; | error = 0; | ||||
break; | break; | ||||
} | } | ||||
ip6 = malloc(sizeof(struct greip6) + 3 * sizeof(uint32_t), | ip6 = malloc(sizeof(struct greip6) + 3 * sizeof(uint32_t), | ||||
M_GRE, M_WAITOK | M_ZERO); | M_GRE, M_WAITOK | M_ZERO); | ||||
ip6->ip6_src = src->sin6_addr; | ip6->ip6_src = src->sin6_addr; | ||||
ip6->ip6_dst = dst->sin6_addr; | ip6->ip6_dst = dst->sin6_addr; | ||||
if (sc->gre_family != 0) { | if (sc->gre_family != 0) { | ||||
/* Detach existing tunnel first */ | /* Detach existing tunnel first */ | ||||
CK_LIST_REMOVE(sc, chain); | CK_LIST_REMOVE(sc, chain); | ||||
CK_LIST_REMOVE(sc, srchash); | |||||
GRE_WAIT(); | GRE_WAIT(); | ||||
free(sc->gre_hdr, M_GRE); | free(sc->gre_hdr, M_GRE); | ||||
/* XXX: should we notify about link state change? */ | /* XXX: should we notify about link state change? */ | ||||
} | } | ||||
sc->gre_family = AF_INET6; | sc->gre_family = AF_INET6; | ||||
sc->gre_hdr = ip6; | sc->gre_hdr = ip6; | ||||
sc->gre_oseq = 0; | sc->gre_oseq = 0; | ||||
sc->gre_iseq = UINT32_MAX; | sc->gre_iseq = UINT32_MAX; | ||||
in6_gre_attach(sc); | in6_gre_attach(sc); | ||||
in6_gre_set_running(sc); | |||||
break; | break; | ||||
case SIOCGIFPSRCADDR_IN6: | case SIOCGIFPSRCADDR_IN6: | ||||
case SIOCGIFPDSTADDR_IN6: | case SIOCGIFPDSTADDR_IN6: | ||||
if (sc->gre_family != AF_INET6) { | if (sc->gre_family != AF_INET6) { | ||||
error = EADDRNOTAVAIL; | error = EADDRNOTAVAIL; | ||||
break; | break; | ||||
} | } | ||||
src = (struct sockaddr_in6 *)&ifr->ifr_addr; | src = (struct sockaddr_in6 *)&ifr->ifr_addr; | ||||
Show All 17 Lines | |||||
{ | { | ||||
struct greip6 *gi6; | struct greip6 *gi6; | ||||
gi6 = mtod(m, struct greip6 *); | gi6 = mtod(m, struct greip6 *); | ||||
gi6->gi6_ip6.ip6_hlim = V_ip6_gre_hlim; | gi6->gi6_ip6.ip6_hlim = V_ip6_gre_hlim; | ||||
return (ip6_output(m, NULL, NULL, IPV6_MINMTU, NULL, NULL, NULL)); | return (ip6_output(m, NULL, NULL, IPV6_MINMTU, NULL, NULL, NULL)); | ||||
} | } | ||||
static const struct srcaddrtab *ipv6_srcaddrtab = NULL; | |||||
static const struct encaptab *ecookie = NULL; | static const struct encaptab *ecookie = NULL; | ||||
static const struct encap_config ipv6_encap_cfg = { | static const struct encap_config ipv6_encap_cfg = { | ||||
.proto = IPPROTO_GRE, | .proto = IPPROTO_GRE, | ||||
.min_length = sizeof(struct greip6) + | .min_length = sizeof(struct greip6) + | ||||
#ifdef INET | #ifdef INET | ||||
sizeof(struct ip), | sizeof(struct ip), | ||||
#else | #else | ||||
sizeof(struct ip6_hdr), | sizeof(struct ip6_hdr), | ||||
#endif | #endif | ||||
.exact_match = ENCAP_DRV_LOOKUP, | .exact_match = ENCAP_DRV_LOOKUP, | ||||
.lookup = in6_gre_lookup, | .lookup = in6_gre_lookup, | ||||
.input = gre_input | .input = gre_input | ||||
}; | }; | ||||
void | void | ||||
in6_gre_init(void) | in6_gre_init(void) | ||||
{ | { | ||||
if (!IS_DEFAULT_VNET(curvnet)) | if (!IS_DEFAULT_VNET(curvnet)) | ||||
return; | return; | ||||
ipv6_srcaddrtab = ip6_encap_register_srcaddr(in6_gre_srcaddr, | |||||
NULL, M_WAITOK); | |||||
ecookie = ip6_encap_attach(&ipv6_encap_cfg, NULL, M_WAITOK); | ecookie = ip6_encap_attach(&ipv6_encap_cfg, NULL, M_WAITOK); | ||||
} | } | ||||
void | void | ||||
in6_gre_uninit(void) | in6_gre_uninit(void) | ||||
{ | { | ||||
if (IS_DEFAULT_VNET(curvnet)) | if (IS_DEFAULT_VNET(curvnet)) { | ||||
ip6_encap_detach(ecookie); | ip6_encap_detach(ecookie); | ||||
if (V_ipv6_hashtbl != NULL) | ip6_encap_unregister_srcaddr(ipv6_srcaddrtab); | ||||
} | |||||
if (V_ipv6_hashtbl != NULL) { | |||||
gre_hashdestroy(V_ipv6_hashtbl); | gre_hashdestroy(V_ipv6_hashtbl); | ||||
gre_hashdestroy(V_ipv6_srchashtbl); | |||||
} | |||||
} | } |