Discussion:
NPF: fast kick
(too old to reply)
Maxime Villard
2018-03-06 19:37:39 UTC
Permalink
Currently, NPF does not immediately kick malformed packets, and performs very
few sanity checks. Here is a patch [1] that fixes that.

When a packet is malformed, we return NPC_FMTERR, and kick it immediately. We
don't send a response (TCP-RST or ICMP).

In addition the checks are strengthened. In particular with fragments:

* As long as we don't see IPPROTO_FRAGMENT, if we fail to advance in the
nbuf that's fatal, and we return NPC_FMTERR.

* After seeing IPPROTO_FRAGMENT for the first time, if we fail to advance
in the nbuf we roll back to the previous header in the chain and process
the packet from there.

The point is that after the first IPPROTO_FRAGMENT we are not guaranteed to
find the L4 layer, so it's legitimate to fail. In the other cases, it's not.

Maxime

[1] http://m00nbsd.net/garbage/npf/fastkick.diff

--
Posted automagically by a mail2news gateway at muc.de e.V.
Please direct questions, flames, donations, etc. to news-***@muc.de
Mindaugas Rasiukevicius
2018-03-08 12:28:13 UTC
Permalink
Hi,

Please CC me when proposing NPF patches and please leave some extra time
for response.
Post by Maxime Villard
Currently, NPF does not immediately kick malformed packets, and performs
very few sanity checks. Here is a patch [1] that fixes that.
<...>
There are a few important points:

- There is a good reason for NPF to be *able* to behave as a silent
observer. It can be used for deep packet inspection, packet analysis,
accounting, etc. Hence the reason why NPF performs minimalistic sanity
checks. Please do not assume that the only mode of operation for NPF
is a traditional firewall.

- Having said that, we should certainly have an option (and I agree it
should be on by default) to perform extensive sanity checks and block
anything unusual. Just please make it a run-time *option* (for now, a
config-level variable will do, and later I will make it changeable via
npf.conf).

- It is better to keep a logical separation between the packet handler
which performs minimalistic sanity checks (just to extract the needed
information for NPF, e.g. populate the npf_cache_t structure) and the
function which performs a thorough validation. So, can you please
introduce something like npf_deep_validate() (with an option to skip
it) where you abstract thorough protocol-level checks.

Thanks.
--
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-03-08 14:16:33 UTC
Permalink
Post by Mindaugas Rasiukevicius
Hi,
Please CC me when proposing NPF patches and please leave some extra time
for response.
Post by Maxime Villard
Currently, NPF does not immediately kick malformed packets, and performs
very few sanity checks. Here is a patch [1] that fixes that.
<...>
- There is a good reason for NPF to be *able* to behave as a silent
observer. It can be used for deep packet inspection, packet analysis,
accounting, etc. Hence the reason why NPF performs minimalistic sanity
checks. Please do not assume that the only mode of operation for NPF
is a traditional firewall.
- Having said that, we should certainly have an option (and I agree it
should be on by default) to perform extensive sanity checks and block
anything unusual. Just please make it a run-time *option* (for now, a
config-level variable will do, and later I will make it changeable via
npf.conf).
I don't understand your point. The checks I added enforce some basic aspects
of the specs; a packet that does not respect these aspects is just a malformed
packet, regardless of whether NPF is acting as a firewall or something else.

If NPF wasn't performing these checks, the packet would still be dropped by
the kernel afterwards.

We've already had too many problems because NPF basically doesn't sanitize
anything.
Post by Mindaugas Rasiukevicius
- It is better to keep a logical separation between the packet handler
which performs minimalistic sanity checks (just to extract the needed
information for NPF, e.g. populate the npf_cache_t structure) and the
function which performs a thorough validation. So, can you please
introduce something like npf_deep_validate() (with an option to skip
it) where you abstract thorough protocol-level checks.
While I understand why you want a logical separation, I think that in all
fairness, the result is just shitty.

Typically, before my commit npc_hlen could point past the end of the mbuf. I
didn't even understand whether this was a real issue or not, because it gets
passed in many different components, and I wasn't able to make sure the
overflow was harmless (ie, make sure there are proper length checks in the
JIT code).

There is little interest in maintaining a logical separation if we are unable
to understand what happens in the complex machinery. As well, there is little
interest in passing a packet in this complex machinery if we know we'll end
up kicking it anyway because it is malformed to begin with.

