Discussion:
frag6: better limitation
(too old to reply)
Maxime Villard
2018-01-25 21:32:42 UTC
Permalink
Hi,
There is something I find a bit inappropriate in our frag6 entry point. When
a fragment is received, the kernel performs the following check:

if (frag6_nfrags >= (u_int)ip6_maxfrags)
goto dropfrag;

Here, frag6_nfrags is the total number of fragments that are pending in
the kernel's memory. But this value is system-wide, and not fine-grained.

On a system that has several addresses/interfaces, it implies that if only
one interface is flooded with fragments, the fragments received on each of
the remaining interfaces will get kicked.

The timeout - after which the pending fragments are dropped - is one minute.

It is easy for a remote attacker to send fragments in a loop, and cause the
check above to be always true. After one minute he just has to send another
round of fragments, the limit will again be reached, and so on.

I ended up writing this [1], without a lot of conviction, to say the truth.
A per-src-IP policy is implemented: each sender is allowed to have a given
number of fragments pending; beyond that limit, they get kicked.

Now, if someone floods the machine with fragments, the kernel will at some
point kick all the fragments that come from this someone's address. Obviously,
an attacker could be able to use a different src address; but then we rely
on the firewall to reject the packets earlier.

With this change I am able to send fragments to a local NetBSD machine that is
being flooded from the outside.

Not sure if that's worth it, or if it's the right thing to do, but our way
of handling fragments does not seem very correct. The other BSDs have this
issue too.

Maxime

[1] http://m00nbsd.net/garbage/ip6/frag6.diff

--
Posted automagically by a mail2news gateway at muc.de e.V.
Please direct questions, flames, donations, etc. to news-***@muc.de
Joerg Sonnenberger
2018-01-25 21:37:23 UTC
Permalink
Post by Maxime Villard
Now, if someone floods the machine with fragments, the kernel will at some
point kick all the fragments that come from this someone's address. Obviously,
an attacker could be able to use a different src address; but then we rely
on the firewall to reject the packets earlier.
I don't understand what you mean here. The typical scenario here is
someone sending fragments with a randomized host part. Given that IPv6
has enough space for that, it is not really possible to restrict that.

Joerg

--
Posted automagically by a mail2news gateway at muc.de e.V.
Please direct questions, flames, donations, etc. to news-***@muc.de
Maxime Villard
2018-01-26 06:37:21 UTC
Permalink
Post by Joerg Sonnenberger
Post by Maxime Villard
Now, if someone floods the machine with fragments, the kernel will at some
point kick all the fragments that come from this someone's address. Obviously,
an attacker could be able to use a different src address; but then we rely
on the firewall to reject the packets earlier.
I don't understand what you mean here. The typical scenario here is
someone sending fragments with a randomized host part. Given that IPv6
has enough space for that, it is not really possible to restrict that.
Perhaps an example will illustrate what I meant. If you have a firewall
configuration that says:

allow incoming IP_A on wm0 (local network)
allow incoming IP_B on wm1 (public network)

An attacker can send fragments (from the outside) with a source address of
IP_B, the firewall won't kick these. The kernel maintains a per-IP limit, so
if there is a flood, the fragments from IP_B will still go through the
firewall but the kernel won't process them.

The point is, meanwhile, IP_A can still send fragments without being
affected: the kernel will process them. So we avoid a form of DoS...

Maxime

--
Posted automagically by a mail2news gateway at muc.de e.V.
Please direct questions, flames, donations, etc. to news-***@muc.de
Joerg Sonnenberger
2018-01-26 12:47:54 UTC
Permalink
Post by Maxime Villard
Post by Joerg Sonnenberger
Post by Maxime Villard
Now, if someone floods the machine with fragments, the kernel will at some
point kick all the fragments that come from this someone's address. Obviously,
an attacker could be able to use a different src address; but then we rely
on the firewall to reject the packets earlier.
I don't understand what you mean here. The typical scenario here is
someone sending fragments with a randomized host part. Given that IPv6
has enough space for that, it is not really possible to restrict that.
Perhaps an example will illustrate what I meant. If you have a firewall
allow incoming IP_A on wm0 (local network)
allow incoming IP_B on wm1 (public network)
An attacker can send fragments (from the outside) with a source address of
IP_B, the firewall won't kick these. The kernel maintains a per-IP limit, so
if there is a flood, the fragments from IP_B will still go through the
firewall but the kernel won't process them.
Firewall configurations with a hard-coded list of IPv6 addresses for
incoming connections are rare. Your patch fixes one form of DoS by
introducing a worse form -- I can just send a couple of fragment from
2001:db8::1, followed by a couple of fragments from 2001::db8::2 etc.
Before the total amount of memory used for fragments had a fixed limit,
now it doesn't.

