+ if (cwh->begin & CAPWAP_F_RMAC)
+ rmac_len = CAPWAP_RMAC_LEN;
+
+ hdr_len = ntohl(cwh->begin & CAPWAP_HLEN_MASK) >> CAPWAP_HLEN_SHIFT;
+
+ if (unlikely(sizeof(struct capwaphdr) + rmac_len + sizeof(struct capwaphdr_wsi) > hdr_len))
+ return -EINVAL;
+
+ /* read wsi header to find out how big it really is */
+ wsi = (struct capwaphdr_wsi *)((u8 *)(cwh + 1) + rmac_len);
+ /* +1 for length byte not included in wsi_len */
+ wsi_len = 1 + wsi->wsi_len;
+
+ if (unlikely(sizeof(struct capwaphdr) + rmac_len + wsi_len != hdr_len))
+ return -EINVAL;
+
+ wsi_len -= sizeof(struct capwaphdr_wsi);
+
+ if (wsi->flags & CAPWAP_WSI_F_KEY64) {
+ struct capwaphdr_wsi_key *opt;
+
+ if (unlikely(wsi_len < sizeof(struct capwaphdr_wsi_key)))
+ return -EINVAL;
+
+ opt = (struct capwaphdr_wsi_key *)(wsi + 1);
+ *key = opt->key;
+ }
+
+ return 0;
+}
+
+static struct sk_buff *process_capwap_proto(struct sk_buff *skb, __be64 *key)
+{
+ struct capwaphdr *cwh = capwap_hdr(skb);
+ int hdr_len = sizeof(struct udphdr);
+
+ if (unlikely((cwh->begin & CAPWAP_ZERO_MASK) != 0))
+ goto error;
+
+ hdr_len += ntohl(cwh->begin & CAPWAP_HLEN_MASK) >> CAPWAP_HLEN_SHIFT;
+ if (unlikely(hdr_len < CAPWAP_MIN_HLEN))
+ goto error;
+
+ if (unlikely(!pskb_may_pull(skb, hdr_len + ETH_HLEN)))
+ goto error;
+
+ cwh = capwap_hdr(skb);
+ __skb_pull(skb, hdr_len);
+ skb_postpull_rcsum(skb, skb_transport_header(skb), hdr_len + ETH_HLEN);
+
+ if (cwh->begin & CAPWAP_F_FRAG) {
+ skb = defrag(skb, (__force bool)(cwh->begin & CAPWAP_F_LASTFRAG));
+ if (!skb)
+ return NULL;
+ cwh = capwap_hdr(skb);