Date: Sun, 18 Nov 2018 00:17:06 +0000 (UTC) From: "Andrey V. Elsukov" <ae@FreeBSD.org> To: src-committers@freebsd.org, svn-src-all@freebsd.org, svn-src-stable@freebsd.org, svn-src-stable-12@freebsd.org Subject: svn commit: r340535 - in stable/12: share/man/man4 sys/net sys/netinet sys/netinet6 Message-ID: <201811180017.wAI0H6cK083038@repo.freebsd.org>
next in thread | raw e-mail | index | archive | help
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); + } }
Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?201811180017.wAI0H6cK083038>