Just as with IPv4, IPv6 fragmentation is best effort. Higher level
protocols should not depend on it to work reliably if they want to work
in a semi-hostile environment.

Joerg

--
Posted automagically by a mail2news gateway at muc.de e.V.
Please direct questions, flames, donations, etc. to news-***@muc.de
Maxime Villard
2018-01-26 13:25:49 UTC
Permalink
Post by Joerg Sonnenberger
Post by Maxime Villard
Post by Joerg Sonnenberger
Post by Maxime Villard
Now, if someone floods the machine with fragments, the kernel will at some
point kick all the fragments that come from this someone's address. Obviously,
an attacker could be able to use a different src address; but then we rely
on the firewall to reject the packets earlier.
I don't understand what you mean here. The typical scenario here is
someone sending fragments with a randomized host part. Given that IPv6
has enough space for that, it is not really possible to restrict that.
Perhaps an example will illustrate what I meant. If you have a firewall
allow incoming IP_A on wm0 (local network)
allow incoming IP_B on wm1 (public network)
An attacker can send fragments (from the outside) with a source address of
IP_B, the firewall won't kick these. The kernel maintains a per-IP limit, so
if there is a flood, the fragments from IP_B will still go through the
firewall but the kernel won't process them.
Firewall configurations with a hard-coded list of IPv6 addresses for
incoming connections are rare. Your patch fixes one form of DoS by
introducing a worse form -- I can just send a couple of fragment from
2001:db8::1, followed by a couple of fragments from 2001::db8::2 etc.
Before the total amount of memory used for fragments had a fixed limit,
now it doesn't.
No, look at the patch. The previous limit is still there. If you send from
different addresses this limit is hit and everything gets kicked, just like
the current behavior.

--
Posted automagically by a mail2news gateway at muc.de e.V.
Please direct questions, flames, donations, etc. to news-***@muc.de
Mindaugas Rasiukevicius
2018-01-29 22:54:52 UTC
Permalink
Post by Maxime Villard
...
I ended up writing this [1], without a lot of conviction, to say the
truth. A per-src-IP policy is implemented: each sender is allowed to have
a given number of fragments pending; beyond that limit, they get kicked.
...
[1] http://m00nbsd.net/garbage/ip6/frag6.diff
So, you introduce another per-IP state and O(n) scan of the IP addresses?
What if the host receives an entire /64 subnet of spoofed packets? Seems
to me that you would hit the same global limit, just wasting more memory
and CPU cycles. I can see your desire to localise the IP fragmentation
attacks. Perhaps it would make more sense to have it per-interface, but
I am not sure whether it is worth the complexity..

On a side note: the IPv4 and IPv6 reassembly logic is conceptually the
same. Although they are implemented separately, sys/netinet/ip_reass.c
and sys/netinet6/frag6.c can generally be merged into one agnostic code.
Like a lot of the IPv4/IPv6 code, so that we have bugs in one place. :)
Just in case you might want to have a look into this, long time ago I
also wrote some testing code, see src/regress/sys/net/frag/ip4_frag_1.c .
--
Mindaugas

--
Posted automagically by a mail2news gateway at muc.de e.V.
Please direct questions, flames, donations, etc. to news-***@muc.de
Maxime Villard
2018-01-31 07:17:36 UTC
Permalink
Post by Mindaugas Rasiukevicius
Post by Maxime Villard
...
I ended up writing this [1], without a lot of conviction, to say the
truth. A per-src-IP policy is implemented: each sender is allowed to have
a given number of fragments pending; beyond that limit, they get kicked.
...
[1] http://m00nbsd.net/garbage/ip6/frag6.diff
So, you introduce another per-IP state and O(n) scan of the IP addresses?
What if the host receives an entire /64 subnet of spoofed packets? Seems
to me that you would hit the same global limit, just wasting more memory
and CPU cycles. I can see your desire to localise the IP fragmentation
attacks. Perhaps it would make more sense to have it per-interface, but
I am not sure whether it is worth the complexity..
I've put at the end of this mail the offlist conversation I had with Joerg.

