setconf(log_martians, 0)
###--------------------------------------------------------------------------
-### Network interfaces.
-
-m4_divert(28)m4_dnl
-## Interface definitions.
-if_dmz=eth0
-if_trusted=eth1
-if_safe=$if_dmz,$if_trusted
-if_untrusted=$if_dmz,$if_trusted
-if_vpn=$if_dmz,$if_trusted
-if_iodine=$if_dmz,$if_trusted
-if_its_mz=$if_dmz,$if_trusted
-if_its_pi=$if_dmz,$if_trusted
-
-m4_divert(-1)
-###--------------------------------------------------------------------------
### artist-specific rules.
m4_divert(84)m4_dnl
done
m4_divert(46)m4_dnl
-## Mark addresses reachable on non-default interfaces as not reachable on the
-## default interface.
-trace "nets = $allnets $allnets6"
+## Build the input classification chains. There's one chain `in-IFACE' for
+## each local interface. This chain does a further dispatch on the source
+## address to the appropriate `mark-from-CLASS' chain for the source network
+## class.
+seen=:
+for iface in $host_ifaces_<::>FWHOST; do
+ ifname=${iface%=*}
+ case $seen in *:$ifname:*) continue ;; esac
+ seen=$seen$ifname:
+ clearchain mangle:in-$ifname
+ run ip46tables -t mangle -A in-classify -i $ifname -g in-$ifname
+done
+
+## Now populate the `in-IFACE' and `out-classify' chains. We iterate over
+## the available networks and add addresses to the appropriate chains. Also,
+## build up a map of which interfaces receive from which address ranged so
+## that we can finish the chains off properly later. This contains entries
+## of the form IFACE=:ADDR:ADDR:...:
+ifnets=""
for net in $allnets; do
- defaultp=nil
- for iface in $defaultifaces; do
- case $net in $iface:*) defaultp=t ;; esac
+
+ ## Determine the addresses and class for this network, and populate the
+ ## `out-classify' chains.
+ eval addr=\$net_inet_$net addr6=\$net_inet6_$net class=\$net_class_$net
+ case $class in virtual) continue ;; esac
+ trace "$net : $class"
+ for a in $addr; do
+ run iptables -t mangle -A out-classify -g mark-to-$class -d $a
done
- case $defaultp in
- nil)
- for iface in $defaultifaces; do
- run iptables -t mangle -A in-$iface \
- -s ${net#*:} -g bad-source-address
- done
- ;;
- esac
-done
-for net in $allnets6; do
- defaultp=nil
- for iface in $defaultifaces; do
- case $net in $iface:*) defaultp=t ;; esac
+ for a in $addr6; do
+ run ip6tables -t mangle -A out-classify -g mark-to-$class -d $a
done
- case $defaultp in
- nil)
- for iface in $defaultifaces; do
- run ip6tables -t mangle -A in-$iface \
- -s ${net#*:} -g bad-source-address
+
+ ## Now work through the interfaces.
+ for iface in $(net_interfaces FWHOST $net); do
+ nets=""
+ case $iface in
+
+ -)
+ ## A special `no interface' marker: we should not receive packets
+ ## from this network at all.
+ continue
+ ;;
+
+ *-+)
+ ## A special marker indicating a collection of point-to-point
+ ## interfaces. We should match an address to a particular interface.
+ ## Later, we'll cap this chain off by rejecting all other traffic.
+ eval hosts=\$net_hosts_$net
+ for host in $hosts; do
+ eval ha=\$host_inet_$host ha6=\$host_inet6_$host
+ trace "$host : $class -> $iface"
+ for a in $ha; do
+ run iptables -t mangle -A in-$iface \
+ -i ${iface%+}$host -s $a -g mark-from-$class
+ nets=$nets$a:
+ done
+ for a in $ha6; do
+ run ip6tables -t mangle -A in-$iface \
+ -i ${iface%+}$host -s $a -g mark-from-$class
+ nets=$nets$a:
+ done
+ done
+ ;;
+
+ *)
+ ## A normal interface. Classify incoming traffic according to the
+ ## source address.
+ trace "$net : $class -> $iface"
+ for a in $addr; do
+ run iptables -t mangle -A in-$iface -g mark-from-$class -s $a
+ nets=$nets$a:
+ done
+ for a in $addr6; do
+ run ip6tables -t mangle -A in-$iface -g mark-from-$class -s $a
+ nets=$nets$a:
+ done
+ case $net in default) nets=${nets}default: ;; esac
+ ;;
+ esac
+
+ ## Record that this interface receives traffic from this network.
+ unset nifnets
+ foundp=nil
+ for ifnet in $ifnets; do
+ case $ifnet in
+ $iface=*:$net:*) addword nifnets $ifnet; foundp=t ;;
+ $iface=*) addword nifnets $ifnet$nets; foundp=t ;;
+ *) addword nifnets $ifnet ;;
+ esac
+ done
+ case $foundp in nil) addword nifnets $iface=:$nets ;; esac
+ ifnets=$nifnets
+
+ done
+done
+
+## Wrap up all of the `in-IFACE' chains. A chain which matches the `default'
+## net should have unmatched but known networks blocked off, and then chain
+## onto `in-default'. Other chains should just chain onto
+## `bad-source-address'.
+trace "ifnets = $ifnets"
+for ifnet in $ifnets; do
+ iface=${ifnet%%=*} nets=${ifnet#*=}
+ case $nets in
+ *:default:*)
+ for n in $allnets; do
+ eval addr=\$net_inet_$n addr6=\$net_inet6_$n
+ for a in $addr; do
+ case $nets in *:$a:*) continue ;; esac
+ nets=$nets$a
+ run iptables -t mangle -A in-$iface -s $a -g bad-source-address
+ done
+ for a in $addr6; do
+ case $nets in *:$a:*) continue ;; esac
+ nets=$nets$a
+ run ip6tables -t mangle -A in-$iface -s $a -g bad-source-address
+ done
done
+ run ip46tables -t mangle -A in-$iface -g in-default
+ ;;
+ *)
+ run ip46tables -t mangle -A in-$iface -g bad-source-address
;;
esac
done
do
run ip6tables -t mangle -A in-default -s $addr -g bad-source-address
done
+run ip46tables -t mangle -A in-default -g mark-from-$net_class_default
m4_divert(92)m4_dnl
## Put the final default decision on the in-default chain, and attach the
## classification chains to the PREROUTING hook.
for iface in $defaultifaces; do
- run ip46tables -t mangle -A in-$iface -g mark-from-$defaultclass
+ run ip46tables -t mangle -A in-$iface -g in-default
done
-run ip46tables -t mangle -A out-classify -g mark-to-$defaultclass
+run ip46tables -t mangle -A out-classify -g mark-to-$net_class_default
run ip46tables -t mangle -A PREROUTING -j in-classify
run ip46tables -t mangle -A PREROUTING -j out-classify
setconf(log_martians, 0)
###--------------------------------------------------------------------------
-### Network interfaces.
-
-m4_divert(28)m4_dnl
-## Interface definitions.
-if_untrusted=eth0
-if_dmz=$if_untrusted
-if_safe=$if_dmz
-if_trusted=$if_dmz
-if_vpn=$if_dmz
-if_iodine=$if_dmz
-if_its_mz=$if_dmz
-if_its_pi=$if_dmz
-
-m4_divert(-1)
-###--------------------------------------------------------------------------
### fender-specific rules.
m4_divert(82)m4_dnl
eval proto_$name=$number
}
+## addword VAR WORD
+##
+## Adds WORD to the value of the shell variable VAR, if it's not there
+## already. Words are separated by a single space; no leading or trailing
+## spaces are introduced.
+addword () {
+ var=$1 word=$2
+ eval val=\$$var
+ case " $val " in
+ *" $word "*) ;;
+ *) eval "$var=\${$var:+\$val }\$word" ;;
+ esac
+}
+
m4_divert(38)m4_dnl
###--------------------------------------------------------------------------
### Utility chains (used by function definitions).
m4_divert(20)m4_dnl
###--------------------------------------------------------------------------
### Packet classification.
+###
+### See `classify.m4' for an explanation of how the firewall machinery for
+### packet classification works.
+###
+### A list of all network names is kept in `allnets'. For each network NET,
+### shell variables are defined describing their properties.
+###
+### net_class_NET The class of the network, as defined by
+### `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_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.
+###
+### Similarly, a list of hosts is kept in `allhosts', and for each host HOST,
+### a shell variables are defined:
+###
+### host_ifaces_HOST List of interfaces for this host and the networks
+### they attach to, in the form IFACE=NET.
## defbitfield NAME WIDTH
##
netclassindex=$(( $netclassindex + 1 ))
}
-## defiface NAME[,NAME,...] NETCLASS:NETWORK/MASK...
-##
-## 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=:
-defaultifaces=""
-allnets= allnets6=
-defiface () {
- set -e
- 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":*) ;;
- *)
- clearchain mangle:in-$name
- run ip46tables -t mangle -A in-classify -i $name -g in-$name
- ;;
+## defnet NET CLASS
+##
+## Define a network. Follow by calls to `addr', `forwards', 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.
+defnet () {
+ net=$1 class=$2
+ addword allnets $net
+ eval net_class_$1=\$class
+}
+
+## addr ADDRESS/LEN ...
+##
+## Define addresses for the network being defined. ADDRESSes are in
+## colon-separated IPv6 or dotted-quad IPv4 form.
+addr () {
+ for i in "$@"; do
+ case "$i" in
+ *:*) addword net_inet6_$net $i ;;
+ *) addword net_inet_$net $i ;;
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
}
-## defvpn IFACE CLASS NET HOST:ADDR ...
+## forwards NET ...
##
-## Defines a VPN interface. If the interface has the form `ROOT+' (i.e., a
-## netfilter wildcard) then define a separate interface ROOTHOST routing to
-## ADDR; otherwise just write a blanket rule allowing the whole NET. All
-## addresses concerned are put in the named CLASS.
-defvpn () {
- set -e
- iface=$1 class=$2 net=$3; shift 3
- case $iface in
- *-+)
- root=${iface%+}
- for host; do
- name=${host%%:*} addr=${host#*:}
- defiface $root$name $class:$addr
- done
- ;;
- *)
- defiface $iface $class:$net
- ;;
+## Declare that packets from this network are forwarded to the other NETs.
+forwards () {
+ eval "net_fwd_$net=\"$*\""
+}
+
+## noxit NET ...
+##
+## Declare that packets from this network must not be forwarded to the other
+## NETs.
+noxit () {
+ eval "net_noxit_$net=\"$*\""
+}
+
+## host HOST ADDR ...
+##
+## Define the address of an individual host on the current network. The
+## ADDRs may be full IPv4 or IPv6 addresses, or offsets from the containing
+## network address, which is a simple number for IPv4, or a suffix beginning
+## with `::' for IPv6. If an IPv6 base address is provided for the network
+## but not for the host then the host's IPv4 address is used as a suffix.
+host () {
+ name=$1; shift
+
+ ## Work out which addresses we've actually been given.
+ unset a6
+ for i in "$@"; do
+ case "$i" in ::*) a6=$i ;; *) a=$i ;; esac
+ done
+ case "${a+t}" in
+ t) ;;
+ *) echo >&2 "$0: no address for $name"; exit 1 ;;
esac
+ case "${a6+t}" in t) ;; *) a6=::$a ;; esac
+
+ ## Work out the IPv4 address.
+ eval nn=\$net_inet_$net
+ for n in $nn; do
+ addr=${n%/*}
+ base=${addr%.*}
+ offset=${addr##*.}
+ case $a in *.*) aa=$a ;; *) aa=$base.$(( $offset + $a )) ;; esac
+ eval host_inet_$name=$aa
+ done
+
+ ## Work out the IPv6 address.
+ eval nn=\$net_inet6_$net
+ for n in $nn; do
+ addr=${n%/*}
+ base=${addr%::*}
+ case $a in ::*) aa=$addr$a ;; *) aa=$a ;; esac
+ eval host_inet6_$name=$aa
+ done
+
+ ## Remember the host in the list.
+ addword net_hosts_$net $name
+}
+
+## defhost NAME
+##
+## Define a new host. Follow by calls to `iface' to define the host's
+## interfaces.
+defhost () {
+ host=$1
+ addword allhosts $host
+ eval host_type_$host=endsys
+}
+
+## router
+##
+## Declare the host to be a router, so it should forward packets and so on.
+router () {
+ eval host_type_$host=router
+}
+
+## iface IFACE NET ...
+##
+## Define a host's interfaces. Specifically, declares that the host has an
+## interface IFACE attached to the listed NETs.
+iface () {
+ name=$1; shift
+ for net in "$@"; do
+ addword host_ifaces_$host $name=$net
+ done
+}
+
+## net_interfaces HOST NET
+##
+## Determine the interfaces on which packets may plausibly arrive from the
+## named NET. Returns `-' if no such interface exists.
+##
+## This algorithm is not very clever. It's just about barely good enough to
+## deduce transitivity through a simple routed network; with complicated
+## networks, it will undoubtedly give wrong answers. Check the results
+## carefully, and, if necessary, list the connectivity explicitly; use the
+## special interface `-' for networks you know shouldn't send packets to a
+## host.
+net_interfaces () {
+ host=$1 startnet=$2
+
+ ## Determine the locally attached networks.
+ targets=:
+ eval ii=\$host_ifaces_$host
+ for i in $ii; do targets=$targets$i:; done
+
+ ## Determine the transitivity.
+ seen=:
+ nets=$startnet
+ while :; do
+
+ ## First pass. Determine whether any of the networks we're considering
+ ## are in the target set. If they are, then return the corresponding
+ ## interfaces.
+ found=""
+ for net in $nets; do
+ tg=$targets
+ while :; do
+ any=nil
+ case $tg in
+ *"=$net:"*)
+ n=${tg%=$net:*}; tg=${n%:*}:; n=${n##*:}
+ addword found $n
+ any=t
+ ;;
+ esac
+ case $any in nil) break ;; esac
+ done
+ done
+ case "$found" in ?*) echo $found; return ;; esac
+
+ ## No joy. Determine the set of networks which (a) these ones can
+ ## forward to, and (b) that we've not considered already. These are the
+ ## nets we'll consider next time around.
+ nextnets=""
+ any=nil
+ for net in $nets; do
+ eval fwd=\$net_fwd_$net
+ for n in $fwd; do
+ case $seen in *":$n:"*) continue ;; esac
+ seen=$seen$n:
+ eval noxit=\$net_noxit_$n
+ case " $noxit " in *" $startnet "*) continue ;; esac
+ case " $nextnets " in
+ *" $n "*) ;;
+ *) addword nextnets $n; any=t ;;
+ esac
+ done
+ done
+
+ ## If we've run out of networks then there's no reachability. Return a
+ ## failure.
+ case $any in nil) echo -; return ;; esac
+ nets=$nextnets
+ done
}
m4_divert(-1)
setconf(forward, 0)
###--------------------------------------------------------------------------
-### Network interfaces.
-
-m4_divert(28)m4_dnl
-## Interface definitions.
-if_trusted=eth0
-if_dmz=$if_trusted
-if_safe=$if_dmz
-if_untrusted=$if_dmz
-if_vpn=$if_dmz
-if_iodine=$if_dmz
-if_its_mz=$if_dmz
-if_its_pi=$if_dmz
-
-m4_divert(-1)
-###--------------------------------------------------------------------------
### gibson-specific rules.
m4_divert(84)m4_dnl
setconf(log_martians, 0)
###--------------------------------------------------------------------------
-### Network interfaces.
-
-m4_divert(28)m4_dnl
-## Interface definitions.
-if_dmz=br-dmz
-if_trusted=br-unsafe
-if_safe=$if_dmz,$if_trusted
-if_untrusted=$if_dmz,$if_trusted
-if_vpn=$if_dmz,$if_trusted
-if_iodine=$if_dmz,$if_trusted
-if_its_mz=$if_dmz,$if_trusted
-if_its_pi=$if_dmz,$if_trusted
-
-m4_divert(-1)
-###--------------------------------------------------------------------------
### ibanez-specific rules.
m4_divert(84)m4_dnl
setconf(log_martians, 0)
###--------------------------------------------------------------------------
-### Network interfaces.
-
-m4_divert(28)m4_dnl
-## Interface definitions.
-if_dmz=eth0
-if_trusted=eth1
-if_safe=$if_dmz,$if_trusted
-if_untrusted=$if_dmz,$if_trusted
-if_vpn=$if_dmz,$if_trusted
-if_iodine=$if_dmz,$if_trusted
-if_its_mz=$if_dmz,$if_trusted
-if_its_pi=$if_dmz,$if_trusted
-
-m4_divert(-1)
-###--------------------------------------------------------------------------
### jem-specific rules.
m4_divert(84)m4_dnl
###--------------------------------------------------------------------------
### Network layout.
-m4_divert(44)m4_dnl
-## Network definitions.
-defiface $if_dmz \
- trusted:62.49.204.144/28 \
- trusted:172.29.199.0/25 \
- untrusted:default
-defiface $if_trusted \
- trusted:172.29.199.0/25 \
- untrusted:default
-defiface $if_safe safe:172.29.199.192/26
-defiface $if_untrusted \
- untrusted:172.29.198.0/25
-defvpn $if_vpn safe 172.29.199.128/27 \
- crybaby:172.29.199.129 \
- terror:172.29.199.130
-defiface $if_iodine untrusted:172.29.198.128/28
-defiface $if_its_mz safe:172.29.199.160/30
-defiface $if_its_pi safe:192.168.0.0/24
+## House networks.
+defnet dmz trusted
+ addr 62.49.204.144/28
+ forwards unsafe untrusted
+defnet unsafe trusted
+ addr 172.29.199.0/25
+ forwards househub
+defnet safe safe
+ addr 172.29.199.192/28
+ forwards househub
+defnet untrusted untrusted
+ addr 172.29.198.0/25
+ forwards househub
+defnet vpn safe
+ addr 172.29.199.128/27
+ forwards househub
+ host crybaby 1
+ host terror 2
+defnet iodine untrusted
+ addr 172.29.198.128/28
+defnet househub virtual
+ forwards housebdry dmz unsafe safe untrusted
+defnet housebdry virtual
+ forwards househub hub
+ noxit dmz
+
+## House hosts.
+defhost radius
+ router
+ iface eth0 dmz
+ iface eth1 unsafe
+ iface eth2 safe
+ iface eth3 untrusted
+defhost roadstar
+ iface eth0 dmz
+ iface eth1 unsafe
+defhost jem
+ iface eth0 dmz
+ iface eth1 unsafe
+defhost artist
+ iface eth0 dmz
+ iface eth1 unsafe
+defhost vampire
+ router
+ iface eth0.0 dmz
+ iface eth0.1 unsafe
+ iface eth0.3 untrusted
+ iface dns0 dns
+ iface vpn-+ vpn
+ iface vpn-precision colobdry vpn
+defhost ibanez
+ iface br-dmz dmz
+ iface br-unsafe unsafe
+
+defhost gibson
+ iface eth0 unsafe
+
+## Colocated networks.
+defnet jump trusted
+ addr 212.13.198.64/28
+ forwards colohub
+defnet colo trusted
+ addr 172.29.199.176/28
+ forwards colohub
+defnet colohub virtual
+ forwards colobdry jump colo
+defnet colobdry virtual
+ forwards colohub hub
+ noxit jump
+
+## Colocated hosts.
+defhost fender
+ iface br-jump jump
+ iface br-colo colo
+defhost precision
+ router
+ iface eth0 jump
+ iface eth1 colo
+ iface vpn-+ vpn
+ iface vpn-vampire housebdry vpn
+defhost telecaster
+ iface eth0 jump
+ iface eth1 colo
+defhost stratocaster
+ iface eth0 jump
+ iface eth1 colo
+defhost jazz
+ iface eth0 jump
+ iface eth1 colo
+
+## Other networks.
+defnet hub virtual
+ forwards housebdry colobdry
+defnet default untrusted
+ addr 62.49.204.144/28
+ addr 212.13.198.64/28
+ forwards dmz untrusted unsafe jump colo
m4_divert(80)m4_dnl
###--------------------------------------------------------------------------
setconf(log_martians, 0)
###--------------------------------------------------------------------------
-### Network interfaces.
-
-m4_divert(28)m4_dnl
-## Interface definitions.
-if_dmz=eth0
-if_trusted=eth1
-if_safe=eth2
-if_untrusted=eth3
-if_vpn=$if_dmz,$if_trusted
-if_iodine=$if_dmz,$if_trusted
-if_its_mz=$if_dmz,$if_trusted
-if_its_pi=$if_dmz,$if_trusted
-
-m4_divert(-1)
-###--------------------------------------------------------------------------
### radius-specific rules.
m4_divert(84)m4_dnl
setconf(log_martians, 0)
###--------------------------------------------------------------------------
-### Network interfaces.
-
-m4_divert(28)m4_dnl
-## Interface definitions.
-if_dmz=eth0
-if_trusted=eth1
-if_safe=$if_dmz,$if_trusted
-if_untrusted=$if_dmz,$if_trusted
-if_vpn=$if_dmz,$if_trusted
-if_iodine=$if_dmz,$if_trusted
-if_its_mz=$if_dmz,$if_trusted
-if_its_pi=$if_dmz,$if_trusted
-
-m4_divert(-1)
-###--------------------------------------------------------------------------
### roadstar-specific rules.
m4_divert(84)m4_dnl
setconf(log_martians, 0)
###--------------------------------------------------------------------------
-### Network interfaces.
-
-m4_divert(28)m4_dnl
-## Interface definitions.
-if_dmz=eth0.0
-if_trusted=eth0.1
-if_safe=$if_dmz,$if_trusted
-if_untrusted=eth0.3
-if_vpn=vpn-+
-if_iodine=dns+
-if_its_mz=$if_dmz,$if_trusted
-if_its_pi=$if_dmz,$if_trusted
-
-m4_divert(-1)
-###--------------------------------------------------------------------------
### vampire-specific rules.
m4_divert(86)m4_dnl