NetBSD-Users archive

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index][Old Index]

Re: npf on a router: configuration issues



Christoph Badura <bad%bsd.de@localhost> writes:

> On Tue, Apr 01, 2025 at 08:57:38AM -0400, Greg Troxel wrote:

> Since I need to write my observations down sometime I might as well start
> now. :-)

Thanks; this is very helpful.

>>    b) Is it really meant that "if a packet does not match any defined
>>       group, then -- and only then -- will it be processed by the special
>>       group default (which is default NOT in quotes, as a keyword not a
>>       name)"?
>
> As far as I can tell: yes.

I have been reading code, and finding it difficult.  I now think (from a
combination of code and docs)

  groups are processed in order, with a group skipped if direction or
  interface of the packet mismatches the group definition

  If a rule in a group matches the packet, even if not a "final" rule or
  not, then when the end of the group is reached, that is the rule that
  controls the result, and the rest of the groups are not evaluated.
  Same for a "final" rule, except that it skips the rest of the group
  rules, as always for final.
  
  If after running the rules in a group, none of them matched
  (implicitly, because they were all too specific and there was no
  default, or there were no rules, like a group with pass out but no in
  rules), then processing continues with the next group.

I think this is what "does not match any defined group" means.

>>    b) Assuming so, and I want to
>>         - block packets heading to the host to most ports, except for a few
>>         - allow outbound transit packets without regard to blocked ports
>>       how do I do this?  It looks like I have to have my block rules
>>       narrowed by $ifaddrs and run on each interface, making groups
>>       awkward.  Surely there must be a better way, as I don't think my
>>       intent is unusual.
>
> You don't actually define what the "host" is.  It seems you mean the
> router's addresses.  You are correct in that you have to narrow the rules
> down to ifaddrs(iface) for all interfaces (except lo0 which you can
> short-circuit).

I have a mental model where the router part of the system forwards
packets but does not receive or transmit them.  And that there is a host
attached to the router, which can receive/transmit packets, accept ssh
connections, etc.  I am thinking of being able to filter on "is this
packet coming from the router part to the host part".


                            host
                            |
                  pass up   | pass down
                            |
  wm0 ---- pass in -------router---- pass out ------- wm1
                          
> group default {
> 	pass final on lo0 all
> 	pass stateful in final on iface0 proto tcp to ifaddrs(iface0) port $allowed_ports
> 	pass stateful in final on iface1 proto tcp to ifaddrs(iface0) port $allowed_ports
> 	...
> }

What I want as an example is

   For pass up, block all, and allow a few ports.  Just like you would
   want on a host which is not a router.

   For packets that come in wm0 and go out wm1, or in wm1 and out wm0,
   don't filter.

   Probably, keep state on pass up for incoming ssh and keep state on
   pass down to allow replies to connections from the host.

A more likely/complicated is to replace the second point with

   For packets that come in wm0 and go out wm1, don't filter, but keep
   state on wm1 output.  For packets that come in wm1, default block
   unless state.

   (Plus, packets from host going out wm1 keep state and allow matching.)


I think you're suggesting accepting that host is mixed in and

   pass in on wm0
   block in on wm0 to ifaddrs(iface0)
   pass in on wm0 to ifaddrs(iface0) port $allowed_ports

so that all packets can be forward, but the 'pass up' leg is default block.

I think one should be able to specify different rules for "connect to
the router" and "what the router will pass".

>> 4) I understand stateful processing on outbound, given a default block
>>    on inbound.  I understand stateful processing for TCP on inbound so
>>    that bare SYN to allowed ports creates a state entry, and then future
>>    packets that match the flow are allowed.
>
> The main reasons I use stateful rules are that:
> - I expect stateful rules to also allow ICMP related to the "connection".
> - it trades processing speed (number of rules that have to be matched for
>   each packet) for space (both in the state table and in the brain because
>   of more complicated rules.)
>
>>    Why would I want to or not want to use stateful processing for
>>    inbound UDP?  Inbound ICMP echo?
>
> Of the top of my hat and with no claim to be extensive:
>
> For inbound UDP so that e.g. PORT UNREACHABLE from the hosts can be
> returned, if you find that useful (e.g. for traceroute).

That makes sense, if the unreachable is otherwise blocked.

>> 5) The NAT examples almost all suggest a group with "pass stateful out
>>    final".
>> 
>>    Is there any reason there needs to be such a group/rules if the rules
>>    that exist anyway on the outbound interface have "pass stateful out"?
>
> Well, certain types of NAT require state, but
> https://rmind.github.io/npf/nat.html says it does stateful filtering
> automatically if required.  That would imply that "pass stateful" is not
> required at all.  I haven't experimented with that, however.  However,
> adding "stateful" to a rule that also is affected by NAT can make it
> explicit that you expect state to be maintained and does no harm.

This remains unclear, and I think we need to experiment, read code, and
document.

Thanks very much for the reply; I understand better now.


Home | Main Index | Thread Index | Old Index