From: Valient Gough <[email protected]>

Add tunnel key support to CAPWAP vport.  Uses the optional WSI field in a
CAPWAP header for storing a 64bit key.  It can also be used without keys, in
which case it is backward compatible with the old code.  Documentation about
the format of the WSI field is in CAPWAP.txt.

Signed-off-by: Valient Gough <[email protected]>
[ [email protected]: remove trailing whitespace from lines
                      removed trailing newlines from files
                      consistently used tabs for indentation
                      minor up-port to new upstream
                      add datapath/CAPWAP.txt to dist tarball ]
Signed-off-by: Simon Horman <[email protected]>
---
 datapath/CAPWAP.txt     |   77 ++++++++++++++++++++++++
 datapath/Makefile.am    |    2 +-
 datapath/vport-capwap.c |  149 +++++++++++++++++++++++++++++++++++++++++------
 lib/netdev-vport.c      |   13 +++-
 4 files changed, 217 insertions(+), 24 deletions(-)
 create mode 100644 datapath/CAPWAP.txt

diff --git a/datapath/CAPWAP.txt b/datapath/CAPWAP.txt
new file mode 100644
index 0000000..4ee5dc4
--- /dev/null
+++ b/datapath/CAPWAP.txt
@@ -0,0 +1,77 @@
+
+References:
+* http://www.rfc-editor.org/rfc/rfc5415.txt
+
+
+The CAPWAP header layout is summarized as follows:
+
+
+        0                   1                   2                   3
+        0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+       +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+       |CAPWAP Preamble|  HLEN   |   RID   | WBID    |T|F|L|W|M|K|Flags|
+       +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+       |          Fragment ID          |     Frag Offset         |Rsvd |
+       +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+       |                 (optional) Radio MAC Address                  |
+       +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+       |            (optional) Wireless Specific Information           |
+       +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+       |                        Payload ....                           |
+       +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+
+The spec defines an optional Wireless Specific Information field which can be
+used to pass arbitrary data in the encapsulation layer:
+
+   Wireless Specific Information:  This optional field may be used to carry
+      per-packet information.  This field is only present if the
+      'W' bit is set.  The WBID field in the CAPWAP Header is used to
+      identify the format of the WSI optional field. The HLEN field assumes
+      4-byte alignment, and this field MUST be padded with zeroes (0x00) if it
+      is not 4-byte aligned.
+
+      The Wireless-Specific Information field uses the following format:
+
+        0                   1                   2                   3
+        0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+       +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+       |    Length     |                Data...
+       +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+
+      Length:  The 8-bit field contains the length of the data field,
+         with a maximum size of 255.
+
+      Data:  Wireless-specific information, defined by the wireless-
+         specific binding specified in the CAPWAP Header's WBID field.
+
+
+   WBID:  A 5-bit field that is the wireless binding identifier.  The
+      identifier will indicate the type of wireless packet associated
+      with the radio.  The following values are defined:
+
+      0 -  Reserved
+      1 -  IEEE 802.11
+      2 -  Reserved
+      3 -  EPCGlobal [EPCGlobal]
+
+      When OpenVSwitch uses this field, it writes the value:
+      30 - OpenVSwitch data
+
+
+OpenVSwitch can make use of this field to pass additional packet routing
+information. When needed, it sets the 'W' bit to indicates the WSI field is
+added, and fills the field as follows:
+
+        0                   1                   2                   3
+        0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+       +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+       |  WSI_LEN      |K|      Flags  |             Reserved          |
+       +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+       |   (optional) 64bit Tunnel Key                                 |
+       +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+
+       K - flag bit to identify presence of a 64bit tunnel key.
+
+
+Adding WSI fields:  Fields must be written and read in consitent order.  New
+fields may be added, but the existing fields always come first.
diff --git a/datapath/Makefile.am b/datapath/Makefile.am
index e1bd3e6..973c15a 100644
--- a/datapath/Makefile.am
+++ b/datapath/Makefile.am
@@ -3,7 +3,7 @@ if LINUX_ENABLED
 SUBDIRS += linux
 endif
 
-EXTRA_DIST = $(dist_headers) $(dist_sources)
+EXTRA_DIST = $(dist_headers) $(dist_sources) CAPWAP.txt
 
 # Suppress warnings about GNU extensions in Modules.mk files.
 AUTOMAKE_OPTIONS = -Wno-portability
