Take the 2-minute tour ×
Super User is a question and answer site for computer enthusiasts and power users. It's 100% free, no registration required.

I've been able to deny all connections to external networks unless my OpenVPN connection is active using pf.conf. However, I lose Wi-Fi connectivity if the connection is broken by closing and opening the laptop lid or toggling Wi-Fi off and on again.

  • I'm on Mac OS 10.8.1.
  • I connect to the Web via Wi-Fi (from varying locations, including Internet cafés).
  • The OpenVPN connection is set up with Viscosity.

I have the following packet filter rules set up in /etc/pf.conf

# Deny all packets unless they pass through the OpenVPN connection
wifi=en1
vpn=tun0

block all

set skip on lo
pass on $wifi proto udp to [OpenVPN server IP address] port 443
pass on $vpn

I start the packet filter service with sudo pfctl -e and load the new rules with sudo pfctl -f /etc/pf.conf.

I have also edited /System/Library/LaunchDaemons/com.apple.pfctl.plist and changed the line <string>-f</string> to read <string>-ef</string> so that the packet filter launches at system startup.

This all seems to works great at first: applications can only connect to the web if the OpenVPN connection is active, so I'm never leaking data over an insecure connection.

But, if I close and reopen my laptop lid or turn Wi-Fi off and on again, the Wi-Fi connection is lost, and I see an exclamation mark in the Wi-Fi icon in the status bar. Clicking the Wi-Fi icon shows an "Alert: No Internet connection" message:

No Internet connection message

To regain the connection, I have to disconnect and reconnect Wi-Fi, sometimes five or six times, before the "Alert: No Internet connection" message disappears and I'm able to open the VPN connection again. Other times, the Wi-Fi alert disappears of its own accord, the exclamation mark clears, and I'm able to connect again. Either way, it can take five minutes or more to get a connection again, which can be frustrating.

Removing the line block all resolves the problem (but allows insecure connections), so it seems there's a service I'm blocking that Apple requires in order to regain and confirm a Wi-Fi connection. I have tried:

  • Enabling icmp by adding pass on $wifi proto icmp all to pf.conf
  • Enabling DNS resolution by adding pass on $wifi proto udp from $wifi to any port 53
  • Trying to learn more by logging blocked packets (by changing block all to block log all), but logging seems to be disabled under OS X, because doing sudo tcpdump -n -e -ttt -i pflog0 to see the log results in "tcpdump: pflog0: No such device exists".

None of this helps re-establish a Wi-Fi connection any faster.

What else can I do to determine what service needs to be available to regain Wi-Fi connectivity, or what rule should I add to pf.conf to make Wi-Fi reconnections more reliable?

share|improve this question

3 Answers 3

up vote 6 down vote accepted

By monitoring network connections using Little Snitch, I've found that Apple uses the mDNSResponder app in the background to check if the Wi-Fi connection is available. mDNSResponder sends UDP packets to nameservers to check connectivity and resolve hostnames to IPs.

Changing the UDP rule I had previously to allow all UDP packets over Wi-Fi allows mDNSResponder to connect, which means Wi-Fi now reconnects first time after a disconnection. In case it helps others in future, my final pf.conf including Apple's default rules for Mountain Lion looks like this:

#
# com.apple anchor point
#
scrub-anchor "com.apple/*"
nat-anchor "com.apple/*"
rdr-anchor "com.apple/*"as
dummynet-anchor "com.apple/*"
anchor "com.apple/*"
load anchor "com.apple" from "/etc/pf.anchors/com.apple"

#
# Allow connection via Viscosity only
#
wifi=en1 #change this to en0 on MacBook Airs and other Macs without ethernet ports
vpn=tun0
vpn2=tap0

block all

set skip on lo          # allow local traffic

pass on p2p0            #allow AirDrop
pass on p2p1            #allow AirDrop
pass on p2p2            #allow AirDrop
pass quick proto tcp to any port 631    #allow AirPrint

