this implements MikroTiks Ethernet over IP protocol support.

The Mikrotik protocol is basically GRE, so this is implemented as
eoip(4) as (yet another) part of if_gre. the main differences between
egre and eoip is that eoip uses gre version 1 (not 0) and 0x6400
as the protocol identifier (not transparent ethernet as per rfc
1701), mandates a key header, but splits the key into 16bit len and
tunnel id fields (a bit like pptp).

the keepalive semantics are also different. keepalives are just 0
length packets, and arent echoed. each side listens for the others
packet to see if theyre up, but doesn't relay the keepalives. theyre
more hellos maybe?

like egre though, it still misaligns the payload, but what doesnt
these days?

this also tweaks tcpdump to better support the protocol.

ok?

Index: sys/net/if_gre.c
===================================================================
RCS file: /cvs/src/sys/net/if_gre.c,v
retrieving revision 1.122
diff -u -p -r1.122 if_gre.c
--- sys/net/if_gre.c    12 Mar 2018 12:47:35 -0000      1.122
+++ sys/net/if_gre.c    27 May 2018 00:12:36 -0000
@@ -124,6 +124,13 @@ struct gre_h_key {
        uint32_t                gre_key;
 } __packed __aligned(4);
 
+#define GRE_EOIP               0x6400
+
+struct gre_h_key_eoip {
+       uint16_t                eoip_len;       /* network order */
+       uint16_t                eoip_tunnel_id; /* little endian */
+} __packed __aligned(4);
+
 #define NVGRE_VSID_RES_MIN     0x000000 /* reserved for future use */
 #define NVGRE_VSID_RES_MAX     0x000fff
 #define NVGRE_VSID_NVE2NVE     0xffffff /* vendor specific NVE-to-NVE comms */
@@ -211,6 +218,12 @@ static struct mbuf *
 #define gre_encap(_t, _m, _p, _ttl, _tos) \
                gre_encap_dst((_t), &(_t)->t_dst, (_m), (_p), (_ttl), (_tos))
 
+static struct mbuf *
+               gre_encap_dst_ip(const struct gre_tunnel *,
+                   const union gre_addr *, struct mbuf *, uint8_t, uint8_t);
+#define gre_encap_ip(_t, _m, _ttl, _tos) \
+               gre_encap_dst_ip((_t), &(_t)->t_dst, (_m), (_ttl), (_tos))
+
 static int
                gre_ip_output(const struct gre_tunnel *, struct mbuf *);
 