diff --git a/datapath/vport-capwap.c b/datapath/vport-capwap.c
index f0bb327..4e51f6d 100644
--- a/datapath/vport-capwap.c
+++ b/datapath/vport-capwap.c
@@ -42,14 +42,32 @@
  * statically create them and we can do very fast parsing by checking all 12
  * fields in one go.
  */
-#define CAPWAP_BEGIN_HLEN __cpu_to_be32(0x00100000)
-#define CAPWAP_BEGIN_WBID __cpu_to_be32(0x00000200)
 #define CAPWAP_BEGIN_FRAG __cpu_to_be32(0x00000080)
 #define CAPWAP_BEGIN_LAST __cpu_to_be32(0x00000040)
+#define CAPWAP_BEGIN_HLEN_2 __cpu_to_be32(0x00100000)
+#define CAPWAP_BEGIN_HLEN_5 __cpu_to_be32(0x00280000)
+
+/*  Old capwap code is hard-coded to look for a WBID value of 2.
+ *  When we insert WSI field, use WBID value of 30, which has been
+ *  proposed for all "experimental" usage - users with no reserved WBID value
+ *  of their own.
+*/
+#define CAPWAP_WBID_30   __cpu_to_be32(0x00003C00)
+#define CAPWAP_WBID_2    __cpu_to_be32(0x00000200)
+#define CAPWAP_WBID_MASK __cpu_to_be32(0x00003F00)
 
-#define NO_FRAG_HDR (CAPWAP_BEGIN_HLEN | CAPWAP_BEGIN_WBID)
+/*
+ * CAPWAP allows an optional 'Wireless Specific Information' field, which is
+ * length prefixed and can contain any data.  If keys are configured for the
+ * vport, then the key will be placed in the WSI field and recovered by the
+ * receiver.
+ */
+#define CAPWAP_FLAG_WSI  __cpu_to_be32(0x00000020)
+
+#define NO_FRAG_HDR 0
 #define FRAG_HDR (NO_FRAG_HDR | CAPWAP_BEGIN_FRAG)
 #define FRAG_LAST_HDR (FRAG_HDR | CAPWAP_BEGIN_LAST)
