X-Git-Url: https://git.distorted.org.uk/~mdw/firewall/blobdiff_plain/1264e9177da9fb94a1fb85853220dd69a7108f0a..3596231a92081cbe4fb32c474d6e6554fdc6c457:/functions.m4 diff --git a/functions.m4 b/functions.m4 index 16c07c8..c8a08c4 100644 --- a/functions.m4 +++ b/functions.m4 @@ -94,12 +94,12 @@ ip46tables () { ## Ensure that the named chains exist and are empty. clearchain () { set -e - for chain; do - case $chain in - *:*) table=${chain%:*} chain=${chain#*:} ;; + for _chain; do + case $_chain in + *:*) table=${_chain%:*} _chain=${_chain#*:} ;; *) table=filter ;; esac - run ip46tables -t $table -N $chain 2>/dev/null || : + run ip46tables -t $table -N $_chain 2>/dev/null || : done } @@ -109,11 +109,19 @@ clearchain () { makeset () { set -e name=$1; shift - if ipset -nL | grep -q "^Name: $name$"; then - : - else - ipset -N "$name" "$@" - fi + v=$(ipset --version) + createp=t + case "$v" in + "ipset v4"*) + if ipset -nL | grep -q "^Name: $name\$"; then createp=nil; fi + ;; + *) + if ipset -n -L | grep -q "^$name\$"; then createp=nil; fi + ;; + esac + case $createp in + t) ipset -N "$name" "$@" ;; + esac } ## errorchain CHAIN ACTION ARGS ... @@ -130,7 +138,7 @@ errorchain () { clearchain $table:$chain run ip46tables -t $table -A $chain -j LOG \ -m limit --limit 3/minute --limit-burst 10 \ - --log-prefix "fw: $chain " --log-level notice + --log-prefix "fw: $chain " --log-level notice || : run ip46tables -t $table -A $chain -j "$@" \ -m limit --limit 20/second --limit-burst 100 run ip46tables -t $table -A $chain -j DROP @@ -183,7 +191,7 @@ setdevopt () { 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" + run sysctl -q net/$ver/conf/$i/$opt="$val" fi done case $any in @@ -231,7 +239,8 @@ m4_divert(38)m4_dnl 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 +run ip6tables -A accept-non-init-frag -j ACCEPT \ + -m ipv6header --header frag m4_divert(20)m4_dnl ## allowservices CHAIN PROTO SERVICE ... @@ -310,6 +319,26 @@ dnsresolver () { done } +## dnsserver CHAIN +## +## Add rules to allow CHAIN to be a DNS server. +dnsserver () { + set -e + chain=$1 + + ## Allow TCP access. Hitting us with SYNs will make us deploy SYN cookies, + ## but that's tolerable. + run ip46tables -A $chain -j ACCEPT -p tcp --destination-port 53 + + ## Avoid being a DDoS amplifier by rate-limiting incoming DNS queries. + clearchain $chain-udp-dns + run ip46tables -A $chain-udp-dns -j ACCEPT \ + -m limit --limit 20/second --limit-burst 300 + run ip46tables -A $chain-udp-dns -g dns-rate-limit + run ip46tables -A $chain -j $chain-udp-dns \ + -p udp --destination-port 53 +} + ## openports CHAIN [MIN MAX] ## ## Add rules to CHAIN to allow the open ports. @@ -321,6 +350,50 @@ openports () { run ip46tables -A $chain -p udp -g interesting --destination-port $1:$2 } +bcp38_setup=: +bcp38 () { + ipv=$1 ifname=$2; shift 2 + ## Add rules for BCP38 egress filtering for IP version IPV (either 4 or 6). + ## IFNAME is the outgoing interface; the remaining arguments are network + ## prefixes. + + ## Sort out which command we're using + case $ipv in + 4) ipt=iptables ;; + 6) ipt=ip6tables ;; + *) echo >&2 "Unknown IP version $ipv"; exit 1 ;; + esac + + ## If we've not set up the error chain then do that. + case $bcp38_setup in + :) + errorchain bcp38 DROP + clearchain bcp38-check + ip46tables -A bcp38-check -g bcp38 + ;; + esac + + ## Stitch our egress filter into the outbound chains if we haven't done + ## that yet. Do this for both IP versions: if we're only ever given + ## IPv6 addresses for a particular interface then we assume that IPv4 + ## packets aren't allowed on it at all. + case $bcp38_setup in + *:$ifname:*) ;; + *) + run ip46tables -A OUTPUT -j bcp38-check -o $ifname + case $forward in + 1) run ip46tables -A FORWARD -j bcp38-check -o $ifname ;; + esac + bcp38_setup=$bcp38_setup$ifname: + ;; + esac + + ## Finally, add in our allowed networks. + for i in "$@"; do + run $ipt -I bcp38-check -j RETURN -s $i + done +} + m4_divert(20)m4_dnl ###-------------------------------------------------------------------------- ### Packet classification. @@ -335,7 +408,7 @@ m4_divert(20)m4_dnl ### `defnetclass'. ### net_inet_NET List of IPv4 address ranges in the network. ### net_inet6_NET List of IPv6 address ranges in the network. -### net_fwd_NET List of other networks that this one forwards to. +### net_via_NET List of other networks that this one forwards via. ### net_hosts_NET List of hosts known to be in the network. ### host_inet_HOST IPv4 address of the named HOST. ### host_inet6_HOST IPv6 address of the named HOST. @@ -407,9 +480,9 @@ defnetclass () { from=$(( $from + $bit )) done to=$(( ($netclassindex << $BIT_TO) )) - tomask=$(( $MASK_MASK ^ (1 << ($netclassindex + $BIT_MASK)) )) + tomask=$(( $MASK_TO | $MASK_MASK ^ (1 << ($netclassindex + $BIT_MASK)) )) trace "from $name --> set $(printf %08x/%08x $from $frommask)" - trace " to $name --> and $(printf %08x/%08x $to $tomask)" + trace " to $name --> set $(printf %08x/%08x $to $tomask)" ## Now establish the mark-from-NAME and mark-to-NAME chains. clearchain mangle:mark-from-$name mangle:mark-to-$name @@ -424,7 +497,7 @@ defnetclass () { ## defnet NET CLASS ## -## Define a network. Follow by calls to `addr', `forwards', etc. to define +## Define a network. Follow by calls to `addr', `via', etc. to define ## properties of the network. Networks are processed in order, so if their ## addresses overlap then the more specific addresses should be defined ## earlier. @@ -447,11 +520,11 @@ addr () { done } -## forwards NET ... +## via NET ... ## ## Declare that packets from this network are forwarded to the other NETs. -forwards () { - eval "net_fwd_$net=\"$*\"" +via () { + eval "net_via_$net=\"$*\"" } ## noxit NET ... @@ -513,14 +586,19 @@ host () { defhost () { host=$1 addword allhosts $host - eval host_type_$host=endsys + eval host_type_$host=server } -## router +## hosttype TYPE ## -## Declare the host to be a router, so it should forward packets and so on. -router () { - eval host_type_$host=router +## Declare the host to have the given type. +hosttype () { + type=$1 + case $type in + router | server | client) ;; + *) echo >&2 "$0: bad host type \`$type'"; exit 1 ;; + esac + eval host_type_$host=$type } ## iface IFACE NET ... @@ -534,15 +612,18 @@ iface () { done } +## matchnets OPT WIN FLAGS PREPARE BASE SUFFIX NEXT NET [NET ...] +## ## Build rules which match a particular collection of networks. +## ## Specifically, use the address-comparison operator OPT (typically `-s' or -## `-d') to match the addresses of NOT, writing the rules to the chain -## BASESUFFIX. If we find a match, dispatch to WIN-CLASS, where CLASS is -## the class of the matching network. In order to deal with networks -## containing negative address ranges, more chains may need to be -## constructed; they will be named BASE#Q for sequence numbers Q starting -## with NEXT. All of this happens on the `mangle' table, and there isn't -## (currently) a way to tweak this. +## `-d') to match the addresses of each NET, writing the rules to the chain +## BASESUFFIX. If we find a match, dispatch to WIN-CLASS, where CLASS is the +## class of the matching network. In order to deal with networks containing +## negative address ranges, more chains may need to be constructed; they will +## be named BASE#Q for sequence numbers Q starting with NEXT. All of this +## happens on the `mangle' table, and there isn't (currently) a way to tweak +## this. ## ## The FLAGS gather additional interesting information about the job, ## separated by colons. The only flag currently is :default: which means @@ -693,8 +774,8 @@ net_interfaces () { nextnets="" any=nil for net in $nets; do - eval fwd=\$net_fwd_$net - for n in $fwd; do + eval via=\$net_via_$net + for n in $via; do case $seen in *":$n:"*) continue ;; esac seen=$seen$n: eval noxit=\$net_noxit_$n