1#!/usr/bin/perl -w
2#
3# Written by Camiel Dobbelaar <cd@sentia.nl>, Aug-2000
4# ipfmeta is in the Public Domain.
5#
6
7use strict;
8use Getopt::Std;
9
10## PROCESS COMMANDLINE
11our($opt_v); $opt_v=1;
12getopts('v:') || die "usage: ipfmeta [-v verboselevel] [objfile]\n";
13my $verbose = $opt_v + 0;
14my $objfile = shift || "ipf.objs";
15my $MAXRECURSION = 10;
16
17## READ OBJECTS
18open(FH, "$objfile") || die "cannot open $objfile: $!\n";
19my @tokens;
20while (<FH>) {
21	chomp;
22	s/#.*$//;	# remove comments
23	s/^\s+//;	# compress whitespace
24	s/\s+$//;
25	next if m/^$/;	# skip empty lines
26	push (@tokens, split);
27}
28close(FH) || die "cannot close $objfile: $!\n";
29# link objects with their values
30my $obj="";
31my %objs;
32while (@tokens) {
33	my $token = shift(@tokens);
34	if ($token =~ m/^\[([^]]*)\]$/) {
35		# new object
36		$obj = $1;
37	} else {
38		# new value
39		push(@{$objs{$obj}}, $token) unless ($obj eq "");
40	}
41}
42
43# sort objects: longest first
44my @objs = sort { length($b) <=> length($a) } keys %objs;
45
46## SUBSTITUTE OBJECTS WITH THEIR VALUES FROM STDIN
47foreach (<STDIN>) {
48	foreach (expand($_, 0)) {
49		print;
50	}
51}
52
53## END
54
55sub expand {
56	my $line = shift;
57	my $level = shift;
58	my @retlines = $line;
59	my $obj;
60	my $val;
61
62	# coarse protection
63	if ($level > $MAXRECURSION) {
64		print STDERR "ERR: recursion exceeds $MAXRECURSION levels\n";
65		return;
66	}
67
68	foreach $obj (@objs) {
69		if ($line =~ m/$obj/) {
70			@retlines = "";
71			if ($level < $verbose) {
72				# add metarule as a comment
73				push(@retlines, "# ".$line);
74			}
75			foreach $val (@{$objs{$obj}}) {
76				my $newline = $line;
77				$newline =~ s/$obj/$val/;
78				push(@retlines, expand($newline, $level+1));
79			}
80			last;
81		}
82	}
83
84	return @retlines;
85}
86
87__END__
88
89=head1 NAME
90
91B<ipfmeta> - use objects in IP filter files
92
93=head1 SYNOPSIS
94
95B<ipfmeta> [F<options>] [F<objfile>]
96
97=head1 DESCRIPTION
98
99B<ipfmeta> is used to simplify the maintenance of your IP filter
100ruleset. It does this through the use of 'objects'.  A matching
101object gets replaced by its values at runtime.  This is similar to
102what a macro processor like m4 does.
103
104B<ipfmeta> is specifically geared towards IP filter. It is line
105oriented, if an object has multiple values, the line with the object
106is duplicated and substituted for each value. It is also recursive,
107an object may have another object as a value.
108
109Rules to be processed are read from stdin, output goes to stdout.
110
111The verbose option allows for the inclusion of the metarules in the
112output as comments.
113
114Definition of the objects and their values is done in a separate
115file, the filename defaults to F<ipf.objs>.  An object is delimited
116by square brackets. A value is delimited by whitespace.  Comments
117start with '#' and end with a newline. Empty lines and extraneous
118whitespace are allowed.  A value belongs to the first object that
119precedes it.
120
121It is recommended that you use all caps or another distinguishing
122feature for object names. You can use B<ipfmeta> for NAT rules also,
123for instance to keep them in sync with filter rules.  Combine
124B<ipfmeta> with a Makefile to save typing.
125
126=head1 OPTIONS
127
128=over 4
129
130=item B<-v> I<verboselevel>
131
132Include metarules in output as comments. Default is 1, the top level
133metarules. Higher levels cause expanded metarules to be included.
134Level 0 does not add comments at all.
135
136=back
137
138=head1 BUGS
139
140A value can not have whitespace in it.
141
142=head1 EXAMPLE
143
144(this does not look good, formatted)
145
146I<ipf.objs>
147
148[PRIVATE] 10.0.0.0/8 127.0.0.0/8 172.16.0.0/12 192.168.0.0/16
149
150[MULTICAST] 224.0.0.0/4
151
152[UNWANTED] PRIVATE MULTICAST
153
154[NOC] xxx.yy.zz.1/32 xxx.yy.zz.2/32
155
156[WEBSERVERS] 192.168.1.1/32 192.168.1.2/32
157
158[MGMT-PORTS] 22 23
159
160I<ipf.metarules>
161
162block in from UNWANTED to any
163
164pass  in from NOC to WEBSERVERS port = MGMT-PORTS
165
166pass  out all
167
168I<Run>
169
170ipfmeta ipf.objs <ipf.metarules >ipf.rules
171
172I<Output>
173
174# block in from UNWANTED to any
175
176block in from 10.0.0.0/8 to any
177
178block in from 127.0.0.0/8 to any
179
180block in from 172.16.0.0/12 to any
181
182block in from 192.168.0.0/16 to any
183
184block in from 224.0.0.0/4 to any
185
186# pass  in from NOC to WEBSERVERS port = MGMT-PORTS
187
188pass  in from xxx.yy.zz.1/32 to 192.168.1.1/32 port = 22
189
190pass  in from xxx.yy.zz.1/32 to 192.168.1.1/32 port = 23
191
192pass  in from xxx.yy.zz.1/32 to 192.168.1.2/32 port = 22
193
194pass  in from xxx.yy.zz.1/32 to 192.168.1.2/32 port = 23
195
196pass  in from xxx.yy.zz.2/32 to 192.168.1.1/32 port = 22
197
198pass  in from xxx.yy.zz.2/32 to 192.168.1.1/32 port = 23
199
200pass  in from xxx.yy.zz.2/32 to 192.168.1.2/32 port = 22
201
202pass  in from xxx.yy.zz.2/32 to 192.168.1.2/32 port = 23
203
204pass  out all
205
206=head1 AUTHOR
207
208Camiel Dobbelaar <cd@sentia.nl>. B<ipfmeta> is in the Public Domain.
209
210=cut
211