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" <[email protected]> wrote: > 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
