svn commit: r340535 - in stable/12: share/man/man4 sys/net sys/netinet sys/netinet6
Andrey V. Elsukov
ae at FreeBSD.org
Sun Nov 18 00:17:09 UTC 2018
Author: ae
Date: Sun Nov 18 00:17:06 2018
New Revision: 340535
URL: https://svnweb.freebsd.org/changeset/base/340535
Log:
MFC r339551:
Add handling for appearing/disappearing of ingress addresses to if_gif(4).
* register handler for ingress address appearing/disappearing;
* add new srcaddr hash table for fast softc lookup by srcaddr;
* when srcaddr disappears, clear IFF_DRV_RUNNING flag from interface,
and set it otherwise;
* remove the note about ingress address from BUGS section.
Sponsored by: Yandex LLC
Differential Revision: https://reviews.freebsd.org/D17134
MFC r339552:
Add handling for appearing/disappearing of ingress addresses to if_gre(4).
* register handler for ingress address appearing/disappearing;
* add new srcaddr hash table for fast softc lookup by srcaddr;
* when srcaddr disappears, clear IFF_DRV_RUNNING flag from interface,
and set it otherwise;
Sponsored by: Yandex LLC
Differential Revision: https://reviews.freebsd.org/D17214
MFC r339553:
Add handling for appearing/disappearing of ingress addresses to if_me(4).
* register handler for ingress address appearing/disappearing;
* add new srcaddr hash table for fast softc lookup by srcaddr;
* when srcaddr disappears, clear IFF_DRV_RUNNING flag from interface,
and set it otherwise;
Sponsored by: Yandex LLC
MFC r339649:
Add the check that current VNET is ready and access to srchash is allowed.
This change is similar to r339646. The callback that checks for appearing
and disappearing of tunnel ingress address can be called during VNET
teardown. To prevent access to already freed memory, add check to the
callback and epoch_wait() call to be sure that callback has finished its
work.
Modified:
stable/12/share/man/man4/gif.4
stable/12/sys/net/if_gif.c
stable/12/sys/net/if_gif.h
stable/12/sys/net/if_gre.c
stable/12/sys/net/if_gre.h
stable/12/sys/net/if_me.c
stable/12/sys/netinet/in_gif.c
stable/12/sys/netinet/ip_gre.c
stable/12/sys/netinet6/in6_gif.c
stable/12/sys/netinet6/ip6_gre.c
Directory Properties:
stable/12/ (props changed)
Modified: stable/12/share/man/man4/gif.4
==============================================================================
--- stable/12/share/man/man4/gif.4 Sun Nov 18 00:11:19 2018 (r340534)
+++ stable/12/share/man/man4/gif.4 Sun Nov 18 00:17:06 2018 (r340535)
@@ -29,7 +29,7 @@
.\"
.\" $FreeBSD$
.\"
-.Dd June 5, 2018
+.Dd October 21, 2018
.Dt GIF 4
.Os
.Sh NAME
@@ -206,15 +206,6 @@ and are picky about outer header fields.
For example, you cannot usually use
.Nm
to talk with IPsec devices that use IPsec tunnel mode.
-.Pp
-The current code does not check if the ingress address
-(outer source address)
-configured in the
-.Nm
-interface makes sense.
-Make sure to specify an address which belongs to your node.
-Otherwise, your node will not be able to receive packets from the peer,
-and it will generate packets with a spoofed source address.
.Pp
If the outer protocol is IPv4,
.Nm
Modified: stable/12/sys/net/if_gif.c
==============================================================================
--- stable/12/sys/net/if_gif.c Sun Nov 18 00:11:19 2018 (r340534)
+++ stable/12/sys/net/if_gif.c Sun Nov 18 00:17:06 2018 (r340535)
@@ -284,6 +284,7 @@ gif_transmit(struct ifnet *ifp, struct mbuf *m)
sc = ifp->if_softc;
if ((ifp->if_flags & IFF_MONITOR) != 0 ||
(ifp->if_flags & IFF_UP) == 0 ||
+ (ifp->if_drv_flags & IFF_DRV_RUNNING) == 0 ||
sc->gif_family == 0 ||
(error = if_tunnel_check_nesting(ifp, m, MTAG_GIF,
V_max_gif_nesting)) != 0) {
@@ -674,7 +675,6 @@ gif_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data)
cmd == SIOCSIFPHYADDR_IN6 ||
#endif
0) {
- ifp->if_drv_flags |= IFF_DRV_RUNNING;
if_link_state_change(ifp, LINK_STATE_UP);
}
}
@@ -689,6 +689,7 @@ gif_delete_tunnel(struct gif_softc *sc)
sx_assert(&gif_ioctl_sx, SA_XLOCKED);
if (sc->gif_family != 0) {
+ CK_LIST_REMOVE(sc, srchash);
CK_LIST_REMOVE(sc, chain);
/* Wait until it become safe to free gif_hdr */
GIF_WAIT();
Modified: stable/12/sys/net/if_gif.h
==============================================================================
--- stable/12/sys/net/if_gif.h Sun Nov 18 00:11:19 2018 (r340534)
+++ stable/12/sys/net/if_gif.h Sun Nov 18 00:17:06 2018 (r340535)
@@ -63,6 +63,7 @@ struct gif_softc {
} gif_uhdr;
CK_LIST_ENTRY(gif_softc) chain;
+ CK_LIST_ENTRY(gif_softc) srchash;
};
CK_LIST_HEAD(gif_list, gif_softc);
MALLOC_DECLARE(M_GIF);
Modified: stable/12/sys/net/if_gre.c
==============================================================================
--- stable/12/sys/net/if_gre.c Sun Nov 18 00:11:19 2018 (r340534)
+++ stable/12/sys/net/if_gre.c Sun Nov 18 00:17:06 2018 (r340535)
@@ -326,7 +326,6 @@ gre_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data)
cmd == SIOCSIFPHYADDR_IN6 ||
#endif
0) {
- ifp->if_drv_flags |= IFF_DRV_RUNNING;
if_link_state_change(ifp, LINK_STATE_UP);
}
}
@@ -342,6 +341,7 @@ gre_delete_tunnel(struct gre_softc *sc)
sx_assert(&gre_ioctl_sx, SA_XLOCKED);
if (sc->gre_family != 0) {
CK_LIST_REMOVE(sc, chain);
+ CK_LIST_REMOVE(sc, srchash);
GRE_WAIT();
free(sc->gre_hdr, M_GRE);
sc->gre_family = 0;
@@ -543,6 +543,7 @@ gre_setseqn(struct grehdr *gh, uint32_t seq)
static int
gre_transmit(struct ifnet *ifp, struct mbuf *m)
{
+ GRE_RLOCK_TRACKER;
struct gre_softc *sc;
struct grehdr *gh;
uint32_t af;
@@ -562,6 +563,7 @@ gre_transmit(struct ifnet *ifp, struct mbuf *m)
sc = ifp->if_softc;
if ((ifp->if_flags & IFF_MONITOR) != 0 ||
(ifp->if_flags & IFF_UP) == 0 ||
+ (ifp->if_drv_flags & IFF_DRV_RUNNING) == 0 ||
sc->gre_family == 0 ||
(error = if_tunnel_check_nesting(ifp, m, MTAG_GRE,
V_max_gre_nesting)) != 0) {
Modified: stable/12/sys/net/if_gre.h
==============================================================================
--- stable/12/sys/net/if_gre.h Sun Nov 18 00:11:19 2018 (r340534)
+++ stable/12/sys/net/if_gre.h Sun Nov 18 00:17:06 2018 (r340535)
@@ -82,6 +82,7 @@ struct gre_softc {
} gre_uhdr;
CK_LIST_ENTRY(gre_softc) chain;
+ CK_LIST_ENTRY(gre_softc) srchash;
};
CK_LIST_HEAD(gre_list, gre_softc);
MALLOC_DECLARE(M_GRE);
@@ -91,7 +92,8 @@ MALLOC_DECLARE(M_GRE);
#endif
#define GRE2IFP(sc) ((sc)->gre_ifp)
-#define GRE_RLOCK() struct epoch_tracker gre_et; epoch_enter_preempt(net_epoch_preempt, &gre_et)
+#define GRE_RLOCK_TRACKER struct epoch_tracker gre_et
+#define GRE_RLOCK() epoch_enter_preempt(net_epoch_preempt, &gre_et)
#define GRE_RUNLOCK() epoch_exit_preempt(net_epoch_preempt, &gre_et)
#define GRE_WAIT() epoch_wait_preempt(net_epoch_preempt)
Modified: stable/12/sys/net/if_me.c
==============================================================================
--- stable/12/sys/net/if_me.c Sun Nov 18 00:11:19 2018 (r340534)
+++ stable/12/sys/net/if_me.c Sun Nov 18 00:17:06 2018 (r340535)
@@ -83,11 +83,13 @@ struct me_softc {
struct in_addr me_dst;
CK_LIST_ENTRY(me_softc) chain;
+ CK_LIST_ENTRY(me_softc) srchash;
};
CK_LIST_HEAD(me_list, me_softc);
#define ME2IFP(sc) ((sc)->me_ifp)
#define ME_READY(sc) ((sc)->me_src.s_addr != 0)
-#define ME_RLOCK() struct epoch_tracker me_et; epoch_enter_preempt(net_epoch_preempt, &me_et)
+#define ME_RLOCK_TRACKER struct epoch_tracker me_et
+#define ME_RLOCK() epoch_enter_preempt(net_epoch_preempt, &me_et)
#define ME_RUNLOCK() epoch_exit_preempt(net_epoch_preempt, &me_et)
#define ME_WAIT() epoch_wait_preempt(net_epoch_preempt)
@@ -95,9 +97,13 @@ CK_LIST_HEAD(me_list, me_softc);
#define ME_HASH_SIZE (1 << 4)
#endif
VNET_DEFINE_STATIC(struct me_list *, me_hashtbl) = NULL;
+VNET_DEFINE_STATIC(struct me_list *, me_srchashtbl) = NULL;
#define V_me_hashtbl VNET(me_hashtbl)
+#define V_me_srchashtbl VNET(me_srchashtbl)
#define ME_HASH(src, dst) (V_me_hashtbl[\
me_hashval((src), (dst)) & (ME_HASH_SIZE - 1)])
+#define ME_SRCHASH(src) (V_me_srchashtbl[\
+ fnv_32_buf(&(src), sizeof(src), FNV1_32_INIT) & (ME_HASH_SIZE - 1)])
static struct sx me_ioctl_sx;
SX_SYSINIT(me_ioctl_sx, &me_ioctl_sx, "me_ioctl");
@@ -155,6 +161,7 @@ me_hashinit(void)
static void
vnet_me_init(const void *unused __unused)
{
+
V_me_cloner = if_clone_simple(mename, me_clone_create,
me_clone_destroy, 0);
}
@@ -165,8 +172,12 @@ static void
vnet_me_uninit(const void *unused __unused)
{
- if (V_me_hashtbl != NULL)
+ if (V_me_hashtbl != NULL) {
free(V_me_hashtbl, M_IFME);
+ V_me_hashtbl = NULL;
+ ME_WAIT();
+ free(V_me_srchashtbl, M_IFME);
+ }
if_clone_detach(V_me_cloner);
}
VNET_SYSUNINIT(vnet_me_uninit, SI_SUB_PROTO_IFATTACHDOMAIN, SI_ORDER_ANY,
@@ -330,6 +341,44 @@ me_lookup(const struct mbuf *m, int off, int proto, vo
return (0);
}
+/*
+ * Check that ingress address belongs to local host.
+ */
+static void
+me_set_running(struct me_softc *sc)
+{
+
+ if (in_localip(sc->me_src))
+ ME2IFP(sc)->if_drv_flags |= IFF_DRV_RUNNING;
+ else
+ ME2IFP(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
+me_srcaddr(void *arg __unused, const struct sockaddr *sa,
+ int event __unused)
+{
+ const struct sockaddr_in *sin;
+ struct me_softc *sc;
+
+ /* Check that VNET is ready */
+ if (V_me_hashtbl == NULL)
+ return;
+
+ MPASS(in_epoch(net_epoch_preempt));
+ sin = (const struct sockaddr_in *)sa;
+ CK_LIST_FOREACH(sc, &ME_SRCHASH(sin->sin_addr.s_addr), srchash) {
+ if (sc->me_src.s_addr != sin->sin_addr.s_addr)
+ continue;
+ me_set_running(sc);
+ }
+}
+
static int
me_set_tunnel(struct me_softc *sc, in_addr_t src, in_addr_t dst)
{
@@ -337,8 +386,10 @@ me_set_tunnel(struct me_softc *sc, in_addr_t src, in_a
sx_assert(&me_ioctl_sx, SA_XLOCKED);
- if (V_me_hashtbl == NULL)
+ if (V_me_hashtbl == NULL) {
V_me_hashtbl = me_hashinit();
+ V_me_srchashtbl = me_hashinit();
+ }
if (sc->me_src.s_addr == src && sc->me_dst.s_addr == dst)
return (0);
@@ -355,8 +406,9 @@ me_set_tunnel(struct me_softc *sc, in_addr_t src, in_a
sc->me_dst.s_addr = dst;
sc->me_src.s_addr = src;
CK_LIST_INSERT_HEAD(&ME_HASH(src, dst), sc, chain);
+ CK_LIST_INSERT_HEAD(&ME_SRCHASH(src), sc, srchash);
- ME2IFP(sc)->if_drv_flags |= IFF_DRV_RUNNING;
+ me_set_running(sc);
if_link_state_change(ME2IFP(sc), LINK_STATE_UP);
return (0);
}
@@ -368,6 +420,7 @@ me_delete_tunnel(struct me_softc *sc)
sx_assert(&me_ioctl_sx, SA_XLOCKED);
if (ME_READY(sc)) {
CK_LIST_REMOVE(sc, chain);
+ CK_LIST_REMOVE(sc, srchash);
ME_WAIT();
sc->me_src.s_addr = 0;
@@ -473,6 +526,7 @@ me_output(struct ifnet *ifp, struct mbuf *m, const str
static int
me_transmit(struct ifnet *ifp, struct mbuf *m)
{
+ ME_RLOCK_TRACKER;
struct mobhdr mh;
struct me_softc *sc;
struct ip *ip;
@@ -490,6 +544,7 @@ me_transmit(struct ifnet *ifp, struct mbuf *m)
if (sc == NULL || !ME_READY(sc) ||
(ifp->if_flags & IFF_MONITOR) != 0 ||
(ifp->if_flags & IFF_UP) == 0 ||
+ (ifp->if_drv_flags & IFF_DRV_RUNNING) == 0 ||
(error = if_tunnel_check_nesting(ifp, m, MTAG_ME,
V_max_me_nesting)) != 0) {
m_freem(m);
@@ -567,6 +622,7 @@ me_qflush(struct ifnet *ifp __unused)
}
+static const struct srcaddrtab *me_srcaddrtab = NULL;
static const struct encaptab *ecookie = NULL;
static const struct encap_config me_encap_cfg = {
.proto = IPPROTO_MOBILE,
@@ -583,10 +639,13 @@ memodevent(module_t mod, int type, void *data)
switch (type) {
case MOD_LOAD:
+ me_srcaddrtab = ip_encap_register_srcaddr(me_srcaddr,
+ NULL, M_WAITOK);
ecookie = ip_encap_attach(&me_encap_cfg, NULL, M_WAITOK);
break;
case MOD_UNLOAD:
ip_encap_detach(ecookie);
+ ip_encap_unregister_srcaddr(me_srcaddrtab);
break;
default:
return (EOPNOTSUPP);
Modified: stable/12/sys/netinet/in_gif.c
==============================================================================
--- stable/12/sys/netinet/in_gif.c Sun Nov 18 00:11:19 2018 (r340534)
+++ stable/12/sys/netinet/in_gif.c Sun Nov 18 00:17:06 2018 (r340535)
@@ -82,12 +82,16 @@ SYSCTL_INT(_net_inet_ip, IPCTL_GIF_TTL, gifttl, CTLFLA
* Interfaces with GIF_IGNORE_SOURCE flag are linked into plain list.
*/
VNET_DEFINE_STATIC(struct gif_list *, ipv4_hashtbl) = NULL;
+VNET_DEFINE_STATIC(struct gif_list *, ipv4_srchashtbl) = NULL;
VNET_DEFINE_STATIC(struct gif_list, ipv4_list) = CK_LIST_HEAD_INITIALIZER();
#define V_ipv4_hashtbl VNET(ipv4_hashtbl)
+#define V_ipv4_srchashtbl VNET(ipv4_srchashtbl)
#define V_ipv4_list VNET(ipv4_list)
#define GIF_HASH(src, dst) (V_ipv4_hashtbl[\
in_gif_hashval((src), (dst)) & (GIF_HASH_SIZE - 1)])
+#define GIF_SRCHASH(src) (V_ipv4_srchashtbl[\
+ fnv_32_buf(&(src), sizeof(src), FNV1_32_INIT) & (GIF_HASH_SIZE - 1)])
#define GIF_HASH_SC(sc) GIF_HASH((sc)->gif_iphdr->ip_src.s_addr,\
(sc)->gif_iphdr->ip_dst.s_addr)
static uint32_t
@@ -119,7 +123,45 @@ in_gif_checkdup(const struct gif_softc *sc, in_addr_t
return (0);
}
+/*
+ * Check that ingress address belongs to local host.
+ */
static void
+in_gif_set_running(struct gif_softc *sc)
+{
+
+ if (in_localip(sc->gif_iphdr->ip_src))
+ GIF2IFP(sc)->if_drv_flags |= IFF_DRV_RUNNING;
+ else
+ GIF2IFP(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
+in_gif_srcaddr(void *arg __unused, const struct sockaddr *sa,
+ int event __unused)
+{
+ const struct sockaddr_in *sin;
+ struct gif_softc *sc;
+
+ /* Check that VNET is ready */
+ if (V_ipv4_hashtbl == NULL)
+ return;
+
+ MPASS(in_epoch(net_epoch_preempt));
+ sin = (const struct sockaddr_in *)sa;
+ CK_LIST_FOREACH(sc, &GIF_SRCHASH(sin->sin_addr.s_addr), srchash) {
+ if (sc->gif_iphdr->ip_src.s_addr != sin->sin_addr.s_addr)
+ continue;
+ in_gif_set_running(sc);
+ }
+}
+
+static void
in_gif_attach(struct gif_softc *sc)
{
@@ -127,6 +169,9 @@ in_gif_attach(struct gif_softc *sc)
CK_LIST_INSERT_HEAD(&V_ipv4_list, sc, chain);
else
CK_LIST_INSERT_HEAD(&GIF_HASH_SC(sc), sc, chain);
+
+ CK_LIST_INSERT_HEAD(&GIF_SRCHASH(sc->gif_iphdr->ip_src.s_addr),
+ sc, srchash);
}
int
@@ -139,6 +184,7 @@ in_gif_setopts(struct gif_softc *sc, u_int options)
if ((options & GIF_IGNORE_SOURCE) !=
(sc->gif_options & GIF_IGNORE_SOURCE)) {
+ CK_LIST_REMOVE(sc, srchash);
CK_LIST_REMOVE(sc, chain);
sc->gif_options = options;
in_gif_attach(sc);
@@ -172,8 +218,10 @@ in_gif_ioctl(struct gif_softc *sc, u_long cmd, caddr_t
error = EADDRNOTAVAIL;
break;
}
- if (V_ipv4_hashtbl == NULL)
+ if (V_ipv4_hashtbl == NULL) {
V_ipv4_hashtbl = gif_hashinit();
+ V_ipv4_srchashtbl = gif_hashinit();
+ }
error = in_gif_checkdup(sc, src->sin_addr.s_addr,
dst->sin_addr.s_addr);
if (error == EADDRNOTAVAIL)
@@ -188,6 +236,7 @@ in_gif_ioctl(struct gif_softc *sc, u_long cmd, caddr_t
ip->ip_dst.s_addr = dst->sin_addr.s_addr;
if (sc->gif_family != 0) {
/* Detach existing tunnel first */
+ CK_LIST_REMOVE(sc, srchash);
CK_LIST_REMOVE(sc, chain);
GIF_WAIT();
free(sc->gif_hdr, M_GIF);
@@ -196,6 +245,7 @@ in_gif_ioctl(struct gif_softc *sc, u_long cmd, caddr_t
sc->gif_family = AF_INET;
sc->gif_iphdr = ip;
in_gif_attach(sc);
+ in_gif_set_running(sc);
break;
case SIOCGIFPSRCADDR:
case SIOCGIFPDSTADDR:
@@ -342,6 +392,7 @@ done:
return (ret);
}
+static const struct srcaddrtab *ipv4_srcaddrtab;
static struct {
const struct encap_config encap;
const struct encaptab *cookie;
@@ -387,6 +438,9 @@ in_gif_init(void)
if (!IS_DEFAULT_VNET(curvnet))
return;
+
+ ipv4_srcaddrtab = ip_encap_register_srcaddr(in_gif_srcaddr,
+ NULL, M_WAITOK);
for (i = 0; i < nitems(ipv4_encap_cfg); i++)
ipv4_encap_cfg[i].cookie = ip_encap_attach(
&ipv4_encap_cfg[i].encap, NULL, M_WAITOK);
@@ -400,8 +454,13 @@ in_gif_uninit(void)
if (IS_DEFAULT_VNET(curvnet)) {
for (i = 0; i < nitems(ipv4_encap_cfg); i++)
ip_encap_detach(ipv4_encap_cfg[i].cookie);
+ ip_encap_unregister_srcaddr(ipv4_srcaddrtab);
}
- if (V_ipv4_hashtbl != NULL)
+ if (V_ipv4_hashtbl != NULL) {
gif_hashdestroy(V_ipv4_hashtbl);
+ V_ipv4_hashtbl = NULL;
+ GIF_WAIT();
+ gif_hashdestroy(V_ipv4_srchashtbl);
+ }
}
Modified: stable/12/sys/netinet/ip_gre.c
==============================================================================
--- stable/12/sys/netinet/ip_gre.c Sun Nov 18 00:11:19 2018 (r340534)
+++ stable/12/sys/netinet/ip_gre.c Sun Nov 18 00:17:06 2018 (r340535)
@@ -75,9 +75,13 @@ SYSCTL_INT(_net_inet_ip, OID_AUTO, grettl, CTLFLAG_VNE
&VNET_NAME(ip_gre_ttl), 0, "Default TTL value for encapsulated packets");
VNET_DEFINE_STATIC(struct gre_list *, ipv4_hashtbl) = NULL;
+VNET_DEFINE_STATIC(struct gre_list *, ipv4_srchashtbl) = NULL;
#define V_ipv4_hashtbl VNET(ipv4_hashtbl)
+#define V_ipv4_srchashtbl VNET(ipv4_srchashtbl)
#define GRE_HASH(src, dst) (V_ipv4_hashtbl[\
in_gre_hashval((src), (dst)) & (GRE_HASH_SIZE - 1)])
+#define GRE_SRCHASH(src) (V_ipv4_srchashtbl[\
+ fnv_32_buf(&(src), sizeof(src), FNV1_32_INIT) & (GRE_HASH_SIZE - 1)])
#define GRE_HASH_SC(sc) GRE_HASH((sc)->gre_oip.ip_src.s_addr,\
(sc)->gre_oip.ip_dst.s_addr)
@@ -138,7 +142,45 @@ in_gre_lookup(const struct mbuf *m, int off, int proto
return (0);
}
+/*
+ * Check that ingress address belongs to local host.
+ */
static void
+in_gre_set_running(struct gre_softc *sc)
+{
+
+ if (in_localip(sc->gre_oip.ip_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
+in_gre_srcaddr(void *arg __unused, const struct sockaddr *sa,
+ int event __unused)
+{
+ const struct sockaddr_in *sin;
+ struct gre_softc *sc;
+
+ /* Check that VNET is ready */
+ if (V_ipv4_hashtbl == NULL)
+ return;
+
+ MPASS(in_epoch(net_epoch_preempt));
+ sin = (const struct sockaddr_in *)sa;
+ CK_LIST_FOREACH(sc, &GRE_SRCHASH(sin->sin_addr.s_addr), srchash) {
+ if (sc->gre_oip.ip_src.s_addr != sin->sin_addr.s_addr)
+ continue;
+ in_gre_set_running(sc);
+ }
+}
+
+static void
in_gre_attach(struct gre_softc *sc)
{
@@ -148,6 +190,8 @@ in_gre_attach(struct gre_softc *sc)
sc->gre_oip.ip_p = IPPROTO_GRE;
gre_updatehdr(sc, &sc->gre_gihdr->gi_gre);
CK_LIST_INSERT_HEAD(&GRE_HASH_SC(sc), sc, chain);
+ CK_LIST_INSERT_HEAD(&GRE_SRCHASH(sc->gre_oip.ip_src.s_addr),
+ sc, srchash);
}
void
@@ -159,6 +203,7 @@ in_gre_setopts(struct gre_softc *sc, u_long cmd, uint3
/* NOTE: we are protected with gre_ioctl_sx lock */
MPASS(sc->gre_family == AF_INET);
CK_LIST_REMOVE(sc, chain);
+ CK_LIST_REMOVE(sc, srchash);
GRE_WAIT();
if (cmd == GRESKEY)
sc->gre_key = value;
@@ -193,8 +238,10 @@ in_gre_ioctl(struct gre_softc *sc, u_long cmd, caddr_t
error = EADDRNOTAVAIL;
break;
}
- if (V_ipv4_hashtbl == NULL)
+ if (V_ipv4_hashtbl == NULL) {
V_ipv4_hashtbl = gre_hashinit();
+ V_ipv4_srchashtbl = gre_hashinit();
+ }
error = in_gre_checkdup(sc, src->sin_addr.s_addr,
dst->sin_addr.s_addr);
if (error == EADDRNOTAVAIL)
@@ -211,6 +258,7 @@ in_gre_ioctl(struct gre_softc *sc, u_long cmd, caddr_t
if (sc->gre_family != 0) {
/* Detach existing tunnel first */
CK_LIST_REMOVE(sc, chain);
+ CK_LIST_REMOVE(sc, srchash);
GRE_WAIT();
free(sc->gre_hdr, M_GRE);
/* XXX: should we notify about link state change? */
@@ -220,6 +268,7 @@ in_gre_ioctl(struct gre_softc *sc, u_long cmd, caddr_t
sc->gre_oseq = 0;
sc->gre_iseq = UINT32_MAX;
in_gre_attach(sc);
+ in_gre_set_running(sc);
break;
case SIOCGIFPSRCADDR:
case SIOCGIFPDSTADDR:
@@ -271,6 +320,7 @@ in_gre_output(struct mbuf *m, int af, int hlen)
return (ip_output(m, NULL, NULL, IP_FORWARDING, NULL, NULL));
}
+static const struct srcaddrtab *ipv4_srcaddrtab = NULL;
static const struct encaptab *ecookie = NULL;
static const struct encap_config ipv4_encap_cfg = {
.proto = IPPROTO_GRE,
@@ -286,6 +336,8 @@ in_gre_init(void)
if (!IS_DEFAULT_VNET(curvnet))
return;
+ ipv4_srcaddrtab = ip_encap_register_srcaddr(in_gre_srcaddr,
+ NULL, M_WAITOK);
ecookie = ip_encap_attach(&ipv4_encap_cfg, NULL, M_WAITOK);
}
@@ -293,8 +345,14 @@ void
in_gre_uninit(void)
{
- if (IS_DEFAULT_VNET(curvnet))
+ if (IS_DEFAULT_VNET(curvnet)) {
ip_encap_detach(ecookie);
- if (V_ipv4_hashtbl != NULL)
+ ip_encap_unregister_srcaddr(ipv4_srcaddrtab);
+ }
+ if (V_ipv4_hashtbl != NULL) {
gre_hashdestroy(V_ipv4_hashtbl);
+ V_ipv4_hashtbl = NULL;
+ GRE_WAIT();
+ gre_hashdestroy(V_ipv4_srchashtbl);
+ }
}
Modified: stable/12/sys/netinet6/in6_gif.c
==============================================================================
--- stable/12/sys/netinet6/in6_gif.c Sun Nov 18 00:11:19 2018 (r340534)
+++ stable/12/sys/netinet6/in6_gif.c Sun Nov 18 00:17:06 2018 (r340535)
@@ -87,12 +87,16 @@ SYSCTL_INT(_net_inet6_ip6, IPV6CTL_GIF_HLIM, gifhlim,
* Interfaces with GIF_IGNORE_SOURCE flag are linked into plain list.
*/
VNET_DEFINE_STATIC(struct gif_list *, ipv6_hashtbl) = NULL;
+VNET_DEFINE_STATIC(struct gif_list *, ipv6_srchashtbl) = NULL;
VNET_DEFINE_STATIC(struct gif_list, ipv6_list) = CK_LIST_HEAD_INITIALIZER();
#define V_ipv6_hashtbl VNET(ipv6_hashtbl)
+#define V_ipv6_srchashtbl VNET(ipv6_srchashtbl)
#define V_ipv6_list VNET(ipv6_list)
#define GIF_HASH(src, dst) (V_ipv6_hashtbl[\
in6_gif_hashval((src), (dst)) & (GIF_HASH_SIZE - 1)])
+#define GIF_SRCHASH(src) (V_ipv6_srchashtbl[\
+ fnv_32_buf((src), sizeof(*src), FNV1_32_INIT) & (GIF_HASH_SIZE - 1)])
#define GIF_HASH_SC(sc) GIF_HASH(&(sc)->gif_ip6hdr->ip6_src,\
&(sc)->gif_ip6hdr->ip6_dst)
static uint32_t
@@ -125,7 +129,45 @@ in6_gif_checkdup(const struct gif_softc *sc, const str
return (0);
}
+/*
+ * Check that ingress address belongs to local host.
+ */
static void
+in6_gif_set_running(struct gif_softc *sc)
+{
+
+ if (in6_localip(&sc->gif_ip6hdr->ip6_src))
+ GIF2IFP(sc)->if_drv_flags |= IFF_DRV_RUNNING;
+ else
+ GIF2IFP(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_gif_srcaddr(void *arg __unused, const struct sockaddr *sa, int event)
+{
+ const struct sockaddr_in6 *sin;
+ struct gif_softc *sc;
+
+ /* Check that VNET is ready */
+ if (V_ipv6_hashtbl == NULL)
+ return;
+
+ MPASS(in_epoch(net_epoch_preempt));
+ sin = (const struct sockaddr_in6 *)sa;
+ CK_LIST_FOREACH(sc, &GIF_SRCHASH(&sin->sin6_addr), srchash) {
+ if (IN6_ARE_ADDR_EQUAL(&sc->gif_ip6hdr->ip6_src,
+ &sin->sin6_addr) == 0)
+ continue;
+ in6_gif_set_running(sc);
+ }
+}
+
+static void
in6_gif_attach(struct gif_softc *sc)
{
@@ -133,6 +175,9 @@ in6_gif_attach(struct gif_softc *sc)
CK_LIST_INSERT_HEAD(&V_ipv6_list, sc, chain);
else
CK_LIST_INSERT_HEAD(&GIF_HASH_SC(sc), sc, chain);
+
+ CK_LIST_INSERT_HEAD(&GIF_SRCHASH(&sc->gif_ip6hdr->ip6_src),
+ sc, srchash);
}
int
@@ -145,6 +190,7 @@ in6_gif_setopts(struct gif_softc *sc, u_int options)
if ((options & GIF_IGNORE_SOURCE) !=
(sc->gif_options & GIF_IGNORE_SOURCE)) {
+ CK_LIST_REMOVE(sc, srchash);
CK_LIST_REMOVE(sc, chain);
sc->gif_options = options;
in6_gif_attach(sc);
@@ -187,8 +233,10 @@ in6_gif_ioctl(struct gif_softc *sc, u_long cmd, caddr_
(error = sa6_embedscope(dst, 0)) != 0)
break;
- if (V_ipv6_hashtbl == NULL)
+ if (V_ipv6_hashtbl == NULL) {
V_ipv6_hashtbl = gif_hashinit();
+ V_ipv6_srchashtbl = gif_hashinit();
+ }
error = in6_gif_checkdup(sc, &src->sin6_addr,
&dst->sin6_addr);
if (error == EADDRNOTAVAIL)
@@ -204,6 +252,7 @@ in6_gif_ioctl(struct gif_softc *sc, u_long cmd, caddr_
ip6->ip6_vfc = IPV6_VERSION;
if (sc->gif_family != 0) {
/* Detach existing tunnel first */
+ CK_LIST_REMOVE(sc, srchash);
CK_LIST_REMOVE(sc, chain);
GIF_WAIT();
free(sc->gif_hdr, M_GIF);
@@ -212,6 +261,7 @@ in6_gif_ioctl(struct gif_softc *sc, u_long cmd, caddr_
sc->gif_family = AF_INET6;
sc->gif_ip6hdr = ip6;
in6_gif_attach(sc);
+ in6_gif_set_running(sc);
break;
case SIOCGIFPSRCADDR_IN6:
case SIOCGIFPDSTADDR_IN6:
@@ -365,6 +415,7 @@ done:
return (ret);
}
+static const struct srcaddrtab *ipv6_srcaddrtab;
static struct {
const struct encap_config encap;
const struct encaptab *cookie;
@@ -410,6 +461,9 @@ in6_gif_init(void)
if (!IS_DEFAULT_VNET(curvnet))
return;
+
+ ipv6_srcaddrtab = ip6_encap_register_srcaddr(in6_gif_srcaddr,
+ NULL, M_WAITOK);
for (i = 0; i < nitems(ipv6_encap_cfg); i++)
ipv6_encap_cfg[i].cookie = ip6_encap_attach(
&ipv6_encap_cfg[i].encap, NULL, M_WAITOK);
@@ -423,7 +477,12 @@ in6_gif_uninit(void)
if (IS_DEFAULT_VNET(curvnet)) {
for (i = 0; i < nitems(ipv6_encap_cfg); i++)
ip6_encap_detach(ipv6_encap_cfg[i].cookie);
+ ip6_encap_unregister_srcaddr(ipv6_srcaddrtab);
}
- if (V_ipv6_hashtbl != NULL)
+ if (V_ipv6_hashtbl != NULL) {
gif_hashdestroy(V_ipv6_hashtbl);
+ V_ipv6_hashtbl = NULL;
+ GIF_WAIT();
+ gif_hashdestroy(V_ipv6_srchashtbl);
+ }
}
Modified: stable/12/sys/netinet6/ip6_gre.c
==============================================================================
--- stable/12/sys/netinet6/ip6_gre.c Sun Nov 18 00:11:19 2018 (r340534)
+++ stable/12/sys/netinet6/ip6_gre.c Sun Nov 18 00:17:06 2018 (r340535)
@@ -66,9 +66,13 @@ SYSCTL_INT(_net_inet6_ip6, OID_AUTO, grehlim, CTLFLAG_
&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_srchashtbl) = NULL;
#define V_ipv6_hashtbl VNET(ipv6_hashtbl)
+#define V_ipv6_srchashtbl VNET(ipv6_srchashtbl)
#define GRE_HASH(src, dst) (V_ipv6_hashtbl[\
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,\
&(sc)->gre_oip6.ip6_dst)
@@ -131,7 +135,46 @@ in6_gre_lookup(const struct mbuf *m, int off, int prot
return (0);
}
+/*
+ * Check that ingress address belongs to local host.
+ */
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;
+
+ /* Check that VNET is ready */
+ if (V_ipv6_hashtbl == 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)
{
@@ -140,6 +183,7 @@ in6_gre_attach(struct gre_softc *sc)
sc->gre_oip6.ip6_nxt = IPPROTO_GRE;
gre_updatehdr(sc, &sc->gre_gi6hdr->gi6_gre);
CK_LIST_INSERT_HEAD(&GRE_HASH_SC(sc), sc, chain);
+ CK_LIST_INSERT_HEAD(&GRE_SRCHASH(&sc->gre_oip6.ip6_src), sc, srchash);
}
void
@@ -151,6 +195,7 @@ in6_gre_setopts(struct gre_softc *sc, u_long cmd, uint
/* NOTE: we are protected with gre_ioctl_sx lock */
MPASS(sc->gre_family == AF_INET6);
CK_LIST_REMOVE(sc, chain);
+ CK_LIST_REMOVE(sc, srchash);
GRE_WAIT();
if (cmd == GRESKEY)
sc->gre_key = value;
@@ -194,8 +239,10 @@ in6_gre_ioctl(struct gre_softc *sc, u_long cmd, caddr_
(error = sa6_embedscope(dst, 0)) != 0)
break;
- if (V_ipv6_hashtbl == NULL)
+ if (V_ipv6_hashtbl == NULL) {
V_ipv6_hashtbl = gre_hashinit();
+ V_ipv6_srchashtbl = gre_hashinit();
+ }
error = in6_gre_checkdup(sc, &src->sin6_addr,
&dst->sin6_addr);
if (error == EADDRNOTAVAIL)
@@ -212,6 +259,7 @@ in6_gre_ioctl(struct gre_softc *sc, u_long cmd, caddr_
if (sc->gre_family != 0) {
/* Detach existing tunnel first */
CK_LIST_REMOVE(sc, chain);
+ CK_LIST_REMOVE(sc, srchash);
GRE_WAIT();
free(sc->gre_hdr, M_GRE);
/* XXX: should we notify about link state change? */
@@ -221,6 +269,7 @@ in6_gre_ioctl(struct gre_softc *sc, u_long cmd, caddr_
sc->gre_oseq = 0;
sc->gre_iseq = UINT32_MAX;
in6_gre_attach(sc);
+ in6_gre_set_running(sc);
break;
case SIOCGIFPSRCADDR_IN6:
case SIOCGIFPDSTADDR_IN6:
@@ -254,6 +303,7 @@ in6_gre_output(struct mbuf *m, int af __unused, int hl
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 encap_config ipv6_encap_cfg = {
.proto = IPPROTO_GRE,
@@ -274,6 +324,8 @@ in6_gre_init(void)
if (!IS_DEFAULT_VNET(curvnet))
return;
+ ipv6_srcaddrtab = ip6_encap_register_srcaddr(in6_gre_srcaddr,
+ NULL, M_WAITOK);
ecookie = ip6_encap_attach(&ipv6_encap_cfg, NULL, M_WAITOK);
}
@@ -281,8 +333,14 @@ void
in6_gre_uninit(void)
{
- if (IS_DEFAULT_VNET(curvnet))
+ if (IS_DEFAULT_VNET(curvnet)) {
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);
+ V_ipv6_hashtbl = NULL;
+ GRE_WAIT();
+ gre_hashdestroy(V_ipv6_srchashtbl);
+ }
}
More information about the svn-src-all
mailing list