bookends.m4: Much more intelligent initialization.
[firewall] / bookends.m4
index 7374cd3..b51f8ae 100644 (file)
@@ -28,14 +28,76 @@ m4_divert(30)m4_dnl
 ## The main chains: set policy to drop, and then clear the rules.  For a
 ## while, incoming packets will be silently dropped, but we should have got
 ## everything going before anyone actually hits a timeout.
-for t in mangle filter; do
-  for i in PREROUTING INPUT FORWARD OUTPUT POSTROUTING; do
-    run ip46tables -t $t -P $i DROP 2>/dev/null || :
-    run ip46tables -t $t -F $i 2>/dev/null || :
+##
+## We don't control some of the chains, so we should preserve them.  This
+## introduces a whole bunch of problems.
+
+## Chains we're meant to preserve
+preserve_chains="filter:fail2ban filter:fail2ban-* $preserve_chains"
+
+## Take the various IP versions in turn.
+unref=nil
+for ip in ip ip6; do
+  for table in $(cat /proc/net/${ip}_tables_names); do
+
+    ## Step 1: clear out the builtin chains.
+    ${ip}tables -nL -t $table |
+    sed -n '/^Chain \([^ ]\+\) (policy .*$/ s//\1/p ' |
+    while read chain; do
+      case $table in
+       nat) policy=ACCEPT ;;
+       *) policy=DROP ;;
+      esac
+      run ${ip}tables -t $table -P $chain $policy
+      run ${ip}tables -t $table -F $chain
+    done
+
+    ## Step 2: clear out user chains.  Unfortunately, we can only clear
+    ## chains which have no references to them, so work through picking off
+    ## unreferenced chains which aren't meant to be preserved until there are
+    ## none left.
+    while :; do
+      progress=nil
+      ${ip}tables -nL -t $table |
+      sed -n '/^Chain \([^ ]\+\) (0 references)$/ s//\1/p ' \
+       >/var/run/firewall-chains.tmp
+      while read chain; do
+       match=nil
+       for pat in $preserve_chains; do
+         case "$table:$chain" in $pat) match=t ;; esac
+       done
+       case $match in
+         nil)
+           run ${ip}tables -t $table -F $chain
+           run ${ip}tables -t $table -X $chain
+           progress=t
+           ;;
+       esac
+      done </var/run/firewall-chains.tmp
+      case $progress in nil) break ;; esac
+    done
+
+    ## Step 3: report on uncleared user chains.  This means that there's a
+    ## serious problem.
+    ${ip}tables -nL -t $table |
+    sed -n '/^Chain \([^ ]\+\) (\([1-9][0-9]*\) references)$/ s//\1 \2/p ' \
+      >/var/run/firewall-chains.tmp
+    while read chain refs; do
+      match=nil
+      for pat in $preserve_chains; do
+       case "$table:$chain" in $pat) match=t ;; esac
+      done
+      case $match in
+       nil)
+         echo >&2 "$0: can't clear referenced $ip chain \`$table:$chain'"
+         unref=t
+         ;;
+      esac
+    done </var/run/firewall-chains.tmp
   done
-  run ip46tables -t $t -F
-  run ip46tables -t $t -X
 done
+rm -f /var/run/firewall-chains.tmp
+case $unref in t) exit 1 ;; esac
 
 m4_divert(32)m4_dnl
 ###--------------------------------------------------------------------------