Versions Compared

Key

  • This line was added.
  • This line was removed.
  • Formatting was changed.
Comment: Migrated to Confluence 4.0
Code Block

#!/usr/bin/perl
#
# $Id: expand-as-macro,v 1.18 2011/09/29 11:45:15 he Exp $
#

# Copyright (c) 2011
#      UNINETT A/S.  All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions
# are met:
# 1. Redistributions of source code must retain the above copyright
#    notice, this list of conditions and the following disclaimer.
# 2. Redistributions in binary form must reproduce the above copyright
#    notice, this list of conditions and the following disclaimer in the
#    documentation and/or other materials provided with the distribution.
#
# THIS SOFTWARE IS PROVIDED BY UNINETT AND ``AS IS'' AND ANY
# EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
# PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL UNINETT OR NORDUnet OR
# THEIR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#


# Expand an as-macro in the IRR into an as-origin list, and format the
# result either for IOS, IOS-XR or JunOS.
#
# Prefers to use peval from the IRRtoolset package, resorts to using
# whois with either whois.ripe.net or whois.radb.net.  The whois
# program is expected to honor POSIX argument parsing rules, in that
# "--" ends the parsing of the program option list.


use Getopt::Std;
use strict;

sub parse_rpsl {
    my($fh) = @_;
    my($inm) = 0;
    my(@f);
    our($opt_v);

    while(<$fh>) {
	chop;
	if ($opt_v) {
	    printf("%s\n", $_);
	}
	if ($inm) {
	    if (/^\s/) {
		$_ =~ s/,/ /g;
		@f = split;
		&add_ases(@f);
	    } else {
		$inm = 0;
	    }
	} 
	if (!$inm) {
	    if (/^members:/) {
		$inm = 1;
		$_ =~ s/,/ /g;
		@f = split;
		shift @f;
		&add_ases(@f);
	    }
	}
    }
    if ($opt_v) {
	print "\n";
    }
}

sub parse_ripe181 {
    my($fh) = @_;
    our($notripe, $opt_v);
    my(@f);

    while(<IN>) {
	chop;
	if ($opt_v) {
	    printf("%s\n", $_);
	}
	if (/^% No entries found/) {
	    $notripe = 1;
	}
	if (/^as-list:/) {
	    @f = split;
	    shift @f;
	    &add_ases(@f);
	}
    }
    
}

sub getmacro_ripe {
    my($mn) = @_;
    our(%seen, $opt_a);
    my($IN);

    $mn = uc($mn);
    $seen{$mn} = 1;
    open($IN, "whois -h whois.ripe.net -- \"$opt_a -r $mn\"|");
    &parse_rpsl($IN);
    close($IN);
}

sub getmacro_radb {
    my($mn) = @_;
    my($inm) = 0;
    our(%seen);
    my($IN);

    $mn = uc($mn);
    $seen{$mn} = 1;
    open($IN, "whois -h whois.radb.net -- \"-r $mn\"|");
    &parse_rpsl($IN);
    close($IN);
}

sub add_ases {
    my(@a) = @_;
    our(%seen);
    our(@expand, @ases);

    foreach my $as (@a) {
	if ($as =~ /AS-[A-Z0-9]*/) {
	    if (! defined($seen{$as})) {
		push(@expand, $as);
	    }
	}
	if ($as =~ /AS([0-9]+)/) {
	    push(@ases, $1);
	}
    }
}

sub getmacro_ratools {
    my($mn) = @_;

    $_ = `peval -h whois.ripe.net -protocol ripe -no-as $mn`;
    if ($? != 0) { return undef; }
    $_ =~ s/\(//g;
    $_ =~ s/\)//g;
    my(@a) = split;
    &add_ases(@a);
    return 1;
}

sub getmacro {
    my($mn) = @_;
    our($notripe);

    if (&getmacro_ratools($mn)) {
	return;
    } else {
	if ($notripe) {
	    &getmacro_radb($mn);
	} else {
	    &getmacro_ripe($mn);
	    if ($notripe) {
		&getmacro_radb($mn);
	    }
	}
    }
}

sub getmacros {
    my(@start) = @_;
    our($notripe);
    our(@expand);

    $notripe = 0;
    push(@expand, @start);
    while($#expand != -1) {
	&getmacro(shift(@expand));
    }
}

