Author: ae Date: Sun Oct 21 18:06:15 2018 New Revision: 339551 URL: https://svnweb.freebsd.org/changeset/base/339551
Log: 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. MFC after: 1 month Sponsored by: Yandex LLC Differential Revision: https://reviews.freebsd.org/D17134 Modified: head/share/man/man4/gif.4 head/sys/net/if_gif.c head/sys/net/if_gif.h head/sys/netinet/in_gif.c head/sys/netinet6/in6_gif.c Modified: head/share/man/man4/gif.4 ============================================================================== --- head/share/man/man4/gif.4 Sun Oct 21 17:55:26 2018 (r339550) +++ head/share/man/man4/gif.4 Sun Oct 21 18:06:15 2018 (r339551) @@ -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: head/sys/net/if_gif.c ============================================================================== --- head/sys/net/if_gif.c Sun Oct 21 17:55:26 2018 (r339550) +++ head/sys/net/if_gif.c Sun Oct 21 18:06:15 2018 (r339551) @@ -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: head/sys/net/if_gif.h ============================================================================== --- head/sys/net/if_gif.h Sun Oct 21 17:55:26 2018 (r339550) +++ head/sys/net/if_gif.h Sun Oct 21 18:06:15 2018 (r339551) @@ -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: head/sys/netinet/in_gif.c ============================================================================== --- head/sys/netinet/in_gif.c Sun Oct 21 17:55:26 2018 (r339550) +++ head/sys/netinet/in_gif.c Sun Oct 21 18:06:15 2018 (r339551) @@ -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,44 @@ 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; + + if (V_ipv4_srchashtbl == 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 +168,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 +183,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 +217,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 +235,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 +244,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 +391,7 @@ done: return (ret); } +static const struct srcaddrtab *ipv4_srcaddrtab; static struct { const struct encap_config encap; const struct encaptab *cookie; @@ -387,6 +437,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 +453,11 @@ 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); + gif_hashdestroy(V_ipv4_srchashtbl); + } } Modified: head/sys/netinet6/in6_gif.c ============================================================================== --- head/sys/netinet6/in6_gif.c Sun Oct 21 17:55:26 2018 (r339550) +++ head/sys/netinet6/in6_gif.c Sun Oct 21 18:06:15 2018 (r339551) @@ -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,44 @@ 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; + + if (V_ipv6_srchashtbl == 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 +174,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 +189,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 +232,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 +251,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 +260,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 +414,7 @@ done: return (ret); } +static const struct srcaddrtab *ipv6_srcaddrtab; static struct { const struct encap_config encap; const struct encaptab *cookie; @@ -410,6 +460,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 +476,10 @@ 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); + gif_hashdestroy(V_ipv6_srchashtbl); + } } _______________________________________________ svn-src-all@freebsd.org mailing list https://lists.freebsd.org/mailman/listinfo/svn-src-all To unsubscribe, send any mail to "svn-src-all-unsubscr...@freebsd.org"