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
This commit is contained in:
Andrey V. Elsukov 2018-10-21 18:06:15 +00:00
parent 8251c68d5c
commit 009d82ee0f
5 changed files with 120 additions and 15 deletions

View File

@ -29,7 +29,7 @@
.\"
.\" $FreeBSD$
.\"
.Dd June 5, 2018
.Dd October 21, 2018
.Dt GIF 4
.Os
.Sh NAME
@ -207,15 +207,6 @@ 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
does not try to perform path MTU discovery for the encapsulated packet

View File

@ -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();

View File

@ -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);

View File

@ -82,12 +82,16 @@ SYSCTL_INT(_net_inet_ip, IPCTL_GIF_TTL, gifttl, CTLFLAG_VNET | CTLFLAG_RW,
* 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,6 +123,43 @@ in_gif_checkdup(const struct gif_softc *sc, in_addr_t src, in_addr_t dst)
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 data)
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 data)
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 data)
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 @@ in_gif_lookup(const struct mbuf *m, int off, int proto, void **arg)
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);
}
}

View File

@ -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,6 +129,43 @@ in6_gif_checkdup(const struct gif_softc *sc, const struct in6_addr *src,
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_t data)
(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_t data)
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_t data)
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 @@ in6_gif_lookup(const struct mbuf *m, int off, int proto, void **arg)
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);
}
}