pf will not let inbound SIP traffic pass

Hey everyone.

Running 8.0-RELEASE here. I am trying to set up either siproxd or asterisk (trying both to eliminate the app as the cause of the issue) on my FreeBSD PC acting as a NAT router.

The issue seems to be that when either siproxd or asterisk sends a REGISTER to a remote SIP server, I never see the "OK" response from the remote server. As a result, both applications retransmit their REGISTER endlessly, even though the remote server sends the reply.

I did a tcpdump and verified that I can, in fact, see the reply packets from the remote server come in. It is just that the datagrams are not being passed on to the application layer (siproxd/asterisk).

I have, as far as I know, correctly opened up the port in /etc/pf.conf and reloaded the rules by doing /etc/rc.d/pf reload (no syntax errors reported).

Here is my ruleset:

Code:
# options
set block-policy drop
set loginterface dc0
set skip on lo

# scrub
#scrub reassemble tcp no-df random-id

# nat/rdr
nat on dc0 inet from !self to any -> (dc0:0)
nat on tun0 inet from !self to any -> (tun0:0)
nat-anchor "ftp-proxy/*"
rdr-anchor "ftp-proxy/*"
rdr on alc0 proto tcp from any to any port ftp -> 127.0.0.1 port 8021
#rdr pass on dc0 proto tcp from any to any port 3389 -> 192.168.0.17 port 3389

# filter rules
block in
pass out keep state
anchor "ftp-proxy/*"
antispoof quick for { lo alc0 }

pass in on dc0 inet proto tcp from any to (dc0) port { 6881, 2222, 113, 81 } flags S/SA keep state
pass in on dc0 inet proto udp from any to (dc0) port 6881 keep state
pass in on dc0 inet proto udp from any to (dc0) port 9000:9050 keep state
pass in inet proto udp from any to any port 5060 keep state
pass in quick on dc0 inet proto {tcp, udp} from x.x.2.173 to any keep state
pass in on gif0 inet6 proto tcp from any to (gif0) port { 6881, 2222, 113, 81 } flags S/SA keep state
pass in inet proto icmp all icmp-type echoreq keep state
pass in quick on alc0

I want to comment that I have tried many various things in my pf ruleset to make this work - none of them have succeeded. I tried pass in quick, for example, and I even put a rule in to allow ALL packets from the remote server (in addition to the rule above it allowing port 5060 from any host).

I know that the "pass in quick on dc0 inet proto {tcp, udp} from x.x.2.173 to any keep state" rule is having some affect becuase I can log on to that server (x.x.2.173) over ssh and do a "telnet <my hostname>" and get a "Connection Refused". Normally, I would not see that because my block policy is set to drop non-matching packets.

The million dollar question is, why does UDP port 5060 have a problem making it through pf?

I have spent several hours tearing my hair out over this issue! I took the drastic action of "/etc/rc.d/pf stop" and guess what, IT STARTED WORKING! So the problem *has* to be in pf somewhere, I just do not understand what I am doing wrong here.

Thanks in advance!
 
Another detail:

If I copy/paste the contents of the 200 OK response into a text file and use netcat on the remote server to send out that response, it makes it into siproxd/asterisk just fine.

What could possibly be the difference? Is there some sort of state-tracking within pf that could be the cause of this? (I tried doing this with and without the "keep state" directives - no help).

Thx.
 
The proper way to reload a ruleset is # pfctl -f /etc/pf.conf. Check whether you're actually running the ruleset you think you're running using # pfctl -sr.

By the way, which interface is your application bound to?
 
I am not a PF expert, but here are some things I noticed with your pf.conf.

According to pf.conf man, the default block-policy is drop. So it would seem redundant to spec out a global drop policy when one is already in place.

You can also remove the 'keep state' options from your pass in statements. Starting in OpenBSD 4.1, all filter rules automatically create a state entry when a packet matches the rule. In earlier versions the filter rule had to explicitly use the 'keep state' option and depending on which website you crafted your rules from, they may or may not have got the memo.

