On 26/02/18(Mon) 11:32, David Gwynne wrote: > 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
I think this should be changed based on the IFF_MULTIPOINT suggestion claudio@ made privately. If we don't need a destination then let's not require one ;) > 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 As I said privately I'd suggest you change the logic in in_ifinit() to *not* create a RTF_CLONING route by default for IFF_MULTIPOINT interface. > 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 instead of doing a 'route change' you simply do a 'route add' ;) > 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? Some comments below, but I'm ok with this going in. > 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 */ This scares me a bit. All other interfaces start with a `struct ifnet'. I'm afraid we might have some code assuming it is the first element. It looks like a micro-optimisation for a memcmp() that I wouldn't do. > 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; Obviously same here. > +}; > + > +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 */ Define maybe? > + 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? */; Better have an empty mgre_rtrequest() to not inherit old hacks. Plus if you plan to keep packets on a hold list while an entry is being resolve you already have the function to empty the list when the entry is added to the kernel. > + 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; I'd appreciate if functions like this one, accessing a global data structure currently protected by the NET_LOCK() would contain a NET_ASSERT_LOCKED() ;) > + 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); You only need rt_getll() if `rt0' can be a RTF_GATEWAY entry. You're doping them below, so you can simply "s/rt0/rt/" ;) > + /* 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) >