-/* $OpenBSD: if_gre.c,v 1.165 2021/02/20 05:01:33 dlg Exp $ */
+/* $OpenBSD: if_gre.c,v 1.166 2021/02/21 03:46:34 dlg Exp $ */
/* $NetBSD: if_gre.c,v 1.9 1999/10/25 19:18:11 drochner Exp $ */
/*
/* for nvgre bridge shizz */
#include <sys/socket.h>
#include <net/if_bridge.h>
+#include <net/if_etherbridge.h>
/*
* packet formats
* Network Virtualisation Using Generic Routing Encapsulation (NVGRE)
*/
-#define NVGRE_AGE_TMO 100 /* seconds */
-
-struct nvgre_entry {
- RB_ENTRY(nvgre_entry) nv_entry;
- struct ether_addr nv_dst;
- uint8_t nv_type;
-#define NVGRE_ENTRY_DYNAMIC 0
-#define NVGRE_ENTRY_STATIC 1
- union gre_addr nv_gateway;
- struct refcnt nv_refs;
- int nv_age;
-};
-
-RBT_HEAD(nvgre_map, nvgre_entry);
-
-static inline int
- nvgre_entry_cmp(const struct nvgre_entry *,
- const struct nvgre_entry *);
-
-RBT_PROTOTYPE(nvgre_map, nvgre_entry, nv_entry, nvgre_entry_cmp);
-
struct nvgre_softc {
struct gre_tunnel sc_tunnel; /* must be first */
unsigned int sc_ifp0;
struct task sc_ltask;
struct task sc_dtask;
- struct rwlock sc_ether_lock;
- struct nvgre_map sc_ether_map;
- unsigned int sc_ether_num;
- unsigned int sc_ether_max;
- int sc_ether_tmo;
- struct timeout sc_ether_age;
+ struct etherbridge sc_eb;
};
RBT_HEAD(nvgre_ucast_tree, nvgre_softc);
uint8_t);
static void nvgre_send(void *);
-static int nvgre_rtfind(struct nvgre_softc *, struct ifbaconf *);
-static void nvgre_flush_map(struct nvgre_softc *);
-static void nvgre_input_map(struct nvgre_softc *,
- const struct gre_tunnel *, const struct ether_header *);
-static void nvgre_age(void *);
+static int nvgre_eb_port_eq(void *, void *, void *);
+static void *nvgre_eb_port_take(void *, void *);
+static void nvgre_eb_port_rele(void *, void *);
+static size_t nvgre_eb_port_ifname(void *, char *, size_t, void *);
+static void nvgre_eb_port_sa(void *, struct sockaddr_storage *, void *);
+
+static const struct etherbridge_ops nvgre_etherbridge_ops = {
+ nvgre_eb_port_eq,
+ nvgre_eb_port_take,
+ nvgre_eb_port_rele,
+ nvgre_eb_port_ifname,
+ nvgre_eb_port_sa,
+};
struct if_clone nvgre_cloner =
IF_CLONE_INITIALIZER("nvgre", nvgre_clone_create, nvgre_clone_destroy);
-struct pool nvgre_pool;
+struct pool nvgre_endpoint_pool;
/* protected by NET_LOCK */
struct nvgre_ucast_tree nvgre_ucast_tree = RBT_INITIALIZER();
struct nvgre_softc *sc;
struct ifnet *ifp;
struct gre_tunnel *tunnel;
+ int error;
- if (nvgre_pool.pr_size == 0) {
- pool_init(&nvgre_pool, sizeof(struct nvgre_entry), 0,
- IPL_SOFTNET, 0, "nvgren", NULL);
+ if (nvgre_endpoint_pool.pr_size == 0) {
+ pool_init(&nvgre_endpoint_pool, sizeof(union gre_addr),
+ 0, IPL_SOFTNET, 0, "nvgreep", NULL);
}
sc = malloc(sizeof(*sc), M_DEVBUF, M_WAITOK|M_ZERO);
snprintf(ifp->if_xname, sizeof(ifp->if_xname), "%s%d",
ifc->ifc_name, unit);
+ error = etherbridge_init(&sc->sc_eb, ifp->if_xname,
+ &nvgre_etherbridge_ops, sc);
+ if (error != 0) {
+ free(sc, M_DEVBUF, sizeof(*sc));
+ return (error);
+ }
+
ifp->if_softc = sc;
ifp->if_hardmtu = ETHER_MAX_HARDMTU_LEN;
ifp->if_ioctl = nvgre_ioctl;
task_set(&sc->sc_ltask, nvgre_link_change, sc);
task_set(&sc->sc_dtask, nvgre_detach, sc);
- rw_init(&sc->sc_ether_lock, "nvgrelk");
- RBT_INIT(nvgre_map, &sc->sc_ether_map);
- sc->sc_ether_num = 0;
- sc->sc_ether_max = 100;
- sc->sc_ether_tmo = 240 * hz;
- timeout_set_proc(&sc->sc_ether_age, nvgre_age, sc); /* ugh */
-
ifmedia_init(&sc->sc_media, 0, egre_media_change, egre_media_status);
ifmedia_add(&sc->sc_media, IFM_ETHER | IFM_AUTO, 0, NULL);
ifmedia_set(&sc->sc_media, IFM_ETHER | IFM_AUTO);
nvgre_down(sc);
NET_UNLOCK();
+ etherbridge_destroy(&sc->sc_eb);
+
ifmedia_delete_instance(&sc->sc_media, IFM_INST_ANY);
ether_ifdetach(ifp);
if_detach(ifp);
return (0);
}
-static int
-nvgre_rtfind(struct nvgre_softc *sc, struct ifbaconf *baconf)
-{
- struct ifnet *ifp = &sc->sc_ac.ac_if;
- struct nvgre_entry *nv;
- struct ifbareq bareq;
- caddr_t uaddr, end;
- int error;
- int age;
-
- if (baconf->ifbac_len == 0) {
- /* single read is atomic */
- baconf->ifbac_len = sc->sc_ether_num * sizeof(bareq);
- return (0);
- }
-
- uaddr = baconf->ifbac_buf;
- end = uaddr + baconf->ifbac_len;
-
- rw_enter_read(&sc->sc_ether_lock);
- RBT_FOREACH(nv, nvgre_map, &sc->sc_ether_map) {
- if (uaddr >= end)
- break;
-
- memcpy(bareq.ifba_name, ifp->if_xname,
- sizeof(bareq.ifba_name));
- memcpy(bareq.ifba_ifsname, ifp->if_xname,
- sizeof(bareq.ifba_ifsname));
- memcpy(&bareq.ifba_dst, &nv->nv_dst,
- sizeof(bareq.ifba_dst));
-
- memset(&bareq.ifba_dstsa, 0, sizeof(bareq.ifba_dstsa));
- switch (sc->sc_tunnel.t_af) {
- case AF_INET: {
- struct sockaddr_in *sin;
-
- sin = (struct sockaddr_in *)&bareq.ifba_dstsa;
- sin->sin_len = sizeof(*sin);
- sin->sin_family = AF_INET;
- sin->sin_addr = nv->nv_gateway.in4;
-
- break;
- }
-#ifdef INET6
- case AF_INET6: {
- struct sockaddr_in6 *sin6;
-
- sin6 = (struct sockaddr_in6 *)&bareq.ifba_dstsa;
- sin6->sin6_len = sizeof(*sin6);
- sin6->sin6_family = AF_INET6;
- sin6->sin6_addr = nv->nv_gateway.in6;
-
- break;
- }
-#endif /* INET6 */
- default:
- unhandled_af(sc->sc_tunnel.t_af);
- }
-
- switch (nv->nv_type) {
- case NVGRE_ENTRY_DYNAMIC:
- age = (ticks - nv->nv_age) / hz;
- bareq.ifba_age = MIN(age, 0xff);
- bareq.ifba_flags = IFBAF_DYNAMIC;
- break;
- case NVGRE_ENTRY_STATIC:
- bareq.ifba_age = 0;
- bareq.ifba_flags = IFBAF_STATIC;
- break;
- }
-
- error = copyout(&bareq, uaddr, sizeof(bareq));
- if (error != 0) {
- rw_exit_read(&sc->sc_ether_lock);
- return (error);
- }
-
- uaddr += sizeof(bareq);
- }
- baconf->ifbac_len = sc->sc_ether_num * sizeof(bareq);
- rw_exit_read(&sc->sc_ether_lock);
-
- return (0);
-}
-
-static void
-nvgre_flush_map(struct nvgre_softc *sc)
-{
- struct nvgre_map map;
- struct nvgre_entry *nv, *nnv;
-
- rw_enter_write(&sc->sc_ether_lock);
- map = sc->sc_ether_map;
- RBT_INIT(nvgre_map, &sc->sc_ether_map);
- sc->sc_ether_num = 0;
- rw_exit_write(&sc->sc_ether_lock);
-
- RBT_FOREACH_SAFE(nv, nvgre_map, &map, nnv) {
- RBT_REMOVE(nvgre_map, &map, nv);
- if (refcnt_rele(&nv->nv_refs))
- pool_put(&nvgre_pool, nv);
- }
-}
-
-static void
-nvgre_input_map(struct nvgre_softc *sc, const struct gre_tunnel *key,
- const struct ether_header *eh)
-{
- struct nvgre_entry *nv, nkey;
- int new = 0;
-
- if (ETHER_IS_BROADCAST(eh->ether_shost) ||
- ETHER_IS_MULTICAST(eh->ether_shost))
- return;
-
- memcpy(&nkey.nv_dst, eh->ether_shost, ETHER_ADDR_LEN);
-
- /* remember where it came from */
- rw_enter_read(&sc->sc_ether_lock);
- nv = RBT_FIND(nvgre_map, &sc->sc_ether_map, &nkey);
- if (nv == NULL)
- new = 1;
- else {
- nv->nv_age = ticks;
-
- if (nv->nv_type != NVGRE_ENTRY_DYNAMIC ||
- gre_ip_cmp(key->t_af, &key->t_dst, &nv->nv_gateway) == 0)
- nv = NULL;
- else
- refcnt_take(&nv->nv_refs);
- }
- rw_exit_read(&sc->sc_ether_lock);
-
- if (new) {
- struct nvgre_entry *onv;
- unsigned int num;
-
- nv = pool_get(&nvgre_pool, PR_NOWAIT);
- if (nv == NULL) {
- /* oh well */
- return;
- }
-
- memcpy(&nv->nv_dst, eh->ether_shost, ETHER_ADDR_LEN);
- nv->nv_type = NVGRE_ENTRY_DYNAMIC;
- nv->nv_gateway = key->t_dst;
- refcnt_init(&nv->nv_refs);
- nv->nv_age = ticks;
-
- rw_enter_write(&sc->sc_ether_lock);
- num = sc->sc_ether_num;
- if (++num > sc->sc_ether_max)
- onv = nv;
- else {
- /* try to give the ref to the map */
- onv = RBT_INSERT(nvgre_map, &sc->sc_ether_map, nv);
- if (onv == NULL) {
- /* count the successful insert */
- sc->sc_ether_num = num;
- }
- }
- rw_exit_write(&sc->sc_ether_lock);
-
- if (onv != NULL)
- pool_put(&nvgre_pool, nv);
- } else if (nv != NULL) {
- rw_enter_write(&sc->sc_ether_lock);
- nv->nv_gateway = key->t_dst;
- rw_exit_write(&sc->sc_ether_lock);
-
- if (refcnt_rele(&nv->nv_refs)) {
- /* ioctl may have deleted the entry */
- pool_put(&nvgre_pool, nv);
- }
- }
-}
-
static inline struct nvgre_softc *
nvgre_mcast_find(const struct gre_tunnel *key, unsigned int if0idx)
{
uint8_t otos)
{
struct nvgre_softc *sc;
+ struct ether_header *eh;
if (ISSET(m->m_flags, M_MCAST|M_BCAST))
sc = nvgre_mcast_find(key, m->m_pkthdr.ph_ifidx);
if (m == NULL)
return (0);
- nvgre_input_map(sc, key, mtod(m, struct ether_header *));
+ eh = mtod(m, struct ether_header *);
+ etherbridge_map(&sc->sc_eb, (void *)&key->t_dst,
+ (struct ether_addr *)eh->ether_shost);
SET(m->m_pkthdr.csum_flags, M_FLOWID);
m->m_pkthdr.ph_flowid = bemtoh32(&key->t_key) & ~GRE_KEY_ENTROPY;
}
error = gre_set_tunnel(tunnel, (struct if_laddrreq *)data, 0);
if (error == 0)
- nvgre_flush_map(sc);
+ etherbridge_flush(&sc->sc_eb, IFBF_FLUSHALL);
break;
case SIOCGLIFPHYADDR:
error = gre_get_tunnel(tunnel, (struct if_laddrreq *)data);
}
error = gre_del_tunnel(tunnel);
if (error == 0)
- nvgre_flush_map(sc);
+ etherbridge_flush(&sc->sc_eb, IFBF_FLUSHALL);
break;
case SIOCSIFPARENT:
}
error = nvgre_set_parent(sc, parent->ifp_parent);
if (error == 0)
- nvgre_flush_map(sc);
+ etherbridge_flush(&sc->sc_eb, IFBF_FLUSHALL);
break;
case SIOCGIFPARENT:
ifp0 = if_get(sc->sc_ifp0);
}
/* commit */
sc->sc_ifp0 = 0;
- nvgre_flush_map(sc);
+ etherbridge_flush(&sc->sc_eb, IFBF_FLUSHALL);
break;
case SIOCSVNETID:
/* commit */
tunnel->t_key = htonl(ifr->ifr_vnetid << GRE_KEY_ENTROPY_SHIFT);
- nvgre_flush_map(sc);
+ etherbridge_flush(&sc->sc_eb, IFBF_FLUSHALL);
break;
case SIOCGVNETID:
error = gre_get_vnetid(tunnel, ifr);
break;
}
tunnel->t_rtableid = ifr->ifr_rdomainid;
- nvgre_flush_map(sc);
+ etherbridge_flush(&sc->sc_eb, IFBF_FLUSHALL);
break;
case SIOCGLIFPHYRTABLE:
ifr->ifr_rdomainid = tunnel->t_rtableid;
break;
case SIOCBRDGSCACHE:
- if (bparam->ifbrp_csize < 1) {
- error = EINVAL;
- break;
- }
-
- /* commit */
- sc->sc_ether_max = bparam->ifbrp_csize;
+ error = etherbridge_set_max(&sc->sc_eb, bparam);
break;
case SIOCBRDGGCACHE:
- bparam->ifbrp_csize = sc->sc_ether_max;
+ error = etherbridge_get_max(&sc->sc_eb, bparam);
break;
case SIOCBRDGSTO:
- if (bparam->ifbrp_ctime < 0 ||
- bparam->ifbrp_ctime > INT_MAX / hz) {
- error = EINVAL;
- break;
- }
- sc->sc_ether_tmo = bparam->ifbrp_ctime * hz;
+ error = etherbridge_set_tmo(&sc->sc_eb, bparam);
break;
case SIOCBRDGGTO:
- bparam->ifbrp_ctime = sc->sc_ether_tmo / hz;
+ error = etherbridge_get_tmo(&sc->sc_eb, bparam);
break;
case SIOCBRDGRTS:
- error = nvgre_rtfind(sc, (struct ifbaconf *)data);
+ error = etherbridge_rtfind(&sc->sc_eb,
+ (struct ifbaconf *)data);
break;
case SIOCBRDGFLUSH:
- nvgre_flush_map(sc);
+ etherbridge_flush(&sc->sc_eb,
+ ((struct ifbreq *)data)->ifbr_ifsflags);
break;
case SIOCADDMULTI:
sc->sc_inm = inm;
SET(sc->sc_ac.ac_if.if_flags, IFF_RUNNING);
- timeout_add_sec(&sc->sc_ether_age, NVGRE_AGE_TMO);
-
return (0);
remove_ucast:
CLR(ifp->if_flags, IFF_RUNNING);
NET_UNLOCK();
- timeout_del_barrier(&sc->sc_ether_age);
ifq_barrier(&ifp->if_snd);
if (!task_del(softnet, &sc->sc_send_task))
taskq_barrier(softnet);
return (0);
}
-static void
-nvgre_age(void *arg)
-{
- struct nvgre_softc *sc = arg;
- struct nvgre_entry *nv, *nnv;
- int tmo = sc->sc_ether_tmo * 2;
- int diff;
-
- if (!ISSET(sc->sc_ac.ac_if.if_flags, IFF_RUNNING))
- return;
-
- rw_enter_write(&sc->sc_ether_lock); /* XXX */
- RBT_FOREACH_SAFE(nv, nvgre_map, &sc->sc_ether_map, nnv) {
- if (nv->nv_type != NVGRE_ENTRY_DYNAMIC)
- continue;
-
- diff = ticks - nv->nv_age;
- if (diff < tmo)
- continue;
-
- sc->sc_ether_num--;
- RBT_REMOVE(nvgre_map, &sc->sc_ether_map, nv);
- if (refcnt_rele(&nv->nv_refs))
- pool_put(&nvgre_pool, nv);
- }
- rw_exit_write(&sc->sc_ether_lock);
-
- timeout_add_sec(&sc->sc_ether_age, NVGRE_AGE_TMO);
-}
-
-static inline int
-nvgre_entry_valid(struct nvgre_softc *sc, const struct nvgre_entry *nv)
-{
- int diff;
-
- if (nv == NULL)
- return (0);
-
- if (nv->nv_type == NVGRE_ENTRY_STATIC)
- return (1);
-
- diff = ticks - nv->nv_age;
- if (diff < sc->sc_ether_tmo)
- return (1);
-
- return (0);
-}
-
static void
nvgre_start(struct ifnet *ifp)
{
struct nvgre_softc *sc = ifp->if_softc;
const struct gre_tunnel *tunnel = &sc->sc_tunnel;
union gre_addr gateway;
- struct nvgre_entry *nv, key;
struct mbuf_list ml = MBUF_LIST_INITIALIZER();
struct ether_header *eh;
struct mbuf *m, *m0;
if (ETHER_IS_BROADCAST(eh->ether_dhost))
gateway = tunnel->t_dst;
else {
- memcpy(&key.nv_dst, eh->ether_dhost,
- sizeof(key.nv_dst));
-
- rw_enter_read(&sc->sc_ether_lock);
- nv = RBT_FIND(nvgre_map, &sc->sc_ether_map, &key);
- if (nvgre_entry_valid(sc, nv))
- gateway = nv->nv_gateway;
- else {
+ const union gre_addr *endpoint;
+
+ smr_read_enter();
+ endpoint = etherbridge_resolve(&sc->sc_eb,
+ (struct ether_addr *)eh->ether_dhost);
+ if (endpoint == NULL) {
/* "flood" to unknown hosts */
- gateway = tunnel->t_dst;
+ endpoint = &tunnel->t_dst;
}
- rw_exit_read(&sc->sc_ether_lock);
+ gateway = *endpoint;
+ smr_read_leave();
}
/* force prepend mbuf because of alignment problems */
RBT_GENERATE(egre_tree, egre_softc, sc_entry, egre_cmp);
-static inline int
-nvgre_entry_cmp(const struct nvgre_entry *a, const struct nvgre_entry *b)
-{
- return (memcmp(&a->nv_dst, &b->nv_dst, sizeof(a->nv_dst)));
-}
-
-RBT_GENERATE(nvgre_map, nvgre_entry, nv_entry, nvgre_entry_cmp);
-
static int
nvgre_cmp_tunnel(const struct gre_tunnel *a, const struct gre_tunnel *b)
{
}
RBT_GENERATE(eoip_tree, eoip_softc, sc_entry, eoip_cmp);
+
+static int
+nvgre_eb_port_eq(void *arg, void *a, void *b)
+{
+ struct nvgre_softc *sc = arg;
+
+ return (gre_ip_cmp(sc->sc_tunnel.t_af, a, b) == 0);
+}
+
+static void *
+nvgre_eb_port_take(void *arg, void *port)
+{
+ union gre_addr *ea = port;
+ union gre_addr *endpoint;
+
+ endpoint = pool_get(&nvgre_endpoint_pool, PR_NOWAIT);
+ if (endpoint == NULL)
+ return (NULL);
+
+ *endpoint = *ea;
+
+ return (endpoint);
+}
+
+static void
+nvgre_eb_port_rele(void *arg, void *port)
+{
+ union gre_addr *endpoint = port;
+
+ pool_put(&nvgre_endpoint_pool, endpoint);
+}
+
+static size_t
+nvgre_eb_port_ifname(void *arg, char *dst, size_t len, void *port)
+{
+ struct nvgre_softc *sc = arg;
+
+ return (strlcpy(dst, sc->sc_ac.ac_if.if_xname, len));
+}
+
+static void
+nvgre_eb_port_sa(void *arg, struct sockaddr_storage *ss, void *port)
+{
+ struct nvgre_softc *sc = arg;
+ union gre_addr *endpoint = port;
+
+ switch (sc->sc_tunnel.t_af) {
+ case AF_INET: {
+ struct sockaddr_in *sin = (struct sockaddr_in *)ss;
+
+ sin->sin_len = sizeof(*sin);
+ sin->sin_family = AF_INET;
+ sin->sin_addr = endpoint->in4;
+ break;
+ }
+#ifdef INET6
+ case AF_INET6: {
+ struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)ss;
+
+ sin6->sin6_len = sizeof(*sin6);
+ sin6->sin6_family = AF_INET6;
+ sin6->sin6_addr = endpoint->in6;
+
+ break;
+ }
+#endif /* INET6 */
+ default:
+ unhandled_af(sc->sc_tunnel.t_af);
+ }
+}