mgre(4) is a basic driver that starts to implement point to multipoint
gre ip tunnels. these are annoyingly hard to explain.

the main difference between gre(4) and mgre(4) is that it is not a
point to point interface (obviously, cos of the name). when configuring
gre(4), you give it a source and destination address for the outer
tunnel, and give it a source and destination address inside the
tunnel. eg:

configuring the gre tunnel addresses:

# ifconfig gre0 tunnel 192.0.2.1 198.51.100.1

configuring the point to point addresses inside the tunne:

# ifconfig gre0 inet 192.168.0.1/32 192.168.0.2

if you have a gre interface on the other side with the swapped
addresses, then pinging from 192.168.0.1 to 192.168.02 will have
the packets encapsulated by gre in packets from 192.0.2.1 to
198.51.100.1, and the replies come back with the addresses swapped:

09:06:33.815358 192.0.2.1 > 198.51.100.1: gre 192.168.0.1 > 192.168.0.2: icmp: 
echo request
09:06:33.815652 198.51.100.1 > 192.0.2.1: gre 192.168.0.2 > 192.168.0.1: icmp: 
echo reply

mgre on the other hand is more like an ethernet interface, but with
ip addresses instead of mac addresses. mgre(4) only needs configuration
of a local tunnel address (but takes src and dst at the moment cos
we have no way of only configuring a local address), and you configure
a subnet on the interface: eg, to configure mgre0 instead of gre0:

# ifconfig mgre0 tunnel 192.0.2.1 192.0.2.1 # dst is accepted but ignored

then you configure it on a subnet:

# ifconfig mgre0 inet 192.168.0.1/24

you'll see this in the routing table:

192.168.0/24       192.168.0.1        UCn        1        0     -     4 mgre0
192.168.0.1        mgre0              UHl        0      479     -     1 mgre0

so if you try to ping 192.168.0.2 with that config, it wont work
because there's no information stored anywhere to map 192.168.0.1
to the tunnel endpoing of 198.51.100.1. however, because it is like
a ethernet interface, the kernel has cloned a route for 192.168.0.2:

192.168.0/24       192.168.0.1        UCn        1        0     -     4 mgre0
192.168.0.1        mgre0              UHl        0      479     -     1 mgre0
192.168.0.2        link#0             UHc        0        1     -     3 mgre0

there's no arp or nd6 on mgre though, so right now we need to change
or add these entries by hand:

# route change -host 192.168.0.2 198.51.100.1 -iface -ifp mgre0

192.168.0/24       192.168.0.1        UCn        1        0     -     4 mgre0
192.168.0.1        mgre0              UHl        0      479     -     1 mgre0
192.168.0.2        198.51.100.1       UHc        0        1     -     3 mgre0

then we can ping:

09:16:44.336944 192.0.2.1 > 198.51.100.1: gre 192.168.0.1 > 192.168.0.2: icmp: 
echo request
09:16:44.337176 198.51.100.1 > 192.0.2.1: gre 192.168.0.2 > 192.168.0.1: icmp: 
echo reply

after that we could add routes instead of tunnels for more sites.
eg:

# route add -host 192.168.0.3 203.0.113.10 -iface -ifp mgre0

then we could ping 192.168.0.3 over mgre0, which would use 203.0.113.10
as the gre dest address:

09:24:31.057820 192.0.2.1 > 203.0.113.10: gre 192.168.0.1 > 192.168.0.3: icmp: 
echo request
09:24:31.058011 203.0.113.10 > 192.0.2.1: gre 192.168.0.3 > 192.168.0.1: icmp: 
echo reply

there are several uses mgre(4) interfaces. the one i am most
interested in now is building hub and spoke vpn topologies.
this would allow me to point gre interfaces on remote routers at
remote sites to a single mgre interface on a central firewall, which
would enforce policy centrally before allowing packets back out to
the spokes. because im looking at a dozen or so remote sites, each
with up to 6 networks to tunnel back to the central firewall, i
would welcome a way to configure less tunnels centrally.

mgre can also be used to build meshes of networks, or dynamic
multipoint vpns. this is a bit rought, but this is a good start.

ok?

