cut nvgre(4) over to use common etherbridge code.
authordlg <dlg@openbsd.org>
Sun, 21 Feb 2021 03:46:34 +0000 (03:46 +0000)
committerdlg <dlg@openbsd.org>
Sun, 21 Feb 2021 03:46:34 +0000 (03:46 +0000)
the "ports" that nvgre provides to etherbridge are ip addresses
used in the underlay network.

ok patrick@ jmatthew@

sys/conf/files
sys/net/if_gre.c

index 0aedbec..e43923b 100644 (file)
@@ -1,4 +1,4 @@
-#      $OpenBSD: files,v 1.696 2021/02/21 03:35:17 dlg Exp $
+#      $OpenBSD: files,v 1.697 2021/02/21 03:46:34 dlg Exp $
 #      $NetBSD: files,v 1.87 1996/05/19 17:17:50 jonathan Exp $
 
 #      @(#)files.newconf       7.5 (Berkeley) 5/10/93
@@ -561,7 +561,7 @@ pseudo-device vlan: ifnet, ether
 pseudo-device carp: ifnet, ether
 pseudo-device sppp: ifnet
 pseudo-device gif: ifnet
-pseudo-device gre: ifnet
+pseudo-device gre: ifnet, ether, etherbridge
 pseudo-device crypto: ifnet
 pseudo-device trunk: ifnet, ether, ifmedia
 pseudo-device aggr: ifnet, ether, ifmedia
index 72e8f46..94e7f7f 100644 (file)
@@ -1,4 +1,4 @@
-/*     $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 $ */
 
 /*
@@ -99,6 +99,7 @@
 /* for nvgre bridge shizz */
 #include <sys/socket.h>
 #include <net/if_bridge.h>
+#include <net/if_etherbridge.h>
 
 /*
  * packet formats
@@ -395,27 +396,6 @@ struct egre_tree egre_tree = RBT_INITIALIZER();
  * 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;
@@ -432,12 +412,7 @@ struct nvgre_softc {
        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);
@@ -474,16 +449,24 @@ static int        nvgre_input(const struct gre_tunnel *, struct mbuf *, int,
                    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();
@@ -763,10 +746,11 @@ nvgre_clone_create(struct if_clone *ifc, int unit)
        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);
@@ -775,6 +759,13 @@ nvgre_clone_create(struct if_clone *ifc, int unit)
        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;
@@ -797,13 +788,6 @@ nvgre_clone_create(struct if_clone *ifc, int unit)
        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);
@@ -825,6 +809,8 @@ nvgre_clone_destroy(struct ifnet *ifp)
                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);
@@ -1317,183 +1303,6 @@ egre_input(const struct gre_tunnel *key, struct mbuf *m, int hlen, uint8_t otos)
        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)
 {
@@ -1535,6 +1344,7 @@ nvgre_input(const struct gre_tunnel *key, struct mbuf *m, int hlen,
     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);
@@ -1549,7 +1359,9 @@ nvgre_input(const struct gre_tunnel *key, struct mbuf *m, int hlen,
        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;
@@ -2737,7 +2549,7 @@ nvgre_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data)
                }
                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);
@@ -2749,7 +2561,7 @@ nvgre_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data)
                }
                error = gre_del_tunnel(tunnel);
                if (error == 0)
-                       nvgre_flush_map(sc);
+                       etherbridge_flush(&sc->sc_eb, IFBF_FLUSHALL);
                break;
 
        case SIOCSIFPARENT:
@@ -2759,7 +2571,7 @@ nvgre_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data)
                }
                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);
@@ -2778,7 +2590,7 @@ nvgre_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data)
                }
                /* commit */
                sc->sc_ifp0 = 0;
-               nvgre_flush_map(sc);
+               etherbridge_flush(&sc->sc_eb, IFBF_FLUSHALL);
                break;
 
        case SIOCSVNETID:
@@ -2794,7 +2606,7 @@ nvgre_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data)
 
                /* 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);
@@ -2808,7 +2620,7 @@ nvgre_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data)
                        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;
@@ -2859,35 +2671,26 @@ nvgre_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data)
                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:
@@ -3636,8 +3439,6 @@ nvgre_up(struct nvgre_softc *sc)
        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:
@@ -3662,7 +3463,6 @@ nvgre_down(struct nvgre_softc *sc)
        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);
@@ -3738,61 +3538,12 @@ nvgre_set_parent(struct nvgre_softc *sc, const char *parent)
        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;
@@ -3816,18 +3567,17 @@ nvgre_start(struct ifnet *ifp)
                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 */
@@ -4311,14 +4061,6 @@ egre_cmp(const struct egre_softc *a, const struct egre_softc *b)
 
 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)
 {
@@ -4438,3 +4180,73 @@ eoip_cmp(const struct eoip_softc *ea, const struct eoip_softc *eb)
 }
 
 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);
+       }
+}