Re: Weird pf NAT failure on apu2

2023-06-25 Thread Stephan Neuhaus

On 6/24/23 13:14, Stuart Henderson wrote:

On 2023-06-24, Stephan Neuhaus  wrote:

I now think that either the documentation is wrong, or
pf is wrong.  At any rate, there seems to be a rather
serious disconnect between the two. The FAQ clearly
says:

When a packet is selected by a match rule, parameters (e.g. nat-to) in
that rule are remembered and are applied to the packet when a pass rule
matching the packet is reached.


Yes that's wrong. Address changes take effect at the time the match rule
is processed when traversing the ruleset; rules processed later "see"
the rewritten address. As pf.conf(5) says,


Translation
  Translation options modify either the source or destination address
  and port of the packets associated with a stateful connection.  pf(4)
  modifies the specified address and/or port in the packet and
  recalculates IP, TCP, and UDP checksums as necessary.

  If specified on a match rule, subsequent rules will see packets as
  they look after any addresses and ports have been translated.  These
  rules will therefore have to filter based on the translated address
  and port number.


OK, that's clear and unambiguous, thanks! So it's a bug in the FAQ, not 
in pf itself.


Cheers

Stephan



Re: Weird pf NAT failure on apu2

2023-06-24 Thread Stephan Neuhaus

Hi Zack

On 6/24/23 03:39, Zack Newman wrote:

There do appear to be contradictions in documentation as well as the pf
book. The Configuring NAT section is correct as you have seen with your
own rules.


I'm not sure about the Configuring NAT section being
correct. I still maintain that the documentation and
observed behaviour are different.

I now have the following small ruleset. The rules for
ports 22 and 53 are just so that I can do my
experiments over ssh. (Port 53 may not be necessary
but the blocked packets mess up my logs.) The em0
interface has the IPv4 address 192.168.0.2.

set skip on lo

block log all

pass in on em0 proto tcp to any port 22
pass out on em0 proto tcp from any port 22
pass on em0 proto udp from any port 53
pass on em0 proto udp to any port 53

pass in on athn0

match out log on em0 from athn0:network to any nat-to (em0)

#pass out log on em0 from athn0:network to any
pass out log on em0 from 192.168.0.2 to any

This causes packets from athn0 to pass and to be
correctly NATed. If I uncomment the penultimate line
and comment out the last line, the packets get matched
(as before), but then blocked by the catch-all rule 0.
This is consistent with the nat-to being applied
immediately after the match rule matches, EXCEPT that
the blocked packet is logged with a source address
inside athn0:network (192.168.3.0/24 in this case) and
not the NATed-to 192.168.0.2. For example:

Jun 24 10:24:46.498327 rule 0/(match) block out on em0: 
192.168.3.32.54459 > 172.217.168.68.443: S 2479117865:2479117865(0) win 
65535  (DF)


I now think that either the documentation is wrong, or
pf is wrong.  At any rate, there seems to be a rather
serious disconnect between the two. The FAQ clearly
says:

When a packet is selected by a match rule, parameters (e.g. nat-to) in 
that rule are remembered and are applied to the packet when a pass rule 
matching the packet is reached.


This seems to me to imply that the nat-to in my match
rule should not be applied BEFORE the pass rule is
attempted, but only AFTER. This seems also to be the
point of these lines in the documentation:

match
When a packet traverses the ruleset and matches a match rule, any 
optional parameters specified in that rule are remembered for future use 
(made "sticky").


pass
This rule allows the packet to be transmitted. If the packet was 
previously matched by a match rule where parameters were specified, they 
will be applied to this packet. [...]


I know that the documentation (at this time) ALSO
says:

match out on interface [af] \
   from src_addr to dst_addr \
   nat-to ext_addr [pool_type] [static-port]
[...]
pass out [log] on interface [af] [proto protocol] \
   from ext_addr [port src_port] \
   to dst_addr [port dst_port]

where the pass rule has ext_addr instead of src_addr,
but at least two people on t...@openbsd.org have
confirmed that this is a bug and have already
submitted a diff that replaces ext_addr with src_addr
in the pass rule, see 
https://marc.info/?l=openbsd-tech=168714686620055=2


