-## 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
+## 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