Commit | Line | Data |
---|---|---|
775bd287 | 1 | ### -*-sh-*- |
bfdc045d MW |
2 | ### |
3 | ### Utility functions for firewall scripts | |
4 | ### | |
5 | ### (c) 2008 Mark Wooding | |
6 | ### | |
7 | ||
8 | ###----- Licensing notice --------------------------------------------------- | |
9 | ### | |
10 | ### This program is free software; you can redistribute it and/or modify | |
11 | ### it under the terms of the GNU General Public License as published by | |
12 | ### the Free Software Foundation; either version 2 of the License, or | |
13 | ### (at your option) any later version. | |
14 | ### | |
15 | ### This program is distributed in the hope that it will be useful, | |
16 | ### but WITHOUT ANY WARRANTY; without even the implied warranty of | |
17 | ### MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
18 | ### GNU General Public License for more details. | |
19 | ### | |
20 | ### You should have received a copy of the GNU General Public License | |
21 | ### along with this program; if not, write to the Free Software Foundation, | |
22 | ### Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. | |
23 | ||
24 | m4_divert(20)m4_dnl | |
25 | ###-------------------------------------------------------------------------- | |
26 | ### Utility functions. | |
27 | ||
28 | ## doit COMMAND ARGS... | |
29 | ## | |
30 | ## If debugging, print the COMMAND and ARGS. If serious, execute them. | |
31 | run () { | |
32 | set -e | |
33 | if [ "$FW_DEBUG" ]; then echo "* $*"; fi | |
34 | if ! [ "$FW_NOACT" ]; then "$@"; fi | |
35 | } | |
36 | ||
37 | ## trace MESSAGE... | |
38 | ## | |
39 | ## If debugging, print the MESSAGE. | |
40 | trace () { | |
41 | set -e | |
42 | if [ "$FW_DEBUG" ]; then echo "$*"; fi | |
43 | } | |
44 | ||
45 | ## defport NAME NUMBER | |
46 | ## | |
47 | ## Define $port_NAME to be NUMBER. | |
48 | defport () { | |
49 | name=$1 number=$2 | |
50 | eval port_$name=$number | |
51 | } | |
52 | ||
ce79e94a MW |
53 | ## defproto NAME NUMBER |
54 | ## | |
55 | ## Define $proto_NAME to be NUMBER. | |
56 | defproto () { | |
57 | name=$1 number=$2 | |
58 | eval proto_$name=$number | |
59 | } | |
60 | ||
beb4f0ee MW |
61 | ## addword VAR WORD |
62 | ## | |
63 | ## Adds WORD to the value of the shell variable VAR, if it's not there | |
64 | ## already. Words are separated by a single space; no leading or trailing | |
65 | ## spaces are introduced. | |
66 | addword () { | |
67 | var=$1 word=$2 | |
68 | eval val=\$$var | |
69 | case " $val " in | |
70 | *" $word "*) ;; | |
71 | *) eval "$var=\${$var:+\$val }\$word" ;; | |
72 | esac | |
73 | } | |
74 | ||
c70bfbbb MW |
75 | m4_divert(38)m4_dnl |
76 | ###-------------------------------------------------------------------------- | |
77 | ### Utility chains (used by function definitions). | |
78 | ||
a4d8cae3 | 79 | m4_divert(20)m4_dnl |
bfdc045d MW |
80 | ###-------------------------------------------------------------------------- |
81 | ### Basic chain constructions. | |
82 | ||
0291d6d5 MW |
83 | ## ip46tables ARGS ... |
84 | ## | |
85 | ## Do the same thing for `iptables' and `ip6tables'. | |
86 | ip46tables () { | |
87 | set -e | |
88 | iptables "$@" | |
89 | ip6tables "$@" | |
90 | } | |
91 | ||
bfdc045d MW |
92 | ## clearchain CHAIN CHAIN ... |
93 | ## | |
94 | ## Ensure that the named chains exist and are empty. | |
95 | clearchain () { | |
96 | set -e | |
97 | for chain; do | |
98 | case $chain in | |
99 | *:*) table=${chain%:*} chain=${chain#*:} ;; | |
100 | *) table=filter ;; | |
101 | esac | |
e91f4bbf | 102 | run ip46tables -t $table -N $chain 2>/dev/null || : |
bfdc045d MW |
103 | done |
104 | } | |
105 | ||
9f3cffaa MW |
106 | ## makeset SET TYPE [PARAMS] |
107 | ## | |
108 | ## Ensure that the named ipset exists. Don't clear it. | |
109 | makeset () { | |
110 | set -e | |
111 | name=$1; shift | |
112 | if ipset -nL | grep -q "^Name: $name$"; then | |
113 | : | |
114 | else | |
115 | ipset -N "$name" "$@" | |
116 | fi | |
117 | } | |
118 | ||
bfdc045d MW |
119 | ## errorchain CHAIN ACTION ARGS ... |
120 | ## | |
121 | ## Make a chain which logs a message and then invokes some other action, | |
122 | ## typically REJECT. Log messages are prefixed by `fw: CHAIN'. | |
123 | errorchain () { | |
124 | set -e | |
125 | chain=$1; shift | |
126 | case $chain in | |
127 | *:*) table=${chain%:*} chain=${chain#*:} ;; | |
128 | *) table=filter ;; | |
129 | esac | |
130 | clearchain $table:$chain | |
0291d6d5 | 131 | run ip46tables -t $table -A $chain -j LOG \ |
bfdc045d | 132 | -m limit --limit 3/minute --limit-burst 10 \ |
fc10e52b | 133 | --log-prefix "fw: $chain " --log-level notice |
a188f549 MW |
134 | run ip46tables -t $table -A $chain -j "$@" \ |
135 | -m limit --limit 20/second --limit-burst 100 | |
136 | run ip46tables -t $table -A $chain -j DROP | |
bfdc045d MW |
137 | } |
138 | ||
a4d8cae3 | 139 | m4_divert(20)m4_dnl |
bfdc045d MW |
140 | ###-------------------------------------------------------------------------- |
141 | ### Basic option setting. | |
142 | ||
143 | ## setopt OPTION VALUE | |
144 | ## | |
145 | ## Set an IP sysctl. | |
146 | setopt () { | |
147 | set -e | |
0f6364ac MW |
148 | opt=$1 val=$2 |
149 | any=nil | |
150 | for ver in ipv4 ipv6; do | |
151 | if [ -f /proc/sys/net/$ver/$opt ]; then | |
152 | run sysctl -q net/$ver/$opt="$val" | |
153 | any=t | |
154 | fi | |
155 | done | |
156 | case $any in | |
157 | nil) echo >&2 "$0: unknown IP option $opt"; exit 1 ;; | |
158 | esac | |
bfdc045d MW |
159 | } |
160 | ||
0f6364ac | 161 | ## setdevopt OPTION VALUE [INTERFACES ...] |
bfdc045d MW |
162 | ## |
163 | ## Set an IP interface-level sysctl. | |
164 | setdevopt () { | |
165 | set -e | |
0f6364ac MW |
166 | opt=$1 val=$2; shift 2 |
167 | case "$#,$1" in | |
168 | 0, | 1,all) | |
169 | set -- $( | |
170 | seen=: | |
171 | for ver in ipv4 ipv6; do | |
172 | cd /proc/sys/net/$ver/conf | |
173 | for i in *; do | |
174 | [ -f $i/$opt ] || continue | |
175 | case "$seen" in (*:$i:*) continue ;; esac | |
176 | echo $i | |
177 | done | |
178 | done) | |
179 | ;; | |
180 | esac | |
181 | for i in "$@"; do | |
182 | any=nil | |
183 | for ver in ipv4 ipv6; do | |
184 | if [ -f /proc/sys/net/$ver/conf/$i/$opt ]; then | |
185 | any=t | |
186 | run sysctl -q net/ipv4/conf/$i/$opt="$val" | |
187 | fi | |
188 | done | |
189 | case $any in | |
190 | nil) echo >&2 "$0: unknown device option $opt"; exit 1 ;; | |
191 | esac | |
bfdc045d MW |
192 | done |
193 | } | |
194 | ||
a4d8cae3 | 195 | m4_divert(20)m4_dnl |
bfdc045d MW |
196 | ###-------------------------------------------------------------------------- |
197 | ### Packet filter construction. | |
198 | ||
199 | ## conntrack CHAIN | |
200 | ## | |
201 | ## Add connection tracking to CHAIN, and allow obvious stuff. | |
202 | conntrack () { | |
203 | set -e | |
204 | chain=$1 | |
0291d6d5 | 205 | run ip46tables -A $chain -p tcp -m state \ |
bfdc045d | 206 | --state ESTABLISHED,RELATED -j ACCEPT |
0291d6d5 | 207 | run ip46tables -A $chain -p tcp ! --syn -g bad-tcp |
bfdc045d MW |
208 | } |
209 | ||
ecdca131 MW |
210 | ## commonrules CHAIN |
211 | ## | |
212 | ## Add standard IP filtering rules to the CHAIN. | |
213 | commonrules () { | |
214 | set -e | |
215 | chain=$1 | |
216 | ||
217 | ## Pass fragments through, assuming that the eventual destination will sort | |
218 | ## things out properly. Except for TCP, that is, which should never be | |
c70bfbbb MW |
219 | ## fragmented. This is an extra pain for ip6tables, which doesn't provide |
220 | ## a pleasant way to detect non-initial fragments. | |
ecdca131 MW |
221 | run iptables -A $chain -p tcp -f -g tcp-fragment |
222 | run iptables -A $chain -f -j ACCEPT | |
0291d6d5 MW |
223 | run ip6tables -A $chain -p tcp -g tcp-fragment \ |
224 | -m ipv6header --soft --header frag | |
c70bfbbb | 225 | run ip6tables -A $chain -j accept-non-init-frag |
ecdca131 MW |
226 | } |
227 | ||
c70bfbbb MW |
228 | m4_divert(38)m4_dnl |
229 | ## Accept a non-initial fragment. This is only needed by IPv6, to work | |
230 | ## around a deficiency in the option parser. | |
231 | run ip6tables -N accept-non-init-frag | |
232 | run ip6tables -A accept-non-init-frag -j RETURN \ | |
233 | -m frag --fragfirst | |
234 | run ip6tables -A accept-non-init-frag -j ACCEPT | |
235 | ||
a4d8cae3 | 236 | m4_divert(20)m4_dnl |
bfdc045d MW |
237 | ## allowservices CHAIN PROTO SERVICE ... |
238 | ## | |
239 | ## Add rules to allow the SERVICES on the CHAIN. | |
240 | allowservices () { | |
241 | set -e | |
242 | chain=$1 proto=$2; shift 2 | |
243 | count=0 | |
244 | list= | |
245 | for svc; do | |
246 | case $svc in | |
247 | *:*) | |
12ac65a1 | 248 | n=2 |
bfdc045d MW |
249 | left=${svc%:*} right=${svc#*:} |
250 | case $left in *[!0-9]*) eval left=\$port_$left ;; esac | |
251 | case $right in *[!0-9]*) eval right=\$port_$right ;; esac | |
252 | svc=$left:$right | |
253 | ;; | |
254 | *) | |
12ac65a1 | 255 | n=1 |
bfdc045d MW |
256 | case $svc in *[!0-9]*) eval svc=\$port_$svc ;; esac |
257 | ;; | |
258 | esac | |
259 | case $svc in | |
260 | *: | :* | "" | *[!0-9:]*) | |
12ac65a1 | 261 | echo >&2 "Bad service name" |
bfdc045d MW |
262 | exit 1 |
263 | ;; | |
264 | esac | |
265 | count=$(( $count + $n )) | |
266 | if [ $count -gt 15 ]; then | |
0291d6d5 | 267 | run ip46tables -A $chain -p $proto -m multiport -j ACCEPT \ |
bfdc045d MW |
268 | --destination-ports ${list#,} |
269 | list= count=$n | |
270 | fi | |
271 | list=$list,$svc | |
272 | done | |
273 | case $list in | |
274 | "") | |
275 | ;; | |
276 | ,*,*) | |
0291d6d5 | 277 | run ip46tables -A $chain -p $proto -m multiport -j ACCEPT \ |
bfdc045d MW |
278 | --destination-ports ${list#,} |
279 | ;; | |
12ac65a1 | 280 | *) |
0291d6d5 | 281 | run ip46tables -A $chain -p $proto -j ACCEPT \ |
bfdc045d MW |
282 | --destination-port ${list#,} |
283 | ;; | |
284 | esac | |
285 | } | |
286 | ||
287 | ## ntpclient CHAIN NTPSERVER ... | |
288 | ## | |
289 | ## Add rules to CHAIN to allow NTP with NTPSERVERs. | |
290 | ntpclient () { | |
291 | set -e | |
ace5a2fb MW |
292 | ntpchain=$1; shift |
293 | ||
294 | clearchain ntp-servers | |
295 | for ntp; do run iptables -A ntp-servers -j ACCEPT -s $ntp; done | |
296 | run iptables -A $ntpchain -j ntp-servers \ | |
297 | -p udp --source-port 123 --destination-port 123 | |
bfdc045d MW |
298 | } |
299 | ||
300 | ## dnsresolver CHAIN | |
301 | ## | |
302 | ## Add rules to allow CHAIN to be a DNS resolver. | |
303 | dnsresolver () { | |
304 | set -e | |
305 | chain=$1 | |
306 | for p in tcp udp; do | |
0291d6d5 | 307 | run ip46tables -A $chain -j ACCEPT \ |
bfdc045d MW |
308 | -m state --state ESTABLISHED \ |
309 | -p $p --source-port 53 | |
310 | done | |
311 | } | |
312 | ||
313 | ## openports CHAIN [MIN MAX] | |
314 | ## | |
315 | ## Add rules to CHAIN to allow the open ports. | |
316 | openports () { | |
317 | set -e | |
318 | chain=$1; shift | |
319 | [ $# -eq 0 ] && set -- $open_port_min $open_port_max | |
0291d6d5 MW |
320 | run ip46tables -A $chain -p tcp -g interesting --destination-port $1:$2 |
321 | run ip46tables -A $chain -p udp -g interesting --destination-port $1:$2 | |
bfdc045d MW |
322 | } |
323 | ||
a4d8cae3 | 324 | m4_divert(20)m4_dnl |
bfdc045d MW |
325 | ###-------------------------------------------------------------------------- |
326 | ### Packet classification. | |
beb4f0ee MW |
327 | ### |
328 | ### See `classify.m4' for an explanation of how the firewall machinery for | |
329 | ### packet classification works. | |
330 | ### | |
331 | ### A list of all network names is kept in `allnets'. For each network NET, | |
332 | ### shell variables are defined describing their properties. | |
333 | ### | |
334 | ### net_class_NET The class of the network, as defined by | |
335 | ### `defnetclass'. | |
336 | ### net_inet_NET List of IPv4 address ranges in the network. | |
337 | ### net_inet6_NET List of IPv6 address ranges in the network. | |
338 | ### net_fwd_NET List of other networks that this one forwards to. | |
339 | ### net_hosts_NET List of hosts known to be in the network. | |
340 | ### host_inet_HOST IPv4 address of the named HOST. | |
341 | ### host_inet6_HOST IPv6 address of the named HOST. | |
342 | ### | |
343 | ### Similarly, a list of hosts is kept in `allhosts', and for each host HOST, | |
344 | ### a shell variables are defined: | |
345 | ### | |
346 | ### host_ifaces_HOST List of interfaces for this host and the networks | |
347 | ### they attach to, in the form IFACE=NET. | |
bfdc045d MW |
348 | |
349 | ## defbitfield NAME WIDTH | |
350 | ## | |
351 | ## Defines MASK_NAME and BIT_NAME symbolic constants for dealing with | |
352 | ## bitfields: x << BIT_NAME yields the value x in the correct position, and | |
353 | ## ff & MASK_NAME extracts the corresponding value. | |
354 | defbitfield () { | |
355 | set -e | |
356 | name=$1 width=$2 | |
357 | eval MASK_$name=$(( (1 << $width) - 1 << $bitindex )) | |
358 | eval BIT_$name=$bitindex | |
359 | bitindex=$(( $bitindex + $width )) | |
360 | } | |
361 | ||
362 | ## Define the layout of the bitfield. | |
363 | bitindex=0 | |
364 | defbitfield MASK 16 | |
365 | defbitfield FROM 4 | |
366 | defbitfield TO 4 | |
367 | ||
368 | ## defnetclass NAME FORWARD-TO... | |
369 | ## | |
370 | ## Defines a netclass called NAME, which is allowed to forward to the | |
371 | ## FORWARD-TO netclasses. | |
372 | ## | |
373 | ## For each netclass, constants from_NAME and to_NAME are defined as the | |
374 | ## appropriate values in the FROM and TO fields (i.e., not including any mask | |
375 | ## bits). | |
376 | ## | |
377 | ## This function also establishes mangle chains mark-from-NAME and | |
378 | ## mark-to-NAME for applying the appropriate mark bits to the packet. | |
379 | ## | |
380 | ## Because it needs to resolve forward references, netclasses must be defined | |
381 | ## in a two-pass manner, using a loop of the form | |
382 | ## | |
383 | ## for pass in 1 2; do netclassindex=0; ...; done | |
384 | netclassess= | |
385 | defnetclass () { | |
386 | set -e | |
387 | name=$1; shift | |
388 | case $pass in | |
389 | 1) | |
390 | ||
391 | ## Pass 1. Establish the from_NAME and to_NAME constants, and the | |
392 | ## netclass's mask bit. | |
16838f59 | 393 | trace "netclass $name = $netclassindex" |
bfdc045d MW |
394 | eval from_$name=$(( $netclassindex << $BIT_FROM )) |
395 | eval to_$name=$(( $netclassindex << $BIT_TO )) | |
396 | eval _mask_$name=$(( 1 << ($netclassindex + $BIT_MASK) )) | |
397 | nets="$nets $name" | |
398 | ;; | |
399 | 2) | |
400 | ||
401 | ## Pass 2. Compute the actual from and to values. We're a little bit | |
402 | ## clever during source classification, and set the TO field to | |
403 | ## all-bits-one, so that destination classification needs only a single | |
404 | ## AND operation. | |
405 | from=$(( ($netclassindex << $BIT_FROM) + (0xf << $BIT_TO) )) | |
406 | for net; do | |
407 | eval bit=\$_mask_$net | |
408 | from=$(( $from + $bit )) | |
409 | done | |
410 | to=$(( ($netclassindex << $BIT_TO) + \ | |
12ac65a1 | 411 | (0xf << $BIT_FROM) + \ |
bfdc045d MW |
412 | (1 << ($netclassindex + $BIT_MASK)) )) |
413 | trace "from $name --> set $(printf %x $from)" | |
414 | trace " to $name --> and $(printf %x $from)" | |
415 | ||
416 | ## Now establish the mark-from-NAME and mark-to-NAME chains. | |
417 | clearchain mangle:mark-from-$name mangle:mark-to-$name | |
0291d6d5 MW |
418 | run ip46tables -t mangle -A mark-from-$name -j MARK --set-mark $from |
419 | run ip46tables -t mangle -A mark-to-$name -j MARK --and-mark $to | |
bfdc045d MW |
420 | ;; |
421 | esac | |
422 | netclassindex=$(( $netclassindex + 1 )) | |
423 | } | |
424 | ||
beb4f0ee MW |
425 | ## defnet NET CLASS |
426 | ## | |
427 | ## Define a network. Follow by calls to `addr', `forwards', etc. to define | |
428 | ## properties of the network. Networks are processed in order, so if their | |
429 | ## addresses overlap then the more specific addresses should be defined | |
430 | ## earlier. | |
431 | defnet () { | |
432 | net=$1 class=$2 | |
433 | addword allnets $net | |
434 | eval net_class_$1=\$class | |
435 | } | |
436 | ||
437 | ## addr ADDRESS/LEN ... | |
438 | ## | |
439 | ## Define addresses for the network being defined. ADDRESSes are in | |
440 | ## colon-separated IPv6 or dotted-quad IPv4 form. | |
441 | addr () { | |
442 | for i in "$@"; do | |
443 | case "$i" in | |
444 | *:*) addword net_inet6_$net $i ;; | |
445 | *) addword net_inet_$net $i ;; | |
bfdc045d MW |
446 | esac |
447 | done | |
448 | } | |
449 | ||
beb4f0ee | 450 | ## forwards NET ... |
bfdc045d | 451 | ## |
beb4f0ee MW |
452 | ## Declare that packets from this network are forwarded to the other NETs. |
453 | forwards () { | |
454 | eval "net_fwd_$net=\"$*\"" | |
455 | } | |
456 | ||
457 | ## noxit NET ... | |
458 | ## | |
459 | ## Declare that packets from this network must not be forwarded to the other | |
460 | ## NETs. | |
461 | noxit () { | |
462 | eval "net_noxit_$net=\"$*\"" | |
463 | } | |
464 | ||
465 | ## host HOST ADDR ... | |
466 | ## | |
467 | ## Define the address of an individual host on the current network. The | |
468 | ## ADDRs may be full IPv4 or IPv6 addresses, or offsets from the containing | |
469 | ## network address, which is a simple number for IPv4, or a suffix beginning | |
470 | ## with `::' for IPv6. If an IPv6 base address is provided for the network | |
471 | ## but not for the host then the host's IPv4 address is used as a suffix. | |
472 | host () { | |
473 | name=$1; shift | |
474 | ||
475 | ## Work out which addresses we've actually been given. | |
476 | unset a6 | |
477 | for i in "$@"; do | |
478 | case "$i" in ::*) a6=$i ;; *) a=$i ;; esac | |
479 | done | |
480 | case "${a+t}" in | |
481 | t) ;; | |
482 | *) echo >&2 "$0: no address for $name"; exit 1 ;; | |
bfdc045d | 483 | esac |
beb4f0ee MW |
484 | case "${a6+t}" in t) ;; *) a6=::$a ;; esac |
485 | ||
486 | ## Work out the IPv4 address. | |
487 | eval nn=\$net_inet_$net | |
488 | for n in $nn; do | |
489 | addr=${n%/*} | |
490 | base=${addr%.*} | |
491 | offset=${addr##*.} | |
492 | case $a in *.*) aa=$a ;; *) aa=$base.$(( $offset + $a )) ;; esac | |
493 | eval host_inet_$name=$aa | |
494 | done | |
495 | ||
496 | ## Work out the IPv6 address. | |
497 | eval nn=\$net_inet6_$net | |
498 | for n in $nn; do | |
499 | addr=${n%/*} | |
500 | base=${addr%::*} | |
1710944b | 501 | case $a6 in ::*) aa=$base$a6 ;; *) aa=$a6 ;; esac |
beb4f0ee MW |
502 | eval host_inet6_$name=$aa |
503 | done | |
504 | ||
505 | ## Remember the host in the list. | |
506 | addword net_hosts_$net $name | |
507 | } | |
508 | ||
509 | ## defhost NAME | |
510 | ## | |
511 | ## Define a new host. Follow by calls to `iface' to define the host's | |
512 | ## interfaces. | |
513 | defhost () { | |
514 | host=$1 | |
515 | addword allhosts $host | |
516 | eval host_type_$host=endsys | |
517 | } | |
518 | ||
519 | ## router | |
520 | ## | |
521 | ## Declare the host to be a router, so it should forward packets and so on. | |
522 | router () { | |
523 | eval host_type_$host=router | |
524 | } | |
525 | ||
526 | ## iface IFACE NET ... | |
527 | ## | |
528 | ## Define a host's interfaces. Specifically, declares that the host has an | |
529 | ## interface IFACE attached to the listed NETs. | |
530 | iface () { | |
531 | name=$1; shift | |
532 | for net in "$@"; do | |
533 | addword host_ifaces_$host $name=$net | |
534 | done | |
535 | } | |
536 | ||
537 | ## net_interfaces HOST NET | |
538 | ## | |
539 | ## Determine the interfaces on which packets may plausibly arrive from the | |
540 | ## named NET. Returns `-' if no such interface exists. | |
541 | ## | |
542 | ## This algorithm is not very clever. It's just about barely good enough to | |
543 | ## deduce transitivity through a simple routed network; with complicated | |
544 | ## networks, it will undoubtedly give wrong answers. Check the results | |
545 | ## carefully, and, if necessary, list the connectivity explicitly; use the | |
546 | ## special interface `-' for networks you know shouldn't send packets to a | |
547 | ## host. | |
548 | net_interfaces () { | |
549 | host=$1 startnet=$2 | |
550 | ||
551 | ## Determine the locally attached networks. | |
552 | targets=: | |
553 | eval ii=\$host_ifaces_$host | |
554 | for i in $ii; do targets=$targets$i:; done | |
555 | ||
556 | ## Determine the transitivity. | |
557 | seen=: | |
558 | nets=$startnet | |
559 | while :; do | |
560 | ||
561 | ## First pass. Determine whether any of the networks we're considering | |
562 | ## are in the target set. If they are, then return the corresponding | |
563 | ## interfaces. | |
564 | found="" | |
565 | for net in $nets; do | |
566 | tg=$targets | |
567 | while :; do | |
568 | any=nil | |
569 | case $tg in | |
570 | *"=$net:"*) | |
571 | n=${tg%=$net:*}; tg=${n%:*}:; n=${n##*:} | |
572 | addword found $n | |
573 | any=t | |
574 | ;; | |
575 | esac | |
576 | case $any in nil) break ;; esac | |
577 | done | |
578 | done | |
579 | case "$found" in ?*) echo $found; return ;; esac | |
580 | ||
581 | ## No joy. Determine the set of networks which (a) these ones can | |
582 | ## forward to, and (b) that we've not considered already. These are the | |
583 | ## nets we'll consider next time around. | |
584 | nextnets="" | |
585 | any=nil | |
586 | for net in $nets; do | |
587 | eval fwd=\$net_fwd_$net | |
588 | for n in $fwd; do | |
589 | case $seen in *":$n:"*) continue ;; esac | |
590 | seen=$seen$n: | |
591 | eval noxit=\$net_noxit_$n | |
592 | case " $noxit " in *" $startnet "*) continue ;; esac | |
593 | case " $nextnets " in | |
594 | *" $n "*) ;; | |
595 | *) addword nextnets $n; any=t ;; | |
596 | esac | |
597 | done | |
598 | done | |
599 | ||
600 | ## If we've run out of networks then there's no reachability. Return a | |
601 | ## failure. | |
602 | case $any in nil) echo -; return ;; esac | |
603 | nets=$nextnets | |
604 | done | |
bfdc045d MW |
605 | } |
606 | ||
607 | m4_divert(-1) | |
608 | ###----- That's all, folks -------------------------------------------------- |