Should I be taking this to another mailing list?
Should I be submitting a bug report? Or am I just
really really dense and am just too stupid to read the
documentation correctly?

Cheers

Stephan



Re: Weird pf NAT failure on apu2

2023-06-23 Thread Stephan Neuhaus




On 6/23/23 18:29, Zack Newman wrote:

On 6/23/23 11:19, Stephan Neuhaus wrote:

# Rule 5
match out log on em0 from athn0:network to any nat-to (em0)

# Rule 6
pass out log on em0 from athn0:network to any


Rule 5 replaces the source IP address with the IP address assigned to
em0-as well as replaces the source port (for TCP and UDP) with an
ephemeral port. Rule 6 does _not_ pass traffic out of em0 from the IP
address assigned to em0 but instead passes traffic where the IP address
is from athn0:network, so of course it won't work.


Thanks for replying!

What you say may well be true, but if it is, it is in
conflict with the documentation and IMO the match/pass
combo is much less useful.

To make my first point, the pf FAQ says the following
about match/pass:

match
When a packet traverses the ruleset and matches a match rule, any 
optional parameters specified in that rule are remembered for future use 
(made "sticky").


pass
This rule allows the packet to be transmitted. If the packet was 
previously matched by a match rule where parameters were specified, they 
will be applied to this packet. [...]


This makes it very clear (to me at least) that the
nat-to in the match rule is "remembered for
future use" in the match, and is applied to the packet
only when it is finally subject to a pass rule.

The FAQ also has the following example:

match out on tl0 from 192.168.1.0/24 to any nat-to 198.51.100.1
pass on tl0 from 192.168.1.0/24 to any

This is what I meant when I said that I took my code
"almost verbatim" from the FAQ.

Also, "The Book of pf", 3rd ed., has a similar match/pass combo on p.53:

match out on $ext_if inet from $localnet nat-to ($ext_if)
pass quick inet proto { tcp, udp } from $localnet to port $udp_services

(See how the "from" address in the pass rule is
$localnet, which would match the packet's original
source address, and not $(ext_if)?)

To make my second point, If the packet would
immediately change its effective source address to
that of the outgoing interface, it would for example
become very hard in later rules to distinguish between
NATed packets and packets originating on the firewall, at least in pass 
out rules.



It would be a lot easier to just have a pass out rule and only filter
ingress traffic.


Agreed, but at this stage, I'm only trying to understand what's going on.




Is there anything you see in these rules, especially
in rules 5 and 6, that is not correct? I don't think
so, I've taken this almost verbatim from the pf FAQ
https://www.openbsd.org/faq/pf/nat.html.


You did not read that FAQ carefully enough, so I wouldn't say you
have followed it "almost verbatim". Under Configuring NAT, the example
shows

match out on interface [af] \
    from src_addr to dst_addr \
    nat-to ext_addr [pool_type] [static-port]
[...]
pass out [log] on interface [af] [proto protocol] \
    from ext_addr [port src_port] \
    to dst_addr [port dst_port]

Notice the pass out rule which states "from *ext_addr*" _not_ "from
src_addr".



Agreed! These very lines were the subject of a previous post of mine, 
and two people from t...@openbsd.org said that it was a typo and that 
the ext_addr in the pass rule should be changed to src_addr. See 
https://marc.info/?l=openbsd-tech=168714686620055=2 Note also that 
these lines are in conflict with the example that comes later in the 
FAQ, and which I have quoted above.



You are also not specifying the IP version which you likely should since
you probably don't want to rely on NAT for IPv6. For example

match out log on em0 inet from athn0:network nat-to (em0)
pass out


Fair point, thanks.

Cheers

Stephan



Re: Weird pf NAT failure on apu2

2023-06-23 Thread Stephan Neuhaus

On 6/23/23 13:19, Stephan Neuhaus wrote:

[...]


Some people have replied to this post off-list and
have made the entirely reasonable conjecture that the
packet changes its effective source address the moment
the match rule matches. With the changed source
address, the pass rule no longer matches. That is
entirely possible and agrees with all the experimental
evidence I have.

