-### -*-m4-*-
+### -*-sh-*-
###
### Utility functions for firewall scripts
###
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.
+## ip46tables ARGS ...
+##
+## Do the same thing for `iptables' and `ip6tables'.
+ip46tables () {
+ set -e
+ iptables "$@"
+ ip6tables "$@"
+}
+
## clearchain CHAIN CHAIN ...
##
## Ensure that the named chains exist and are empty.
*:*) table=${chain%:*} chain=${chain#*:} ;;
*) table=filter ;;
esac
- run iptables -t $table -N $chain
+ run ip46tables -t $table -N $chain 2>/dev/null || :
done
}
+## makeset SET TYPE [PARAMS]
+##
+## Ensure that the named ipset exists. Don't clear it.
+makeset () {
+ set -e
+ name=$1; shift
+ if ipset -nL | grep -q "^Name: $name$"; then
+ :
+ else
+ ipset -N "$name" "$@"
+ fi
+}
+
## errorchain CHAIN ACTION ARGS ...
##
## Make a chain which logs a message and then invokes some other action,
*) table=filter ;;
esac
clearchain $table:$chain
- run iptables -t $table -A $chain -j LOG \
+ run ip46tables -t $table -A $chain -j LOG \
-m limit --limit 3/minute --limit-burst 10 \
--log-prefix "fw: $chain " --log-level notice
- run iptables -t $table -A $chain -j "$@"
+ run ip46tables -t $table -A $chain -j "$@" \
+ -m limit --limit 20/second --limit-burst 100
+ run ip46tables -t $table -A $chain -j DROP
}
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
}
conntrack () {
set -e
chain=$1
- run iptables -A $chain -p tcp -m state \
+ run ip46tables -A $chain -p tcp -m state \
--state ESTABLISHED,RELATED -j ACCEPT
- run iptables -A $chain -p tcp ! --syn -g bad-tcp
+ run ip46tables -A $chain -p tcp ! --syn -g bad-tcp
}
## commonrules CHAIN
## 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-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.
for svc; do
case $svc in
*:*)
- n=2
+ n=2
left=${svc%:*} right=${svc#*:}
case $left in *[!0-9]*) eval left=\$port_$left ;; esac
case $right in *[!0-9]*) eval right=\$port_$right ;; esac
svc=$left:$right
;;
*)
- n=1
+ n=1
case $svc in *[!0-9]*) eval svc=\$port_$svc ;; esac
;;
esac
case $svc in
*: | :* | "" | *[!0-9:]*)
- echo >&2 "Bad service name"
+ echo >&2 "Bad service name"
exit 1
;;
esac
count=$(( $count + $n ))
if [ $count -gt 15 ]; then
- run iptables -A $chain -p $proto -m multiport -j ACCEPT \
+ run ip46tables -A $chain -p $proto -m multiport -j ACCEPT \
--destination-ports ${list#,}
list= count=$n
fi
"")
;;
,*,*)
- run iptables -A $chain -p $proto -m multiport -j ACCEPT \
+ run ip46tables -A $chain -p $proto -m multiport -j ACCEPT \
--destination-ports ${list#,}
;;
- *)
- run iptables -A $chain -p $proto -j ACCEPT \
+ *)
+ run ip46tables -A $chain -p $proto -j ACCEPT \
--destination-port ${list#,}
;;
esac
set -e
chain=$1
for p in tcp udp; do
- run iptables -A $chain -j ACCEPT \
+ run ip46tables -A $chain -j ACCEPT \
-m state --state ESTABLISHED \
-p $p --source-port 53
done
set -e
chain=$1; shift
[ $# -eq 0 ] && set -- $open_port_min $open_port_max
- run iptables -A $chain -p tcp -g interesting --destination-port $1:$2
- run iptables -A $chain -p udp -g interesting --destination-port $1:$2
+ run ip46tables -A $chain -p tcp -g interesting --destination-port $1:$2
+ run ip46tables -A $chain -p udp -g interesting --destination-port $1:$2
}
m4_divert(28)m4_dnl
from=$(( $from + $bit ))
done
to=$(( ($netclassindex << $BIT_TO) + \
- (0xf << $BIT_FROM) + \
+ (0xf << $BIT_FROM) + \
(1 << ($netclassindex + $BIT_MASK)) ))
trace "from $name --> set $(printf %x $from)"
trace " to $name --> and $(printf %x $from)"
## Now establish the mark-from-NAME and mark-to-NAME chains.
clearchain mangle:mark-from-$name mangle:mark-to-$name
- run iptables -t mangle -A mark-from-$name -j MARK --set-mark $from
- run iptables -t mangle -A mark-to-$name -j MARK --and-mark $to
+ run ip46tables -t mangle -A mark-from-$name -j MARK --set-mark $from
+ run ip46tables -t mangle -A mark-to-$name -j MARK --and-mark $to
;;
esac
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
-allnets=
+defaultifaces=""
+allnets= allnets6=
defiface () {
set -e
- name=$1; shift
- case $ifaces in
- *:"$name":*) ;;
- *)
- clearchain mangle:in-$name
- run iptables -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 iptables -t mangle -A out-classify -g mark-to-$netclass
- ;;
+ 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
}
*-+)
root=${iface%+}
for host; do
- name=${host%:*} addr=${host#*:}
+ name=${host%%:*} addr=${host#*:}
defiface $root$name $class:$addr
done
;;