I’ve been running a OpenBSD server as my home router for a number of years now, moving between various configuration, scripts, and hardware. I’ve been running on the FW1 for a year now. I originally built the router as an escape from both ISP-provided router/modem combos, as well as custom firmware such as dd-wrt; I wanted more control.

Ever since I was a teenager, I always enjoyed the command line interface. It let me instruct EXACTLY what I wanted the OS to do, and have more control than any GUI out there. However, one area on my router that has always annoyed me was UPnP. This was, as far as I knew, a necessary evil. Without UPnP, online games had a difficult time with multiplayer.

I used miniupnpd for a couple of years, locking it down as best I could. But with multiplayer support, comes security risks. Risks that I thought were a necessary evil. Miniupnpd worked flawlessly for years, but then I got a Nintendo Switch.

I did not expect to experience multiplayer issues on a modern console, as I incorrectly assumed that the Switch would support UPnP. Needless to say, it does not support UPnP currently, or this article would not exist. From Nintendo’s own support site, Nintendo requires that all UDP ports be forwarded to the switch for multiplayer to work! What a wonderful thing (not).

Since I had buddies that wanted to use multiplayer mode on this new Switch, and forwarding all UDP ports was simply not an option, I thought there had to be a better way.

After a day of research, I discovered another reason to love pf; static-port translation. Here is a snippet from pf’s man page:

static-port
           With nat-to rules, the static-port option prevents pf(4) from
           modifying the source port on TCP and UDP packets.

My what a perfect application for this. This seemed to be precisely what I needed. Adding the following lines to my pf did the trick

# Table for consoles that require static ports
table <staticports> persist file "/etc/pf.conf.d/staticports"

...

# Setup static NAT for special devices
pass out on $external from <staticports> to any nat-to ($external:0) static-port

You will of course need a NAT rule for non-static devices. I then removed my miniupnpd anchor, and successfully tested out the Switches multiplayer mode!

Has been tested on OpenBSD 6.4