Still, I don't think that this is what's going on, for
the following reasons.

1. It is in conflict with the documentation. The FAQ
http://www.openbsd.org/faq/pf/nat.html says

match
When a packet traverses the ruleset and matches a match rule, any 
optional parameters specified in that rule are remembered for future use 
(made "sticky").


pass
This rule allows the packet to be transmitted. If the packet was 
previously matched by a match rule where parameters were specified, they 
will be applied to this packet. [...]


This makes it very clear that the nat-to in the match
rule is only "remembered for future use", and is
applied to the packet only when it is finally subject
to a pass rule.

Similarly, "The Book of pf", 3rd ed., has a match/pass
combo on p.53 that goes

match out on $ext_if inet from $localnet nat-to ($ext_if)
pass quick inet proto { tcp, udp } from $localnet to port $udp_services

As you can see, the pass rule has the packet's
original source specification $localnet, not the
natted-to $(ext_if).

2. The match/pass combo would be much less useful. If
the packet would immediately change its effective
source address to that of the outgoing interface, it
would for example become very hard in later rules to
distinguish between NATed packets and packets
originating on the firewall.

Cheers

Stephan



Re: Weird pf NAT failure on apu2

2023-06-23 Thread Stephan Neuhaus

On 6/23/23 13:19, Stephan Neuhaus wrote:

Hi list [...]

In other words, now the same packets that weren't
passed using the match/pass combo are not passed when
the nat-to is part of the pass rule.


That should have been "...combo are NOW passed...". Sorry.

Cheers

Stephan



Weird pf NAT failure on apu2

2023-06-23 Thread Stephan Neuhaus

Hi list

I am using a PC Engines apu2 board as a firewall. Or
rather, I want to use it as one, but it doesn't work
as I think it should.

First up, some information about my system. It has
three gigabit wired Ethernet interfaces, em0, em1, and
em2, as well as an 802.11n interface, athn0. Only em0
and athn0 will be relevant for this case.

# uname -a
OpenBSD my.host.name 7.3 GENERIC.MP#1125 amd64
# ifconfig em0 # I've zeroed out the lladdr
em0: flags=8843 mtu 1500
lladdr 00:00:00:00:00:00
description: egress interface
index 1 priority 0 llprio 3
groups: egress
media: Ethernet autoselect (1000baseT 
full-duplex,master,rxpause,txpause)

status: active
inet 192.168.0.2 netmask 0xff00 broadcast 192.168.0.255

# ifconfig athn0 # I've zeroed out the lladdr, bssid and changed the nwid.
athn0: flags=8843 mtu 1500
lladdr 00:00:00:00:00:00
description: wireless interface
index 4 priority 4 llprio 3
groups: wlan
media: IEEE802.11 autoselect mode 11n hostap
status: active
ieee80211: nwid mynw chan 56 bssid 00:00:00:00:00:00 -67dBm 
wpakey wpaprotos wpa2 wpaakms psk wpaciphers ccmp wpagroupcipher ccmp

inet 192.168.3.2 netmask 0xff00 broadcast 192.168.3.255

What I want to do is NAT the wireless interface to the
egress interface. I have this experimental pf setup,
which has many problems, and which therefore has a big
comment at the top:

# PF configuration file to test matching and NAT
#
# DO NOT USE IN PRODUCTION
#
set skip on lo

block log all # Rule 0

pass in on em0 proto tcp to any port 22# Rule 1
pass out on em0 proto tcp from any port 22 # Rule 2
pass out on em0 proto udp to any port 53   # Rule 3

pass in on athn0   # Rule 4

# Rule 5
match out log on em0 from athn0:network to any nat-to (em0)

# Rule 6
pass out log on em0 from athn0:network to any

Rules 1--3 are there so I can do my experiments over
SSH. These rules affect the rule numbering as shown by
pflog, but are otherwise not the point.

Is there anything you see in these rules, especially
in rules 5 and 6, that is not correct? I don't think
so, I've taken this almost verbatim from the pf FAQ
https://www.openbsd.org/faq/pf/nat.html.

When I connect my phone to the wireless network on
athn0, this is what I see:

