functions.m4, numbers.m4: Define protocol number for IPv6 tunnelling.
[firewall] / functions.m4
index 3214483..5cc70f8 100644 (file)
@@ -1,4 +1,4 @@
-### -*-m4-*-
+### -*-sh-*-
 ###
 ### Utility functions for firewall scripts
 ###
@@ -50,6 +50,18 @@ defport () {
   eval port_$name=$number
 }
 
+## defproto NAME NUMBER
+##
+## Define $proto_NAME to be NUMBER.
+defproto () {
+  name=$1 number=$2
+  eval proto_$name=$number
+}
+
+m4_divert(38)m4_dnl
+###--------------------------------------------------------------------------
+### Utility chains (used by function definitions).
+
 m4_divert(22)m4_dnl
 ###--------------------------------------------------------------------------
 ### Basic chain constructions.
@@ -104,19 +116,50 @@ m4_divert(24)m4_dnl
 ## Set an IP sysctl.
 setopt () {
   set -e
-  opt=$1; shift; val=$*
-  run sysctl -q net/ipv4/$opt="$val"
+  opt=$1 val=$2
+  any=nil
+  for ver in ipv4 ipv6; do
+    if [ -f /proc/sys/net/$ver/$opt ]; then
+      run sysctl -q net/$ver/$opt="$val"
+      any=t
+    fi
+  done
+  case $any in
+    nil) echo >&2 "$0: unknown IP option $opt"; exit 1 ;;
+  esac
 }
 