@@ -266,7 +279,7 @@ static int  gre_ioctl(struct ifnet *, u_l
 
 static int     gre_up(struct gre_softc *);
 static int     gre_down(struct gre_softc *);
-static void    gre_link_state(struct gre_softc *);
+static void    gre_link_state(struct ifnet *, unsigned int);
 
 static int     gre_input_key(struct mbuf **, int *, int, int,
                    struct gre_tunnel *);
@@ -457,6 +470,61 @@ struct nvgre_ucast_tree nvgre_ucast_tree
 struct nvgre_mcast_tree nvgre_mcast_tree = RBT_INITIALIZER();
 
 /*
+ * MikroTik Ethernet over IP protocol (eoip)
+ */
+
+struct eoip_softc {
+       struct gre_tunnel       sc_tunnel; /* must be first */
+       uint16_t                sc_tunnel_id;
+       RBT_ENTRY(eoip_softc)   sc_entry;
+
+       struct arpcom           sc_ac;
+       struct ifmedia          sc_media;
+
+       struct timeout          sc_ka_send;
+       struct timeout          sc_ka_hold;
+
+       unsigned int            sc_ka_state;
+       unsigned int            sc_ka_timeo;
+       unsigned int            sc_ka_count;
+
+       unsigned int            sc_ka_holdmax;
+       unsigned int            sc_ka_holdcnt;
+};
+
+RBT_HEAD(eoip_tree, eoip_softc);
+
+static inline int
+               eoip_cmp(const struct eoip_softc *, const struct eoip_softc *);
+
+RBT_PROTOTYPE(eoip_tree, eoip_softc, sc_entry, eoip_cmp);
+
+static int     eoip_clone_create(struct if_clone *, int);
+static int     eoip_clone_destroy(struct ifnet *);
+
+static void    eoip_start(struct ifnet *);
+static int     eoip_ioctl(struct ifnet *, u_long, caddr_t);
+
+static void    eoip_keepalive_send(void *);
+static void    eoip_keepalive_recv(struct eoip_softc *);
+static void    eoip_keepalive_hold(void *);
+
+static int     eoip_up(struct eoip_softc *);
+static int     eoip_down(struct eoip_softc *);
+
+static struct mbuf *
+               eoip_encap(struct eoip_softc *, struct mbuf *, uint8_t);
+
+static struct mbuf *
+               eoip_input(struct gre_tunnel *, struct mbuf *,
+                   const struct gre_header *, int);
+struct if_clone eoip_cloner =
+    IF_CLONE_INITIALIZER("eoip", eoip_clone_create, eoip_clone_destroy);
+ 
+/* protected by NET_LOCK */
+struct eoip_tree eoip_tree = RBT_INITIALIZER();
+
+/*
  * It is not easy to calculate the right value for a GRE MTU.
  * We leave this task to the admin and use the same default that
  * other vendors use.
@@ -482,6 +550,7 @@ greattach(int n)
        if_clone_attach(&mgre_cloner);
        if_clone_attach(&egre_cloner);
        if_clone_attach(&nvgre_cloner);
+       if_clone_attach(&eoip_cloner);
 }
 
 static int
@@ -725,6 +794,65 @@ nvgre_clone_destroy(struct ifnet *ifp)
        return (0);
 }
 
+static int
+eoip_clone_create(struct if_clone *ifc, int unit)
+{
+       struct eoip_softc *sc;
+       struct ifnet *ifp;
+
+       sc = malloc(sizeof(*sc), M_DEVBUF, M_WAITOK|M_ZERO);
+       ifp = &sc->sc_ac.ac_if;
+
+       snprintf(ifp->if_xname, sizeof(ifp->if_xname), "%s%d",
+           ifc->ifc_name, unit);
+
+       ifp->if_softc = sc;
+       ifp->if_ioctl = eoip_ioctl;
+       ifp->if_start = eoip_start;
+       ifp->if_xflags = IFXF_CLONED;
+       IFQ_SET_MAXLEN(&ifp->if_snd, IFQ_MAXLEN);
+       ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST;
+       ether_fakeaddr(ifp);
+
+       sc->sc_tunnel.t_ttl = ip_defttl;
+       sc->sc_tunnel.t_df = htons(0);
+
+       sc->sc_ka_timeo = 10;
+       sc->sc_ka_count = 10;
+
+       timeout_set(&sc->sc_ka_send, eoip_keepalive_send, sc);
+       timeout_set_proc(&sc->sc_ka_hold, eoip_keepalive_hold, sc);
+       sc->sc_ka_state = GRE_KA_DOWN;
+
+       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);
+
+       if_attach(ifp);
+       ether_ifattach(ifp);
+
+       return (0);
+}
+
+static int
+eoip_clone_destroy(struct ifnet *ifp)
+{
+       struct eoip_softc *sc = ifp->if_softc;
+
+       NET_LOCK();
+       if (ISSET(ifp->if_flags, IFF_RUNNING))
+               eoip_down(sc);
+       NET_UNLOCK();
+
+       ifmedia_delete_instance(&sc->sc_media, IFM_INST_ANY);
+       ether_ifdetach(ifp);
+       if_detach(ifp);
+
+       free(sc, M_DEVBUF, sizeof(*sc));
+
+       return (0);
+}
+
 int
 gre_input(struct mbuf **mp, int *offp, int type, int af)
 {
@@ -802,6 +930,31 @@ mgre_find(const struct gre_tunnel *key)
        return (NULL);
 }
 
+static struct mbuf *
+gre_input_1(struct gre_tunnel *key, struct mbuf *m,
+    const struct gre_header *gh, int iphlen)
+{
+       switch (gh->gre_proto) {
+       case htons(ETHERTYPE_PPP):
+#ifdef PIPEX
+               if (pipex_enable) {
+                       struct pipex_session *session;
+
+                       session = pipex_pptp_lookup_session(m);
+                       if (session != NULL &&
+                           pipex_pptp_input(m, session) == NULL)
+                               return (NULL);
+               }
+#endif
+               break;
+       case htons(GRE_EOIP):
+               return (eoip_input(key, m, gh, iphlen));
+               break;
+       }
+
+       return (m);
+}
+
 static int
 gre_input_key(struct mbuf **mp, int *offp, int type, int af,
     struct gre_tunnel *key)
@@ -821,6 +974,8 @@ gre_input_key(struct mbuf **mp, int *off
        if (!gre_allow)
                goto decline;
 
+       key->t_rtableid = m->m_pkthdr.ph_rtableid;
+
        hlen = iphlen + sizeof(*gh);
        if (m->m_pkthdr.len < hlen)
                goto decline;
@@ -838,16 +993,9 @@ gre_input_key(struct mbuf **mp, int *off
                break;
 
        case htons(GRE_VERS_1):
-#ifdef PIPEX
-               if (pipex_enable) {
-                       struct pipex_session *session;
-
-                       session = pipex_pptp_lookup_session(m);
-                       if (session != NULL &&
-                           pipex_pptp_input(m, session) == NULL)
-                               return (IPPROTO_DONE);
-               }
-#endif
+               m = gre_input_1(key, m, gh, iphlen);
+               if (m == NULL)
+                       return (IPPROTO_DONE);
                /* FALLTHROUGH */
        default:
                goto decline;
@@ -875,8 +1023,6 @@ gre_input_key(struct mbuf **mp, int *off
        } else
                key->t_key_mask = GRE_KEY_NONE;
 
-       key->t_rtableid = m->m_pkthdr.ph_rtableid;
-
        if (gh->gre_proto == htons(ETHERTYPE_TRANSETHER)) {
                if (egre_input(key, m, hlen) == -1 &&
                    nvgre_input(key, m, hlen) == -1)
@@ -1381,7 +1527,7 @@ gre_keepalive_recv(struct ifnet *ifp, st
                        break;
 
                sc->sc_ka_state = GRE_KA_UP;
-               gre_link_state(sc);
+               gre_link_state(&sc->sc_if, sc->sc_ka_state);
                break;
 
        case GRE_KA_UP:
@@ -1815,6 +1961,13 @@ gre_encap_dst(const struct gre_tunnel *t
                }
        }
 
+       return (gre_encap_dst_ip(tunnel, dst, m, ttl, tos));
+}
+
+static struct mbuf *
+gre_encap_dst_ip(const struct gre_tunnel *tunnel, const union gre_addr *dst,
+    struct mbuf *m, uint8_t ttl, uint8_t tos)
+{
        switch (tunnel->t_af) {
        case AF_INET: {
                struct ip *ip;
@@ -2446,6 +2599,140 @@ nvgre_ioctl(struct ifnet *ifp, u_long cm
 }
 
 static int
+eoip_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data)
+{
+       struct eoip_softc *sc = ifp->if_softc;
+       struct ifreq *ifr = (struct ifreq *)data;
+       struct ifkalivereq *ikar = (struct ifkalivereq *)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 = eoip_up(sc);
+                       else
+                               error = 0;
+               } else {
+                       if (ISSET(ifp->if_flags, IFF_RUNNING))
+                               error = eoip_down(sc);
+               }
+               break;
+
+       case SIOCSETKALIVE:
+               if (ISSET(ifp->if_flags, IFF_RUNNING)) {
+                       error = EBUSY;
+                       break;
+               }
+
+               if (ikar->ikar_timeo < 0 || ikar->ikar_timeo > 86400 ||
+                   ikar->ikar_cnt < 0 || ikar->ikar_cnt > 256)
+                       return (EINVAL);
+
+               if (ikar->ikar_timeo == 0 || ikar->ikar_cnt == 0) {
+                       sc->sc_ka_count = 0;
+                       sc->sc_ka_timeo = 0;
+                       sc->sc_ka_state = GRE_KA_NONE;
+               } else {
+                       sc->sc_ka_count = ikar->ikar_cnt;
+                       sc->sc_ka_timeo = ikar->ikar_timeo;
+                       sc->sc_ka_state = GRE_KA_DOWN;
+               }
+               break;
+
+       case SIOCGETKALIVE:
+               ikar->ikar_cnt = sc->sc_ka_count;
+               ikar->ikar_timeo = sc->sc_ka_timeo;
+               break;
+
+       case SIOCSVNETID:
+               if (ISSET(ifp->if_flags, IFF_RUNNING)) {
+                       error = EBUSY;
+                       break;
+               }
+               if (ifr->ifr_vnetid < 0 || ifr->ifr_vnetid > 0xffff)
+                       return (EINVAL);
+
+               sc->sc_tunnel.t_key = htole16(ifr->ifr_vnetid); /* for cmp */
+               sc->sc_tunnel_id = htole16(ifr->ifr_vnetid);
+               break;
+
+       case SIOCGVNETID:
+               ifr->ifr_vnetid = letoh16(sc->sc_tunnel_id);
+               break;
+
+       case SIOCSLIFPHYADDR:
+               if (ISSET(ifp->if_flags, IFF_RUNNING)) {
+                       error = EBUSY;
+                       break;
+               }
+
+               error = gre_set_tunnel(&sc->sc_tunnel,
+                   (struct if_laddrreq *)data, 1);
+               break;
+       case SIOCGLIFPHYADDR:
+               error = gre_get_tunnel(&sc->sc_tunnel,
+                   (struct if_laddrreq *)data);
+               break;
+       case SIOCDIFPHYADDR:
+               if (ISSET(ifp->if_flags, IFF_RUNNING)) {
+                       error = EBUSY;
+                       break;
+               }
+
+               error = gre_del_tunnel(&sc->sc_tunnel);
+               break;
+
+       case SIOCSLIFPHYRTABLE:
+               if (ISSET(ifp->if_flags, IFF_RUNNING)) {
+                       error = EBUSY;
+                       break;
+               }
+
+               if (ifr->ifr_rdomainid < 0 ||
+                   ifr->ifr_rdomainid > RT_TABLEID_MAX ||
+                   !rtable_exists(ifr->ifr_rdomainid)) {
+                       error = EINVAL;
+                       break;
+               }
+               sc->sc_tunnel.t_rtableid = ifr->ifr_rdomainid;
+               break;
+       case SIOCGLIFPHYRTABLE:
+               ifr->ifr_rdomainid = sc->sc_tunnel.t_rtableid;
+               break;
+
+       case SIOCSLIFPHYTTL:
+               if (ifr->ifr_ttl < 1 || ifr->ifr_ttl > 0xff) {
+                       error = EINVAL;
+                       break;
+               }
+
+               /* commit */
+               sc->sc_tunnel.t_ttl = (uint8_t)ifr->ifr_ttl;
+               break;
+       case SIOCGLIFPHYTTL:
+               ifr->ifr_ttl = (int)sc->sc_tunnel.t_ttl;
+               break;
+
+       case SIOCSLIFPHYDF:
+               /* commit */
+               sc->sc_tunnel.t_df = ifr->ifr_df ? htons(IP_DF) : htons(0);
+               break;
+       case SIOCGLIFPHYDF:
+               ifr->ifr_df = sc->sc_tunnel.t_df ? 1 : 0;
+               break;
+
+       default:
+               error = ether_ioctl(ifp, &sc->sc_ac, cmd, data);
+               break;
+       }
+
+       return (error);
+}
+
+static int
 gre_up(struct gre_softc *sc)
 {
        NET_ASSERT_LOCKED();
@@ -2477,21 +2764,19 @@ gre_down(struct gre_softc *sc)
                        timeout_barrier(&sc->sc_ka_send);
 
                sc->sc_ka_state = GRE_KA_DOWN;
-
-               gre_link_state(sc);
+               gre_link_state(&sc->sc_if, sc->sc_ka_state);
        }
 
        return (0);
 }
 
 static void
