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

Reply via email to