Index: if_gre.c
===================================================================
RCS file: /cvs/src/sys/net/if_gre.c,v
retrieving revision 1.114
diff -u -p -r1.114 if_gre.c
--- if_gre.c    25 Feb 2018 01:52:25 -0000      1.114
+++ if_gre.c    25 Feb 2018 21:42:54 -0000
@@ -187,6 +187,9 @@ struct gre_tunnel {
 };
 
 static int
+               gre_cmp_src(const struct gre_tunnel *,
+                   const struct gre_tunnel *);
+static int
                gre_cmp(const struct gre_tunnel *, const struct gre_tunnel *);
 
 static int     gre_set_tunnel(struct gre_tunnel *, struct if_laddrreq *, int);
@@ -217,11 +220,11 @@ static int        gre_tunnel_ioctl(struct ifnet
  */
 
 struct gre_softc {
-       struct ifnet            sc_if;
-
-       struct gre_tunnel       sc_tunnel;
+       struct gre_tunnel       sc_tunnel; /* must be first */
        TAILQ_ENTRY(gre_softc)  sc_entry;
 
+       struct ifnet            sc_if;
+
        struct timeout          sc_ka_send;
        struct timeout          sc_ka_hold;
 
@@ -264,13 +267,48 @@ static void       gre_link_state(struct gre_so
 
 static int     gre_input_key(struct mbuf **, int *, int, int,
                    struct gre_tunnel *);
-static struct gre_softc *
-               gre_find(const struct gre_tunnel *);
 
 static void    gre_keepalive_send(void *);
 static void    gre_keepalive_recv(struct ifnet *ifp, struct mbuf *);
 static void    gre_keepalive_hold(void *);
 
+static struct mbuf *
+               gre_l3_encap_dst(const struct gre_tunnel *, const void *,
+                   struct mbuf *m, sa_family_t);
+
+#define gre_l3_encap(_t, _m, _af) \
+               gre_l3_encap_dst((_t), &(_t)->t_dst, (_m), (_af))
+
+struct mgre_softc {
+       struct gre_tunnel       sc_tunnel; /* must be first */
+       RBT_ENTRY(mgre_softc)   sc_entry;
+
+       struct ifnet            sc_if;
+};
+
+RBT_HEAD(mgre_tree, mgre_softc);
+
+static inline int
+               mgre_cmp(const struct mgre_softc *, const struct mgre_softc *);
+
+RBT_PROTOTYPE(mgre_tree, mgre_softc, sc_entry, mgre_cmp);
+
+static int     mgre_clone_create(struct if_clone *, int);
+static int     mgre_clone_destroy(struct ifnet *);
+
+struct if_clone mgre_cloner =
+    IF_CLONE_INITIALIZER("mgre", mgre_clone_create, mgre_clone_destroy);
+
+static int     mgre_output(struct ifnet *, struct mbuf *, struct sockaddr *,
+                   struct rtentry *);
+static void    mgre_start(struct ifnet *);
+static int     mgre_ioctl(struct ifnet *, u_long, caddr_t);
+
+static int     mgre_up(struct mgre_softc *);
+static int     mgre_down(struct mgre_softc *);
+
+struct mgre_tree mgre_tree = RBT_INITIALIZER();
+
 /*
  * Ethernet GRE tunnels
  */
@@ -431,6 +469,7 @@ void
 greattach(int n)
 {
        if_clone_attach(&gre_cloner);
+       if_clone_attach(&mgre_cloner);
        if_clone_attach(&egre_cloner);
        if_clone_attach(&nvgre_cloner);
 }
@@ -498,6 +537,59 @@ gre_clone_destroy(struct ifnet *ifp)
 }
 
 static int
+mgre_clone_create(struct if_clone *ifc, int unit)
+{
+       struct mgre_softc *sc;
+       struct ifnet *ifp;
+
+       sc = malloc(sizeof(*sc), M_DEVBUF, M_WAITOK|M_ZERO);
+       ifp = &sc->sc_if;
+
+       snprintf(ifp->if_xname, sizeof(ifp->if_xname),
+           "%s%d", ifc->ifc_name, unit);
+
+       ifp->if_softc = sc;
+       ifp->if_type = IFT_L3IPVLAN;
+       ifp->if_hdrlen = 24; /* IP + GRE */
+       ifp->if_mtu = GREMTU;
+       ifp->if_flags = 0; /* it's not p2p, and can't mcast or bcast */
+       ifp->if_xflags = IFXF_CLONED;
+       ifp->if_rtrequest = p2p_rtrequest; /* maybe? */;
+       ifp->if_output = mgre_output;
+       ifp->if_start = mgre_start;
+       ifp->if_ioctl = mgre_ioctl;
+
+       sc->sc_tunnel.t_ttl = ip_defttl;
+       sc->sc_tunnel.t_df = htons(0);
+
+       if_attach(ifp);
+       if_alloc_sadl(ifp);
+
+#if NBPFILTER > 0
+       bpfattach(&ifp->if_bpf, ifp, DLT_LOOP, sizeof(uint32_t));
+#endif
+
+       return (0);
+}
+
+static int
+mgre_clone_destroy(struct ifnet *ifp)
+{
+       struct mgre_softc *sc = ifp->if_softc;
+
+       NET_LOCK();
+       if (ISSET(ifp->if_flags, IFF_RUNNING))
+               mgre_down(sc);
+       NET_UNLOCK();
+
+       if_detach(ifp);
+
+       free(sc, M_DEVBUF, sizeof(*sc));
+
+       return (0);
+}
+
+static int
 egre_clone_create(struct if_clone *ifc, int unit)
 {
        struct egre_softc *sc;
@@ -669,7 +761,7 @@ gre_input6(struct mbuf **mp, int *offp, 
 }
 #endif /* INET6 */
 
-static struct gre_softc *
+static inline struct ifnet *
 gre_find(const struct gre_tunnel *key)
 {
        struct gre_softc *sc;
@@ -681,20 +773,32 @@ gre_find(const struct gre_tunnel *key)
                if (!ISSET(sc->sc_if.if_flags, IFF_RUNNING))
                        continue;
 
-               return (sc);
+               return (&sc->sc_if);
        }
 
        return (NULL);
 }
 
+static inline struct ifnet *
+mgre_find(const struct gre_tunnel *key)
+{
+       struct mgre_softc *sc;
+
+       sc = RBT_FIND(mgre_tree, &mgre_tree, (const struct mgre_softc *)key);
+       if (sc != NULL)
+               return (&sc->sc_if);
+
+       return (NULL);
+}
+
 static int
 gre_input_key(struct mbuf **mp, int *offp, int type, int af,
     struct gre_tunnel *key)
 {
        struct mbuf *m = *mp;
        int iphlen = *offp, hlen;
-       struct gre_softc *sc;
        struct ifnet *ifp;
+       const struct gre_tunnel *tunnel;
        caddr_t buf;
        struct gre_header *gh;
        struct gre_h_key *gkh;
@@ -770,11 +874,12 @@ gre_input_key(struct mbuf **mp, int *off
                return (IPPROTO_DONE);
        }
 
-       sc = gre_find(key);
-       if (sc == NULL)
-               goto decline;
-
-       ifp = &sc->sc_if;
+       ifp = gre_find(key);
+       if (ifp == NULL) {
+               ifp = mgre_find(key);
+               if (ifp == NULL)
+                       goto decline;
+       }
 
        switch (gh->gre_proto) {
        case htons(GRE_WCCP): {
@@ -836,6 +941,11 @@ gre_input_key(struct mbuf **mp, int *off
                break;
 #endif
        case htons(0):
+               if (ifp->if_type != IFT_TUNNEL) {
+                       /* keepalives dont make sense for mgre */
+                       goto decline;
+               }
+
 #if NBPFILTER > 0
                bpf_af = AF_UNSPEC;
 #endif
@@ -846,9 +956,13 @@ gre_input_key(struct mbuf **mp, int *off
                goto decline;
        }
 
+       /* it's ours now */
+
        m_adj(m, hlen);
 
-       if (sc->sc_tunnel.t_ttl == -1) {
+       tunnel = ifp->if_softc; /* gre and mgre tunnel info is at the front */
+
+       if (tunnel->t_ttl == -1) {
                m = m_pullup(m, ttloff + 1);
                if (m == NULL)
                        return (IPPROTO_DONE);
@@ -856,6 +970,11 @@ gre_input_key(struct mbuf **mp, int *off
                *(m->m_data + ttloff) = key->t_ttl;
        }
 
+       if (tunnel->t_key_mask == GRE_KEY_ENTROPY) {
+               m->m_pkthdr.ph_flowid = M_FLOWID_VALID |
+                   (bemtoh32(&key->t_key) & ~GRE_KEY_ENTROPY);
+       }
+
        m->m_flags &= ~(M_MCAST|M_BCAST);
        m->m_flags |= mcast;
        m->m_pkthdr.ph_ifidx = ifp->if_index;
@@ -865,11 +984,6 @@ gre_input_key(struct mbuf **mp, int *off
        pf_pkt_addr_changed(m);
 #endif
 
-       if (sc->sc_tunnel.t_key_mask == GRE_KEY_ENTROPY) {
-               m->m_pkthdr.ph_flowid = M_FLOWID_VALID |
-                   (bemtoh32(&key->t_key) & ~GRE_KEY_ENTROPY);
-       }
-
        ifp->if_ipackets++;
        ifp->if_ibytes += m->m_pkthdr.len;
 
@@ -1338,76 +1452,178 @@ gre_start(struct ifnet *ifp)
 {
        struct gre_softc *sc = ifp->if_softc;
        struct mbuf *m;
-       uint8_t ttl, tos;
-       int tttl;
-       uint16_t proto;
+       int af;
 #if NBPFILTER > 0
        caddr_t if_bpf;
 #endif
-       int ttloff;
-
-       tttl = sc->sc_tunnel.t_ttl;
 
        while ((m = ifq_dequeue(&ifp->if_snd)) != NULL) {
+               af = m->m_pkthdr.ph_family;
+
 #if NBPFILTER > 0
                if_bpf = ifp->if_bpf;
-               if (if_bpf) {
-                       int af = m->m_pkthdr.ph_family;
+               if (if_bpf)
                        bpf_mtap_af(if_bpf, af, m, BPF_DIRECTION_OUT);
-               }
 #endif
 
-               switch (m->m_pkthdr.ph_family) {
-               case AF_INET: {
-                       struct ip *ip;
+               m = gre_l3_encap(&sc->sc_tunnel, m, af);
+               if (m == NULL || gre_ip_output(&sc->sc_tunnel, m) != 0) {
+                       ifp->if_oerrors++;
+                       continue;
+               }
+       }
+}
 
-                       m = m_pullup(m, sizeof(*ip));
-                       if (m == NULL)
-                               continue;
+static int
+mgre_output(struct ifnet *ifp, struct mbuf *m, struct sockaddr *dest,
+    struct rtentry *rt0)
+{
+       struct mgre_softc *sc = ifp->if_softc;
+       struct sockaddr *gate;
+       struct rtentry *rt;
+       struct m_tag *mtag;
+       int error = 0;
+       sa_family_t af;
+       const void *addr;
 
-                       ip = mtod(m, struct ip *);
-                       tos = ip->ip_tos;
+       if (!gre_allow) {
+               error = EACCES;
+               goto drop;
+       }
 
-                       ttloff = offsetof(struct ip, ip_ttl);
-                       proto = htons(ETHERTYPE_IP);
-                       break;
-               }
+       if (!ISSET(ifp->if_flags, IFF_RUNNING)) {
+               error = ENETDOWN;
+               goto drop;
+       }
+
+       switch (dest->sa_family) {
+       case AF_INET:
 #ifdef INET6
-               case AF_INET6:
-                       tos = 0;
-                       ttloff = offsetof(struct ip6_hdr, ip6_hlim);
-                       proto = htons(ETHERTYPE_IPV6);
-                       break;
+       case AF_INET6:
 #endif
 #ifdef MPLS
-               case AF_MPLS:
-                       ttloff = 3;
-                       tos = 0;
-
-                       if (m->m_flags & (M_BCAST | M_MCAST))
-                               proto = htons(ETHERTYPE_MPLS_MCAST);
-                       else
-                               proto = htons(ETHERTYPE_MPLS);
-                       break;
+       case AF_MPLS:
 #endif
-               default:
-                       unhandled_af(m->m_pkthdr.ph_family);
+               break;
+       default:
+               error = EAFNOSUPPORT;
+               goto drop;
+       }
+
+       if (ISSET(m->m_flags, M_MCAST|M_BCAST)) {
+               error = ENETUNREACH;
+               goto drop;
+       }
+
+       rt = rt_getll(rt0);
+
+       /* chech rt_expire? */
+       if (ISSET(rt->rt_flags, RTF_REJECT)) {
+               error = (rt == rt0) ? EHOSTDOWN : EHOSTUNREACH;
+               goto drop;
+       }
+       if (!ISSET(rt->rt_flags, RTF_HOST)) {
+               error = EHOSTUNREACH;
+               goto drop;
+       }
+       if (ISSET(rt->rt_flags, RTF_GATEWAY)) {
+               error = EINVAL;
+               goto drop;
+       }
+
+       gate = rt->rt_gateway;
+       af = gate->sa_family;
+       if (af != sc->sc_tunnel.t_af) {
+               error = EAGAIN;
+               goto drop;
+       }
+ 
+       /* Try to limit infinite recursion through misconfiguration. */
+       for (mtag = m_tag_find(m, PACKET_TAG_GRE, NULL); mtag;
+            mtag = m_tag_find(m, PACKET_TAG_GRE, mtag)) {
+               if (memcmp((caddr_t)(mtag + 1), &ifp->if_index,
+                   sizeof(ifp->if_index)) == 0) {
+                       error = EIO;
+                       goto drop;
                }
+       }
+
+       mtag = m_tag_get(PACKET_TAG_GRE, sizeof(ifp->if_index), M_NOWAIT);
+       if (mtag == NULL) {
+               error = ENOBUFS;
+               goto drop;
+       }
+       memcpy((caddr_t)(mtag + 1), &ifp->if_index, sizeof(ifp->if_index));
+       m_tag_prepend(m, mtag);
+
+       switch (af) {
+       case AF_INET: {
+               struct sockaddr_in *sin = (struct sockaddr_in *)gate;
+               addr = &sin->sin_addr;
+               break;
+       }
+#ifdef INET6
+       case AF_INET6: {
+               struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)gate;
+               addr = &sin6->sin6_addr;
+               break;
+       }
+ #endif
+       default:
+               unhandled_af(af);
+               /* NOTREACHED */
+       }
 
-               if (tttl == -1) {
-                       m = m_pullup(m, ttloff + 1);
-                       if (m == NULL)
-                               continue;
+       m = gre_l3_encap_dst(&sc->sc_tunnel, addr, m, dest->sa_family);
+       if (m == NULL)
+               return (ENOBUFS);
 
-                       ttl = *(m->m_data + ttloff);
-               } else
-                       ttl = tttl;
+       m->m_pkthdr.ph_family = dest->sa_family;
 
-               m = gre_encap(&sc->sc_tunnel, m, proto, ttl, tos);
-               if (m == NULL || gre_ip_output(&sc->sc_tunnel, m) != 0) {
-                       ifp->if_oerrors++;
-                       continue;
+       error = if_enqueue(ifp, m);
+       if (error)
+               ifp->if_oerrors++;
+       return (error);
+
+drop:
+       m_freem(m);
+       return (error);
+}
+
+static void
+mgre_start(struct ifnet *ifp)
+{
+       struct mgre_softc *sc = ifp->if_softc;
+       struct mbuf *m;
+#if NBPFILTER > 0
+       caddr_t if_bpf;
+#endif
+
+       while ((m = ifq_dequeue(&ifp->if_snd)) != NULL) {
+#if NBPFILTER > 0
+               if_bpf = ifp->if_bpf;
+               if (if_bpf) {
+                       struct m_hdr mh;
+                       struct mbuf *n;
+                       int off;
+
+                       n = m_getptr(m, ifp->if_hdrlen, &off);
+                       KASSERT(n != NULL);
+
+                       mh.mh_flags = 0;
+                       mh.mh_next = n->m_next;
+                       mh.mh_len = n->m_len - off;
+                       mh.mh_data = n->m_data + off;
+
+                       bpf_mtap_af(if_bpf, m->m_pkthdr.ph_family,
+                           (struct mbuf *)&mh, BPF_DIRECTION_OUT);
                }
+#endif
+
+               if (m == NULL || gre_ip_output(&sc->sc_tunnel, m) != 0) {
+                       ifp->if_oerrors++;
+                       continue;
+               }
        }
 }
 
@@ -1454,6 +1670,64 @@ egre_start(struct ifnet *ifp)
 }
 
 static struct mbuf *
+gre_l3_encap_dst(const struct gre_tunnel *tunnel, const void *dst,
+    struct mbuf *m, sa_family_t af)
+{
+       uint16_t proto;
+       uint8_t ttl, tos;
+       int tttl = tunnel->t_ttl;
+       int ttloff;
+
+       switch (af) {
+       case AF_INET: {
+               struct ip *ip;
+
+               m = m_pullup(m, sizeof(*ip));
+               if (m == NULL)
+                       return (NULL);
+
+               ip = mtod(m, struct ip *);
+               tos = ip->ip_tos;
+
+               ttloff = offsetof(struct ip, ip_ttl);
+               proto = htons(ETHERTYPE_IP);
+               break;
+       }
+#ifdef INET6
+       case AF_INET6:
+               tos = 0;
+               ttloff = offsetof(struct ip6_hdr, ip6_hlim);
+               proto = htons(ETHERTYPE_IPV6);
+               break;
+ #endif
+#ifdef MPLS
+       case AF_MPLS:
+               ttloff = 3;
+               tos = 0;
+ 
+               if (m->m_flags & (M_BCAST | M_MCAST))
+                       proto = htons(ETHERTYPE_MPLS_MCAST);
+               else
+                       proto = htons(ETHERTYPE_MPLS);
+               break;
+#endif
+       default:
+               unhandled_af(af);
+       }
+ 
+       if (tttl == -1) {
+               m = m_pullup(m, ttloff + 1);
+               if (m == NULL)
+                       return (NULL);
+ 
+               ttl = *(m->m_data + ttloff);
+       } else
+               ttl = tttl;
+
+       return (gre_encap_dst(tunnel, dst, m, proto, ttl, tos));
+}
+
+static struct mbuf *
 gre_encap_dst(const struct gre_tunnel *tunnel, const union gre_addr *dst,
     struct mbuf *m, uint16_t proto, uint8_t ttl, uint8_t tos)
 {
@@ -1715,6 +1989,63 @@ gre_ioctl(struct ifnet *ifp, u_long cmd,
 }
 
 static int
+mgre_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data)
+{
+       struct mgre_softc *sc = ifp->if_softc;
+       struct ifreq *ifr = (struct ifreq *)data;
+       int error = 0;
+
+       switch(cmd) {
+       case SIOCSIFADDR:
+               break;
+       case SIOCSIFFLAGS:
+               if (ISSET(ifp->if_flags, IFF_UP)) {
+                       if (!ISSET(ifp->if_flags, IFF_RUNNING))
+                               error = mgre_up(sc);
+                       else
+                               error = 0;
+               } else {
+                       if (ISSET(ifp->if_flags, IFF_RUNNING))
+                               error = mgre_down(sc);
+               }
+               break;
+
+       case SIOCSLIFPHYTTL:
+               if (ifr->ifr_ttl != -1 &&
+                   (ifr->ifr_ttl < 1 || ifr->ifr_ttl > 0xff)) {
+                       error = EINVAL;
+                       break;
+               }
+
+               /* commit */
+               sc->sc_tunnel.t_ttl = ifr->ifr_ttl;
+               break;
+
+       case SIOCGLIFPHYTTL:
+               ifr->ifr_ttl = sc->sc_tunnel.t_ttl;
+               break;
+
+       case SIOCSLIFPHYADDR:
+               /* XXX */
+       case SIOCSVNETID:
+       case SIOCDVNETID:
+       case SIOCDIFPHYADDR:
+       case SIOCSLIFPHYRTABLE:
+               if (ISSET(ifp->if_flags, IFF_RUNNING)) {
+                       error = EBUSY;
+                       break;
+               }
+
+               /* FALLTHROUGH */
+       default:
+               error = gre_tunnel_ioctl(ifp, &sc->sc_tunnel, cmd, data);
+               break;
+       }
+
+       return (error);
+}
+
+static int
 egre_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data)
 {
        struct egre_softc *sc = ifp->if_softc;
@@ -2357,6 +2688,54 @@ gre_get_vnetflowid(struct gre_tunnel *tu
 }
 
 static int
+mgre_up(struct mgre_softc *sc)
+{
+       unsigned int hlen;
+
+       switch (sc->sc_tunnel.t_af) {
+       case AF_UNSPEC:
+               return (EDESTADDRREQ);
+       case AF_INET:
+               hlen = sizeof(struct ip);
+               break;
+#ifdef INET6
+       case AF_INET6:
+               hlen = sizeof(struct ip6_hdr);
+               break;
+#endif /* INET6 */
+       }
+
+       hlen += sizeof(struct gre_header);
+       if (sc->sc_tunnel.t_key_mask != GRE_KEY_NONE)
+               hlen += sizeof(struct gre_h_key);
+
+       NET_ASSERT_LOCKED();
+
+       if (RBT_INSERT(mgre_tree, &mgre_tree, sc) != NULL)
+               return (EADDRINUSE);
+
+       sc->sc_if.if_hdrlen = hlen;
+       SET(sc->sc_if.if_flags, IFF_RUNNING);
+
+       return (0);
+}
+
+static int
+mgre_down(struct mgre_softc *sc)
+{
+       NET_ASSERT_LOCKED();
+
+       CLR(sc->sc_if.if_flags, IFF_RUNNING);
+       sc->sc_if.if_hdrlen = 24; /* symmetry */
+
+       RBT_REMOVE(mgre_tree, &mgre_tree, sc);
+
+       /* barrier? */
+
+       return (0);
+}
+
+static int
 egre_up(struct egre_softc *sc)
 {
        if (sc->sc_tunnel.t_af == AF_UNSPEC)
@@ -2828,32 +3207,12 @@ gre_ip_cmp(int af, const union gre_addr 
 }
 
 static int
-gre_cmp(const struct gre_tunnel *a, const struct gre_tunnel *b)
+gre_cmp_src(const struct gre_tunnel *a, const struct gre_tunnel *b)
 {
        uint32_t ka, kb;
        uint32_t mask;
        int rv;
 
-       /* sort by routing table */
-       if (a->t_rtableid > b->t_rtableid)
-               return (1);
-       if (a->t_rtableid < b->t_rtableid)
-               return (-1);
-
-       /* sort by address */
-       if (a->t_af > b->t_af)
-               return (1);
-       if (a->t_af < b->t_af)
-               return (-1);
-
-       rv = gre_ip_cmp(a->t_af, &a->t_dst, &b->t_dst);
-       if (rv != 0)
-               return (rv);
-
-       rv = gre_ip_cmp(a->t_af, &a->t_src, &b->t_src);
-       if (rv != 0)
-               return (rv);
-
        /* is K set at all? */
        ka = a->t_key_mask & GRE_KEY_ENTROPY;
        kb = b->t_key_mask & GRE_KEY_ENTROPY;
@@ -2879,8 +3238,44 @@ gre_cmp(const struct gre_tunnel *a, cons
                        return (-1);
        }
 
+       /* sort by routing table */
+       if (a->t_rtableid > b->t_rtableid)
+               return (1);
+       if (a->t_rtableid < b->t_rtableid)
+               return (-1);
+
+       /* sort by address */
+       if (a->t_af > b->t_af)
+               return (1);
+       if (a->t_af < b->t_af)
+               return (-1);
+
+       rv = gre_ip_cmp(a->t_af, &a->t_src, &b->t_src);
+       if (rv != 0)
+               return (rv);
+
        return (0);
 }
+
+static int
+gre_cmp(const struct gre_tunnel *a, const struct gre_tunnel *b)
+{
+       int rv;
+
+       rv = gre_cmp_src(a, b);
+       if (rv != 0)
+               return (rv);
+
+       return (gre_ip_cmp(a->t_af, &a->t_dst, &b->t_dst));
+}
+
+static inline int
+mgre_cmp(const struct mgre_softc *a, const struct mgre_softc *b)
+{
+       return (gre_cmp_src(&a->sc_tunnel, &b->sc_tunnel));
+}
+
+RBT_GENERATE(mgre_tree, mgre_softc, sc_entry, mgre_cmp);
 
 static inline int
 egre_cmp(const struct egre_softc *a, const struct egre_softc *b)

Reply via email to