The Hungry Hacker's Explanation of Everything

Home » Operating Systems

UPnP-IGD on FreeBSD with PF

7 April 2010 4 Comments

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
rdr-anchor miniupnpd

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.

Configuring MiniUPNPd

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.

4 Comments »

  • FreeBSD + pf(4) + miniupnpd « fwaggle.org said:

    […] So I thought I’d get around to posting some articles to my Hungry Hacker website (which I moved the WordPress-powered one over to the main hostname, hopefully motivating myself to update it more often), one of which is my article about getting UPnP working with pf(4) on FreeBSD. […]

  • kestasjk said:

    Excellent stuff, an article really tailor made to my needs. Who knows how long it would have taken me to find static-port and rdr-anchor without it (which really miniupnpd should refer to on its man page)

    Thanks for that

  • Me said:

    Thank you!!! This helped me immensely with MiniUPnPd on OpenBSD + PF, did the same and everything works now – type 2 NAT on PS3, was driving me nuts. Reading forums didn’t help much at all, cause they’re talking about linksys, dlink, etc. routers.

    I was always getting disconnected because your router/NAT is too strict and other messages.

  • Johan D said:

    Thank you very much for this one!

    I was having a tough time with getting a PS3 to work with online games behind my FreeBSD router. Tried forwarding a multitude of ports that I found recommended online without any result at all. But now the PS3 works nicely with UPNP.

    As a reminder if someone reads this:

    You need to setup a few things in the miniupnpd.conf file as well and if you want it to restart on reboot you’ll need to add the following lines to your /etc/rc.conf:

    miniupnpd_enable=”YES”
    # change the path to what you name your miniupnpd.conf to:
    miniupnpd_config=”/usr/local/etc/miniupnpd.conf”
    miniupnpd_flags=””

    Cheers to you for this blog post!!

Leave your response!

Add your comment below, or trackback from your own site. You can also subscribe to these comments via RSS.

Be nice. Keep it clean. Stay on topic. No spam.

You can use these tags:
<a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong>

This is a Gravatar-enabled weblog. To get your own globally-recognized-avatar, please register at Gravatar. Note: By filling out this comment form or emailing us you are signifying that you have read and agree to the terms laid out on the Contact Us page.