So now let's perform basic sanitization early, outside of any ruleset, by
simply checking the return values of function calls that are *already there*.

Maxime

--
Posted automagically by a mail2news gateway at muc.de e.V.
Please direct questions, flames, donations, etc. to news-***@muc.de
Mindaugas Rasiukevicius
2018-03-12 19:46:22 UTC
Permalink
Post by Maxime Villard
Post by Mindaugas Rasiukevicius
- There is a good reason for NPF to be *able* to behave as a silent
observer. It can be used for deep packet inspection, packet analysis,
accounting, etc. Hence the reason why NPF performs minimalistic sanity
checks. Please do not assume that the only mode of operation for NPF
is a traditional firewall.
- Having said that, we should certainly have an option (and I agree it
should be on by default) to perform extensive sanity checks and block
anything unusual. Just please make it a run-time *option* (for now, a
config-level variable will do, and later I will make it changeable via
npf.conf).
I don't understand your point. The checks I added enforce some basic
aspects of the specs; a packet that does not respect these aspects is
just a malformed packet, regardless of whether NPF is acting as a
firewall or something else.
Just a note: my reply was also with your other email thread "NPF: TCP
options" in mind. Perhaps this caused some confusion.
Post by Maxime Villard
If NPF wasn't performing these checks, the packet would still be dropped
by the kernel afterwards.
Sure, but if NPF is used for deep packet inspection, analysis, accounting
or monitoring -- this is a desirable behaviour. If my application is
observing traffic at some transient point in the network, then I do not
want it to drop the packets. Even if the packets are malformed, because
the main purpose of such application is to be packet *observer*. NPF can
operate as a *filter*, but it may as well operate as an *observer*. I am
just saying -- please do not disregard the latter use case and give user
an option.
Post by Maxime Villard
We've already had too many problems because NPF basically doesn't sanitize
anything.
Yes, but was kind of a rational choice. I think the core packet handlers
of NPF should still perform the minimum sanity checks, required merely for
NPF itself. Keep the core code simple and usable for packet observation.

There can be many defence mechanisms. Protecting the end hosts against
malformed or "unusual" packets is one of them. I am all for adding more
aggressive checks. But again -- please give user an option to control
this. I might *want* to send malformed packets, for whatever reason.
Post by Maxime Villard
While I understand why you want a logical separation, I think that in all
fairness, the result is just shitty.
Typically, before my commit npc_hlen could point past the end of the
mbuf. I didn't even understand whether this was a real issue or not,
because it gets passed in many different components, and I wasn't able to
make sure the overflow was harmless (ie, make sure there are proper
length checks in the JIT code).
There is little interest in maintaining a logical separation if we are
unable to understand what happens in the complex machinery. As well,
there is little interest in passing a packet in this complex machinery if
we know we'll end up kicking it anyway because it is malformed to begin
with.
So now let's perform basic sanitization early, outside of any ruleset, by
simply checking the return values of function calls that are *already there*.
I do not see what logical separation has to do with npf_hlen bug or
"complex machinery". If you mean basic sanity checks such that NPF
could parse the information it needs -- sure; if there are cases where
ranges are not checked or overflows can happen -- those are bugs and
should be fixed.

Since NPF primarily operates from layer 3 and does not yet support L2
filtering, I think your extra checks for IP fragments are reasonable
(although I would rename the NPF_FMTERR to NPF_L3ERR as it reflects the
intention better). However, your suggested checks on the TCP options
should be optional. This is the point where we should have logical
separation i.e. L4 checks abstracted in a separate routine, potentially
with a way to configure how aggressive they should be (besides an option
to disable it completely).
--
Mindaugas

--
Posted automagically by a mail2news gateway at muc.de e.V.
Please direct questions, flames, donations, etc. to news-***@muc.de
Mindaugas Rasiukevicius
2018-03-12 23:23:43 UTC
Permalink
Post by Mindaugas Rasiukevicius
Sure, but if NPF is used for deep packet inspection, analysis,
accounting or monitoring -- this is a desirable behaviour. If my
application is observing traffic at some transient point in the
network, then I do not want it to drop the packets. Even if the
packets are malformed, because the main purpose of such application is
to be packet *observer*. NPF can operate as a *filter*, but it may as
well operate as an *observer*. I am just saying -- please do not
disregard the latter use case and give user an option.
The kernel performs sanity checks on IP packets even before invoking NPF.
This "accounting malformed packets" thing does not hold in the first
place, because at several points the kernel can drop the packets before,
and after, invoking NPF.
<...>
A real "observer" would have to be some pseudo device, to which a copy of
the packet is given at the very entry point (L2) - in order to ensure
that neither the kernel nor any driver has sanitized anything. Perhaps we
already have this, I didn't really check. In all cases, NPF is never
going to achieve that, as long as it doesn't get called before anything
else - which will likely never happen, because it would be wrong.
What kernel? :) I think you miss the use cases such as NPF + DPDK.
It is actually a bigger user of NPF than NetBSD (at least as far as
I am aware).

