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