UPnP-IGD on FreeBSD with PF
I used to always run either OpenBSD or FreeBSD powered routers – basically since around 2000 (before that it was Linux, but we don’t speak of those days anymore). In recent years, starting with my Cayman 3546 router I just started enjoying the simplicity that appliance-type devices offered.
Our most recent setup has been a Linksys WRT54G, which has been rather crap in it’s duties really. For some reason it’s consistently dropping packets and possibly rebooting (but with the garbage default firmware, there’s absolutely no way to tell if it’s rebooted or not) so I decided to replace it with a FreeBSD machine again – at least temporarily.
Networking in fwaggle-land these days is not without it’s new challenges, bumped well up from “lolsecurityissue” to “must have, design requirement” is Universal Plug and Play, or UPNP for short.
The Lowdown on UPNP
UPNP-IGD is both a terrible idea and an awesome idea at the same time – or more accurately: it’s an awesome idea, but a terrible implementation. Basically in it’s “Internet Gateway Device” form, it allows devices behind a firewall to arbitrarily set up port forwarding and open those ports temporarily, completely transparently to the user. For computing, this is probably unwanted – malware can easily make use of it and your firewall’s essentially useless.
But for gaming on consoles, it’s a godsend. It’s the difference between an awful experience on a Playstation 3, and an event-free, enjoyable gaming experience.
Setting up Routing
For the purposes of this article (which is already long enough, thanks to my story-telling) I’ll assume you’re familiar with setting up NAT under FreeBSD. We’re also going to assume you’re using the PF packet filter from OpenBSD – if you’re not and you’re clever enough to work out setting up NAT with one of the others, figuring out PF can be done in an afternoon.
We’ll assume at this point that your machine is up, routing and translating traffic correctly and is protected by some form of firewall ruleset in PF. We’re also going to assume you have a working DHCP server and all your machines are able to connect to the internet in some form. Now we make the top of your PF ruleset look like this:
scrub on dsl0 no-df
nat on dsl0 from lan0:network to any -> (dsl0) static-port
Obviously interface names need changing (or you can rename your interfaces, like I did – which requires only a one-line change in /etc/rc.conf if you happen to swap cards out for a different card/driver later on, instead of grepping for fxp0 in /usr/local/etc/. Reload the ruleset in PF if you haven’t already:
pfctl -f /etc/pf.conf
Let’s explain what’s going on here. First of all, it appears as though PF might scrub at least partially by default these days – I’m not sure about this, I haven’t confirmed it, but my Playstation was complaining about fragmented packets not going through and some Google searching appeared to indicate the PS3 likes to do dumb shit like sending fragmented packets with the DF bit set. This rule helped immensely and removed all complaints from my PS3 once miniupnpd was enabled.
Adding static-port prevents PF from re-arranging port numbers on the WAN side as it makes sense, which might break stuff occasionally, but for our purposes it’ll break far more if PF re-arranges port numbers on UDP traffic – internet games on consoles are stupid. If Modern Warfare 2 sends game data from port 3658, to another host on port 3658, and the packet doesn’t arrive with source port 3658, the host appears to drop it.
Now that you have an anchor set up for MiniUPNPd to add rules to, it’s time to configure the daemon itself. Copy the miniupnpd.conf.sample to miniupnpd.conf in /usr/local/etc, and then start editing it with your favourite editor. The important bits I changed are:
ext_ifname=dsl0 # the interface name of your WAN device
listening_ip=10.0.0.1 # the LAN IP of your router
secure_mode=yes # this is default, but I wanted to point out what a great idea it is
allow 1024-65535 0.0.0.0/0 1024-65535 # access control, but you can do it via PF anyway
secure_mode is important – UPNP-IGD specs allow devices to set up firewall rules for other devices, which is where the broken-by-design issues with regards the UPNP spec come into play. You almost certainly do not want to allow this, and I can’t for the life of me figure out any reason you would need such functionality.
Access control can be configured based on IPs, you can also firewall the UPNP service (UDP port 5555) so that your network doesn’t appear to even support UPNP for hosts that you’d rather not have it. Our network is configured to assign all consoles into a /24 of their own, and to only allow those IPs to use the UPNP service. You can be as draconian as you like with UPNP access control, either via MiniUPNPd’s configuration or just by firewalling the UPNP service.
Testing it out
The first step was testing the internet configuration in the network settings menu. After correctly allowing UPNP, I successfully reached “NAT Type 2” as far as the Playstation 3 is concerned. Next it was time to test if a game would actually work, so I loaded up Modern Warfare 2 and tried it out. At first I had “NAT Type: Strict” which basically meant that I was boned, until I found the above gotcha about source ports. Adding a static-port keyword to my ruleset in PF and reloading gave me “NAT Type: Open”, and I was all set.
With our PS3s in their own subnet, I’m able to enjoy hassle-free internet gaming without sacrificing the security of the rest of our network.