sub print_j {
    my($apgn, @ases) = @_;
    my($n, $s, $pfx);

    printf("policy-options {\n");
    printf("replace:\n");
    printf("  as-path-group %s {\n", $apgn);
    $n = 0;
    $s = "";
    foreach my $as (@ases) {
	if ($s eq "" || length($s) >= 65) {
	    if ($s ne "") {
		$s .= ")\";";
		printf("%s\n", $s);
	    }
	    $n++;
	    $pfx = "    as-path " . sprintf("%d \".*(", $n);
	    $s = $pfx;
	} else {
	    $s .= "|";
	}
	$s .= sprintf("%s", $as);
    }
    if ($s ne $pfx) {
	printf("%s)\";\n", $s);
    }
    printf("  }\n}\n");
}

sub print_x {
    my($apgn, @ases) = @_;
    my($n, $s, $pfx);

    printf("as-path-set %s\n", $apgn);
    $n = 0;
    $s = "";
    foreach my $as (@ases) {
	if ($s eq "" || length($s) >= 65) {
	    if ($s ne "") {
		$s .= ")\$',";
		printf("%s\n", $s);
	    }
	    $n++;
	    $pfx = "   ios-regex '_(";
	    $s = $pfx;
	} else {
	    $s .= "|";
	}
	$s .= sprintf("%s", $as);
    }
    printf("%s)\$'\n", $s);
    printf("end-set\n");
}


# Plain printing; just the numbers

sub print_p {
    my(@ases) = @_;

    foreach my $as (@ases) {
	printf("%d\n", $as);
    }
}

sub printases {
    my($num, @ases) = @_;
    my(%as);
    our($opt_p, $opt_j, $opt_x, $opt_1);

    foreach my $as (@ases) {
	$as{$as} = 1;
    }
    undef @ases;
    foreach my $as (keys %as) {
	push(@ases, $as);
    }
    my @sortas = sort {$a <=> $b} @ases;

    if ($opt_p) {
	print_p(@sortas);
	return;
    }

    if ($opt_j) {
	print_j($num, @sortas);
	return;
    }

    if ($opt_x) {
	print_x($num, @sortas);
	return;
    }

    my($s, $pfx, $sol);
    printf("!\nno ip as-path access-list $num\n!\n");
    $pfx = "ip as-path access-list $num permit ";
    if (defined($opt_1)) {
	foreach my $as (@sortas) {
	    printf("%s_%d\$\n", $pfx, $as);
	}
    } else {
	$sol = $pfx . "_(";		# start of line
	$s = "";
	foreach my $as (@sortas) {
	    if ($s eq "" || length($s) >= 65) {
		if ($s ne "") {
		    $s .= ")\$";
		    printf("%s\n", $s);
		}
		$s = $sol;
	    } else {
		$s .= "|";
	    }
	    $s .= sprintf("%s", $as);
	}
	if ($s ne $sol) {
	    printf("%s)\$\n", $s);
	}
    }
    printf("!\n");
}

getopts('1ajn:pvx');
our($opt_a, $opt_n, $opt_p);
our(@ases);

if (!defined($opt_n) && !defined($opt_p)) {
    printf(STDERR
	   "usage: $0 [-a] [-p] [-v] [-j] [-x] [-1] " .
	   "-n aclnum/name as-macro ...\n");
    printf(STDERR "\n");
    printf(STDERR "-a pass through to whois to RIPE (all sources)\n");
    printf(SDTERR "-p plain -- just the AS numbers, one per line\n");
    printf(STDERR "-v verbose -- print whois output to stdout\n");
    printf(STDERR "-j JunOS -- format as-path-group for JunOS\n");
    printf(STDERR "-x IOS-XR -- format as-path-set for IOS-XR\n");
    printf(STDERR "(none) IOS -- default is to print IOS ACL\n");
    printf(STDERR "-1 with IOS output, only one AS per line\n");
    printf(STDERR "-n name or number of IOS ACL / as-path-{set|group}\n");
    exit(1);
}
if (defined($opt_a)) {
    $opt_a = "-a";
}

&getmacros(@ARGV);
&printases($opt_n, @ases);