I am late to the NPF party, but let’s face it: I don’t use NetBSD because I want the latest and greatest, I use NetBSD because I want something I can (mostly) make work wherever I put it.

Since setting up a proper desktop machine with NetBSD I decided to use it as a playground for NPF with the ultimate goal of switching to NPF from IPF (IPFilter) where it actually matters to me most; IPF still works for me, but it seems to become more quirky with each NetBSD release.

It was not obvious - at all - to me from the documentation how to get NPF working with IPv6 and IPv4 (I have both at home, thanks excellent broadband provider!). With IPF there are two separate .conf files that are mostly independent from each other. Anyway, in case anyone else is struggling similarly I ended up doing the following and couldn’t figure a way to completely avoid duplication:

# Simple npf.conf for Desktop with wired connection and IPv4 and IPv6

$ext4_if = inet4(bge0)
$ext6_if = inet6(bge0)

$services_in_tcp = domain
$services_in_udp = domain

procedure "log" {
	log: npflog0
}

group "external" on $ext4_if {
	pass stateful out final all

	pass stateful in final family inet4 proto tcp to $ext4_if port ssh apply "log"
	pass stateful in final proto tcp to $ext4_if port $services_in_tcp
	pass stateful in final proto udp to $ext4_if port $services_in_udp

	# Passive FTP
	pass stateful in final proto tcp to $ext4_if port 49151-65535
	# Traceroute
	pass stateful in final proto udp to $ext4_if port 33434-33600
}

group "external6" on $ext6_if {
	pass stateful out final all

	pass stateful in final proto tcp to $ext6_if port $services_in_tcp
	pass stateful in final proto udp to $ext6_if port $services_in_udp

	# Passive FTP
	pass stateful in final proto tcp to $ext6_if port 49151-65535
	# Traceroute
	pass stateful in final proto udp to $ext6_if port 33434-33600
}
group default {
	pass final on lo0 all
	block all
}

Attempts to have one group that would work on both IPv6 and IPv4 failed. Maybe it is possible somehow, but I sure as hell couldn’t figure it out and the above does work.

There are some good tips at the bottom of the npfctl man page that you can use to test the rules are loaded and working:

  1. Use npfctl reload, not load otherwise you get a weird error of “npfctl_config_load: no such file or directory”.
  2. Then use npfctl start
  3. Then use npfctl show which should list the rules.

Since I seemingly had it working on the desktop I had a whirl on XEN, which requires building your own kernel and then being able to publish the kernel somewhere you can download it from so you can boot it. This is more fiddly than it sounds when your web host is also your XEN NetBSD install. However, after doing all that it turns out NPF is broken on XEN on 7.0.2. I could fight it, but I’ve decided to just wait until 7.1.

Coming back to the desktop I was surprised to find that just having npf=YES in /etc/rc.conf wasn’t enough to actually load the rules and start npf on boot - or at least that is what I thought. I started playing about with trying to explicitly call /etc/rc.d/npf reload and /etc/rc.d/npf start in rc.local, but then found it would produce an error because the interfaces didn’t seem to be ready. After a bit more searching I found this: npf startup failure when using dhcpcd inet4 and inet6. The exact issue I was seeing.

Guess I’m waiting for 7.1 on the desktop as well!

Definitely feel fine with not rushing towards npf


[EDIT: 2018-01-04] The above ruleset is not so accurate anymore. Especially since the demise of my desktop NetBSD machine. I did just figure out NPF on my Linode install and had issues there with router solicitation, however I found /usr/share/examples/npf/host-npf.conf which covers this so I’m basically still using the above with tweaks:

  • Same 4 and 6 grouping.
  • No FTP.
  • icmp and ipv6-icmp rules basically as per the host-npf example.
  • Only logging on the block in the default group.