vxlan: Improve support for header flags
authorTom Herbert <therbert@google.com>
Thu, 8 Jan 2015 20:31:18 +0000 (12:31 -0800)
committerDavid S. Miller <davem@davemloft.net>
Mon, 12 Jan 2015 21:05:01 +0000 (16:05 -0500)
This patch cleans up the header flags of VXLAN in anticipation of
defining some new ones:

- Move header related definitions from vxlan.c to vxlan.h
- Change VXLAN_FLAGS to be VXLAN_HF_VNI (only currently defined flag)
- Move check for unknown flags to after we find vxlan_sock, this
  assumes that some flags may be processed based on tunnel
  configuration
- Add a comment about why the stack treating unknown set flags as an
  error instead of ignoring them

Signed-off-by: Tom Herbert <therbert@google.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
drivers/net/vxlan.c
include/net/vxlan.h

index 2ab0922af0b430f0cf6e6e0b88099cffba3d698f..3a18d8ed89ca28cdb9c8af4f08a2ae733f3de211 100644 (file)
 #define FDB_AGE_DEFAULT 300 /* 5 min */
 #define FDB_AGE_INTERVAL (10 * HZ)     /* rescan interval */
 
-#define VXLAN_N_VID    (1u << 24)
-#define VXLAN_VID_MASK (VXLAN_N_VID - 1)
-#define VXLAN_HLEN (sizeof(struct udphdr) + sizeof(struct vxlanhdr))
-
-#define VXLAN_FLAGS 0x08000000 /* struct vxlanhdr.vx_flags required value. */
-
 /* UDP port for VXLAN traffic.
  * The IANA assigned port is 4789, but the Linux default is 8472
  * for compatibility with early adopters.
@@ -1095,18 +1089,21 @@ static int vxlan_udp_encap_recv(struct sock *sk, struct sk_buff *skb)
 {
        struct vxlan_sock *vs;
        struct vxlanhdr *vxh;
+       u32 flags, vni;
 
        /* Need Vxlan and inner Ethernet header to be present */
        if (!pskb_may_pull(skb, VXLAN_HLEN))
                goto error;
 
-       /* Return packets with reserved bits set */
        vxh = (struct vxlanhdr *)(udp_hdr(skb) + 1);
-       if (vxh->vx_flags != htonl(VXLAN_FLAGS) ||
-           (vxh->vx_vni & htonl(0xff))) {
-               netdev_dbg(skb->dev, "invalid vxlan flags=%#x vni=%#x\n",
-                          ntohl(vxh->vx_flags), ntohl(vxh->vx_vni));
-               goto error;
+       flags = ntohl(vxh->vx_flags);
+       vni = ntohl(vxh->vx_vni);
+
+       if (flags & VXLAN_HF_VNI) {
+               flags &= ~VXLAN_HF_VNI;
+       } else {
+               /* VNI flag always required to be set */
+               goto bad_flags;
        }
 
        if (iptunnel_pull_header(skb, VXLAN_HLEN, htons(ETH_P_TEB)))
@@ -1116,6 +1113,19 @@ static int vxlan_udp_encap_recv(struct sock *sk, struct sk_buff *skb)
        if (!vs)
                goto drop;
 
+       if (flags || (vni & 0xff)) {
+               /* If there are any unprocessed flags remaining treat
+                * this as a malformed packet. This behavior diverges from
+                * VXLAN RFC (RFC7348) which stipulates that bits in reserved
+                * in reserved fields are to be ignored. The approach here
+                * maintains compatbility with previous stack code, and also
+                * is more robust and provides a little more security in
+                * adding extensions to VXLAN.
+                */
+
+               goto bad_flags;
+       }
+
        vs->rcv(vs, skb, vxh->vx_vni);
        return 0;
 
@@ -1124,6 +1134,10 @@ drop:
        kfree_skb(skb);
        return 0;
 
+bad_flags:
+       netdev_dbg(skb->dev, "invalid vxlan flags=%#x vni=%#x\n",
+                  ntohl(vxh->vx_flags), ntohl(vxh->vx_vni));
+
 error:
        /* Return non vxlan pkt */
        return 1;
@@ -1563,7 +1577,7 @@ static int vxlan6_xmit_skb(struct vxlan_sock *vs,
        }
 
        vxh = (struct vxlanhdr *) __skb_push(skb, sizeof(*vxh));
-       vxh->vx_flags = htonl(VXLAN_FLAGS);
+       vxh->vx_flags = htonl(VXLAN_HF_VNI);
        vxh->vx_vni = vni;
 
        skb_set_inner_protocol(skb, htons(ETH_P_TEB));
@@ -1607,7 +1621,7 @@ int vxlan_xmit_skb(struct vxlan_sock *vs,
                return -ENOMEM;
 
        vxh = (struct vxlanhdr *) __skb_push(skb, sizeof(*vxh));
-       vxh->vx_flags = htonl(VXLAN_FLAGS);
+       vxh->vx_flags = htonl(VXLAN_HF_VNI);
        vxh->vx_vni = vni;
 
        skb_set_inner_protocol(skb, htons(ETH_P_TEB));
index 903461aa5644ce9d1e366818fa6cbda1ba0cc1b1..a0d80736224f61dacda492e75e1c6e5ca055c523 100644 (file)
@@ -17,6 +17,13 @@ struct vxlanhdr {
        __be32 vx_vni;
 };
 
+/* VXLAN header flags. */
+#define VXLAN_HF_VNI 0x08000000
+
+#define VXLAN_N_VID     (1u << 24)
+#define VXLAN_VID_MASK  (VXLAN_N_VID - 1)
+#define VXLAN_HLEN (sizeof(struct udphdr) + sizeof(struct vxlanhdr))
+
 struct vxlan_sock;
 typedef void (vxlan_rcv_t)(struct vxlan_sock *vh, struct sk_buff *skb, __be32 key);