+#define CAPWAP_FRAG_FLAGS (CAPWAP_BEGIN_FRAG | CAPWAP_BEGIN_LAST)
 
 struct capwaphdr {
        __be32 begin;
@@ -57,6 +75,19 @@ struct capwaphdr {
        __be16 frag_off;
 };
 
+/*
+ * We use the WSI field to hold additional tunnel data.
+ * The first eight bits store the size of the wsi data in bytes.
+ */
+struct capwaphdr_wsi {
+       unsigned char wsi_len;
+       unsigned char flags;
+       __be16 reserved_padding;
+};
+
+/* Flag indicating a 64bit key is stored in WSI data field */
+#define CAPWAP_WSI_FLAG_KEY64 0x80
+
 static inline struct capwaphdr *capwap_hdr(const struct sk_buff *skb)
 {
        return (struct capwaphdr *)(udp_hdr(skb) + 1);
@@ -117,18 +148,17 @@ static struct socket *capwap_rcv_socket;
 
 static int capwap_hdr_len(const struct tnl_mutable_config *mutable)
 {
+       int size = CAPWAP_HLEN;
+
        /* CAPWAP has no checksums. */
        if (mutable->flags & TNL_F_CSUM)
                return -EINVAL;
 
-       /* CAPWAP has no keys, so check that the configuration for keys is the
-        * default if no key-specific attributes are used.
-        */
-       if ((mutable->flags & (TNL_F_IN_KEY_MATCH | TNL_F_OUT_KEY_ACTION)) !=
-           (TNL_F_IN_KEY_MATCH | TNL_F_OUT_KEY_ACTION))
-               return -EINVAL;
+       /* if keys are specified, then add WSI field */
+       if (mutable->out_key || (mutable->flags & TNL_F_OUT_KEY_ACTION))
+               size += 12;  /* wsi framing overhead + 8 byte key */
 
-       return CAPWAP_HLEN;
+       return size;
 }
 
 static void capwap_build_header(const struct vport *vport,
@@ -145,6 +175,35 @@ static void capwap_build_header(const struct vport *vport,
        cwh->begin = NO_FRAG_HDR;
        cwh->frag_id = 0;
        cwh->frag_off = 0;
+
+       if (mutable->out_key || (mutable->flags & TNL_F_OUT_KEY_ACTION)) {
+               struct capwaphdr_wsi *wsi = (struct capwaphdr_wsi *)(cwh + 1);
+               __be32 *options = (__be32 *)(wsi + 1);
+
+               cwh->begin |= CAPWAP_FLAG_WSI | CAPWAP_WBID_30;
+
+               wsi->wsi_len = 3; /* initial wsi header */
+               wsi->flags = 0;
+               wsi->reserved_padding = 0;
+
+               if (mutable->out_key) {
+                       __be64 *options64 = (__be64 *)options;
+                       *options64 = mutable->out_key;
+                       options += 2;
+
+                       wsi->wsi_len += 8;
+                       wsi->flags |= CAPWAP_WSI_FLAG_KEY64;
+               } else {
+                       /* space left intentionally blank, to be filled in
+                          by capwap_update_header */
+               }
+
+               /* set hlen field, easy since we only support 1 option. */
+               cwh->begin |= CAPWAP_BEGIN_HLEN_5;
+       } else {
+               /* make packet readable by old capwap code */
+               cwh->begin |= CAPWAP_WBID_2 | CAPWAP_BEGIN_HLEN_2;
+       }
 }
 
 static struct sk_buff *capwap_update_header(const struct vport *vport,
@@ -154,6 +213,14 @@ static struct sk_buff *capwap_update_header(const struct 
vport *vport,
 {
        struct udphdr *udph = udp_hdr(skb);
 
+       if (mutable->flags & TNL_F_OUT_KEY_ACTION) {
+               /* first field in WSI is key */
+               struct capwaphdr *cwh = (struct capwaphdr *)(udph + 1);
+               struct capwaphdr_wsi *wsi = (struct capwaphdr_wsi *)(cwh + 1);
+               __be64 *options = (__be64 *)(wsi + 1);
+               *options = OVS_CB(skb)->tun_id;
+       }
+
        udph->len = htons(skb->len - skb_transport_offset(skb));
 
        if (unlikely(skb->len - skb_network_offset(skb) > dst_mtu(dst)))
@@ -166,11 +233,11 @@ static inline struct sk_buff *process_capwap_proto(struct 
sk_buff *skb)
 {
        struct capwaphdr *cwh = capwap_hdr(skb);
 
-       if (likely(cwh->begin == NO_FRAG_HDR))
+       if (likely((cwh->begin & CAPWAP_FRAG_FLAGS) == 0))
                return skb;
-       else if (cwh->begin == FRAG_HDR)
+       else if (cwh->begin & CAPWAP_BEGIN_FRAG)
                return defrag(skb, false);
-       else if (cwh->begin == FRAG_LAST_HDR)
+       else if (cwh->begin & CAPWAP_BEGIN_LAST)
                return defrag(skb, true);
        else {
                if (net_ratelimit())
@@ -187,25 +254,69 @@ static int capwap_rcv(struct sock *sk, struct sk_buff 
*skb)
        struct vport *vport;
        const struct tnl_mutable_config *mutable;
        struct iphdr *iph;
+       struct capwaphdr *cwh;
+       int hdr_len = CAPWAP_HLEN;
+       __be64 key = 0;
 
-       if (unlikely(!pskb_may_pull(skb, CAPWAP_HLEN + ETH_HLEN)))
+       if (unlikely(!pskb_may_pull(skb, hdr_len + ETH_HLEN)))
                goto error;
 
-       __skb_pull(skb, CAPWAP_HLEN);
-       skb_postpull_rcsum(skb, skb_transport_header(skb), CAPWAP_HLEN + 
ETH_HLEN);
-
        skb = process_capwap_proto(skb);
        if (unlikely(!skb))
                goto out;
 
        iph = ip_hdr(skb);
-       vport = tnl_find_port(iph->daddr, iph->saddr, 0,
-                             TNL_T_PROTO_CAPWAP | TNL_T_KEY_EXACT, &mutable);
+       cwh = capwap_hdr(skb);
+       if (cwh->begin & CAPWAP_FLAG_WSI) {
+               struct capwaphdr_wsi *wsi = (struct capwaphdr_wsi *)(cwh + 1);
+               __be32 *options;
+               int remaining_words;
+
+               /* minimum wsi length - 4 bytes */
+               if (unlikely(!pskb_may_pull(skb, hdr_len + ETH_HLEN + 4)))
+                       goto error;
+
+               /* len prefix + data must be multiple of 4 bytes */
+               if (unlikely((wsi->wsi_len & 0x3) != 0x3))
+                       goto error;
+
+               hdr_len += 1 + (unsigned int)wsi->wsi_len;
+
+               if (unlikely(!pskb_may_pull(skb, hdr_len + ETH_HLEN)))
+                       goto error;
+
+               /* don't parse WBID types we don't know about */
+               if (unlikely((cwh->begin & CAPWAP_WBID_MASK) != CAPWAP_WBID_30))
+                       goto skip_wsi;
+
+               /* parse wsi field */
+               remaining_words = (hdr_len >> 2) - 1;
+               options = (__be32 *)(wsi + 1);
+
+               if ((wsi->flags & CAPWAP_WSI_FLAG_KEY64) && (remaining_words >= 
2) ) {
+                       __be64 *options64 = (__be64 *)options;
+                       key = *options64;
+                       options += 2;
+                       remaining_words -= 2;
+               }
+       }
+
+skip_wsi:
+       vport = tnl_find_port(iph->daddr, iph->saddr, key,
+                             TNL_T_PROTO_CAPWAP | TNL_T_KEY_EITHER, &mutable);
        if (unlikely(!vport)) {
                icmp_send(skb, ICMP_DEST_UNREACH, ICMP_PORT_UNREACH, 0);
                goto error;
        }
 
+       if (mutable->flags & TNL_F_IN_KEY_MATCH)
+               OVS_CB(skb)->tun_id = key;
+       else
+               OVS_CB(skb)->tun_id = 0;
+
+       __skb_pull(skb, hdr_len);
+       skb_postpull_rcsum(skb, skb_transport_header(skb), hdr_len + ETH_HLEN);
+
        tnl_rcv(vport, skb, iph->tos);
        goto out;
 
diff --git a/lib/netdev-vport.c b/lib/netdev-vport.c
index b9c1bfe..ea5138a 100644
--- a/lib/netdev-vport.c
+++ b/lib/netdev-vport.c
@@ -578,6 +578,7 @@ parse_tunnel_config(const char *name, const char *type,
 {
     bool is_gre = false;
     bool is_ipsec = false;
+    bool supports_keys = false;
     struct shash_node *node;
     bool ipsec_mech_set = false;
     ovs_be32 daddr = htonl(0);
@@ -586,11 +587,15 @@ parse_tunnel_config(const char *name, const char *type,
     flags = TNL_F_DF_DEFAULT | TNL_F_PMTUD | TNL_F_HDR_CACHE;
     if (!strcmp(type, "gre")) {
         is_gre = true;
+        supports_keys = true;
     } else if (!strcmp(type, "ipsec_gre")) {
         is_gre = true;
         is_ipsec = true;
         flags |= TNL_F_IPSEC;
         flags &= ~TNL_F_HDR_CACHE;
+        supports_keys = true;
+    } else if (!strcmp(type, "capwap")) {
+        supports_keys = true;
     }
 
     SHASH_FOR_EACH (node, args) {
@@ -668,9 +673,9 @@ parse_tunnel_config(const char *name, const char *type,
                     || !strcmp(node->name, "private_key")
                     || !strcmp(node->name, "use_ssl_cert"))) {
             /* Ignore options not used by the netdev. */
-        } else if (is_gre && (!strcmp(node->name, "key") ||
-                              !strcmp(node->name, "in_key") ||
-                              !strcmp(node->name, "out_key"))) {
+        } else if (supports_keys && (!strcmp(node->name, "key") ||
+                                    !strcmp(node->name, "in_key") ||
+                                    !strcmp(node->name, "out_key"))) {
             /* Handled separately below. */
         } else {
             VLOG_WARN("%s: unknown %s argument '%s'", name, type, node->name);
@@ -700,7 +705,7 @@ parse_tunnel_config(const char *name, const char *type,
         }
     }
 
-    if (is_gre) {
+    if (supports_keys) {
         set_key(args, "in_key", ODP_TUNNEL_ATTR_IN_KEY, options);
         set_key(args, "out_key", ODP_TUNNEL_ATTR_OUT_KEY, options);
     }
-- 
1.7.5.4

_______________________________________________
dev mailing list
[email protected]
http://openvswitch.org/mailman/listinfo/dev

Reply via email to