On Wed, Jan 10, 2007 at 02:18:57PM +0100, Johan Segern?s wrote:
> Today it looks like (very stripped down)
> pass in on $FOO_NET inet from $FOO_IPS to any keep state
> pass in on $BAR_NET inet from $BAR_IPS to any keep state
> 
> Instead I would like to do it like
> for i in FOO BAR; do
>       pass in on ${i}_NET inet from ${i}_IPS to any keep state
> done

> Or something. Is this possible within pf.conf or would I have to make a
> shell loop creating this little extra pf config file and include in
> pf.conf?

You could also do it with dfd_keeper, available off my web site.
It's a framework for writing pf rules using python.  There used
to be a static version, but I think it didn't present enough
utility to continue to maintain static-only as a seperate library.

You could do it as a shell script, or any kind of preprocessor.
I don't really like the way m4 syntax looks, or how it re-reads
expanded macros as input again.  The cpp would be fine if it wasn't
specific to C, so I wrote a little script that mimics cpp (but offers
much more flexibility) and I've attached it for your convenience.
-- 
``Unthinking respect for authority is the greatest enemy of truth.''
-- Albert Einstein -><- <URL:http://www.subspacefield.org/~travis/>
#! /usr/local/bin/perl -w

# $Id: preprocess 11947 2007-01-12 04:01:15Z user $

$main::command="#"; # semicolon is the default command indicator
$main::ignore_header = 1;  # ignore interpreter line and leading #commands
$main::quiet = 0; # give error messages for invalid commands
$main::leading_spaces = 1;
$main::fi_match = 0;
$main::empty_fi = 1; # Empty commands are fi commands by default.

use Getopt::Long;

GetOptions("command=s", \$main::command,
           "define=s%", \%main::preprocess,
           "quiet!", \$main::quiet,
           "ignore!", \$main::ignore_header,
           "leading-spaces!", \$main::leading_spaces,
           "fi-match!", \$main::fi_match,
           "empty!", \$main::empty_fi);

@main::regions = ( [ 1, ""] ); # The root-level region is a printing region.

# Force user-defined variables into preprocess namespace.
while (($key, $val) = each (%main::preprocess)) {
    eval "\$preprocess::$key = $val";
}

$main::skip_spaces = ($main::leading_spaces ? '\s*' : '');

# We found a command line, so do something.
sub process_command {
    my ($command) = @_;

    $_ = $command;

    # Get rid of leading/trailing white space.
    s/^\s*//;
    s/\s*$//;

    # Do we want to allow trailing comments?  Other comments?

    # Empty commands are fi commands by default.
    $_ = "fi" if ($main::empty_fi and $_ eq "");

    # Process any end-of-region markers.
    if (/^(endif|fi)(\s+(.*))?$/) {
        die "Unexpected end of region at line $.\n" unless $#main::regions;
        my ($truth, $expr) = @{pop @main::regions};
        if (defined($3) and $main::fi_match and $expr ne $3) {
            die "Unmatched fi at line $. (\"$expr\" ne \"$3\")\n";
        }
        return;
    }

    # NB: We do not ignore if commands inside conditional regions that are
    #     false because we allow nested if statements.  We need to see the
    #     if statement so we can skip the next end-of-region marker.
    if (/^(unless|if)\s+(.*)$/) {
        my $expr = $2;
        package preprocess;
        my $condition = eval $expr;
        # Undefined expressions are false.
        $condition = 0 unless defined($condition);
        package main;
        my $cond_truth = ($1 eq "if") ? $condition : !$condition;

        # NB: If we are in a false region, all nested regions are false.
        push(@main::regions, [ $cond_truth && $main::current_truth, $expr ]);

        return;
    }

    # Ignore anything else if we are in a conditional region that is false.
    return unless $main::current_truth;

    if (/([^=]+)\s*=\s*([^=]+)/) {
        package preprocess;
        eval "$1=$2";
        return;
    }

    die "Invalid command $_ at line $.\n" unless $main::quiet;
}

while (<STDIN>) {
    chomp;

    # NB: Easier to short-circuit the header if we are ignoring it.
    if (/^#/ and $main::ignore_header) { print "$_\n"; next; }
        $main::ignore_header = 0;

        # Is the current region true?
        $main::current_truth = $main::regions[-1]->[0];

        if ($_ =~ /^$main::skip_spaces$main::command(.*)$/) {
            process_command($1);
            next;
        }
        print "$_\n" if $main::current_truth;
    }

    if ($#main::regions) { die "Not enough fi statements.\n"; }

exit 0;

__END__

=head1 NAME

preprocess - Preprocess arbitrary files.

=head1 SYNPOSIS

preprocess --command=';' --quiet <whatever.in > whatever

=head1 DESCRIPTION

This program is used for conditionally printing various regions of a file.
It is much like the C preprocessor, except that it is meant for any text
file, not just C code.  Its command language is a restricted subset of Perl.

=item --command delimiter

Specify that you may introduce commands to preprocess by beginning the line
with the specified delimiter.

=item --define var=val

Define a variable.  Must be in perl syntax (e.g. '$foo = bar').

=item --quiet

Ignore lines that start with the command delimiter, but are not valid commands.

=item --ignore

Don't treat any leading lines beginning with hash (#) as commands, even if the
command delimiter is hash.

=item --leading-spaces

Allow leading spaces before the command character.

=item --fi-match

Allow expressions to follow fi statements to match them to if statements.
This is to catch errors such as forgetting a fi statement.

=head2 COMMANDS

The commands in the file may be one of the following:

=item if [expr]

The following lines are printed if the Perl expression is true.

=item unless [expr]

The following lines are printed unless the expression is true.

=item fi [expr]

Ends an if or unless block.

If --fi-match is enabled, you may optionally add an expression which
must match the corresponding if/unless expression.

=item var=expr

Set the value of a variable.

=head2 EXAMPLES

#! /bin/sh
; if $foo
$foo is defined
; endif

=head2 AUTHOR

The author is [EMAIL PROTECTED]

=cut

Attachment: pgpqY52wiL4Zm.pgp
Description: PGP signature

Reply via email to