-## setdevopt OPTION VALUE
+## setdevopt OPTION VALUE [INTERFACES ...]
 ##
 ## Set an IP interface-level sysctl.
 setdevopt () {
   set -e
-  opt=$1; shift; val=$*
-  for i in /proc/sys/net/ipv4/conf/*; do
-    [ -f $i/$opt ] &&
-      run sysctl -q net/ipv4/conf/${i#/proc/sys/net/ipv4/conf/}/$opt="$val"
+  opt=$1 val=$2; shift 2
+  case "$#,$1" in
+    0, | 1,all)
+      set -- $(
+       seen=:
+       for ver in ipv4 ipv6; do
+         cd /proc/sys/net/$ver/conf
+         for i in *; do
+           [ -f $i/$opt ] || continue
+           case "$seen" in (*:$i:*) continue ;; esac
+           echo $i
+         done
+       done)
+      ;;
+  esac
+  for i in "$@"; do
+    any=nil
+    for ver in ipv4 ipv6; do
+      if [ -f /proc/sys/net/$ver/conf/$i/$opt ]; then
+       any=t
+       run sysctl -q net/ipv4/conf/$i/$opt="$val"
+      fi
+    done
+    case $any in
+      nil) echo >&2 "$0: unknown device option $opt"; exit 1 ;;
+    esac
   done
 }
 
@@ -144,15 +187,24 @@ commonrules () {
 
   ## Pass fragments through, assuming that the eventual destination will sort
   ## things out properly.  Except for TCP, that is, which should never be
-  ## fragmented.
+  ## fragmented.  This is an extra pain for ip6tables, which doesn't provide
+  ## a pleasant way to detect non-initial fragments.
   run iptables -A $chain -p tcp -f -g tcp-fragment
   run iptables -A $chain -f -j ACCEPT
   run ip6tables -A $chain -p tcp -g tcp-fragment \
          -m ipv6header --soft --header frag
-  run ip6tables -A $chain -j ACCEPT \
-         -m frag ! --fragfirst
+  run ip6tables -A $chain -j accept-non-init-frag
 }
 
+m4_divert(38)m4_dnl
+## Accept a non-initial fragment.  This is only needed by IPv6, to work
+## around a deficiency in the option parser.
+run ip6tables -N accept-non-init-frag
+run ip6tables -A accept-non-init-frag -j RETURN \
+       -m frag --fragfirst
+run ip6tables -A accept-non-init-frag -j ACCEPT
+
+m4_divert(26)m4_dnl
 ## allowservices CHAIN PROTO SERVICE ...
 ##
 ## Add rules to allow the SERVICES on the CHAIN.
@@ -318,51 +370,67 @@ defnetclass () {
   netclassindex=$(( $netclassindex + 1 ))
 }
 
-## defiface NAME NETCLASS:NETWORK/MASK...
+## defiface NAME[,NAME,...] NETCLASS:NETWORK/MASK...
 ##
-## Declares a network interface NAME and associates with it a number of
-## reachable networks.  During source classification, a packet arriving on
-## interface NAME from an address in NETWORK/MASK is classified as coming
-## from to NETCLASS.  During destination classification, all packets going to
-## NETWORK/MASK are classified as going to NETCLASS, regardless of interface
-## (which is good, because the outgoing interface hasn't been determined
-## yet).
+## Declares network interfaces with the given NAMEs and associates with them
+## a number of reachable networks.  During source classification, a packet
+## arriving on interface NAME from an address in NETWORK/MASK is classified
+## as coming from to NETCLASS.  During destination classification, all
+## packets going to NETWORK/MASK are classified as going to NETCLASS,
+## regardless of interface (which is good, because the outgoing interface
+## hasn't been determined yet).
 ##
 ## As a special case, the NETWORK/MASK can be the string `default', which
 ## indicates that all addresses not matched elsewhere should be considered.
 ifaces=:
-defaultiface=none
+defaultifaces=""
 allnets= allnets6=
 defiface () {
   set -e
-  name=$1; shift
-  case $ifaces in
-    *:"$name":*) ;;
-    *)
-      clearchain mangle:in-$name
-      run ip46tables -t mangle -A in-classify -i $name -g in-$name
-      ;;
-  esac
-  ifaces=$ifaces$name:
-  for item; do
-    netclass=${item%:*} addr=${item#*:}
-    case $addr in
-      default)
-       defaultiface=$name
-       defaultclass=$netclass
-       run ip46tables -t mangle -A out-classify -g mark-to-$netclass
-       ;;
-      *:*)
-       run ip6tables -t mangle -A in-$name -s $addr -g mark-from-$netclass
-       run ip6tables -t mangle -A out-classify -d $addr -g mark-to-$netclass
-       allnets6="$allnets6 $name:$addr"
-       ;;
+  names=$1; shift
+  seen=:
+  for name in $(echo $names | sed 'y/,/ /'); do
+    case $seen in *:"$name":*) continue ;; esac
+    seen=$seen$name:
+    case $ifaces in
+      *:"$name":*) ;;
       *)
-       run iptables -t mangle -A in-$name -s $addr -g mark-from-$netclass
-       run iptables -t mangle -A out-classify -d $addr -g mark-to-$netclass
-       allnets="$allnets $name:$addr"
+       clearchain mangle:in-$name
+       run ip46tables -t mangle -A in-classify -i $name -g in-$name
        ;;
     esac
+    ifaces=$ifaces$name:
+    for item; do
+      netclass=${item%:*} addr=${item#*:}
+      case $addr in
+       default)
+         case "$defaultifaces,$defaultclass" in
+           ,* | *,$netclass)
+             defaultifaces="$defaultifaces $name"
+             defaultclass=$netclass
+             ;;
+           *)
+             echo >&2 "$0: inconsistent default netclasses"
+             exit 1
+             ;;
+         esac
+         ;;
+       *:*)
+         run ip6tables -t mangle -A in-$name -g mark-from-$netclass \
+               -s $addr
+         run ip6tables -t mangle -A out-classify -g mark-to-$netclass \
+               -d $addr
+         allnets6="$allnets6 $name:$addr"
+         ;;
+       *)
+         run iptables -t mangle -A in-$name -g mark-from-$netclass \
+               -s $addr
+         run iptables -t mangle -A out-classify -g mark-to-$netclass \
+               -d $addr
+         allnets="$allnets $name:$addr"
+         ;;
+      esac
+    done
   done
 }