diff options
author | David Gwynne <dlg@cvs.openbsd.org> | 2018-02-20 03:53:55 +0000 |
---|---|---|
committer | David Gwynne <dlg@cvs.openbsd.org> | 2018-02-20 03:53:55 +0000 |
commit | 01cecb9cd17443e36d55c29307aed362c7da651b (patch) | |
tree | b3bb0e6606b7940b2d66b6d4689a12f058188ce2 /sys/net/if_gre.c | |
parent | 81a4f149acdf6d0d239dea13f9c7699310202537 (diff) |
add support for vnetflowid.
when enabled, the 32bit key on gre a packet is split into a 24bit
key and an 8 bit flow id. this allows better use of multipath links
if the intermediate routers feed the gre key into their hashing
algorithms. because gre can encapsulate pretty much anything, it
can be non-trivial for a router to reach into a payload to harvest
entropy for feeding into a hashing algorithm. having the endpoints
do it and feed it into the gre header is a lot simpler.
this allows interoperationg with cisco gre tunnels with key entropy
enabled. this was tested against a csr1000v.
also, this arrangement coincides with how nvgre works, so it paves
the way for supporting that protocol.
right now the driver relies on the flowid in mbufs to populate the
packet field. this generally means that pf should be enabled to
provide the flowid.
Diffstat (limited to 'sys/net/if_gre.c')
-rw-r--r-- | sys/net/if_gre.c | 115 |
1 files changed, 105 insertions, 10 deletions
diff --git a/sys/net/if_gre.c b/sys/net/if_gre.c index cf743c93148..57102ae31bb 100644 --- a/sys/net/if_gre.c +++ b/sys/net/if_gre.c @@ -1,4 +1,4 @@ -/* $OpenBSD: if_gre.c,v 1.105 2018/02/19 00:46:27 dlg Exp $ */ +/* $OpenBSD: if_gre.c,v 1.106 2018/02/20 03:53:54 dlg Exp $ */ /* $NetBSD: if_gre.c,v 1.9 1999/10/25 19:18:11 drochner Exp $ */ /* @@ -142,6 +142,14 @@ union gre_addr { struct in6_addr in6; }; +#define GRE_KEY_MIN 0x00000000U +#define GRE_KEY_MAX 0xffffffffU +#define GRE_KEY_SHIFT 0 + +#define GRE_KEY_ENTROPY_MIN 0x00000000U +#define GRE_KEY_ENTROPY_MAX 0x00ffffffU +#define GRE_KEY_ENTROPY_SHIFT 8 + struct gre_tunnel { uint32_t t_key_mask; #define GRE_KEY_NONE htonl(0x00000000U) @@ -172,6 +180,9 @@ static int gre_set_vnetid(struct gre_tunnel *, struct ifreq *); static int gre_get_vnetid(struct gre_tunnel *, struct ifreq *); static int gre_del_vnetid(struct gre_tunnel *); +static int gre_set_vnetflowid(struct gre_tunnel *, struct ifreq *); +static int gre_get_vnetflowid(struct gre_tunnel *, struct ifreq *); + static struct mbuf * gre_encap(const struct gre_tunnel *, struct mbuf *, uint16_t, uint8_t, uint8_t); @@ -649,6 +660,11 @@ gre_input_key(struct mbuf **mp, int *offp, int type, int af, 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; @@ -709,6 +725,11 @@ egre_input(const struct gre_tunnel *key, struct mbuf *m, int hlen) 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); + } + ml_enqueue(&ml, m); if_input(&sc->sc_ac.ac_if, &ml); @@ -991,6 +1012,12 @@ gre_encap(const struct gre_tunnel *tunnel, struct mbuf *m, uint16_t proto, gkh = (struct gre_h_key *)(gh + 1); gkh->gre_key = tunnel->t_key; + + if (tunnel->t_key_mask == GRE_KEY_ENTROPY && + ISSET(m->m_pkthdr.ph_flowid, M_FLOWID_VALID)) { + gkh->gre_key |= htonl(~GRE_KEY_ENTROPY & + (m->m_pkthdr.ph_flowid & M_FLOWID_MASK)); + } } switch (tunnel->t_af) { @@ -1101,6 +1128,14 @@ gre_tunnel_ioctl(struct ifnet *ifp, struct gre_tunnel *tunnel, error = gre_del_vnetid(tunnel); break; + case SIOCSVNETFLOWID: + error = gre_set_vnetflowid(tunnel, ifr); + break; + + case SIOCGVNETFLOWID: + error = gre_get_vnetflowid(tunnel, ifr); + break; + case SIOCSLIFPHYADDR: error = gre_set_tunnel(tunnel, (struct if_laddrreq *)data); break; @@ -1248,6 +1283,7 @@ egre_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data) case SIOCSVNETID: case SIOCDVNETID: + case SIOCSVNETFLOWID: case SIOCSLIFPHYADDR: case SIOCDIFPHYADDR: case SIOCSLIFPHYRTABLE: @@ -1576,17 +1612,25 @@ static int gre_set_vnetid(struct gre_tunnel *tunnel, struct ifreq *ifr) { uint32_t key; + uint32_t min = GRE_KEY_MIN; + uint32_t max = GRE_KEY_MAX; + unsigned int shift = GRE_KEY_SHIFT; + uint32_t mask = GRE_KEY_MASK; + + if (tunnel->t_key_mask == GRE_KEY_ENTROPY) { + min = GRE_KEY_ENTROPY_MIN; + max = GRE_KEY_ENTROPY_MAX; + shift = GRE_KEY_ENTROPY_SHIFT; + mask = GRE_KEY_ENTROPY; + } - if (ifr->ifr_vnetid < 0 || ifr->ifr_vnetid > 0xffffffff) - return EINVAL; - - key = htonl(ifr->ifr_vnetid); + if (ifr->ifr_vnetid < min || ifr->ifr_vnetid > max) + return (EINVAL); - if (tunnel->t_key_mask == GRE_KEY_MASK && tunnel->t_key == key) - return (0); + key = htonl(ifr->ifr_vnetid << shift); /* commit */ - tunnel->t_key_mask = GRE_KEY_MASK; + tunnel->t_key_mask = mask; tunnel->t_key = key; return (0); @@ -1595,10 +1639,20 @@ gre_set_vnetid(struct gre_tunnel *tunnel, struct ifreq *ifr) static int gre_get_vnetid(struct gre_tunnel *tunnel, struct ifreq *ifr) { - if (tunnel->t_key_mask == GRE_KEY_NONE) + int shift; + + switch (tunnel->t_key_mask) { + case GRE_KEY_NONE: return (EADDRNOTAVAIL); + case GRE_KEY_ENTROPY: + shift = GRE_KEY_ENTROPY_SHIFT; + break; + case GRE_KEY_MASK: + shift = GRE_KEY_SHIFT; + break; + } - ifr->ifr_vnetid = (int64_t)ntohl(tunnel->t_key); + ifr->ifr_vnetid = ntohl(tunnel->t_key) >> shift; return (0); } @@ -1612,6 +1666,47 @@ gre_del_vnetid(struct gre_tunnel *tunnel) } static int +gre_set_vnetflowid(struct gre_tunnel *tunnel, struct ifreq *ifr) +{ + uint32_t mask, key; + + if (tunnel->t_key_mask == GRE_KEY_NONE) + return (EADDRNOTAVAIL); + + mask = ifr->ifr_vnetid ? GRE_KEY_ENTROPY : GRE_KEY_MASK; + if (tunnel->t_key_mask == mask) { + /* nop */ + return (0); + } + + key = ntohl(tunnel->t_key); + if (mask == GRE_KEY_ENTROPY) { + if (key > GRE_KEY_ENTROPY_MAX) + return (ERANGE); + + key = htonl(key << GRE_KEY_ENTROPY_SHIFT); + } else + key = htonl(key >> GRE_KEY_ENTROPY_SHIFT); + + /* commit */ + tunnel->t_key_mask = mask; + tunnel->t_key = key; + + return (0); +} + +static int +gre_get_vnetflowid(struct gre_tunnel *tunnel, struct ifreq *ifr) +{ + if (tunnel->t_key_mask == GRE_KEY_NONE) + return (EADDRNOTAVAIL); + + ifr->ifr_vnetid = tunnel->t_key_mask == GRE_KEY_ENTROPY; + + return (0); +} + +static int egre_up(struct egre_softc *sc) { if (sc->sc_tunnel.t_af == AF_UNSPEC) |