My patch is useful only if you restrict addresses in the first place. In that
case there is only a limited number of craftable src addresses. Typically:

wm0 [public side]: allow 10 IPv6 addresses
wm1 [local side]: allow everything

You set:

maxfragsip = 50
maxfrags = 10*50 + 100 = 600

Now, even if an attacker floods your public interface with fragments, you
will still have 100 fragments that can be treated from your local network.

But yes, it is in O(n). The frag code is not optimized anyway: there is only
one lock we take all the time, and we call kmem_intr_zalloc repeatedly too. It
would be better to use pool_cache.

Maxime



-------- Message transféré --------
Sujet : Re: Fwd: Re: frag6: better limitation
Date : Fri, 26 Jan 2018 17:53:59 +0100
Post by Mindaugas Rasiukevicius
Post by Maxime Villard
Post by Joerg Sonnenberger
Firewall configurations with a hard-coded list of IPv6 addresses for
incoming connections are rare. Your patch fixes one form of DoS by
introducing a worse form -- I can just send a couple of fragment from
2001:db8::1, followed by a couple of fragments from 2001::db8::2 etc.
Before the total amount of memory used for fragments had a fixed limit,
now it doesn't.
No, look at the patch. The previous limit is still there. If you send from
different addresses this limit is hit and everything gets kicked, just like
the current behavior.
But how is it supposed to really help then? I can keep a steady stream
of fragments with changing source address and the limit will prevent any
new entries to be cached. LRU doesn't help either -- the sender just
need to drown out the legimate sources.
I understand what you say. It is clear that if you don't restrict the IPv6
addresses in the firewall, my patch does not change anything.

If you do restrict the addresses, however, you can configure the parameters
to prevent DoSes. If you allow 10 external addresses on wm0, with a maximum
of 50 fragments per address, then you just have to set the total limit to >
10*50. So, if you set the total limit to 600, you will always have 100
fragments that can be treated; and the remaining interfaces on your machine
(like wm1) won't be affected by the mess that may be happening on wm0.

Maxime


--
Posted automagically by a mail2news gateway at muc.de e.V.
Please direct questions, flames, donations, etc. to news-***@muc.de
Manuel Bouyer
2018-01-31 08:46:34 UTC
Permalink
Post by Maxime Villard
[...]
My patch is useful only if you restrict addresses in the first place. In that
wm0 [public side]: allow 10 IPv6 addresses
wm1 [local side]: allow everything
This seems to be a very specific and rare use case ...
--
Manuel Bouyer <***@antioche.eu.org>
NetBSD: 26 ans d'experience feront toujours la difference
--

--
Posted automagically by a mail2news gateway at muc.de e.V.
Please direct questions, flames, donations, etc. to news-***@muc.de
David Brownlee
2018-01-31 11:38:27 UTC
Permalink
Post by Manuel Bouyer
Post by Maxime Villard
[...]
My patch is useful only if you restrict addresses in the first place. In that
wm0 [public side]: allow 10 IPv6 addresses
wm1 [local side]: allow everything
This seems to be a very specific and rare use case ...
Would a combination of global and a per interface limit handle most of
the cases without increased DoS risk?

--
Posted automagically by a mail2news gateway at muc.de e.V.
Please direct questions, flames, donations, etc. to news-***@muc.de
Maxime Villard
2018-02-02 09:19:53 UTC
Permalink
Post by David Brownlee
Post by Manuel Bouyer
Post by Maxime Villard
[...]
My patch is useful only if you restrict addresses in the first place. In that
wm0 [public side]: allow 10 IPv6 addresses
wm1 [local side]: allow everything
This seems to be a very specific and rare use case ...
Would a combination of global and a per interface limit handle most of
the cases without increased DoS risk?
Yes, probably. In fact, it would be a lot better if it was done in the
firewall directly.

--
Posted automagically by a mail2news gateway at muc.de e.V.
Please direct questions, flames, donations, etc. to news-***@muc.de
Loading...