1#! /bin/bash
2
3# This is a wrapper script to simulate ipfwadm 2.3a.  It ain't pretty,
4# but it should work (for valid commands).  `-V' is translated to `-W'
5# or ignored if a `-W' option is already there, but always warned
6# about.
7
8# Paul ``Rusty'' Russell, Nov-1997.  ipchains@rustcorp.com.
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
22# Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
23#
24# Version: 1.0.1: Fixed -t (no longer de-hexizes).
25#
26# Version: 1.0.2: Fixed undocumented `-a masq'.
27#                 Should be OK now with bash v1.
28#                 If we can't find ip_fwnames, call /sbin/ipfwadm.real
29#
30# Version: 1.1: Fixed printing counts for accounting chains list.
31#		Fixed -A -z and -A -f cases to do all 3 acct. rules.
32#
33# Version: 1.1.1: Fixed syntax error by escaping ( and ).
34#
35# Version: 1.1.2: Fixed REDIR thanks to Ambrose Li.
36#		  Fixed "printf --" thanks to Alain Knaff.
37#		  Fixed masquerade policy
38#		  Fixed bug report message unquoted `;'.
39#		  Fixed -k/-y and -S/-D options [ found by Charlie Brady ]
40#		  Barf on all ipchains failures.
41if [ -n "$DEBUG_IPFWADM" ]; then IPCHAINS=print_ipchains;
42else IPCHAINS=/sbin/ipchains;
43fi
44PROC_FIREWALL_NAMES="/proc/net/ip_fwnames"
45SPECIAL_CHAIN="IpFwAdM!"
46START_MARK=10000
47
48barf()
49{
50    echo "$@"
51    echo
52    echo If this command worked with the original ipfwadm 2.3, please
53    echo submit a bug report to \`ipchains@rustcorp.com\'.  Note that you
54    echo now need to be root, even to list the chains \(complain to Alan Cox\).
55    echo
56    echo The best way to do this is to submit the output of \`$0 --version\',
57    echo the command used to obtain this error, any previous ipfwadm
58    echo commands, and the output of \`ipchains-save\'.
59    echo
60    echo Then try flushing all the rules \`ipchains -F\; ipchains -X\',
61    echo setting the DEBUG_IPFWADM variable \`export DEBUG_IPFWADM=1\' or
62    echo \`setenv DEBUG_IPFWADM 1\' and rerunning the command\(s\) which
63    echo caused this error.
64    exit 1
65}
66
67print_ipchains()
68{
69    echo ipchains "$@" 1>&2
70    /sbin/ipchains "$@"
71}
72
73setup_chains()
74{
75    if [ `wc -l < $PROC_FIREWALL_NAMES` != 3 -a -z "$DEBUG_IPFWADM" ]
76    then
77	echo You cannot mix the \`ipfwadm\' wrapper with ipchains. 1>&2
78	echo You must delete all user chains and flush all built-in chains 1>&2
79	echo if you want to use the \`ipfwadm\' wrapper. 1>&2
80	exit 1
81    fi
82    $IPCHAINS -N acctin
83    $IPCHAINS -N acctout
84    $IPCHAINS -N acctboth
85    $IPCHAINS -N inp
86    $IPCHAINS -N out
87    $IPCHAINS -N fwd
88
89    # Let all fragments through like the old code used to.
90    $IPCHAINS -A input -f -j ACCEPT
91    $IPCHAINS -A output -f -j ACCEPT
92    $IPCHAINS -A forward -f -j ACCEPT
93
94    # Jump to accounting rules.  Order of traversal of acct rules
95    # doesn't matter.
96    $IPCHAINS -A input -j acctin
97    $IPCHAINS -A input -j acctboth
98    $IPCHAINS -A output -j acctout
99    $IPCHAINS -A output -j acctboth
100
101    # Now go to `real' chains.
102    $IPCHAINS -A input -j inp
103    $IPCHAINS -A output -j out
104    $IPCHAINS -A forward -j fwd
105
106    # Create dummy chains to mark this as an ipfwadm-emulation firewall.
107    $IPCHAINS -N $SPECIAL_CHAIN
108    # Insert min and max mark values.
109    $IPCHAINS -A $SPECIAL_CHAIN -m $START_MARK
110    $IPCHAINS -A $SPECIAL_CHAIN -m $(($START_MARK + 1))
111}
112
113# SIGH.  We use identical marks to indicate which rules are actually
114# the same rule (to simulate multiple ports, and -y without -P tcp).
115
116# We start the marks at 1,000,000, so we can insert before them or append
117# after them.
118
119# In the accounting chain, marks are unique between the three acct* chains,
120# so we can tell ordering.
121
122print_count()
123{
124    count=$(($1))
125    if let $(($count > 99999))
126    then
127	cntkb=$((($count + 500) / 1000))
128	if let $((cntkb > 9999))
129	then
130	    cntmb=$((($count + 500000) / 1000000))
131	    printf "%4sM " $cntmb
132	else
133	    printf "%4sK " $cntkb
134	fi
135    else
136	printf "%5s " $count
137    fi
138}
139
140dump_rule()
141{
142# ARGS: $LIST_VERBOSE $EXPAND_NUMBERS $BIDIR $SYN_NO_PROTO $SRCPORTS $DSTPTS
143# $PCNT $BCNT $TARG $PROTO $FLAGS $TOSA $TOSX $IFNM $NUM $SRCIP $DSTIP $REDIR
144# $PRINT_COUNTS
145
146# The ipfwadm code looks like: (* = -e only)
147# *    *               *    *    *    *    *
148# pcnt bcnt kind proto bkyo TOSA TXOR IFNM IFADD SRC DST SPTs -> DPTs REDIR
149    if [ -n "$1" -o -n "${19}" ]
150    then
151	# Packet and byte counts.
152	if [ -n "$2" ]; then printf "%8u " $7; else print_count $7; fi
153	if [ -n "$2" ]; then printf "%8u " $8; else print_count $8; fi
154    fi
155
156    # Kind
157    case "$9" in
158	in) printf "%-3s " "$9" ;;
159	out) printf "%-3s " "$9" ;;
160	i/o) printf "%-3s " "$9" ;;
161	*) printf "%-5s " "$9" ;;
162    esac
163
164    # Proto
165    printf "%-5s" "${10}"
166
167    if [ -n "$1" ]
168    then
169	# Flags
170	if [ "$3" != 0 ]; then printf "b"; else printf "%s" "-"; fi
171	case "${11}" in
172	    *!y*) printf "k-" ;;
173	    *y*) printf "%s" "-y" ;;
174	    *) printf "%s" "--" ;;
175	esac
176	case "${11}" in
177	    *l*) printf "l " ;;
178	    *) printf "%s" "- " ;;
179	esac
180
181	# TOS
182	printf "${12} ${13} "
183
184	# Interface name
185	printf "%-7.16s " "${14}"
186
187	# Interface address
188	if [ -n "${15}" ]; then printf "%-15s " 0.0.0.0;
189	else printf "%-15s " any;
190	fi
191    fi
192
193    # Source and dest.
194    printf "%-20s " "${16}"
195    printf "%-20s" "${17}"
196
197    # Source Ports.
198    if [ "${10}" != tcp -a "${10}" != udp -a "${10}" != icmp ]
199    then
200	echo " n/a"
201	return
202    fi
203    printf " "
204    printf "$5" | tr ' ' ','
205
206    if [ "${10}" = icmp ]
207    then
208	echo
209	return
210    fi
211
212    # Dest ports.
213    if [ "$5" != "n/a" ]
214    then
215	printf " -> "
216	printf "$6" | tr ' ' ','
217    fi
218
219    # redirect ports.
220    if [ "$9" = "acc/r" ]
221    then
222	printf " => %s" "${18}"
223    fi
224    echo
225}
226
227get_policy() # CHAIN
228{
229    case "`ipchains -L $1 | head -1`" in
230    *ACCEPT*)
231	echo accept;;
232    *MASQ*)
233	echo accept/masquerade;;
234    *REJECT*)
235	echo reject;;
236    *DENY*)
237	echo deny;;
238    *)
239	barf "Unknown policy for \`$1' - `ipchains -L $1 2>&1`"
240    esac
241}
242
243list_chain() # $CHAIN $LIST_VERBOSE $NUMERIC $EXPAND_NUMBERS
244{
245# if (!(format & FMT_NOCOUNTS)) {
246# 	if (format & FMT_KILOMEGA) {
247# 		fprintf(fp, FMT("%5s ","%s "), "pkts");
248# 		fprintf(fp, FMT("%5s ","%s "), "bytes");
249# 	} else {
250# 		fprintf(fp, FMT("%8s ","%s "), "pkts");
251# 		fprintf(fp, FMT("%10s ","%s "), "bytes");
252# 	}
253# }
254    IS_ACCT=""
255    case "$1" in
256    acct*) IS_ACCT="Y";;
257    inp) printf "IP firewall input rules, default policy: "
258	get_policy input
259	;;
260    out) printf "IP firewall output rules, default policy: "
261	get_policy output
262	;;
263    fwd) printf "IP firewall forward rules, default policy: "
264	get_policy forward
265	;;
266    *) barf "Unknown chain for list_chain - \`$1'"
267	;;
268    esac
269
270    if [ -n "$2" -o -n "$IS_ACCT" ]
271    then
272	if [ -z "$4" ]
273	then
274	    printf "%5s " pkts
275	    printf "%5s " bytes
276	else
277	    printf "%8s " pkts
278	    printf "%10s " bytes
279	fi
280    fi
281
282# if (!(format & FMT_NOKIND)) {
283# 	if (chain == CHN_ACCT)
284# 		fprintf(fp, FMT("%-3s ","%s "), "dir");
285# 	else
286# 		fprintf(fp, FMT("%-5s ","%s "), "type");
287# }
288    case "$1" in
289    acct*) printf "%-3s " dir ;;
290    *) printf "%-5s " type ;;
291    esac
292
293# fputs("prot ", fp);
294    printf "prot "
295
296# if (format & FMT_OPTIONS)
297# 	fputs("opt  ", fp);
298# if (format & FMT_TOS)
299# 	fputs("tosa tosx ", fp);
300# if (format & FMT_VIA) {
301# 	fprintf(fp, FMT("%-7s ","(%s "), "ifname");
302# 	fprintf(fp, FMT("%-15s ","%s) "), "ifaddress");
303# }
304    if [ -n "$2" ]
305    then
306	printf "opt  tosa tosx %-7s %-15s " ifname ifaddress
307    fi
308
309# fprintf(fp, FMT("%-20s ","%s "), "source");
310# fprintf(fp, FMT("%-20s ","%s "), "destination");
311# fputs("ports\n", fp);
312# }
313    printf "%-20s %-20s ports" source destination
314    echo
315
316    case "$1" in
317	acct*) shift;
318		(list_chain_real acctin "$@" "1" "1"
319		 list_chain_real acctout "$@" "1" "1"
320		 list_chain_real acctboth "$@" "1" "1") | sort -n | cut -c11-;;
321	*) list_chain_real "$@" ;;
322    esac
323}
324
325list_chain_real() # $CHAIN $LIST_VERBOSE $NUMERIC $EXPAND_NUMBERS $PRINT_COUNTS $PREPEND_MARK
326{
327    CHAIN="$1"
328    LIST_VERBOSE="$2"
329    NUMERIC="$3"
330    EXPAND_NUMBERS="$4"
331    PRINT_COUNTS="$5"
332    PREPEND_MARK="$6"
333
334    # The ipfwadm code looks like: (* = -e only)
335    # *    *               *    *    *    *    *
336    # pcnt bcnt kind proto bkyo TOSA TXOR IFNM IFADD SRC DST SPTs -> DPTs REDIR
337    #
338    # The ipchains code looks like: (* = -v only)
339    # *    *               *    *    *    *    *
340    # pcnt bcnt targ proto !yfl TOSA TXOR IFNM MARK SRC DST SPTs -> DPTs REDIR
341    LAST_MARK=xxx
342
343    BIDIR=0
344    SYN_NO_PROTO=0
345    SRCPORTS=""
346    DSTPORTS=""
347
348    [ -z "$NUMERIC" ] || NUMERIC="-n"
349    $IPCHAINS -L $CHAIN -v -x $NUMERIC | tail +3 |
350    while true
351    do
352	if ! read PCNT BCNT TARG PROTO FLAGS TOSA TOSX IFNM MARK SRCIP DSTIP SRCPTS IGN1 DSTPTS REDIR
353	then
354	    # Dump last rule.
355	    if [ "$LAST_MARK" != "xxx" ]
356	    then
357		[ -z "$PREPEND_MARK" ] || printf "%-10s " "$LAST_MARK"
358		dump_rule "$LIST_VERBOSE" "$EXPAND_NUMBERS" $BIDIR $SYN_NO_PROTO "$SRCPORTS" "$DSTPORTS" $LAST_PCNT $LAST_BCNT $LAST_TARG $LAST_PROTO $LAST_FLAGS $LAST_TOSA $LAST_TOSX "$LAST_IFNM" "$NUMERIC" $LAST_SRCIP $LAST_DSTIP "$LAST_REDIR" "$PRINT_COUNTS"
359	    fi
360	    return
361	fi
362	[ -z "$DEBUG_IPFWADM" ] || echo RULE is "$PCNT $BCNT $TARG $PROTO $FLAGS $TOSA $TOSX "$IFNM" $MARK $SRCIP $DSTIP $SRCPTS $IGN1 $DSTPTS $REDIR" 1>&2
363
364	if [ "$LAST_MARK" = "$MARK" ]
365	then
366# Fold rules back together.
367
368# We combine for any of the following reasons:
369# -k or -y used with no protocol: first rule has proto TCP and 'y'.
370# -b used: SRC & DST reversed.
371# Multiple ports: all the same but for port.
372
373# Worst cases:
374# ipfwadm -I -a accept -b -P tcp -S 0/0 1 4 -D 1/1 5 9
375# => pcnt bcnt targ proto !yfl TOSA TXOR IFNM MARK SRC DST SPTs -> DPTs REDIR
376#    ?    ?    ?    TCP   ?    ?    ?    ?    ?    0/0 1/1 1       5
377#    ?    ?    ?    TCP   ?    ?    ?    ?    ?    1/1 0/0 5       1
378#    ?    ?    ?    TCP   ?    ?    ?    ?    ?    0/0 1/1 1       9
379#    ?    ?    ?    TCP   ?    ?    ?    ?    ?    1/1 0/0 9       1
380#    ?    ?    ?    TCP   ?    ?    ?    ?    ?    0/0 1/1 4       5
381#    ?    ?    ?    TCP   ?    ?    ?    ?    ?    1/1 0/0 5       4
382#    ?    ?    ?    TCP   ?    ?    ?    ?    ?    0/0 1/1 4       9
383#    ?    ?    ?    TCP   ?    ?    ?    ?    ?    1/1 0/0 9       4
384#
385# ipfwadm -I -a accept -b -y -S 0/0 -D 1/1
386# => pcnt bcnt targ proto !yfl TOSA TXOR IFNM MARK SRC DST SPTs -> DPTs REDIR
387#    ?    ?    ?    TCP   ?y?? ?    ?    ?    ?    0/0 1/1
388#    ?    ?    ?    TCP   ?y?? ?    ?    ?    ?    1/1 0/0
389#    ?    ?    ?    ANY   ?-?? ?    ?    ?    ?    0/0 1/1
390#    ?    ?    ?    ANY   ?-?? ?    ?    ?    ?    1/1 0/0
391# 	    if [ -n "$DEBUG_IPFWADM" ]
392# 	    then
393#		echo LAST_PROTO = \`"$LAST_PROTO"\'
394#		echo PROTO = \`"$PROTO"\'
395# 		echo LAST_SRCIP = \`"$LAST_SRCIP"\'
396# 		echo DSTIP = \`"$DSTIP"\'
397# 		echo LAST_DSTIP = \`"$LAST_DSTIP"\'
398# 		echo SRCIP = \`"$SRCIP"\'
399# 		echo LAST_SRCPTS = \`"$LAST_SRCPTS"\'
400# 		echo DSTPTS = \`"$DSTPTS"\'
401# 		echo LAST_DSTPTS = \`"$LAST_DSTPTS"\'
402# 		echo SRCPTS = \`"$SRCPTS"\'
403# 	    fi
404	    if [ "$LAST_PROTO" = \!tcp -a "$PROTO" = tcp ]
405	    then
406		[ -n "$DEBUG_IPFWADM" ] && echo "Found SYN rule."
407		SYN_NO_PROTO=1
408		PCNT=$(($LAST_PCNT + $PCNT))
409		BCNT=$(($LAST_BCNT + $BCNT))
410		PROTO="all"
411	    elif [ "$LAST_SRCIP" = "$DSTIP" -a "$LAST_DSTIP" = "$SRCIP" -a "$LAST_SRCPTS" = "$DSTPTS" -a "$LAST_DSTPTS" = "$SRCPTS" ]
412	    then
413		[ -n "$DEBUG_IPFWADM" ] && echo "Found bidir rule."
414		BIDIR=1
415		LAST_PCNT=$(($LAST_PCNT + $PCNT))
416		LAST_BCNT=$(($LAST_BCNT + $BCNT))
417		# Don't transfer this rule to LAST_ vars - effectively ignore.
418		continue;
419	    else
420		[ -n "$DEBUG_IPFWADM" ] && echo "Found port rule."
421		# For n source ports and m dest ports, there will be
422		# n x m rules.  So, we add to SRCPORTS when we see a new
423		# SRCPTS, but only add to DSTPORTS for the first SRCPORTS.
424		if [ "$SRCPTS" != "$LAST_SRCPTS" ]
425		then
426		    SRCPORTS="$SRCPORTS $SRCPTS"
427		fi
428		if [ "$SRCPORTS" = "$SRCPTS" ]
429		then
430		    DSTPORTS="$DSTPORTS $DSTPTS"
431		fi
432		PCNT=$(($LAST_PCNT + $PCNT))
433		BCNT=$(($LAST_BCNT + $BCNT))
434	    fi
435	else
436	    # Dump last rule.
437	    if [ "$LAST_MARK" != "xxx" ]
438	    then
439		[ -z "$PREPEND_MARK" ] || printf "%-10s " "$LAST_MARK"
440		dump_rule "$LIST_VERBOSE" "$EXPAND_NUMBERS" $BIDIR $SYN_NO_PROTO "$SRCPORTS" "$DSTPORTS" $LAST_PCNT $LAST_BCNT $LAST_TARG $LAST_PROTO $LAST_FLAGS $LAST_TOSA $LAST_TOSX "$LAST_IFNM" "$NUMERIC" $LAST_SRCIP $LAST_DSTIP "$LAST_REDIR" "$PRINT_COUNTS"
441	    fi
442
443	    BIDIR=0
444	    SYN_NO_PROTO=0
445	    SRCPORTS="$SRCPTS"
446	    DSTPORTS="$DSTPTS"
447	fi
448
449	# Save for next iteration, in case mark the same.
450	LAST_PCNT=$PCNT
451	LAST_BCNT=$BCNT
452	LAST_PROTO=$PROTO
453	LAST_FLAGS=$FLAGS
454	LAST_TOSA=$TOSA
455	LAST_TOSX=$TOSX
456	LAST_IFNM="$IFNM"
457	LAST_MARK=$MARK
458	LAST_SRCIP=$SRCIP
459	LAST_DSTIP=$DSTIP
460	LAST_REDIR="$REDIR"
461	LAST_SRCPTS="$SRCPTS"
462	LAST_DSTPTS="$DSTPTS"
463	case "$CHAIN" in
464	acctin) LAST_TARG=in ;;
465	acctout) LAST_TARG=out ;;
466	acctboth) LAST_TART=i/o ;;
467	*)
468	    case "$TARG" in
469	    REDIRECT) LAST_TARG="acc/r" ;;
470	    MASQ) LAST_TARG="acc/m" ;;
471	    ACCEPT) LAST_TARG="acc" ;;
472	    REJECT) LAST_TARG="rej" ;;
473	    DENY) LAST_TARG="deny" ;;
474	    *) barf Unknown target \`"$TARG"\'. ;;
475	    esac
476	    ;;
477	esac
478    done
479}
480
481############################################################################
482
483if [ ! -f $PROC_FIREWALL_NAMES ]
484then
485    if [ -f /proc/net/ip_input -o -f /proc/net/ip_acct ]
486    then
487	# Old kernel.  Let's play nice.
488	[ -x /sbin/ipfwadm.real ] && exec /sbin/ipfwadm.real "$@"
489    fi
490    echo "Generic IP Firewall Chains not in this kernel" 1>&2
491    exit 1
492fi
493
494while [ $# != 0 ]
495do
496    case "$1" in
497    -A)
498	case x"$2" in
499	x-*) CHAIN=acctboth ;;
500	xboth) CHAIN=acctboth; shift ;;
501	xin) CHAIN=acctin; shift ;;
502	xout) CHAIN=acctout; shift ;;
503	x) CHAIN=acctboth ;;
504 	*) barf Unknown option \`"$2"\' ;;
505	esac
506	;;
507    -I)
508	CHAIN=inp
509	;;
510    -O)
511	CHAIN=out
512	;;
513    -F)
514	CHAIN=fwd
515	;;
516    -M)
517	MASQ_MODE=1
518	;;
519    -a)
520	COMMAND=-A
521	case x"$2" in
522	x-*) TARGET="" ;;
523	x) TARGET="" ;;
524	xr*) TARGET=REJECT; shift ;;
525	xd*) TARGET=DENY; shift ;;
526	xa*) TARGET=ACCEPT; shift ;;
527	xm*) TARGET=ACCEPT; MASQ=1; shift ;;
528 	*) barf Unknown policy for append: \`"$2"\' ;;
529	esac
530	;;
531    -i)
532	COMMAND="-I "
533	case x"$2" in
534	x-*) TARGET="" ;;
535	x) TARGET="" ;;
536	xr*) TARGET=REJECT; shift ;;
537	xd*) TARGET=DENY; shift ;;
538	xa*) TARGET=ACCEPT; shift ;;
539 	*) barf Unknown policy for insert: \`"$2"\' ;;
540	esac
541	;;
542    -d)
543	COMMAND=-D
544	case x"$2" in
545	x-*) TARGET="" ;;
546	x) TARGET="" ;;
547	xr*) TARGET=REJECT; shift ;;
548	xd*) TARGET=DENY; shift ;;
549	xa*) TARGET=ACCEPT; shift ;;
550 	*) barf Unknown policy for delete: \`"$2"\' ;;
551	esac
552	;;
553    -l)
554	LIST=1
555	;;
556    -z)
557	COMMAND=-Z
558	;;
559    -f)
560	COMMAND=-F
561	;;
562    -p)
563	COMMAND=-P
564	case "$2" in
565	r*) TARGET=REJECT; shift ;;
566	d*) TARGET=DENY; shift ;;
567	a*) TARGET=ACCEPT; shift ;;
568	m*) TARGET=MASQ; shift ;;
569 	*) barf Unknown policy for -p: \`"$2"\' ;;
570	esac
571	;;
572
573    -s)
574	COMMAND=-S
575	OPTIONS="$2 $3 $4"
576	shift 3
577	;;
578    -c)
579	COMMAND=-C
580	;;
581    -h)
582	print_help
583	;;
584    -P)
585	PROTOCOL="-p $2"
586	shift
587	;;
588    -S)
589	SRC_OPTIONS="-s $2"
590	shift
591	while true
592	do
593	    case x"$2" in
594		x) break ;;
595		x-*) break ;;
596		x?*) SRC_PORTS="$2 $SRC_PORTS" ;;
597	    esac
598	    shift
599	done
600	;;
601    -D)
602	DST_OPTIONS="-d $2"
603	shift
604	while true
605	do
606	    case x"$2" in
607		x) break ;;
608		x-*) break ;;
609		x?*) DST_PORTS="$2 $DST_PORTS" ;;
610	    esac
611	    shift
612	done
613	;;
614    -V)
615	VIA_ADDR="$2"
616	shift
617	;;
618    -W)
619	INTERFACE="$2"
620	OPTIONS="$OPTIONS -i $2"
621	shift
622	;;
623    -b)
624	OPTIONS="$OPTIONS -b"
625	;;
626    -e)
627	LIST_VERBOSE=1
628	;;
629    -k)
630	TCPSYN="! -y"
631	;;
632    -m)
633	MASQ=1
634	;;
635    -n)
636	NUMERIC=1
637	;;
638    -o)
639	OPTIONS="$OPTIONS -l"
640	;;
641    -r)
642	case x"$2" in
643	    x-*) REDIR=0 ;;
644	    x) REDIR=0 ;;
645	    x?*) REDIR="$2"; shift ;;
646	esac
647	;;
648    -t)
649	TOSAND=$(($2 | 0x01))
650	TOSXOR=$(($3 & 0xFE))
651	OPTIONS="$OPTIONS -t "`printf "0x%02x 0x%02x" $TOSAND $TOSXOR`
652	shift 2
653	;;
654    -v)
655	OPTIONS="$OPTIONS -v"
656	;;
657    -x)
658	EXPAND_NUMBERS=1;
659	;;
660    -y)
661	TCPSYN="-y"
662	;;
663
664    --version)
665	echo "ipfwadm wrapper version 1.1.2"
666	exit 0
667	;;
668
669    -??*)
670	echo "ERROR: Please separate arguments, eg \`-Mle' => \`-M -l -e'." >&2
671	exit 1
672	;;
673
674    *) barf Unexpected argument \`"$1"\'.
675	;;
676    esac
677    shift
678done
679
680# Variables to worry about:
681#  $CHAIN - actual chain to work on.
682# X$MASQ_MODE - set if -M given.
683# X$COMMAND - set if this is a simple command conversion.
684# X$TARGET - set for COMMAND of -A, -I, -D or -P (but see REDIR and MASQ).
685# X$LIST - set if they want a list.
686# X$NUMERIC - list with -n.
687# X$LIST_VERBOSE - list all info.
688# X$EXPAND_NUMBERS - list full numbers.
689# X$OPTIONS - miscellaneous easy-to-convert options.
690# X$SRC_OPTIONS - set if a source address is specified.
691# X$SRC_PORTS - space-separated list of specified source ports/ranges.
692# X$DST_OPTIONS - set if a dest address is specified.
693# X$DST_PORTS - space-separated list of specified dest ports/ranges.
694#  $VIA_ADDR - an interface address if one is specified.
695#  $INTERFACE - an interface name if one is specified.
696# X$TCPSYN - set if `-k' or `-y' is specified.
697# X$MASQ - set if `-m' is specified.
698# X$REDIR - set to the port if `-r port' is specified
699
700if [ -n "$MASQ_MODE" ]
701then
702    if [ -n "$LIST" ]
703    then
704	$IPCHAINS -M -L $OPTIONS
705    else
706	$IPCHAINS $COMMAND $OPTIONS
707    fi
708elif [ -n "$LIST" ]
709then
710    if ! grep -q IpFwAdM! < $PROC_FIREWALL_NAMES
711    then
712	echo "Chains are empty. (ie. ipfwadm has not been used on them)." 1>&2
713	exit 0
714    fi
715    # Construct a list.
716    if [ x$COMMAND = x-Z ]
717    then
718	# We have to atomically zero and list a chain.  This is
719	# currently impossible, so we:
720	# 1) stop all packets on the given chain.
721	# 2) list the values.
722	# 3) clear the counters.
723	# 4) resume on the given chain.
724	case "$CHAIN" in
725	    acct*)
726		$IPCHAINS -I 1 input -j DENY
727		$IPCHAINS -I 1 output -j DENY
728		;;
729	    inp)
730		$IPCHAINS -I 1 input -j DENY
731		;;
732	    out)
733		$IPCHAINS -I 1 output -j DENY
734		;;
735	    fwd)
736		$IPCHAINS -I 1 forward -j DENY
737		;;
738	    *) barf Unknown chain to stop: \`"$CHAIN"\'.
739	esac
740
741	list_chain $CHAIN "$LIST_VERBOSE" "$NUMERIC" "$EXPAND_NUMBERS"
742	$IPFWADM -Z $CHAIN
743
744	case "$CHAIN" in
745	    acct*)
746		$IPCHAINS -D 1 input
747		$IPCHAINS -D 1 output
748		;;
749	    inp)
750		$IPCHAINS -D 1 input
751		;;
752	    out)
753		$IPCHAINS -D 1 output
754		;;
755	    fwd)
756		$IPCHAINS -D 1 forward
757		;;
758	    *) barf Unknown chain to restart: \`"$CHAIN"\'.
759	esac
760    else
761	list_chain $CHAIN "$LIST_VERBOSE" "$NUMERIC" "$EXPAND_NUMBERS"
762    fi
763elif [ x"$COMMAND" = x"-F" -o x"$COMMAND" = x"-Z" -o x"$COMMAND" = x"-C" ]
764then
765    if ! grep -q IpFwAdM! < $PROC_FIREWALL_NAMES
766    then
767	echo "Chains are empty. (ie. ipfwadm has not been used on them)." 1>&2
768	exit 0
769    fi
770    if [ "$CHAIN" = acctboth ]
771    then
772	# Do it to all of them.
773	$IPCHAINS $COMMAND acctin $OPTIONS
774	$IPCHAINS $COMMAND acctout $OPTIONS
775    fi
776    $IPCHAINS $COMMAND $CHAIN $OPTIONS
777else
778    grep -q IpFwAdM! < $PROC_FIREWALL_NAMES || setup_chains
779
780    # Figure out what the target should be.
781    if [ -n "$REDIR" ]
782    then
783	TARGET="REDIRECT $REDIR"
784    elif [ -n "$MASQ" ]
785    then
786	TARGET=MASQ
787    fi
788
789    if [ x"$COMMAND" = x"-P" ]
790    then
791	case "$CHAIN" in
792	inp) CHAIN=input ;;
793	out) CHAIN=output ;;
794	fwd) CHAIN=forward ;;
795	*) barf Illegal chain for -P: \`"$CHAIN"\'.;;
796	esac
797	$IPCHAINS $COMMAND $CHAIN $TARGET $OPTIONS
798    else
799	# If they used -V, and not -W, then try to figure out interface
800	# name.  ALWAYS warn about difference.
801	if [ -n "$VIA_ADDR" ]
802	then
803	    if [ -n "$INTERFACE" ]
804	    then
805		echo Warning: \`-V $VIA_ADDR\' option ignored\; using \`-W $INTERFACE\' only.
806	    else
807		INTERFACE=`ifconfig | awk -v ADDR=$VIA_ADDR '/^[a-z0-9A-Z]/ { IFNAME=$1 } $0 ~ "^[^A-Za-z0-9:]*inet addr:" ADDR { print IFNAME}'`
808		if [ -z "$INTERFACE" ]
809		then
810		    echo Can\'t handle -V option: can\'t find interface name for the address \`$VIA_ADDR\'. 1>&2
811		    echo Please replace the -V with the appropriate -W option. 1>&2
812		    exit 1
813		fi
814		echo Replacing \`-V $VIA_ADDR\' with \`-W $INTERFACE\'.
815		OPTIONS="$OPTIONS -i $INTERFACE"
816	    fi
817	fi
818
819	# Insert, append or delete.
820	case $COMMAND in
821	    # For Insert, get (and decrement) minimal mark #.
822	    -I*) MARK=$(set $($IPCHAINS -L $SPECIAL_CHAIN -v | head -3 | tail -1); echo $9)
823		$IPCHAINS -R $SPECIAL_CHAIN 1 -m $(($MARK - 1))
824		MARK="-m $MARK"
825		;;
826
827	    # For Append, get (and increment) maximum mark #.
828	    -A) MARK=$(set $($IPCHAINS -L $SPECIAL_CHAIN -v | head -4 | tail -1); echo $9)
829		$IPCHAINS -R $SPECIAL_CHAIN 2 -m $(($MARK + 1))
830		MARK="-m $MARK"
831		;;
832	esac
833
834	# Only care about TCP SYN if -p TCP not specified.
835	if [ -n "$TCPSYN" ]
836	then
837	    case "$PROTOCOL" in
838		*[Tt][Cc][Pp])
839		    OPTIONS="$OPTIONS $PROTOCOL $TCPSYN"
840		    TCPSYN=""
841		;;
842	    esac
843	else
844	    OPTIONS="$OPTIONS $PROTOCOL"
845	fi
846
847	# Mangle source port and dest port args.
848	if [ -z "$SRC_PORTS" ]; then SRC_PORTS="X"; fi
849	if [ -z "$DST_PORTS" ]; then DST_PORTS="X"; fi
850
851	[ -n "$TARGET" ] && TARGET="-j $TARGET"
852	for SRC in $SRC_PORTS
853	do
854	    if [ $SRC = "X" ]; then SRC=""; fi
855	    for DST in $DST_PORTS
856	    do
857		if [ $DST = "X" ]; then DST=""; fi
858		if [ -n "$TCPSYN" ]
859		then
860		    $IPCHAINS $COMMAND $CHAIN $SRC_OPTIONS $SRC $DST_OPTIONS $DST $OPTIONS -p ! tcp $TARGET $MARK || barf "ipchains failed!"
861		    $IPCHAINS $COMMAND $CHAIN $SRC_OPTIONS $SRC $DST_OPTIONS $DST $OPTIONS -p tcp $TCPSYN $TARGET $MARK  || barf "ipchains failed!"
862		else
863		    $IPCHAINS $COMMAND $CHAIN $SRC_OPTIONS $SRC $DST_OPTIONS $DST $OPTIONS $TARGET $MARK || barf "ipchains failed!"
864		fi
865	    done
866	done
867    fi
868fi
869