diff --git a/Makefile b/Makefile
index 5db5c90d8839a37f92b12e6d522d238a4e4f613a..709fefae28a5b50c918123d55c9966b3c62e0860 100644
--- a/Makefile
+++ b/Makefile
@@ -15,7 +15,8 @@ install:
 	$(INSTALL) -d $(DESTDIR)$(sbindir)
 	$(INSTALL) -d $(DESTDIR)$(sharedir)
 	$(INSTALL) -d $(DESTDIR)$(fwconfdir)
-	$(INSTALL) -m 755 firewall $(DESTDIR)$(sbindir)/firewall
+	$(INSTALL) -m 755 update-firewall $(DESTDIR)$(sbindir)/update-firewall
+	$(INSTALL) -m 755 update-ipset $(DESTDIR)$(sbindir)/update-ipset
 	(for t in $(TABLES); do \
 	  $(INSTALL) -d $(DESTDIR)$(fwconfdir)/$$t.d ; \
 	  $(INSTALL) -d $(DESTDIR)$(sharedir)/$$t.d ; \
diff --git a/README.md b/README.md
index c0c5c9bfb055f3dafe517340a563eff8ff64cbb5..8ae1bd54216e4fbf62c8fd31636ed96a72918ddf 100644
--- a/README.md
+++ b/README.md
@@ -67,6 +67,11 @@ iptables options (the default is simply `-j ALLOW`).
 Allow incoming traffic to the specified ports. *PORT_SPEC*
 should be a comma-separated list of destination ports.
 
+# Usage
+
+Run *update-firewall* to set up iptables whenever the rules below
+/etc/firewall change.
+
 # Notes
 
 The firewall script will always attempt to setup IPv6 rules, even if
diff --git a/conf-dist/filter.d/02ipset b/conf-dist/filter.d/02ipset
index 35f4c75e2bed5ecece2aabe0a16dae37da38335c..704db90a642ed090f065f434510e8d516dca45cb 100644
--- a/conf-dist/filter.d/02ipset
+++ b/conf-dist/filter.d/02ipset
@@ -2,41 +2,9 @@
 # Scales easily with large number of blocked entries.
 #
 # Reads the blacklists from:
-# /etc/firewall/blocked_{ips,nets}.{ipv4,ipv6}
+# /etc/firewall/blocked/{ip,net}/{ipv4,ipv6}/*
 
-d=${CONFIG_DIR:-/etc/firewall}
-
-ipset_add_file() {
-    local proto="$1"
-    local set_type="$2"
-    local set_name="block_${set_type}"
-    local family=
-    case "${proto}" in
-        ipv4)
-            family=inet
-            ;;
-        ipv6)
-            family=inet6
-            set_name="${set_name}6"
-            ;;
-    esac            
-    local source_file="${d}/blocked_${set_type}s.${proto}"
-
-    echo "create ${set_name} hash:${set_type} family ${family} hashsize 1024 maxelem 65536"
-    echo "flush ${set_name}"
-    if [ -e ${source_file} ]; then
-        grep -v '^#' ${source_file} \
-            | grep -v '^$' \
-            | sed -e "s/^\\(.*\\)\$/add ${set_name} \\1/"
-    fi
-}
-
-(
-    ipset_add_file ipv4 ip
-    ipset_add_file ipv6 ip
-    ipset_add_file ipv4 net
-    ipset_add_file ipv6 net
-) | ipset restore -exist
+update-ipset
 
 add_rule4 -A pre-input -m set --match-set block_ip src -j DROP
 add_rule6 -A pre-input -m set --match-set block_ip6 src -j DROP
diff --git a/debian/firewall.service b/debian/firewall.service
index c16f2e19550bfb85409accba1277e3bee47c1144..54a6478d7284506130a1568fca69e49a50956c3f 100644
--- a/debian/firewall.service
+++ b/debian/firewall.service
@@ -4,7 +4,7 @@ Description=Set up firewall
 [Service]
 Type=oneshot
 EnvironmentFile=-/etc/default/firewall
-ExecStart=/usr/sbin/firewall
+ExecStart=/usr/sbin/update-firewall
 
 [Install]
 WantedBy=multi-user.target
diff --git a/firewall b/update-firewall
similarity index 100%
rename from firewall
rename to update-firewall
diff --git a/update-ipset b/update-ipset
new file mode 100644
index 0000000000000000000000000000000000000000..d8da43c633ec723d438140f172884dcfeaac76d5
--- /dev/null
+++ b/update-ipset
@@ -0,0 +1,42 @@
+#!/bin/sh
+#
+# Create or reload ipset tables using data from /etc/firewall/blocked/
+#
+
+basedir=${CONFIG_DIR:-/etc/firewall/blocked}
+
+gen_set() {
+    local proto="$1"
+    local set_type="$2"
+    local set_name="block_${set_type}"
+    local family=
+    case "${proto}" in
+        ipv4)
+            family=inet
+            ;;
+        ipv6)
+            family=inet6
+            set_name="${set_name}6"
+            ;;
+    esac
+
+    echo "create ${set_name} hash:${set_type} family ${family} hashsize 1024 maxelem 65536"
+    echo "flush ${set_name}"
+
+    local source_dir="${basedir}/${set_type}/${proto}"
+    if [ -d "${source_dir}" ]; then
+        cat $(run-parts --list "${source_dir}") \
+            | grep -v '^#' ${source_file} \
+            | grep -v '^$' \
+            | sed -e "s/^\\(.*\\)\$/add ${set_name} \\1/"
+    fi
+}
+
+set -o pipefail
+
+(
+    gen_set ipv4 ip
+    gen_set ipv6 ip
+    gen_set ipv4 net
+    gen_set ipv6 net
+) | ipset restore -exist