[ovs-dev] [PATCH] datapath: add key support to CAPWAP tunnel

Valient Gough vgough at pobox.com
Wed Jun 29 14:44:40 UTC 2011


The base patch has some additional problems, which Jesse identified in his
review. I have a new version that is almost complete, needing only work on
handling incoming fragmented packets. I'm currently traveling for work, and
will follow up in more detail later this week.

- Valient (from phone)
On Jun 29, 2011 12:36 AM, "Simon Horman" <horms at verge.net.au> wrote:
> From: Valient Gough <vgough at pobox.com>
>
> 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 <vgough at pobox.com>
> [ horms at verge.net.au: 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 <horms at verge.net.au>
> ---
> 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
>
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://mail.openvswitch.org/pipermail/ovs-dev/attachments/20110629/1c8102c3/attachment-0003.html>


More information about the dev mailing list