-gre_link_state(struct gre_softc *sc)
+gre_link_state(struct ifnet *ifp, unsigned int state)
 {
-       struct ifnet *ifp = &sc->sc_if;
        int link_state = LINK_STATE_UNKNOWN;
 
        if (ISSET(ifp->if_flags, IFF_RUNNING)) {
-               switch (sc->sc_ka_state) {
+               switch (state) {
                case GRE_KA_NONE:
                        /* maybe up? or down? it's unknown, really */
                        break;
@@ -2613,14 +2898,15 @@ static void
 gre_keepalive_hold(void *arg)
 {
        struct gre_softc *sc = arg;
+       struct ifnet *ifp = &sc->sc_if;
 
-       if (!ISSET(sc->sc_if.if_flags, IFF_RUNNING) ||
+       if (!ISSET(ifp->if_flags, IFF_RUNNING) ||
            sc->sc_ka_state == GRE_KA_NONE)
                return;
 
        NET_LOCK();
        sc->sc_ka_state = GRE_KA_DOWN;
-       gre_link_state(sc);
+       gre_link_state(ifp, sc->sc_ka_state);
        NET_UNLOCK();
 }
 
@@ -3330,6 +3616,263 @@ nvgre_send(void *arg)
        ifp->if_oerrors += oerrors; /* XXX should be ifq_oerrors */
 }
 
+static int
+eoip_up(struct eoip_softc *sc)
+{
+       if (sc->sc_tunnel.t_af == AF_UNSPEC)
+               return (EDESTADDRREQ);
+
+       NET_ASSERT_LOCKED();
+
+       if (RBT_INSERT(eoip_tree, &eoip_tree, sc) != NULL)
+               return (EADDRINUSE);
+
+       SET(sc->sc_ac.ac_if.if_flags, IFF_RUNNING);
+
+       if (sc->sc_ka_state != GRE_KA_NONE) {
+               sc->sc_ka_holdmax = sc->sc_ka_count;
+               eoip_keepalive_send(sc);
+       }
+
+       return (0);
+}
+
+static int
+eoip_down(struct eoip_softc *sc)
+{
+       NET_ASSERT_LOCKED();
+       CLR(sc->sc_ac.ac_if.if_flags, IFF_RUNNING);
+
+       if (sc->sc_ka_state != GRE_KA_NONE) {
+               if (!timeout_del(&sc->sc_ka_hold))
+                       timeout_barrier(&sc->sc_ka_hold);
+               if (!timeout_del(&sc->sc_ka_send))
+                       timeout_barrier(&sc->sc_ka_send);
+
+               sc->sc_ka_state = GRE_KA_DOWN;
+               gre_link_state(&sc->sc_ac.ac_if, sc->sc_ka_state);
+       }
+
+       RBT_REMOVE(eoip_tree, &eoip_tree, sc);
+
+       return (0);
+}
+
+static void
+eoip_start(struct ifnet *ifp)
+{
+       struct eoip_softc *sc = ifp->if_softc;
+       struct mbuf *m0, *m;
+#if NBPFILTER > 0
+       caddr_t if_bpf;
+#endif
+
+       if (!gre_allow) {
+               ifq_purge(&ifp->if_snd);
+               return;
+       }
+
+       while ((m0 = ifq_dequeue(&ifp->if_snd)) != NULL) {
+#if NBPFILTER > 0
+               if_bpf = ifp->if_bpf;
+               if (if_bpf)
+                       bpf_mtap_ether(if_bpf, m0, BPF_DIRECTION_OUT);
+#endif
+
+               m = m_gethdr(M_DONTWAIT, m0->m_type);
+               if (m == NULL) {
+                       m_freem(m0);
+                       continue;
+               }
+
+               M_MOVE_PKTHDR(m, m0);
+               m->m_next = m0;
+
+               MH_ALIGN(m, 0);
+               m->m_len = 0;
+
+               m = eoip_encap(sc, m, 0);
+               if (m == NULL || gre_ip_output(&sc->sc_tunnel, m) != 0) {
+                       ifp->if_oerrors++;
+                       continue;
+               }
+       }
+}
+
+static struct mbuf *
+eoip_encap(struct eoip_softc *sc, struct mbuf *m, uint8_t tos)
+{
+       struct gre_header *gh;
+       struct gre_h_key_eoip *eoiph;
+       int len = m->m_pkthdr.len;
+
+       m = m_prepend(m, sizeof(*gh) + sizeof(*eoiph), M_DONTWAIT);
+       if (m == NULL)
+               return (NULL);
+
+       gh = mtod(m, struct gre_header *);
+       gh->gre_flags = htons(GRE_VERS_1 | GRE_KP);
+       gh->gre_proto = htons(GRE_EOIP);
+
+       eoiph = (struct gre_h_key_eoip *)(gh + 1);
+       htobem16(&eoiph->eoip_len, len);
+       eoiph->eoip_tunnel_id = sc->sc_tunnel_id;
+
+       return (gre_encap_ip(&sc->sc_tunnel, m, sc->sc_tunnel.t_ttl, tos));
+}
+
+static void
+eoip_keepalive_send(void *arg)
+{
+       struct eoip_softc *sc = arg;
+       struct mbuf *m;
+       int linkhdr;
+
+       if (!ISSET(sc->sc_ac.ac_if.if_flags, IFF_RUNNING))
+               return;
+
+       /* this is really conservative */
+#ifdef INET6
+       linkhdr = max_linkhdr + MAX(sizeof(struct ip), sizeof(struct ip6_hdr)) +
+           sizeof(struct gre_header) + sizeof(struct gre_h_key_eoip);
+#else
+       linkhdr = max_linkhdr + sizeof(struct ip) +
+           sizeof(struct gre_header) + sizeof(struct gre_h_key_eoip);
+#endif
+       MGETHDR(m, M_DONTWAIT, MT_DATA);
+       if (m == NULL)
+               return;
+
+       if (linkhdr > MHLEN) {
+               MCLGETI(m, M_DONTWAIT, NULL, linkhdr);
+               if (!ISSET(m->m_flags, M_EXT)) {
+                       m_freem(m);
+                       return;
+               }
+       }
+
+       m->m_pkthdr.len = m->m_len = linkhdr;
+       m_adj(m, linkhdr);
+
+       m = eoip_encap(sc, m, IPTOS_PREC_INTERNETCONTROL);
+       if (m == NULL)
+               return;
+
+       gre_ip_output(&sc->sc_tunnel, m);
+
+       timeout_add_sec(&sc->sc_ka_send, sc->sc_ka_timeo);
+}
+
+static void
+eoip_keepalive_hold(void *arg)
+{
+       struct eoip_softc *sc = arg;
+       struct ifnet *ifp = &sc->sc_ac.ac_if;
+
+       if (!ISSET(ifp->if_flags, IFF_RUNNING))
+               return;
+
+       NET_LOCK();
+       sc->sc_ka_state = GRE_KA_DOWN;
+       gre_link_state(ifp, sc->sc_ka_state);
+       NET_UNLOCK();
+}
+
+static void
+eoip_keepalive_recv(struct eoip_softc *sc)
+{
+       switch (sc->sc_ka_state) {
+       case GRE_KA_NONE:
+               return;
+       case GRE_KA_DOWN:
+               sc->sc_ka_state = GRE_KA_HOLD;
+               sc->sc_ka_holdcnt = sc->sc_ka_holdmax;
+               sc->sc_ka_holdmax = MIN(sc->sc_ka_holdmax * 2,
+                   16 * sc->sc_ka_count);
+               break;
+       case GRE_KA_HOLD:
+               if (--sc->sc_ka_holdcnt > 0)
+                       break;
+
+               sc->sc_ka_state = GRE_KA_UP;
+               gre_link_state(&sc->sc_ac.ac_if, sc->sc_ka_state);
+               break;
+
+       case GRE_KA_UP:
+               sc->sc_ka_holdmax--;
+               sc->sc_ka_holdmax = MAX(sc->sc_ka_holdmax, sc->sc_ka_count);
+               break;
+       }
+
+       timeout_add_sec(&sc->sc_ka_hold, sc->sc_ka_timeo * sc->sc_ka_count);
+}
+
+static struct mbuf *
+eoip_input(struct gre_tunnel *key, struct mbuf *m,
+    const struct gre_header *gh, int iphlen)
+{
+       struct mbuf_list ml = MBUF_LIST_INITIALIZER();
+       struct eoip_softc *sc;
+       struct gre_h_key_eoip *eoiph;
+       int hlen, len;
+       caddr_t buf;
+
+       if (gh->gre_flags != htons(GRE_KP | GRE_VERS_1))
+               goto decline;
+
+       hlen = iphlen + sizeof(*gh) + sizeof(*eoiph);
+       if (m->m_pkthdr.len < hlen)
+               goto decline;
+
+       m = m_pullup(m, hlen);
+       if (m == NULL)
+               return (NULL);
+
+       buf = mtod(m, caddr_t);
+       gh = (struct gre_header *)(buf + iphlen);
+       eoiph = (struct gre_h_key_eoip *)(gh + 1);
+
+       key->t_key = eoiph->eoip_tunnel_id;
+
+       NET_ASSERT_LOCKED();
+       sc = RBT_FIND(eoip_tree, &eoip_tree, (const struct eoip_softc *)key);
+       if (sc == NULL)
+               goto decline;
+
+       /* it's ours now */
+       len = bemtoh16(&eoiph->eoip_len);
+       if (len == 0) {
+               eoip_keepalive_recv(sc);
+               goto drop;
+       }
+
+       m = gre_ether_align(m, hlen);
+       if (m == NULL)
+               return (NULL);
+
+       if (m->m_pkthdr.len < len)
+               goto drop;
+       if (m->m_pkthdr.len != len)
+               m_adj(m, len - m->m_pkthdr.len);
+
+       m->m_flags &= ~(M_MCAST|M_BCAST);
+
+#if NPF > 0
+       pf_pkt_addr_changed(m);
+#endif
+
+       ml_enqueue(&ml, m);
+       if_input(&sc->sc_ac.ac_if, &ml);
+
+       return (NULL);
+
+decline:
+       return (m);
+drop:
+       m_freem(m);
+       return (NULL);
+}
+
 int
 gre_sysctl(int *name, u_int namelen, void *oldp, size_t *oldlenp, void *newp,
     size_t newlen)
@@ -3543,3 +4086,40 @@ nvgre_cmp_mcast_sc(const struct nvgre_so
 
 RBT_GENERATE(nvgre_ucast_tree, nvgre_softc, sc_uentry, nvgre_cmp_ucast);
 RBT_GENERATE(nvgre_mcast_tree, nvgre_softc, sc_mentry, nvgre_cmp_mcast_sc);
+
+static inline int
+eoip_cmp(const struct eoip_softc *ea, const struct eoip_softc *eb)
+{
+       const struct gre_tunnel *a = &ea->sc_tunnel;
+       const struct gre_tunnel *b = &eb->sc_tunnel;
+       int rv;
+
+       if (a->t_key > b->t_key)
+               return (1);
+       if (a->t_key < b->t_key)
+               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);
+
+       rv = gre_ip_cmp(a->t_af, &a->t_dst, &b->t_dst);
+       if (rv != 0)
+               return (rv);
+
+       return (0);
+}
+
+RBT_GENERATE(eoip_tree, eoip_softc, sc_entry, eoip_cmp);
Index: usr.sbin/tcpdump/print-gre.c
===================================================================
RCS file: /cvs/src/usr.sbin/tcpdump/print-gre.c,v
retrieving revision 1.19
diff -u -p -r1.19 print-gre.c
--- usr.sbin/tcpdump/print-gre.c        24 Feb 2018 08:53:36 -0000      1.19
+++ usr.sbin/tcpdump/print-gre.c        27 May 2018 00:12:36 -0000
@@ -79,6 +79,8 @@ struct wccp_redirect {
 
 void gre_print_0(const u_char *, u_int);
 void gre_print_1(const u_char *, u_int);
+void gre_print_pptp(const u_char *, u_int, uint16_t);
+void gre_print_eoip(const u_char *, u_int, uint16_t);
 void gre_sre_print(u_int16_t, u_int8_t, u_int8_t, const u_char *, u_int);
 void gre_sre_ip_print(u_int8_t, u_int8_t, const u_char *, u_int);
 void gre_sre_asn_print(u_int8_t, u_int8_t, const u_char *, u_int);
@@ -271,7 +273,7 @@ trunc:
 void
 gre_print_1(const u_char *p, u_int length)
 {
-       uint16_t flags, proto, len;
+       uint16_t flags, proto;
        int l;
 
        l = snapend - p;
@@ -281,10 +283,45 @@ gre_print_1(const u_char *p, u_int lengt
        l -= sizeof(flags);
        length -= sizeof(flags);
 
+       if (l < sizeof(proto))
+               goto trunc;
+
+       proto = EXTRACT_16BITS(p);
+       p += sizeof(proto);
+       l -= sizeof(proto);
+       length -= sizeof(proto);
+
+       switch (proto) {
+       case ETHERTYPE_PPP:
+               gre_print_pptp(p, length, flags);
+               break;
+       case 0x6400:
+               /* MikroTik RouterBoard Ethernet over IP (EoIP) */
+               gre_print_eoip(p, length, flags);
+               break;
+       default:
+               printf("unknown-gre1-proto-%04x", proto);
+               break;
+       }
+
+       return;
+
+trunc:
+       printf("[|gre1]");
+}
+
+void
+gre_print_pptp(const u_char *p, u_int length, uint16_t flags)
+{
+       uint16_t len;
+       int l;
+
+       l = snapend - p;
+
        printf("pptp");
 
        if (vflag) {
-               printf(" [%s%s%s%s%s%s] ",
+               printf(" [%s%s%s%s%s%s]",
                    (flags & GRE_CP) ? "C" : "",
                    (flags & GRE_RP) ? "R" : "",
                    (flags & GRE_KP) ? "K" : "",
@@ -293,14 +330,6 @@ gre_print_1(const u_char *p, u_int lengt
                    (flags & GRE_AP) ? "A" : "");
        }
 
-       if (l < sizeof(proto))
-               goto trunc;
-
-       proto = EXTRACT_16BITS(p);
-       p += sizeof(proto);
-       l -= sizeof(proto);
-       length -= sizeof(proto);
-
        if (flags & GRE_CP) {
                printf(" cpset!");
                return;
@@ -365,18 +394,67 @@ gre_print_1(const u_char *p, u_int lengt
 
        printf(": ");
 
-       switch (proto) {
-       case ETHERTYPE_PPP:
-               ppp_hdlc_print(p, len);
-               break;
-       default:
-               printf("unknown-proto-%04x", proto);
-               break;
-       }
+       ppp_hdlc_print(p, len);
        return;
 
 trunc:
        printf("[|pptp]");
+}
+
+void
+gre_print_eoip(const u_char *p, u_int length, uint16_t flags)
+{
+       uint16_t len, id;
+       int l;
+
+       l = snapend - p;
+
+       printf("eoip");
+
+       flags &= ~GRE_VERS;
+       if (flags != GRE_KP) {
+               printf(" unknown-eoip-flags-%04x!", flags);
+               return;
+       }
+
+       if (l < sizeof(len))
+               goto trunc;
+
+       len = EXTRACT_16BITS(p);
+       p += sizeof(len);
+       l -= sizeof(len);
+       length -= sizeof(len);
+
+       if (l < sizeof(id))
+               goto trunc;
+
+       id = EXTRACT_LE_16BITS(p);
+       p += sizeof(id);
+       l -= sizeof(id);
+       length -= sizeof(id);
+
+       if (vflag)
+               printf(" len=%u tunnel-id=%u", len, id);
+       else
+               printf(" %u", id);
+
+        if (length < len) {
+                (void)printf(" truncated-eoip - %d bytes missing!",
+                   len - length);
+               len = length;
+       }
+
+       printf(": ");
+
+       if (len == 0)
+               printf("keepalive");
+       else
+               ether_tryprint(p, len, 0);
+
+       return;
+
+trunc:
+       printf("[|eoip]");
 }
 
 void
Index: share/man/man4/Makefile
===================================================================
RCS file: /cvs/src/share/man/man4/Makefile,v
retrieving revision 1.681
diff -u -p -r1.681 Makefile
--- share/man/man4/Makefile     23 May 2018 22:34:31 -0000      1.681
+++ share/man/man4/Makefile     27 May 2018 00:12:36 -0000
@@ -23,7 +23,7 @@ MAN=  aac.4 ac97.4 acphy.4 acrtc.4 \
        dc.4 dcphy.4 ddb.4 de.4 diskmap.4 divert.4 dpt.4 drm.4 dwctwo.4 \
        dwdog.4 dwge.4 dwiic.4 dwmmc.4 dwpcie.4 dwxe.4 \
        eap.4 ec.4 eephy.4 ef.4 eg.4 ehci.4 eisa.4 el.4 em.4 emc.4 gcu.4 \
-       emu.4 enc.4 endrun.4 envy.4 ep.4 epic.4 esa.4 \
+       emu.4 enc.4 endrun.4 envy.4 eoip.4 ep.4 epic.4 esa.4 \
        eso.4 ess.4 et.4 etherip.4 etphy.4 ex.4 exphy.4 exrtc.4 \
        fd.4 fdc.4 fec.4 fins.4 fintek.4 fms.4 fuse.4 fxp.4 gdt.4 \
        gentbi.4 gem.4 gif.4 \
Index: share/man/man4/eoip.4
===================================================================
RCS file: share/man/man4/eoip.4
diff -N share/man/man4/eoip.4
--- /dev/null   1 Jan 1970 00:00:00 -0000
+++ share/man/man4/eoip.4       27 May 2018 00:12:36 -0000
@@ -0,0 +1,202 @@
+.\" $OpenBSD: gre.4,v 1.72 2018/04/20 22:55:53 bentley Exp $
+.\" $NetBSD: gre.4,v 1.10 1999/12/22 14:55:49 kleink Exp $
+.\"
+.\" Copyright 1998 (c) The NetBSD Foundation, Inc.
+.\" All rights reserved.
+.\"
+.\" This code is derived from software contributed to The NetBSD Foundation
+.\" by Heiko W. Rupp <h...@pilhuhn.de>
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\"    notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\"    notice, this list of conditions and the following disclaimer in the
+.\"    documentation and/or other materials provided with the distribution.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
+.\" ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+.\" TO, THE  IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+.\" PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
+.\" BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+.\" CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+.\" SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+.\" INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+.\" CONTRACT, STRICT  LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+.\" ARISING IN ANY WAY  OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+.\" POSSIBILITY OF SUCH DAMAGE.
+.\"
+.Dd $Mdocdate: April 20 2018 $
+.Dt EOIP 4
+.Os
+.Sh NAME
+.Nm eoip
+.Nd MikroTik Ethernet over IP tunnel network device
+.Sh SYNOPSIS
+.Cd "pseudo-device gre"
+.Sh DESCRIPTION
+The
+.Nm
+interface provides tunnelling of IP protocols across
+IPv4 and IPv6 networks using the
+MikroTik Ethernet over IP (EoIP) encapsulation protocol.
+.Pp
+The protocol is based on the Generic Routing and Encapsulation (GRE)
+protocol.
+GRE datagrams (IP protocol number 47) consist of a GRE header
+and an outer IP header for encapsulating another protocol's datagram.
+The GRE header specifies a version, and the type of the encapsulated datagram,
+allowing for the tunnelling of multiple protocols.
+EoIP uses GRE version 1 and it's own protocol identifier for Ethernet
+and it's own keepalive semantics,
+making it distinct from GRE version 0 protocols.
+However, it is implemented as part of the same driver providing
+.Xr gre 4 ,
+.Xr egre 4 ,
+.Xr mgre 4 ,
+and
+.Xr nvgre 4 .
+.Pp
+Different tunnels between the same endpoints are distinguished
+by a 16-bit tunnel identifier field in the header.
+.Pp
+All GRE packet processing in the system is allowed or denied by setting the
+.Va net.inet.gre.allow
+.Xr sysctl 8
+variable.
+To allow GRE packet processing, set
+.Va net.inet.gre.allow
+to 1.
+.Pp
+.Nm
+interfaces can be created at runtime using the
+.Ic ifconfig eoip Ns Ar N Ic create
+command or by setting up a
+.Xr hostname.if 5
+configuration file for
+.Xr netstart 8 .
+.Pp
+For correct operation, encapsulated traffic must not be routed
+over the interface itself.
+This can be implemented by adding a distinct or a more specific
+route to the tunnel destination than the hosts or networks routed
+via the tunnel interface.
+Alternatively, the tunnel traffic may be configured in a separate
+routing table to the encapsulated traffic.
+.Ss Programming Interface
+.Nm
+interfaces support the following
+.Xr ioctl 2
+calls for configuring tunnel options:
+.Bl -tag -width indent -offset 3n
+.It Dv SIOCSLIFPHYADDR Fa "struct if_laddrreq *"
+Set the unicast IPv4 or IPv6 addresses for the encapsulating IP packets.
+The addresses may only be configured while the interface is down.
+.It Dv SIOCGLIFPHYADDR Fa "struct if_laddrreq *"
+Get the addresses used for the encapsulating IP packets.
+.It Dv SIOCDIFPHYADDR Fa "struct ifreq *"
+Clear the addresses used for the encapsulating IP packets.
+The addresses may only be cleared while the interface is down.
+.It Dv SIOCSVNETID Fa "struct ifreq *"
+Configure a virtual network identifier for use as the Tunnel Identifier.
+The virtual network identifier may only be configured while the
+interface is down.
+The Tunnel Identifier is a 16-bit value.
+.It Dv SIOCGVNETID Fa "struct ifreq *"
+Get the virtual network identifer used in the GRE Key header.
+.It Dv SIOCSLIFPHYRTABLE Fa "struct ifreq *"
+Set the routing table the tunnel traffic operates in.
+The routing table may only be configured while the interface is down.
+.It Dv SIOCGLIFPHYRTABLE Fa "struct ifreq *"
+Get the routing table the tunnel traffic operates in.
+.It Dv SIOCSLIFPHYTTL Fa "struct ifreq *"
+Set the Time-To-Live field in IPv4 encapsulation headers, or the
+Hop Limit field in IPv6 encapsulation headers.
+.It Dv SIOCGLIFPHYTTL Fa "struct ifreq *"
+Get the value used in the Time-To-Live field in a IPv4 encapsulation
+header or the Hop Limit field in a IPv6 encapsulation header.
+.It Dv SIOCSLIFPHYDF Fa "struct ifreq *"
+Configure whether the tunnel traffic sent by the interface can be
+fragmented or not.
+This sets the Don't Fragment (DF) bit on IPv4 packets,
+and disables fragmentation of IPv6 packets.
+.It Dv SIOCGLIFPHYDF Fa "struct ifreq *"
+Get whether the tunnel traffic sent by the interface can be fragmented
+or not.
+.It Dv SIOCSETKALIVE Fa "struct ifkalivereq *"
+Enable the transmission of keepalive packets to detect tunnel failure.
+Keepalives may only be configured while the interace is down.
+.Pp
+Setting the keepalive period or count to 0 disables keepalives on
+the tunnel.
+.It Dv SIOCGETKALIVE Fa "struct ifkalivereq *"
+Get the configuration of keepalive packets.
+.El
+.Ss Security Considerations
+EoIP does not provide any integrated security features.
+It should only be deployed on trusted private networks,
+or protected with IPsec to add authentication and encryption for
+confidentiality.
+IPsec is especially recommended when transporting EoIP over the
+public internet.
+.Pp
+The Packet Filter
+.Xr pf 4
+can be used to filter tunnel traffic with endpoint policies
+.Xr pf.conf 5 .
+.Pp
+The Time-to-Live (TTL) value of a tunnel can be set to 1 or a low
+value to restrict the traffic to the local network:
+.Bd -literal -offset indent
+# ifconfig eoipN tunnelttl 1
+.Ed
+.Sh EXAMPLES
+.Bd -literal
+Host X ---- Host A ------------ tunnel ----------- MikroTik D --- Host E
+               \e                                      /
+                \e                                    /
+                 +------ Host B ------ Host C ------+
+.Ed
+.Pp
+On Host A
+.Pq Ox :
+.Bd -literal -offset indent
+# route add default B
+# ifconfig eoipN create
+# ifconfig eoipN tunnel A D
+# ifconfig eoipN up
+# route add E D
+.Ed
+.Pp
+On Host D (MikroTik):
+.Bd -literal -offset indent
+[admin@MikroTik] > interface eoip
+[admin@MikroTik] /interface eoip> add name="eoipN" \e
+\e... local-address=D remote-address=A
+[admin@MikroTik] /interface eoip> enable eoipN
+.Ed
+.Sh SEE ALSO
+.Xr egre 4 ,
+.Xr inet 4 ,
+.Xr ip 4 ,
+.Xr netintro 4 ,
+.Xr options 4 ,
+.Xr hostname.if 5 ,
+.Xr protocols 5 ,
+.Xr ifconfig 8 ,
+.Xr netstart 8 ,
+.Xr sysctl 8
+.Sh STANDARDS
+.Rs
+.%A S. Hanks
+.%A "T. Li"
+.%A D. Farinacci
+.%A P. Traina
+.%D October 1994
+.%R RFC 1701
+.%T Generic Routing Encapsulation (GRE)
+.Re
+.Sh AUTHORS
+.An David Gwynne Aq Mt d...@openbsd.org

Reply via email to