1 #!/usr/bin/perl -w 2 # 3 # Written by Camiel Dobbelaar <cd (at] sentia.nl>, Aug-2000 4 # ipfmeta is in the Public Domain. 5 # 6 7 use strict; 8 use Getopt::Std; 9 10 ## PROCESS COMMANDLINE 11 our($opt_v); $opt_v=1; 12 getopts('v:') || die "usage: ipfmeta [-v verboselevel] [objfile]\n"; 13 my $verbose = $opt_v + 0; 14 my $objfile = shift || "ipf.objs"; 15 my $MAXRECURSION = 10; 16 17 ## READ OBJECTS 18 open(FH, "$objfile") || die "cannot open $objfile: $!\n"; 19 my @tokens; 20 while (<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 } 28 close(FH) || die "cannot close $objfile: $!\n"; 29 # link objects with their values 30 my $obj=""; 31 my %objs; 32 while (@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 44 my @objs = sort { length($b) <=> length($a) } keys %objs; 45 46 ## SUBSTITUTE OBJECTS WITH THEIR VALUES FROM STDIN 47 foreach (<STDIN>) { 48 foreach (expand($_, 0)) { 49 print; 50 } 51 } 52 53 ## END 54 55 sub 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 91 B<ipfmeta> - use objects in IP filter files 92 93 =head1 SYNOPSIS 94 95 B<ipfmeta> [F<options>] [F<objfile>] 96 97 =head1 DESCRIPTION 98 99 B<ipfmeta> is used to simplify the maintenance of your IP filter 100 ruleset. It does this through the use of 'objects'. A matching 101 object gets replaced by its values at runtime. This is similar to 102 what a macro processor like m4 does. 103 104 B<ipfmeta> is specifically geared towards IP filter. It is line 105 oriented, if an object has multiple values, the line with the object 106 is duplicated and substituted for each value. It is also recursive, 107 an object may have another object as a value. 108 109 Rules to be processed are read from stdin, output goes to stdout. 110 111 The verbose option allows for the inclusion of the metarules in the 112 output as comments. 113 114 Definition of the objects and their values is done in a separate 115 file, the filename defaults to F<ipf.objs>. An object is delimited 116 by square brackets. A value is delimited by whitespace. Comments 117 start with '#' and end with a newline. Empty lines and extraneous 118 whitespace are allowed. A value belongs to the first object that 119 precedes it. 120 121 It is recommended that you use all caps or another distinguishing 122 feature for object names. You can use B<ipfmeta> for NAT rules also, 123 for instance to keep them in sync with filter rules. Combine 124 B<ipfmeta> with a Makefile to save typing. 125 126 =head1 OPTIONS 127 128 =over 4 129 130 =item B<-v> I<verboselevel> 131 132 Include metarules in output as comments. Default is 1, the top level 133 metarules. Higher levels cause expanded metarules to be included. 134 Level 0 does not add comments at all. 135 136 =back 137 138 =head1 BUGS 139 140 A value can not have whitespace in it. 141 142 =head1 EXAMPLE 143 144 (this does not look good, formatted) 145 146 I<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 160 I<ipf.metarules> 161 162 block in from UNWANTED to any 163 164 pass in from NOC to WEBSERVERS port = MGMT-PORTS 165 166 pass out all 167 168 I<Run> 169 170 ipfmeta ipf.objs <ipf.metarules >ipf.rules 171 172 I<Output> 173 174 # block in from UNWANTED to any 175 176 block in from 10.0.0.0/8 to any 177 178 block in from 127.0.0.0/8 to any 179 180 block in from 172.16.0.0/12 to any 181 182 block in from 192.168.0.0/16 to any 183 184 block in from 224.0.0.0/4 to any 185 186 # pass in from NOC to WEBSERVERS port = MGMT-PORTS 187 188 pass in from xxx.yy.zz.1/32 to 192.168.1.1/32 port = 22 189 190 pass in from xxx.yy.zz.1/32 to 192.168.1.1/32 port = 23 191 192 pass in from xxx.yy.zz.1/32 to 192.168.1.2/32 port = 22 193 194 pass in from xxx.yy.zz.1/32 to 192.168.1.2/32 port = 23 195 196 pass in from xxx.yy.zz.2/32 to 192.168.1.1/32 port = 22 197 198 pass in from xxx.yy.zz.2/32 to 192.168.1.1/32 port = 23 199 200 pass in from xxx.yy.zz.2/32 to 192.168.1.2/32 port = 22 201 202 pass in from xxx.yy.zz.2/32 to 192.168.1.2/32 port = 23 203 204 pass out all 205 206 =head1 AUTHOR 207 208 Camiel Dobbelaar <cd@sentia.nl>. B<ipfmeta> is in the Public Domain. 209 210 =cut 211