PF Allow incoming packets on a port used for NAT

I would like to pass incoming packets on a specific port, which is used for NAT, even if these packets don't match a connection from the NAT table.

I use a machine with FreeBSD and pf as a router. A PBX on my local network must both reach a phone provider on the internet and be reachable through the internet with the same port (5060/udp SIP). More precisely: packets destined for the phone provider must have the source and destination port set to 5060/udp, or else they will be rejected by the phone provider (yes, the source port has to be 5060 also). Plus, all incoming packets on the external interface of my router on port 5060 have to be forwarded to the PBX machine (including incoming connections). Since the PBX only has a local IPv4, I use the following NAT rule to be able to send packets to the provider.
nat on $ext_if inet proto udp from $pbx port 5060 to ! $pbx port 5060 -> ($ext_if:0) static-port
But with this rule in place, I can only receive packets that match a connection from the NAT table. If the provider wants to initiate a connection to my router, the packets get dropped by pf. Even if there are no block rules. I tried various redirection rules, for example:
rdr on $ext_if inet proto udp from ! $pbx port 5060 to ($ext_if:0) port 5060 -> $pbx port 5060

... but without any success, which seems logical to me, because NAT rules "win" over redirect rules.

Maybe there is a way to tell pf, to accept these incoming connections.
Maybe there is a way to change the source address without a NAT rule. But I don't know how :(

Here is a simplified version of my /etc/pf.conf
Code:
ext_if = "ix0"
int_if = "ix1"
local_net = "192.168.0.0/24"
pbx = "192.168.0.2"
provider = "{ 92.197.176.16 92.197.176.17 92.197.176.18 92.197.176.19 92.197.177.16 92.197.177.17 92.197.177.18 92.197.177.19 92.197.182.16 92.197.182.17 92.197.182.18 92.197.182.19 }"

set block-policy drop
set skip on lo0

nat on $ext_if inet proto udp from $pbx port 5060 to ! $pbx port 5060 -> ($ext_if:0) static-port
nat on $ext_if inet from $local_net to any -> ($ext_if:0)
rdr on $ext_if inet proto udp from ! $pbx port 5060 to ($ext_if:0) port 5060 -> $pbx port 5060

block in log (all) all
pass in on $ext_if proto udp from any port 5060
pass in on $int_if proto udp from $pbx port 5060
 
If the phone provider is going to reject the packets, why configure the firewall to reject them? Or... wait, you're using PF to rewrite the source port?
 
You need SIP proxy. It won't work if you only redirect port 5060.

When you are creating PAT you actually creating a static rule in your NAT table so you don't need this:
nat on $ext_if inet proto udp from $pbx port 5060 to ! $pbx port 5060 -> ($ext_if:0) static-port
SIP is using TCP and UDP for session initiation then it dynamically open another high range ports for the actual voice traffic. This is the role of the sip proxy which stand on your firewall and intercept those 5060 connections.

There's also another method to allow the sip traffic based on the tos 0xb8 or allowing the udp range from 10000:20000 to pass.

 
If the phone provider is going to reject the packets, why configure the firewall to reject them? Or... wait, you're using PF to rewrite the source port?
Not really, I use pf to rewrite the source address. If I would not use NAT, outgoing packets would look like this:
src addr: 192.168.0.2 | dst addr: 92.197.176.16
src port: 5060 | dst port: 5060

Of course, an answer to this packet would not be routable through the internet. So, I use a NAT rule. With "normal" NAT the outgoing packet would look like this:
src addr: 212.345.67.8 | dst addr: 92.197.176.16
src port: 57221 | dst port: 5060

... where 212.345.67.8 is the public address of my router and 57221 is some higher random port for NAT. That would be fine. At this point the rdr rule would work, too. But my provider wants those packets with the source port set to 5060. To do this I use the static-port flag on the nat rule to prevent NAT from changing the port. Now the packet is in the correct form:
src addr: 212.345.67.8 | dst addr: 92.197.176.16
src port: 5060 | dst port: 5060

But with this last step I created a new problem: Incoming packets on 5060 get dropped by pf, unless they belong to outgoing connection. So if say "ping" to my provider, he can say "pong", but if he says "ping" to me, I drop that message... which is not the behavior I am aiming for. So if I only could rewrite the the source address without all the connection handling stuff, then I would have a solution. But I dont know how to do that.
 
Oh, I see. You need a static port mapping. But that's going to limit you to a single IP that can make calls. Hence the SIP proxy. OpenSIPS can apparently act like one.
 
How do I achieve a "true" static port mapping? The single ip limit is fine. But with this line I don't get the incoming connections.
nat on $ext_if inet proto udp from $pbx port 5060 to ! $pbx port 5060 -> ($ext_if:0) static-port
 
packets destined for the phone provider must have the source and destination port set to 5060/udp, or else they will be rejected by the phone provider
How do you know that? Just asking because it sounds very unusual :-/
Except for that bit, I have the same configuration (a SIP PBX behind a pf firewall, IPv4-only, so rewriting needed) and it worked fine with two different providers so far...
 
