On Tue, Aug 02, 2011 at 01:56:22PM +0700, Jesse Gross wrote: > On Tue, Aug 2, 2011 at 12:01 PM, Simon Horman <ho...@verge.net.au> wrote: > > Hi, > > > > in an effort to move things forward I decided to take the liberty > > of posting a fresh version of this patch which addresses each > > of the issues that Jesse raises below. > > Thanks for picking this up Simon. > > > On Fri, Jul 22, 2011 at 02:18:47PM -0700, Jesse Gross wrote: > >> On Wed, Jul 13, 2011 at 10:05 PM, Valient Gough <vgo...@pobox.com> wrote: > >> > diff --git a/datapath/vport-capwap.c b/datapath/vport-capwap.c > >> > index f0bb327..161c6d4 100644 > >> > --- a/datapath/vport-capwap.c > >> > +++ b/datapath/vport-capwap.c > >> > +/* > >> > + * Should be sized as multiple of u32, for hash functions. > >> > + */ > >> > struct frag_match { > >> > __be32 saddr; > >> > __be32 daddr; > >> > __be16 id; > >> > + __be16 reserved; > >> > + __be64 key; > >> > }; > >> > >> I think this violates the CAPWAP spec because it changes the > >> parameters that reassembly is based on. A device that understands > >> keys can now put together packets in a different way than one that > >> does not if there are overlapping IDs. If you go down the route of > >> checking that all of the fragments have the same ID, I think you need > >> to do reassembly as before and check to see whether all segments have > >> the same key and drop if not. Probably the simpler implementation > >> would be to just declare that only the key in the first fragment > >> matters. > > > > My reading is that can be achieved by simply not > > adding reserved and key to struct frag_match, not > > setting those values in defrag() and not comparing key > > in capwap_frag_match(). Is that correct? > > I believe that this is similar to what Valient had in his original > patch. My concern is that it is somewhat non-deterministic as it will > take the key from the last fragment that arrived. IPv4 has security > problems with fragment reassembly by being somewhat loose in the > specification of how invalid packets should be handled. This led to > problems because different implementations would do different things > and it would be possible to slip attacks past a security appliance by > taking advantage of these differences if, for example, the appliance > was running Linux and the target ran Windows. IPv6 addresses this by > prescribing the exact behavior and specifying that anything else must > be dropped. Since we're effectively defining a new protocol here, I > want to avoid the mistakes of the past by not leaving things up to > chance. I think if we simply moved the key extraction after > reassembly then we can avoid this by mandating that it is the first > key in the stream that is used.
Thanks, I now understand :-) I'm a little unsure what a clean/efficient implementation would be as process_capwap_proto() is needed when a fragment is received in order to determine its length (assuming we don't trust cwh->begin & CAPWAP_HLEN_MASK) and it is now also needed to extract the key later on. The implementation I have gone for is to call process_capwap_proto() with a NULL key early on and process_capwap_proto() skips key extraction in that case. It does also occur to me that it would be possible to get a mix of old and new format fragments. But as you're happy to ignore key miss-matches between fragments I think mixed formats should also be ok. > > I have reworked process_capwap_wsi() as follows which > > I believe addresses both the potentially invalid pointer and > > incorrect length check issues. > > > > static int process_capwap_wsi(struct sk_buff *skb, __be64 *key, int > > *hdr_len) > > { > > struct capwaphdr *cwh; > > struct capwaphdr_wsi *wsi; > > int min_wsi_len = sizeof(struct capwaphdr_wsi); > > int wsi_len; > > > > if (unlikely(!pskb_may_pull(skb, CAPWAP_MIN_HLEN + min_wsi_len + > > ETH_HLEN))) > > return -ENOMEM; > > > > /* read wsi header to find out how big it really is */ > > cwh = capwap_hdr(skb); > > wsi = (struct capwaphdr_wsi *)(cwh + 1); > > /* +1 for length byte not included in wsi_len */ > > wsi_len = 1 + (unsigned int)wsi->wsi_len; > > *hdr_len += wsi_len; > > > > /* parse wsi field */ > > if (wsi_len > min_wsi_len && wsi->flags & CAPWAP_WSI_F_KEY64) { > > struct capwaphdr_wsi_key *opt; > > > > if (unlikely(wsi_len < min_wsi_len + sizeof(struct > > capwaphdr_wsi_key))) > > return -EINVAL; > > Depending on the length we'll get different results for invalid > packets - the WSI header is too short for anything we will accept the > packet but won't get a key. On the other hand, if the header is above > the minimum length but too short for the key that it indicates is > there then we will reject the packet. I think for consistency we > should reject all invalid packets. Sorry for not spotting that earlier. I have changed the check around to: if (unlikely(wsi_len < min_wsi_len + sizeof(struct capwaphdr_wsi_key) || !(wsi->flags & CAPWAP_WSI_F_KEY64))) return -EINVAL; > >> > +static inline struct sk_buff *process_capwap_proto(struct sk_buff *skb, > >> > + __be64 *key) > >> > +{ > >> > + struct capwaphdr *cwh = capwap_hdr(skb); > >> > + int hdr_len = CAPWAP_MIN_HLEN; > >> > + int wbid = cwh->begin & CAPWAP_WBID_MASK; > >> > >> This triggers sparse errors; it can be fixed by using __be32 instead. > > > > Done. > > One other thing that I noticed in this function is that we have > somewhat inconsistent handling regarding the header length. If it is > using the new format we ignore the header length, always pulling the > base header length plus the WSI length. If it is using the old format > then we require a specific length. Since we're enforcing a specific > set of flags probably we should check the length in the new format. > Either that or learn to skip header fields that we don't understand. I think that checking hdr_len against (cwh->begin & CAPWAP_HLEN_MASK) >> 17 should make this consistent between formats. if (wbid == CAPWAP_WBID_30) { if ((cwh->begin & CAPWAP_F_WSI) && process_capwap_wsi(skb, key, &hdr_len)) goto error; cwh = capwap_hdr(skb); } else if (wbid != CAPWAP_WBID_2) goto error; if (unlikely((cwh->begin & CAPWAP_HLEN_MASK) >> CAPWAP_HLEN_SHIFT != hdr)) goto error; > > >> > @@ -628,7 +768,8 @@ static int capwap_frag_match(struct inet_frag_queue > >> > *ifq, void *a_) > >> > struct frag_match *a = a_; > >> > struct frag_match *b = &ifq_cast(ifq)->match; > >> > > >> > - return a->id == b->id && a->saddr == b->saddr && a->daddr == > >> > b->daddr; > >> > + return a->id == b->id && a->saddr == b->saddr && > >> > + a->daddr == b->daddr && a->key == b->key; > >> > >> Might as well use memcmp() here now that the struct won't have any > >> extra padding in it. > > > > Done. > > This only works with the extra padding that was introduced in previous > patch. With the current struct layout the compiler will insert some > padding composed of uninitialized memory that will lead to comparison > failures. Oops. Undone. _______________________________________________ dev mailing list dev@openvswitch.org http://openvswitch.org/mailman/listinfo/dev