:D

For stateful connections, the default is 'flags S/SA', so those can be dropped too.

I do not understand the reason for the double NAT entries. While it might be clever to not (!) an interface, standard convention is to declare the interfaces via $ext_if and $int_if under #macros. Additionally, all of the examples that I have seen to not an interface has a space between the exclamation point and the interface. I don't know if it makes a difference or not ... no pun intended.

This looks to be part of your problem:
pass in inet proto udp from any to any port 5060 keep state
RFC 3261 says:
18.2.1 Receiving Requests said:
For any port and interface that a server listens on for UDP, it MUST listen on that same port and interface for TCP.

You might try a construct like this instead:
Code:
pass in on proto { udp, tcp } to port sip

and see what happens.
 
And I forgot to add that running:

tcpdump -eni pflog0

will tell you what rule is matching along with interface and port numbers.
 
Thanks for the replies, everyone.

Back to business: I ditched asterisk as I am convinced this is not an application problem. I would rather use siproxd instead of asterisk on my home server as all I am trying to do is register to a *remote* asterisk server.

DutchDaemon: I am running the rules I expected pf to be running. Even though
Code:
/etc/rc.d/pf reload
isn't the "proper" way, it seems to work as I expected regardless. But I did try reloading the rules as you suggested and it didn't help.

johnblue: I used that command to watch the pflog and I saw that nothing was being blocked. The 200 OK response from the remote Asterisk was being matched in a "pass in" rule, not blocked by the "block" rule. BTW - adding a tcp pass in rule didn't help.

With that said, I've made another discovery:

If I comment out my NAT rules completely, everything works exactly as expected! No issues whatsoever.

If I comment out all the NAT rules except this one, I am still unable to make SIP calls:
Code:
nat on dc0 from !self to any -> (dc0:0)

If I comment that rule out, everything magically starts working!

So, what is it about NAT, or my particular use of NAT as described in that rule that is such a problem?

johnblue - You were commenting about my NAT rules earlier, specifically the two NAT rules. Now that have eliminated all of the NAT rules except that one NAT rule (above), do you still have any comments/criticisms of that particular rule?

Either pf's NAT implementation interferes with what I am doing or my NAT rule needs to be changed. Any comments?

BTW - Here is my latest pf.conf. Again - if all NAT rules are commented out, everything works great. If I uncomment that one NAT rule at the top, it breaks.

Code:
# options
set loginterface dc0
set skip on lo

# scrub
#scrub reassemble tcp no-df random-id

# nat/rdr
#nat on dc0 from !self to any -> (dc0:0)
#nat on tun0 from !self to any -> (tun0:0)
#nat-anchor "ftp-proxy/*"
#rdr-anchor "ftp-proxy/*"
#rdr on alc0 proto tcp from any to any port ftp -> 127.0.0.1 port 8021
#rdr pass on dc0 proto tcp from any to any port 3389 -> 192.168.0.17 port 3389

# filter rules
block in log all
pass out 
#anchor "ftp-proxy/*"
#antispoof quick for { lo alc0 }

pass in on dc0 proto tcp from any to (dc0) port { 6881, 2222, 113, 81 }
pass in on dc0 proto udp from any to (dc0) port 6881 

pass in log on dc0 proto udp to (dc0) port 9000:9050 
pass in log on dc0 proto { udp, tcp } to (dc0) port 5060

pass in on gif0 inet6 proto tcp from any to (gif0) port { 6881, 2222, 113, 81 }
pass in inet proto icmp all icmp-type echoreq 
pass in quick on alc0
 
Try

Code:
nat on dc0 from ! dc0:0 to any -> dc0:0

I think your '!self" casts too wide a net; it appears to grab IP addresses from every interface, including tunnels and localhost.

Compare 'pfctl -sn' on both settings.
 
Back
Top