I know it from customer support. It sounds unusual fro me, too... They even ignore the sip contact header. So it is also no solution to be reachable on 5061.
 
I found something interesting in the manual page for natd(8). natd knows an option -deny_incoming. So with natd you can control what do to with incoming packets that don't match a connection from the NAT table. Is there something similar for pf?
-deny_incoming | -d
Do not pass incoming packets that have no entry in the inter-
nal translation table.
If this option is not used, then such a packet will be al-
tered using the rules in -target_address below, and the entry
will be made in the internal translation table.
 
I really don't like how FreeBSD passes the buck on documenting their older version of pf by just warning that the documents they link to may be out of date. That being said, I achieve a static port mapping on OpenBSD using "pass in ... rdr-to"
 
... but without any success, which seems logical to me, because NAT rules "win" over redirect rules.
That's not what the man page says:
Evaluation order of the translation rules is dependent on the type of the
translation rules and of the direction of a packet. binat rules are al-
ways evaluated first. Then either the rdr rules are evaluated on an in-
bound packet or the nat rules on an outbound packet. Rules of the same
type are evaluated in the same order in which they appear in the ruleset.
The first matching rule decides what action is taken.

Speaking of binat, have you tried it?
# BINAT
# Translate outgoing packets' source address (any protocol).
# Translate incoming packets' destination address to an internal machine
# (bidirectional).
binat on $ext_if from 10.1.2.150 to any -> $ext_if

I found something interesting in the manual page for natd(8)...
Natd is only used by ipfw and only sometimes:
 
I really don't like how FreeBSD passes the buck on documenting their older version of pf by just warning that the documents they link to may be out of date. That being said, I achieve a static port mapping on OpenBSD using "pass in ... rdr-to"
This rdr-to rule has no effect, if ther is also a nat rule on port 5060. And without the nat rule I don't send my sip packets with the source port set to 5060.
 
Speaking of binat, have you tried it?
# BINAT
# Translate outgoing packets' source address (any protocol).
# Translate incoming packets' destination address to an internal machine
# (bidirectional).
binat on $ext_if from 10.1.2.150 to any -> $ext_if


Natd is only used by ipfw and only sometimes:
Sadly binat is not an option for me, because I don't have a second public IPv4 address to sacrifice. Since binat maps all ports, the rest of my network would be without internet access, if I would use binat with my sole public ip.
 
Your question is of interest to me too; perhaps I can be of help.

Like Zirias has a functional PBX behind FBSD firewall, I have one here too. In my case, the firewall is PfSense and the PBX is a bhyve-vm. It has a public IP yet only its SIP port@5060 is opened on the pfsense firewall. The port is only opened to the IP addresses of the SIP registrar - several of them like you have above. Otherwise, you will be dealing with robocalls.

In a nutshell, you may have luck with pfsense than an OS acting as a firewall. I had trouble with the OS as an OpenVPN server but not pfsense. Yet, our PBX also has a private IP and firewall of the base OS was equally configured to allow SIP traffic to the VM bridge/interface. And I can confirm that it works. We however use the public/static IP given that there is a passthru on the box for one of its NICs. Offense has several configurations to do to make it robust; and you will be glad you did. It's a dedicated FBSD for firewall.
 
Sadly binat is not an option for me, because I don't have a second public IPv4 address to sacrifice. Since binat maps all ports, the rest of my network would be without internet access, if I would use binat with my sole public ip.
I'm not super familiar with binat, having never used it, but it looks like you're right. At least TCP traffic will still work, but any outgoing UDP connections from any machines other than the SIP host would not work.
 
Probably something like this, just by poking around for five minutes

rdr pass on $ext_if inet proto udp from any to $ext_if port 5060 -> $pbx port 5060
 
I solved it. It turned out that the problem wasn't because of nat not letting in incoming connections through, but due to fragmentation. I didn't use traffic normalization and coincidentally all the incoming connections from my provider were fragmented (unlike packets from the provider that came in as responses). I figured this out by recreating my scenario with netcat in udp mode instead of the PBX. When there were no problems within the nc scenario, I compared the ip/udp headers. I found the comment from Jose very helpful, where he stated that nat rules are not necessary evaluated before rdr rules. Instead, it is either all nat rules first and then all rdr rules, OR all rdr rules first and then all nat rules... depending on which kind matches first.

The solution was adding the flowing line, so that packets get reassembled.
Code:
scrub fragment reassemble
 
I solved it. It turned out that the problem wasn't because of nat not letting in incoming connections through, but due to fragmentation.
Do you block ping (icmp echoreq) in your firewall by chance? This will break path MTU discovery, and may be the reason why you're getting fragmented packets in the first place.

I would leave the normalization rules in your pf.conf, however. There's a lot of garbage flowing through the Internet.
 
Back
Top