pass on $wifi proto udp # allow only UDP packets over unprotected Wi-Fi
pass on $vpn            # allow everything else through the VPN (tun interface)
pass on $vpn2           # allow everything else through the VPN (tap interface)

This means that data can now be leaked over Wi-Fi by the small number of applications that use the UDP protocol, unfortunately, such as ntpd (for time synchronisation) and mDNSResponder. But this still seems better than allowing data to travel unprotected over TCP, which is what the majority of applications use. If anyone has any suggestions to improve on this setup, comments or further answers are welcome.

share|improve this answer
    
This is something I've casually been interested in, seeing your results has inspired me to go home and try it! thanks! –  jakev Sep 5 '12 at 22:52
    
@SixSlayer It seems to work pretty well! I have Viscosity set up to autoconnect on startup and on dropped connections, which makes the whole thing pretty much seamless. Main thing to note is that pf.conf and com.apple.pfctl.plist get reset to the default after OS updates, apparently, so it's worth keeping a backup of both. –  Nick Sep 5 '12 at 22:53
    
IMHO the UDP thing is kind of a bummer. I'm not a network guy but this kinda thing helps me learn, and I have a fascination with having control over these kinds of details. I'll spend some time looking for a work around, but if anyone beats me to it, just as well. –  jakev Sep 6 '12 at 1:51
    
this is awesome - exactly what I was looking for. Thank you! –  keo Sep 7 '12 at 19:04
    
Have you maybe managed to have many OpenVPN connections open at the same time, and routing through them in parallel? (to gain and add up bandwidth) –  keo Sep 7 '12 at 19:04

You don't need to allow all UDP. The 'm' in mDNS means 'multicast', and it uses a specific multicast destination IP address called the "link local multicast address", and a UDP port number 5353.

This means in your solution above, you are unnecessarily allowing traffic to all 65535 UDP ports to all 3.7 Billion routable IP addresses in the world to bypass your VPN. You'd be surprised how many applications use UDP, so you are significantly defeating the purpose of your original idea to prevent outgoing traffic when the VPN is down.

Why not use this rule instead:

pass on $wifi proto udp to 224.0.0.251 port 5353

A very important rule of thumb with firewall configuration - when making exceptions through your firewall, always try to use the most specific rule possible. The specificity sometimes comes at the expense of convenience & ease of use, i.e. you might then find there's some other link-local protocol that needs to be let through, and add yet another specific rule.

If you swap in the above rule and find that the original wifi problem returns, then your PF may be blocking DHCP, the protocol used to autoconfigure your network devices' IP addresses. (in a home network, typically your broadband router would be your DHCP server). The rule you'd need to allow DHCP, would be:

pass on $wifi proto udp from 0.0.0.0 port 68 to 255.255.255.255 port 67

*Note: you may need to substitute 0.0.0.0 for any. The DHCPREQUEST packet your computer first sends, has a source address 0.0.0.0 because at that stage, your computer doesn't have an IP address yet.
To be honest, I would lean more towards using any. Another option is to rip out any source specification, i.e. pass on $wifi proto udp to 255.255.255.255 port 67, but that means we lose the source-port part of the rule, and being as specific as possible is always the most secure option.

Hope that helps. Here are some useful links:

mDNS: http://en.wikipedia.org/wiki/Multicast_DNS#Packet_structure

DHSP: http://en.wikipedia.org/wiki/Dynamic_Host_Configuration_Protocol#DHCP_discovery

share|improve this answer
    
Thanks for this – that's very helpful indeed. –  Nick Jul 28 at 10:06

This gave me sufficient background info to make the big leap and use pf.conf. Here's what I use on my 10.8 to make it reconnect after the VPN connection drops :

(I only use ethernet but you can change $lan for $wifi and it should work)

lan=en0
wifi=en1
vpn=tun0
block all
set skip on lo
pass on $lan proto { udp,tcp } to 8.8.8.8
pass on $lan proto tcp to vpn.btguard.com port 1194
pass on $vpn
share|improve this answer

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.