# doas tcpdump -n -e -ttt -i pflog0
tcpdump: WARNING: snaplen raised from 116 to 160
tcpdump: listening on pflog0, link-type PFLOG
Jun 23 12:48:28.349710 rule def/(ip-option) pass in on athn0: :: > 
ff02::16: HBH multicast listener report v2, 1 group record(s) [hlim 1]
Jun 23 12:48:28.714929 rule 5/(match) match out on em0: 
192.168.3.32.54151 > 172.217.168.68.443: S 940430546:940430546(0) win 
65535  (DF)
Jun 23 12:48:28.714932 rule 0/(match) block out on em0: 
192.168.3.32.54151 > 172.217.168.68.443: S 940430546:940430546(0) win 
65535  (DF)
Jun 23 12:48:28.716461 rule 5/(match) match out on em0: 
192.168.3.32.37973 > 216.58.215.227.80: S 2291102750:2291102750(0) win 
65535  (DF)
Jun 23 12:48:28.716463 rule 0/(match) block out on em0: 
192.168.3.32.37973 > 216.58.215.227.80: S 2291102750:2291102750(0) win 
65535  (DF)
Jun 23 12:48:29.728732 rule 5/(match) match out on em0: 
192.168.3.32.54151 > 172.217.168.68.443: S 940430546:940430546(0) win 
65535  (DF)
Jun 23 12:48:29.728736 rule 0/(match) block out on em0: 
192.168.3.32.54151 > 172.217.168.68.443: S 940430546:940430546(0) win 
65535  (DF)


As you can see, the packet is being matched by the
match rule (rule 5) but then NOT matched by the pass
rule (rule 6) and consequently by the block-all rule
(rule 0).

When I remove rule 5 and change rule 6 to
this (making it the new rule 5):

pass out log on em0 from athn0:network to any nat-to (em0)

this is what I see in the logs:

Jun 23 12:50:59.791736 rule def/(ip-option) pass in on athn0: :: > 
ff02::16: HBH multicast listener report v2, 1 group record(s) [hlim 1]
Jun 23 12:51:00.091647 rule def/(ip-option) pass in on athn0: :: > 
ff02::16: HBH multicast listener report v2, 1 group record(s) [hlim 1]
Jun 23 12:51:00.152530 rule 5/(match) pass out on em0: 
192.168.3.32.37988 > 216.58.215.227.80: S 749016608:749016608(0) win 
65535  (DF)
Jun 23 12:51:00.152614 rule 5/(match) pass out on em0: 
192.168.3.32.54168 > 172.217.168.68.443: S 2019795291:2019795291(0) win 
65535  (DF)


In other words, now the same packets that weren't
passed using the match/pass combo are not passed when
the nat-to is part of the pass rule.

No matter how I read the docs, there is no way I can explain what's 
happening. Yet my setup is so simple that I MUST be doing something 
wrong, right?


Can you help me see what's going on?

Cheers

Stephan

PS: I didn't want to make this message even longer by
including a dmesg, but it is of course available on
request.



Possible typo in pf NAT FAQ

2023-06-18 Thread Stephan Neuhaus

Hi list

I think I have found a typo in the pf NAT FAQ here:
https://www.openbsd.org/faq/pf/nat.html. In the
"Configuring NAT" section it says:

  The general format in pf.conf looks something like this:

  match out on interface [af] \
 from src_addr to dst_addr \
 nat-to ext_addr [pool_type] [static-port]
  [...]
  pass out [log] on interface [af] [proto protocol] \
 from ext_addr [port src_port] \
 to dst_addr [port dst_port]

As you can see, the pass rule says "from ext_addr".
But beneath the description of the various options, it
says:

  This would lead to a most basic form of these lines similar to this:

  match out on tl0 from 192.168.1.0/24 to any nat-to 198.51.100.1
  pass on tl0 from 192.168.1.0/24 to any

Here you can see that the "from" part is what the
above description calls the src_addr, not the
ext_addr, as it claims. This makes much more sense and
is consistent with all the other documentation that
I've seen.

So could it be a typo in the docs? Or have I missed some things?

Thanks in advance

Stephan