Layer 2 filtering is in the plans.
If the basic sanity checks I added in NPF (which consist, as I already
said, in checking the return values of function calls that are _already
there_) were to be an option, we would have to go through the ruleset
machinery, which is exactly where bugs can happen. That wouldn't be "fast
kick", it would be "slow, buggy kick". (heh!)
It is the main and ultimate purpose of the "ruleset machinery" -- for the
packets to go through it. It also defines the default behaviour (to pass
or block the "rest" of the traffic). In the perfect world, everything
ought to be passed through the ruleset inspection -- and only through it,
since that would be a simple and "clean" design. In reality, things are
more complex and protocols are not always nice and simple.

BPF byte-code engine is an old and robust piece of code. I think the bugs
are much more likely to be somewhere else.. :)
Not checking the return values of the nbuf functions, just for the sake of
some hypothetical "observer" thing that doesn't hold in the first place,
doesn't make any sense, apart from wanting more bugs by default.
Huh? I am not sure why do you think I suggest that. NPF nbuf API was
added to abstract mbufs, but another purpose also to provide stronger
safety. The return values should certainly be checked.
<...>
So NPF's behavior should be aligned to that of the kernel; that is to
say, NPF should ignore TCP options with uncommon lengths - which does not
mean dropping the packet. (We can discuss about changing the kernel's
behavior to be that of NPF, but as I said in my answer to Joerg, the
kernel's behavior is the one that is the most "common".)
Not exactly, no. NPF is not a host/kernel. It is a man in the middle,
concerning packets sent by different hosts (which might have different
TCP/IP implementations and applications). It operates based on its own
set of rules.

Let me put it this way:

- NPF should perform minimum sanity checks to be able to read the basic
L3 payload from the packet for its own operation (-- I don't think we have
a disagreement here). However, generally, the *default rule* decides
whether unrecognizable (or malformed) packets are passed or not; this is
more or less how other packet filters work too.

- NPF should be able to operate as packet observer at L3 (and, in the
future, at L2). This should be a supported mode of operation, but I am
not trying to suggest that this should be the default.

- NPF ought to support additional protocol-level checks (at L3, L4 and
may as well go beyond). However, how strict and aggressive such checks
should be is something what should be tunable. For example, extensive
checks can protect hosts with weak or vulnerable TCP/IP stacks; but it
might also break some applications or legitimate traffic, because in
reality, not everybody on the Internet plays by the rules and standards.
--
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-03-12 21:37:03 UTC
Permalink
Post by Mindaugas Rasiukevicius
Post by Maxime Villard
Post by Mindaugas Rasiukevicius
- There is a good reason for NPF to be *able* to behave as a silent
observer. It can be used for deep packet inspection, packet analysis,
accounting, etc. Hence the reason why NPF performs minimalistic sanity
checks. Please do not assume that the only mode of operation for NPF
is a traditional firewall.
- Having said that, we should certainly have an option (and I agree it
should be on by default) to perform extensive sanity checks and block
anything unusual. Just please make it a run-time *option* (for now, a
config-level variable will do, and later I will make it changeable via
npf.conf).
I don't understand your point. The checks I added enforce some basic
aspects of the specs; a packet that does not respect these aspects is
just a malformed packet, regardless of whether NPF is acting as a
firewall or something else.
Just a note: my reply was also with your other email thread "NPF: TCP
options" in mind. Perhaps this caused some confusion.
Post by Maxime Villard
If NPF wasn't performing these checks, the packet would still be dropped
by the kernel afterwards.
Sure, but if NPF is used for deep packet inspection, analysis, accounting
or monitoring -- this is a desirable behaviour. If my application is
observing traffic at some transient point in the network, then I do not
want it to drop the packets. Even if the packets are malformed, because
the main purpose of such application is to be packet *observer*. NPF can
operate as a *filter*, but it may as well operate as an *observer*. I am
just saying -- please do not disregard the latter use case and give user
an option.
The kernel performs sanity checks on IP packets even before invoking NPF.
This "accounting malformed packets" thing does not hold in the first place,
because at several points the kernel can drop the packets before, and after,
invoking NPF.

If the basic sanity checks I added in NPF (which consist, as I already said,
in checking the return values of function calls that are _already there_)
were to be an option, we would have to go through the ruleset machinery, which
is exactly where bugs can happen. That wouldn't be "fast kick", it would be
"slow, buggy kick". (heh!)

Not checking the return values of the nbuf functions, just for the sake of
some hypothetical "observer" thing that doesn't hold in the first place,
doesn't make any sense, apart from wanting more bugs by default.

A real "observer" would have to be some pseudo device, to which a copy of the
packet is given at the very entry point (L2) - in order to ensure that neither
the kernel nor any driver has sanitized anything. Perhaps we already have this,
I didn't really check. In all cases, NPF is never going to achieve that, as
long as it doesn't get called before anything else - which will likely never
happen, because it would be wrong.
Post by Mindaugas Rasiukevicius
Post by Maxime Villard
We've already had too many problems because NPF basically doesn't sanitize
anything.
Yes, but was kind of a rational choice. I think the core packet handlers
of NPF should still perform the minimum sanity checks, required merely for
NPF itself. Keep the core code simple and usable for packet observation.
There can be many defence mechanisms. Protecting the end hosts against
malformed or "unusual" packets is one of them. I am all for adding more
aggressive checks. But again -- please give user an option to control
this. I might *want* to send malformed packets, for whatever reason.
Post by Maxime Villard
While I understand why you want a logical separation, I think that in all
fairness, the result is just shitty.
Typically, before my commit npc_hlen could point past the end of the
mbuf. I didn't even understand whether this was a real issue or not,
because it gets passed in many different components, and I wasn't able to
make sure the overflow was harmless (ie, make sure there are proper
length checks in the JIT code).
There is little interest in maintaining a logical separation if we are
unable to understand what happens in the complex machinery. As well,
there is little interest in passing a packet in this complex machinery if
we know we'll end up kicking it anyway because it is malformed to begin
with.
So now let's perform basic sanitization early, outside of any ruleset, by
simply checking the return values of function calls that are *already there*.
I do not see what logical separation has to do with npf_hlen bug or
"complex machinery". If you mean basic sanity checks such that NPF
could parse the information it needs -- sure; if there are cases where
ranges are not checked or overflows can happen -- those are bugs and
should be fixed.
What I'm saying is that there are overflows - npc_hlen that is beyond the
nbuf -, but I'm not sure whether they are real bugs.
Post by Mindaugas Rasiukevicius
Since NPF primarily operates from layer 3 and does not yet support L2
filtering, I think your extra checks for IP fragments are reasonable
(although I would rename the NPF_FMTERR to NPF_L3ERR as it reflects the
intention better). However, your suggested checks on the TCP options
should be optional. This is the point where we should have logical
separation i.e. L4 checks abstracted in a separate routine, potentially
with a way to configure how aggressive they should be (besides an option
to disable it completely).
You are mixing the "NPF: fast kick" and "NPF: TCP options" threads, but my
point is different in each.

For the latter, my point is that the processing done by NPF and by the kernel
should be the same, because if there is a divergence then there is a way to
bypass certain normalization procedures.

So NPF's behavior should be aligned to that of the kernel; that is to say, NPF
should ignore TCP options with uncommon lengths - which does not mean dropping
the packet. (We can discuss about changing the kernel's behavior to be that
of NPF, but as I said in my answer to Joerg, the kernel's behavior is the one
that is the most "common".)

Maxime

--
Posted automagically by a mail2news gateway at muc.de e.V.
Please direct questions, flames, donations, etc. to news-***@muc.de
Maxime Villard
2018-03-13 19:20:35 UTC
Permalink
 [...]
- NPF should perform minimum sanity checks to be able to read the basic
L3 payload from the packet for its own operation (-- I don't think we have
a disagreement here). However, generally, the *default rule* decides
whether unrecognizable (or malformed) packets are passed or not; this is
more or less how other packet filters work too.
The change I made was exactly your first sentence: perform minimum sanity
checks, to ensure the basic operation of NPF. If the basic operation cannot
be assured, then fast-kick the packet.

If you pass the packet to the ruleset machinery, things can go wrong, because
the basic operation of the machinery cannot be assured.

Maxime

--
Posted automagically by a mail2news gateway at muc.de e.V.
Please direct questions, flames, donations, etc. to news-***@muc.de
Mindaugas Rasiukevicius
2018-03-13 19:48:26 UTC
Permalink
Post by Maxime Villard
The change I made was exactly your first sentence: perform minimum sanity
checks, to ensure the basic operation of NPF. If the basic operation
cannot be assured, then fast-kick the packet.
If you pass the packet to the ruleset machinery, things can go wrong,
because the basic operation of the machinery cannot be assured.
And why not?
--
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-03-13 20:05:03 UTC
Permalink
Post by Mindaugas Rasiukevicius
Post by Maxime Villard
The change I made was exactly your first sentence: perform minimum sanity
checks, to ensure the basic operation of NPF. If the basic operation
cannot be assured, then fast-kick the packet.
If you pass the packet to the ruleset machinery, things can go wrong,
because the basic operation of the machinery cannot be assured.
And why not?
Because the stateful-inspection/ruleset-machinery/JIT-code/etc use the values
that were constructed when parsing the packet. If these values are wrong,
correctness of the operations is not ensured.

--
Posted automagically by a mail2news gateway at muc.de e.V.
Please direct questions, flames, donations, etc. to news-***@muc.de
Mindaugas Rasiukevicius
2018-03-13 20:23:33 UTC
Permalink
Post by Maxime Villard
Post by Mindaugas Rasiukevicius
Post by Maxime Villard
The change I made was exactly your first sentence: perform minimum
sanity checks, to ensure the basic operation of NPF. If the basic
operation cannot be assured, then fast-kick the packet.
If you pass the packet to the ruleset machinery, things can go wrong,
because the basic operation of the machinery cannot be assured.
And why not?
Because the stateful-inspection/ruleset-machinery/JIT-code/etc use the
values that were constructed when parsing the packet. If these values are
wrong, correctness of the operations is not ensured.
Yes (in a typical use case), contained in npf_cache_t with information
flags on what was parsed/cached. So, keep those flags correct -- that
is pretty much all you need to do. And let the rules decide what to do
with the unrecognized/malformed/invalid packets.

Note that the BPF byte-code interpreter (or JIT-code) itself merely
needs a valid mbuf chain; there cannot be any overflows there.
--
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-03-13 20:50:03 UTC
Permalink
Post by Mindaugas Rasiukevicius
Post by Maxime Villard
Post by Mindaugas Rasiukevicius
Post by Maxime Villard
The change I made was exactly your first sentence: perform minimum
sanity checks, to ensure the basic operation of NPF. If the basic
operation cannot be assured, then fast-kick the packet.
If you pass the packet to the ruleset machinery, things can go wrong,
because the basic operation of the machinery cannot be assured.
And why not?
Because the stateful-inspection/ruleset-machinery/JIT-code/etc use the
values that were constructed when parsing the packet. If these values are
wrong, correctness of the operations is not ensured.
Yes (in a typical use case), contained in npf_cache_t with information
flags on what was parsed/cached. So, keep those flags correct -- that
is pretty much all you need to do. And let the rules decide what to do
with the unrecognized/malformed/invalid packets.
Yes. And npc_hlen is contained in npf_cache_t, so it needs to be correct,
which is exactly what I ensure in my basic checks.
Post by Mindaugas Rasiukevicius
Note that the BPF byte-code interpreter (or JIT-code) itself merely
needs a valid mbuf chain; there cannot be any overflows there.
Yes, there are no overflows until I look at the code and find a dozen. That's
not a reason for not having basic sanity checks beforehand.

--
Posted automagically by a mail2news gateway at muc.de e.V.
Please direct questions, flames, donations, etc. to news-***@muc.de
Thor Lancelot Simon
2018-03-14 03:08:06 UTC
Permalink
The kernel performs sanity checks on IP packets even before invoking NPF.
This "accounting malformed packets" thing does not hold in the first place,
because at several points the kernel can drop the packets before, and after,
invoking NPF.
NPF does not run only in the NetBSD kernel.

Thor

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