Re: Weird pf NAT failure on apu2
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
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
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
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
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
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
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