1#!/usr/local/bin/perl -w 2# 3# $Id: ipfmeta,v 1.4 2001/01/29 08:37:56 camield Exp $ 4# 5# Copyright (c) 2000, 2001 Camiel Dobbelaar <cd@sentia.nl> 6# All rights reserved. 7# 8# Redistribution and use in source and binary forms, with or without 9# modification, are permitted provided that the following conditions 10# are met: 11# 1. Redistributions of source code must retain the above copyright 12# notice, this list of conditions and the following disclaimer. 13# 2. Redistributions in binary form must reproduce the above copyright 14# notice, this list of conditions and the following disclaimer in the 15# documentation and/or other materials provided with the distribution. 16# 3. The name of the author may not be used to endorse or promote products 17# derived from this software without specific prior written permission. 18# 19# THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 20# IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 21# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 22# DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, 23# INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 24# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 25# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 26# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, 27# STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN 28# ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 29# POSSIBILITY OF SUCH DAMAGE. 30# 31 32use strict; 33 34# Globals 35my $Group = 0; 36my $Maxrec = 20; 37my $Objfile = "ipf.objs"; 38my $Obj; my @Objs; 39my $Val; my %Vals; 40my $Verbose = 0; 41 42my $objfile = shift || $Objfile; 43readobjects($objfile); 44 45my @all_lines = <STDIN>; 46@all_lines = cutw(@all_lines); 47 48my $oline; 49foreach (@all_lines) { 50 if (m/^%/) { 51 command($_); 52 next; 53 } 54 55 if (m/^(#|$)/) { 56 print "$_\n"; 57 next; 58 } 59 60 foreach $oline (expand(agroup($_))) { 61 print "$oline\n"; 62 } 63} 64 65# end of main 66 67sub agroup 68{ 69 my $line = shift; 70 71 if ($Group && ($line !~ m/group/)) { 72 $line = "$line group $Group"; 73 } 74 return $line; 75} 76 77sub command 78{ 79 my $line = shift; 80 81 for ($line) { 82 m/verbose (\d+)/i and $Verbose = $1; 83 m/group (\d+)/i and $Group = $1; 84 m/dump/i and objdump(); 85 } 86} 87 88sub cutw 89{ 90 my @ret_lines; 91 92 foreach (@_) { 93 push(@ret_lines, join(' ', split)); 94 } 95 return @ret_lines; 96} 97 98sub expand 99{ 100 my $line = shift; 101 my $level = shift || 0; 102 my @retlines = $line; 103 104 # coarse protection 105 if ($level > $Maxrec) { 106 warn "ERR: recursion exceeds $Maxrec levels\n"; 107 return; 108 } 109 110 foreach $Obj (@Objs) { 111 if ($line =~ m/$Obj/) { 112 undef(@retlines); 113 if ($level < $Verbose) { 114 # add metarule as a comment 115 push(@retlines, "# ".$line); 116 } 117 foreach $Val (@{$Vals{$Obj}}) { 118 my $newline = $line; 119 $newline =~ s/$Obj/$Val/; 120 push(@retlines, expand($newline, $level+1)); 121 } 122 last; 123 } 124 } 125 return @retlines; 126} 127 128sub objdump 129{ 130 unless (@Objs) { 131 print "# dump: no objects\n"; 132 return; 133 } 134 foreach $Obj (sort @Objs) { 135 print "# [$Obj]\n"; 136 foreach $Val (sort @{$Vals{$Obj}}) { 137 print "# \t '$Val'\n"; 138 } 139 } 140} 141 142sub readobjects 143{ 144 my $objfile = shift; 145 my @tokens; 146 147 unless (open(FH, "$objfile")) { 148 if ($objfile eq $Objfile) { 149 return; 150 } else { 151 die "cannot open $objfile: $!\n"; 152 } 153 } 154 while (<FH>) { 155 push(@tokens, tokenparse(cutw($_))); 156 } 157 close(FH) || die "cannot close $objfile: $!\n"; 158 # link objects with their values 159 $Obj = ""; 160 while (@tokens) { 161 my $token = shift(@tokens); 162 if ($token =~ m/^\[([^\]]*)\]$/) { 163 # new object 164 $Obj = $1; 165 } else { 166 # new value 167 push(@{$Vals{$Obj}}, $token) unless ($Obj eq ""); 168 } 169 } 170 # sort objects: longest first 171 @Objs = sort { length($b) <=> length($a) } keys %Vals; 172} 173 174sub tokenparse 175{ 176 my @ret_tokens; 177 $_ = shift; 178 179 while ($_) { 180 next if (s/^\s+//); 181 last if (m/^#/); 182 my $token = ""; 183 if (s/^"//) { 184 $token = $1 if (s/^([^"]*)"//); 185 } elsif (s/^\[//) { 186 $token = "[$1]" if (s/^([^\]]*)\]//); 187 } else { 188 $token = $1 if (s/^([^\s]+)//); 189 } 190 push(@ret_tokens, $token) unless ($token eq ""); 191 } 192 return @ret_tokens; 193} 194 195__END__ 196 197=head1 NAME 198 199B<ipfmeta> - use objects in IPfilter files 200 201=head1 SYNOPSIS 202 203B<ipfmeta> [F<objfile>] 204 205=head1 DESCRIPTION 206 207B<ipfmeta> is used to simplify the maintenance of your IPfilter 208ruleset. It does this through the use of 'objects'. A matching 209object gets replaced by its values at runtime. This is similar to 210what a macro processor like m4 does. 211 212B<ipfmeta> is specifically geared towards IPfilter. It is line 213oriented: if an object has multiple values, the line with the object 214is duplicated and substituted for each value. It is also recursive: 215an object may have another object as a value. 216 217Metarules to be processed are read from stdin, output rules go to 218stdout. 219 220Definition of the objects and their values is done in a separate 221file; the filename defaults to F<ipf.objs>. An object is delimited 222by square brackets. A value is delimited by whitespace, except when 223it is enclosed by double-quotes. Comments start with '#' and end 224with a newline. Empty lines and extraneous whitespace are allowed. 225A value belongs to the object that precedes it. 226 227B<ipfmeta> has a command mode. Metarules starting with '%' are 228passed to the command processor. The commands are listed below. 229 230It is recommended that you use all caps or another distinguishing 231feature for object names. You can use B<ipfmeta> for NAT rules also, 232for instance to keep them in sync with filter rules. Combine 233B<ipfmeta> with a Makefile to save typing. 234 235=head1 COMMANDS 236 237=over 4 238 239=item B<dump> 240 241Include a list of all objects with their values in the output as 242comments. This can be used to verify if the objectfile is parsed 243correctly. 244 245=item B<group> F<n> 246 247Append 'group n' to subsequent output rules. Use 'group 0' to stop 248appending groups to the output. This is also the default. 249 250=item B<verbose> F<level> 251 252Include expanded metarules in output as comments. The default is 2530, do not add any comments. Higher verbosity levels cause deeper 254levels of expanded metarules to be included. 255 256=back 257 258=head1 EXAMPLES 259 260ipfmeta ipf.objs <ipf.metarules >ipf.rules 261 262cat ipf.metarules | ipfmeta | ipf -I -Fa -vf - 263 264=head1 AUTHOR 265 266Camiel Dobbelaar <cd@sentia.nl> 267 268=cut 269