summaryrefslogtreecommitdiff
path: root/sys
diff options
context:
space:
mode:
authorDavid Gwynne <dlg@cvs.openbsd.org>2018-02-20 03:53:55 +0000
committerDavid Gwynne <dlg@cvs.openbsd.org>2018-02-20 03:53:55 +0000
commit01cecb9cd17443e36d55c29307aed362c7da651b (patch)
treeb3bb0e6606b7940b2d66b6d4689a12f058188ce2 /sys
parent81a4f149acdf6d0d239dea13f9c7699310202537 (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')
-rw-r--r--sys/net/if_gre.c115
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)