0

I would like to set up a simple forward rule (not port forward!) on FreeBSD 12.3 that filters based on received-on interface and going-out-on interface. IP networks should not be part of the rule as it acts like a router for all kind of IPs. The routed networks will be set dynamically by a routing daemon (BIRD with OSPF).

In FreeBSD using PF I can only set one ifspec per filter rule ([ "on" ifspec ]) as per man 5 pf.conf:

 pf-rule = action [ ( "in" | "out" ) ] [ "log" [ "(" logopts ")"] ] [ "quick" ] [ "on" ifspec ] [ route ] [ af ] [ protospec ] hosts [ filteropt-list ] 

I would like the combination of both input-interface and output-interface to match. How can I do that?


In Linux using nft/nftables I would do this:

define iface_site2site = { "tun0", "tun1", "tun9" } [...] chain forward { type filter hook forward priority 0; policy drop; iifname $iface_site2site oifname $iface_site2site accept \ comment "Freely forward packets between site-to-site links, firewalled at final destination." } [...] 

In Linux using iptables I would do this:

iptables -A [...] --in-interface tun+ --out-interface tun+ -j ACCEPT 

How can I do the above on FreeBSD?

Just to be clear; I'm NOT looking for port forwarding or NAT rules.

2
  • pf does not work with chains but evaluates all rules in order (or until quick). Mayby your scenario can be served by a simple route-to interface. If you want the explicit AND which you imply then I would look into tag/tagged (aka policy filtering). Do your filter on pass in on $tun and set a tag. Then pass out on $tun and check for the tag. Commented Oct 28, 2022 at 3:15
  • 1
    @ClausAndersen yeah, after some more reading tags seem the way to go for my use case. someone else hinted this to me on another channel as well. will continue working on this soon and experiment with it. thanks! :) Commented Oct 31, 2022 at 10:20

1 Answer 1

0

It seems not possible to do this in a single rule on FreeBSD. So, instead we can use a match rule to tag the traffic inbound on those interfaces, followed by two pass rules, one for in and one for out, which then only allows traffic to go out if this was tagged as such.

As per man pf.conf(5) on FreeBSD 12.3:

tag <string> Packets matching this rule will be tagged with the specified string. The tag acts as an internal marker that can be used to identify these packets later on. This can be used, for example, to provide trust between interfaces and to determine if packets have been processed by translation rules. Tags are "sticky", meaning that the packet will be tagged even if the rule is not the last matching rule. [...] 

which mentions the use case of the question nicely ("provide trust between interfaces"). 😃

FreeBSD's tags are similar to marks in Linux' netfilter.

Example:

site_to_site_links = "{" tun0 tun1 tun9 "}" [...] block log all # Allow forwarding freely between the site-to-site interfaces # (not to self); traffic firewalled at final destination. match in on $site_to_site_links from any to any tag S2S no state pass in quick on $site_to_site_links to ! self no state pass out quick on $site_to_site_links tagged S2S no state 

Note that traffic also needs to be passed in, except when it's destined for itself or else we would potentially bypass other incoming firewall rules.

Additional debugging tip: enable pflog (set pflog_enable="YES" in rc.conf) and use tcpdump -e -n -i pflog0 to see what is being blocked. The -e option shows which rule matches to cause the packet to be blocked.

You must log in to answer this question.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.