1
0
mirror of https://github.com/bgp/bgpq4.git synced 2024-05-11 05:55:05 +00:00

initial commit into git

This commit is contained in:
Alexandre Snarskii
2013-10-07 17:43:42 +04:00
commit 349b3e860b
25 changed files with 8364 additions and 0 deletions

3
.gitignore vendored Normal file
View File

@@ -0,0 +1,3 @@
CVS/
Makefile
config.h

112
CHANGES Normal file
View File

@@ -0,0 +1,112 @@
0.1.20 (2013-10-07)
- socket close code fixed. Thanks to Martin J. Levy.
0.1.19 (2013-05-09)
- CLANG compilation issues fixed.
- bgpq3.spec added. Thanks to Arnoud Vermeer.
0.1.18 (2013-01-08)
- JSON output format. Thanks to Job Snijders (Atrato Networks).
0.1.17 (2012-10-25)
- route-sets handling in command-line added. Thanks to Alexandr Turovsky
for pointing out.
- bug in aggregation documentation fixed. Thanks to Nikolay Shopik.
0.1.16 (2012-01-19)
- new option -m <len>: maximum length of accepted prefixes.
Suggested by Eugene Demidov, used to discard 'too long prefixes'
(like /30-/32) even if they are registered in IRR. By default
limit is not set and all prefixes accepted.
- documentation redesigned into text/markdown and text/html (manpage
supported still).
0.1.15 (2011-07-15)
- prefix-set's for Cisco IOS XR now supported too.
0.1.14 (2011-06-18)
- Fixed bug in sx_maxsockbuf in rare cases of OS maxsockbuf >2M.
Thanks to Andreas Lundin.
0.1.13 (2011-06-14)
- never publically released.
0.1.12 (2010-10-08)
- Fixed bug preventing AS262144 (that's AS4.0 in asdot) to expand.
Thanks to Sergey Matveychuk
0.1.11 (2010-04-19)
- Fixed another bug in aggregation (-A) mode, thanks to Dmitry Tejblum.
0.1.10 (2009-06-13)
- Fixed bug in aggregation (-A) mode, thanks to Sergey Gonchar.
0.1.9 (2009-03-27)
- RIPE changed ASN32 notation to asplain. And RADB does not support
asplain indexing (yet?).... Fixed. Thanks to Pavel Gluchouk.
0.1.8 (2008-12-25)
- new flag -D for Cisco asdot notation. Cisco behaviour is a bit
strange for me, but, well, that's their decision:
When the asdot format is enabled as the default, any regular expressions
to match 4-byte autonomous system numbers must be written using the asdot
format, or else the regular expression match will fail.
(c) http://www.cisco.com/en/US/docs/ios/12_0s/release/ntes/120SNEWF.html
#wp3521658 (note the URL wrap).
0.1.7 (2008-12-19):
- man page. Finally :)
- option -h now means not help, but now it can be used to point to
alternate IRRD host, like in old bgpq.
0.1.6 (2008-08-08):
- maxsockbuf call added, that can help with pipelining of really large
as-sets.
- new key -M <extra match condition> for juniper route-filters, f.e.:
bgpq3 -JEM "protocol bgp;\n community no-export" -l PolicyName/TermName
will generate term with additional match conditions, like:
policy-options {
policy-statement PolicyName {
term TermName {
replace:
from {
protocol bgp;
community no-export;
route-filter 10.0.0.0/24 exact;
}
}
}
}
0.1.5 (2008-06-02):
- route-set's expansion added. Fully functional for IPv4 prefixes, but
not for IPv6 - only those prefixes explicitely marked as 'member-of: RS..'
will be expanded. This is due to limitation in IRRd.
- extended access-lists (Cisco) and route-filters (Juniper) generation
is supported now with new -E key. For Cisco ipv6 access-lists is not
yet supported.
0.1.4 (2008-05-30):
- bugfix for juniper as-path group generation. Thanks to Alexander Shikoff.
0.1.3 (2008-05-20):
- aggregation (-A) now supported for Cisco prefix-lists.
- pipelining now can be enabled for RIPE-style queries too (ipv6).
- more-specific routes (-R len) feature ported from bgpq
- pipelining now set by default. -T flag now disables pipelining.
- strlcpy.c imported into sources. Not found on Linux :)
0.1.2 (2008-05-19):
- final support for asn32, now with correct syntax for Juniper.
- experimental 'pipelining' mode (flag -T), much faster when
working with big as-set's.
- RIPE-style query (-i origin) now requests only route6 objects.
0.1.1 (2008-05-16):
- initial support for asn32 added (flag -3). By default it's off,
and when bgpq sees 32-bit asn in resolver queue, it either replaces
it with AS23456 (in as-path generation mode) or queries radb for
prefixes with that origin.
Note: for now only JunOS 9.1 can handle asn32, not Cisco IOS..

26
COPYRIGHT Normal file
View File

@@ -0,0 +1,26 @@
/*-
* Copyright (c) 2007-2013 Alexandre Snarskii <snar@snar.spb.ru>
* 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 THE AUTHOR AND CONTRIBUTORS ``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 THE AUTHOR OR 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.
*
*/

41
Makefile.in Normal file
View File

@@ -0,0 +1,41 @@
CC=@CC@
CFLAGS=@CFLAGS@ @DEFS@ -g -Wall -I. -O0
LDADD=@LDFLAGS@ @LIBS@
INSTALL=@INSTALL@
prefix = @prefix@
bindir = @bindir@
exec_prefix = @exec_prefix@
datarootdir = @datarootdir@
datadir = @datadir@
infodir = @infodir@
mandir = @mandir@
OBJECTS=bgpq3.o sx_report.o bgpq_expander.o sx_slentry.o bgpq3_printer.o \
sx_prefix.o strlcpy.o sx_maxsockbuf.o
SRCS=bgpq3.c sx_report.c bgpq_expander.c sx_slentry.c bgpq3_printer.c \
sx_prefix.c strlcpy.c sx_maxsockbuf.c
all: bgpq3
bgpq3: ${OBJECTS}
${CC} ${CFLAGS} -o bgpq3 ${OBJECTS} ${LDADD}
.c.o:
${CC} ${CFLAGS} -c $<
clean:
rm -rf Makefile autom4te.cache bgpq3 config.h config.log config.status
rm -rf *.o *.core core.* core
install: bgpq3
${INSTALL} -c -s -m 755 bgpq3 @bindir@
if test ! -d @prefix@/man/man8 ; then mkdir -p @prefix@/man/man8 ; fi
${INSTALL} -m 644 bgpq3.8 @prefix@/man/man8
depend:
makedepend -- $(CFLAGS) -- $(SRCS)
bgpq3.html: bgpq3.txt
markdown2 bgpq3.txt > bgpq3.html

234
bgpq3.8 Normal file
View File

@@ -0,0 +1,234 @@
.\" Copyright (c) 2007-2011 Alexandre Snarskii
.\" 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 THE AUTHOR AND CONTRIBUTORS ``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 THE AUTHOR OR 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.
.\" "$Id: bgpq3.8,v 1.9 2013/01/08 12:21:14 snar Exp $
.\"
.Dd Oct 27, 2008
.Dt BGPQ3 8
.Os
.Sh NAME
.Nm bgpq3
.Nd "bgp filtering automation for cisco and juniper routers"
.Sh SYNOPSIS
.Nm
.Op Fl h Ar host
.Op Fl S Ar sources
.Op Fl EP
.Oo
.Fl f Ar asn |
.Fl G Ar asn
.Oc
.Op Fl 36ADdJjX
.Op Fl R Ar len
.Op Fl m Ar max
.Ar OBJECTS
.Op "..."
.Sh DESCRIPTION
The
.Nm
utility used to generate Cisco and Juniper prefix-lists, extended
access-lists, policy-statement terms and as-path lists based on RADB data.
.Pp
The options are as follows:
.Bl -tag -width Ds
.It Fl 3
assume that your device is asn32-safe.
.It Fl 6
generate IPv6 prefix/access-lists (IPv4 by default).
.It Fl A
try to aggregate prefix-lists as much as possible (not all output
formats supported).
.It Fl d
enable some debugging output.
.It Fl D
use asdot notation for Cisco as-path access-lists.
.It Fl E
generate extended access-list (Cisco) or policy-statement term using
route-filters (Juniper).
.It Fl f Ar number
generate input as-path access-list.
.It Fl G Ar number
generate output as-path access-list.
.It Fl h Ar host
host running IRRD database (default: whois.radb.net).
.It Fl J
generate config for Juniper (default: Cisco).
.It Fl j
generate output in JSON format (default: Cisco).
.It Fl m Ar len
maximum prefix-length of accepted prefixes (default: 32 for IPv4 and
128 for IPv6).
.It Fl M Ar match
extra match conditions for Juniper route-filters.
.It Fl l Ar name
name of generated entry.
.It Fl P
generate prefix-list (default, backward compatibility).
.It Fl R Ar len
allow specific routes up to specified masklen too.
.It Fl S Ar sources
use specified sources only (default: RADB,RIPE,APNIC).
.It Fl T
disable pipelining.
.It Fl X
generate config for Cisco IOS XR devices (plain IOS by default).
.It Ar OBJECTS
means networks (in prefix format), autonomous systems and as-macros.
.El
.Sh EXAMPLES
Generating named juniper prefix-filter for AS20597:
.nf
.RS
~>bgpq3 -Jl eltel AS20597
policy-options {
replace:
prefix-list eltel {
81.9.0.0/20;
81.9.32.0/20;
81.9.96.0/20;
81.222.128.0/20;
81.222.192.0/18;
85.249.8.0/21;
85.249.224.0/19;
89.112.0.0/19;
89.112.4.0/22;
89.112.32.0/19;
89.112.64.0/19;
217.170.64.0/20;
217.170.80.0/20;
}
}
.RE
.fi
.Pp
For Cisco we can use aggregation (-A) flag to make this prefix-filter
more compact:
.nf
.RS
~>bgpq3 -Al eltel AS20597
no ip prefix-list eltel
ip prefix-list eltel permit 81.9.0.0/20
ip prefix-list eltel permit 81.9.32.0/20
ip prefix-list eltel permit 81.9.96.0/20
ip prefix-list eltel permit 81.222.128.0/20
ip prefix-list eltel permit 81.222.192.0/18
ip prefix-list eltel permit 85.249.8.0/21
ip prefix-list eltel permit 85.249.224.0/19
ip prefix-list eltel permit 89.112.0.0/18 ge 19 le 19
ip prefix-list eltel permit 89.112.4.0/22
ip prefix-list eltel permit 89.112.64.0/19
ip prefix-list eltel permit 217.170.64.0/19 ge 20 le 20
.RE
.fi
- you see, prefixes 89.112.0.0/19 and 89.112.32.0/19 now aggregated
into single entry 89.112.0.0/18 ge 19 le 19.
.Pp
Well, for Juniper we can generate even more interesting policy-options,
using -M <extra match conditions>, -R <len> and hierarchical names:
.nf
.RS
~>bgpq3 -AJEl eltel/specifics -R 32 -M "community blackhole" AS20597
policy-options {
policy-statement eltel {
term specifics {
replace:
from {
community blackhole;
route-filter 81.9.0.0/20 upto /32;
route-filter 81.9.32.0/20 upto /32;
route-filter 81.9.96.0/20 upto /32;
route-filter 81.222.128.0/20 upto /32;
route-filter 81.222.192.0/18 upto /32;
route-filter 85.249.8.0/21 upto /32;
route-filter 85.249.224.0/19 upto /32;
route-filter 89.112.0.0/18 prefix-length-range /19-/32;
route-filter 89.112.64.0/19 upto /32;
route-filter 217.170.64.0/19 prefix-length-range /20-/32;
}
}
}
}
.RE
.fi
generated policy-option term now allows all specifics for eltel networks
if they match with special community 'blackhole'.
.Pp
Of course, this version supports IPv6 (-6):
.nf
.RS
~>bgpq3 -6l as-retn-6 AS-RETN6
no ipv6 prefix-list as-retn-6
ipv6 prefix-list as-retn-6 permit 2001:7fb:fe00::/48
ipv6 prefix-list as-retn-6 permit 2001:7fb:fe01::/48
[....]
.RE
.fi
and support for ASN 32 is also here
.nf
.RS
~>bgpq3 -J3f 112 AS-SPACENET
policy-options {
replace:
as-path-group NN {
as-path a0 "^112(112)*$";
as-path a1 "^112(.)*(1898|5539|8495|8763|8878|12136|12931|15909)$";
as-path a2 "^112(.)*(21358|23456|23600|24151|25152|31529|34127|34906)$";
as-path a3 "^112(.)*(35052|41720|43628|44450|196611)$";
}
}
.RE
.fi
see AS196611 in the end of the list ? That's AS3.3 in 'asplain' notation.
.Pp
For non-ASN32 capable routers you should not use switch -3,
and the result will be next:
.nf
.RS
~>bgpq3 -f 112 AS-SPACENET
no ip as-path access-list NN
ip as-path access-list NN permit ^112(_112)*$
ip as-path access-list NN permit ^112(_[0-9]+)*_(1898|5539|8495|8763)$
ip as-path access-list NN permit ^112(_[0-9]+)*_(8878|12136|12931|15909)$
ip as-path access-list NN permit ^112(_[0-9]+)*_(21358|23456|23600|24151)$
ip as-path access-list NN permit ^112(_[0-9]+)*_(25152|31529|34127|34906)$
ip as-path access-list NN permit ^112(_[0-9]+)*_(35052|41720|43628|44450)$
.RE
.fi
.Pp
AS196611 is no more in the list, however, AS23456 (transition AS)
would be added to list if it were not present.
.Sh DIAGNOSTICS
When everything is OK,
.Nm
generates access-list to standard output and exits with status == 0.
In case of errors they are printed to stderr and program exits with
non-zero status.
.Sh SEE ALSO
.Sy http://www.radb.net/
Routing Arbiter project
.Sy http://tools.ietf.org/html/draft-michaelson-4byte-as-representation-05
for information on 'asdot' and 'asplain' notations.
.Sy http://www.cisco.com/en/US/docs/ios/12_0s/release/ntes/120SNEWF.html#wp3521658
for information on Cisco implementation of ASN32.
.Sh AUTHOR
.An Alexandre Snarskii Aq snar@snar.spb.ru

379
bgpq3.c Normal file
View File

@@ -0,0 +1,379 @@
#if HAVE_CONFIG_H
#include <config.h>
#endif
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <strings.h>
#include <unistd.h>
#include "bgpq3.h"
#include "sx_report.h"
extern int debug_expander;
extern int debug_aggregation;
extern int pipelining;
int
usage(int ecode)
{
printf("\nUsage: bgpq3 [-h host] [-S sources] [-P|E|G <num>|f <num>]"
" [-36ADJjXd] [-R len] <OBJECTS>...\n");
printf(" -3 : assume that your device is asn32-safe\n");
printf(" -6 : generate IPv6 prefix-lists (IPv4 by default)\n");
printf(" -A : try to aggregate Cisco prefix-lists or Juniper "
"route-filters\n as much as possible\n");
printf(" -d : generate some debugging output\n");
printf(" -D : use asdot notation in as-path\n");
printf(" -E : generate extended access-list(Cisco) or "
"route-filter(Juniper)\n");
printf(" -f number : generate input as-path access-list\n");
printf(" -G number : generate output as-path access-list\n");
printf(" -h host : host running IRRD software (whois.radb.net by "
"default)\n");
printf(" -J : generate config for JunOS (Cisco IOS by default)\n");
printf(" -j : generate JSON output (Cisco IOS by default)\n");
printf(" -M match : extra match conditions for JunOS route-filters\n");
printf(" -m len : maximum prefix length (default: 32 for IPv4, "
"128 for IPv6)\n");
printf(" -l name : use specified name for generated access/prefix/.."
" list\n");
printf(" -P : generate prefix-list (default, just for backward"
" compatibility)\n");
printf(" -R len : allow specific routes up to masklen specified\n");
printf(" -S sources: use only specified sources (default:"
" RADB,RIPE,APNIC)\n");
printf(" -T : disable pipelining (experimental, faster mode)\n");
printf(" -X : generate config for IOS XR (Cisco IOS by default)\n");
printf("\n" PACKAGE_NAME " version: " PACKAGE_VERSION "\n");
printf("Copyright(c) Alexandre Snarskii <snar@snar.spb.ru> 2007-2013\n\n");
exit(ecode);
};
void
exclusive()
{
fprintf(stderr,"-E, -f <asnum>, -G <asnum> and -P are mutually "
"exclusive\n");
exit(1);
};
void
vendor_exclusive()
{
fprintf(stderr, "-J (JunOS), -j (JSON) and -X (IOS XR) options are mutually"
" exclusive\n");
exit(1);
};
int
parseasnumber(struct bgpq_expander* expander, char* optarg)
{
char* eon=NULL;
expander->asnumber=strtoul(optarg,&eon,10);
if(expander->asnumber<1 || expander->asnumber>(65535ul*65535)) {
sx_report(SX_FATAL,"Invalid AS number: %s\n", optarg);
exit(1);
};
if(eon && *eon=='.') {
/* -f 3.3, for example */
uint32_t loas=strtoul(eon+1,&eon,10);
if(expander->asnumber>65535) {
/* should prevent incorrect numbers like 65537.1 */
sx_report(SX_FATAL,"Invalid AS number: %s\n", optarg);
exit(1);
};
if(loas<1 || loas>65535) {
sx_report(SX_FATAL,"Invalid AS number: %s\n", optarg);
exit(1);
};
if(eon && *eon) {
sx_report(SX_FATAL,"Invalid symbol in AS number: %c (%s)\n",
*eon, optarg);
exit(1);
};
expander->asnumber=(expander->asnumber<<16)+loas;
} else if(eon && *eon) {
sx_report(SX_FATAL,"Invalid symbol in AS number: %c (%s)\n",
*eon, optarg);
exit(1);
};
return 0;
};
int
main(int argc, char* argv[])
{
int c;
struct bgpq_expander expander;
int af=AF_INET;
int widthSet=0, aggregate=0, refine=0;
unsigned long maxlen=0;
bgpq_expander_init(&expander,af);
expander.sources=getenv("IRRD_SOURCES");
while((c=getopt(argc,argv,"36AdDES:jJf:l:m:M:W:PR:G:Th:X"))!=EOF) {
switch(c) {
case '3':
expander.asn32=1;
break;
case '6': af=AF_INET6;
expander.family=AF_INET6;
expander.tree->family=AF_INET6;
break;
case 'A':
if(aggregate) debug_aggregation++;
aggregate=1;
break;
case 'd': debug_expander++;
break;
case 'D': expander.asdot=1;
break;
case 'E': if(expander.generation) exclusive();
expander.generation=T_EACL;
break;
case 'h': expander.server=optarg;
break;
case 'J': if(expander.vendor) vendor_exclusive();
expander.vendor=V_JUNIPER;
break;
case 'j': if(expander.vendor) vendor_exclusive();
expander.vendor=V_JSON;
break;
case 'f':
if(expander.generation) exclusive();
expander.generation=T_ASPATH;
parseasnumber(&expander,optarg);
break;
case 'G':
if(expander.generation) exclusive();
expander.generation=T_OASPATH;
parseasnumber(&expander,optarg);
break;
case 'P':
if(expander.generation) exclusive();
expander.generation=T_PREFIXLIST;
break;
case 'R':
refine=strtoul(optarg,NULL,10);
if(!refine) {
sx_report(SX_FATAL,"Invalid refine length: %s\n", optarg);
exit(1);
};
break;
case 'l': expander.name=optarg;
break;
case 'm': maxlen=strtoul(optarg, NULL, 10);
if (!maxlen) {
sx_report(SX_FATAL, "Invalid maxlen (-m): %s\n", optarg);
exit(1);
};
break;
case 'M': {
char* c, *d;
expander.match=strdup(optarg);
c=d=expander.match;
while(*c) {
if(*c=='\\') {
if(*(c+1)=='n') {
*d='\n';
d++;
c+=2;
} else if(*(c+1)=='r') {
*d='\r';
d++;
c+=2;
} else if(*(c+1)=='\\') {
*d='\\';
d++;
c+=2;
};
} else {
if(c!=d) {
*d=*c;
};
d++;
c++;
};
};
*d=0;
};
case 'T': pipelining=0;
break;
case 'S': expander.sources=optarg;
break;
case 'W': expander.aswidth=atoi(optarg);
if(expander.aswidth<1) {
sx_report(SX_FATAL,"Invalid as-width: %s\n", optarg);
exit(1);
};
widthSet=1;
break;
case 'X': if(expander.vendor) vendor_exclusive();
expander.vendor=V_CISCO_XR;
break;
default : usage(1);
};
};
argc-=optind;
argv+=optind;
if(!widthSet) {
if(expander.generation==T_ASPATH) {
if(expander.vendor==V_CISCO) {
expander.aswidth=4;
} else if(expander.vendor==V_JUNIPER) {
expander.aswidth=8;
};
} else if(expander.generation==T_OASPATH) {
if(expander.vendor==V_CISCO) {
expander.aswidth=5;
} else if(expander.vendor==V_JUNIPER) {
expander.aswidth=8;
};
};
};
if(!expander.generation) {
expander.generation=T_PREFIXLIST;
};
/*
if(expander.vendor==V_CISCO && expander.asn32 &&
expander.generation<T_PREFIXLIST) {
sx_report(SX_FATAL,"Sorry, AS32-safety is not yet ready for Cisco\n");
};
*/
if(expander.vendor==V_CISCO_XR && expander.generation!=T_PREFIXLIST) {
sx_report(SX_FATAL, "Sorry, only prefix-sets supported for IOS XR\n");
};
if(expander.vendor==V_JSON && expander.generation!=T_PREFIXLIST) {
sx_report(SX_FATAL, "Sorry, only prefix-lists supported for JSON "
"output\n");
};
if(expander.asdot && expander.vendor!=V_CISCO) {
sx_report(SX_FATAL,"asdot notation supported only for Cisco, Juniper"
" uses asplain only\n");
};
if(!expander.asn32 && expander.asnumber>65535) {
expander.asnumber=23456;
};
if(aggregate && expander.vendor==V_JUNIPER &&
expander.generation==T_PREFIXLIST) {
sx_report(SX_FATAL, "Sorry, aggregation (-A) does not work in"
" Juniper prefix-lists\nYou can try route-filters (-E) instead"
" of prefix-lists (-P, default)\n");
exit(1);
};
if(aggregate && expander.generation<T_PREFIXLIST) {
sx_report(SX_FATAL, "Sorry, aggregation (-A) used only for prefix-"
"lists, extended access-lists and route-filters\n");
exit(1);
};
if(refine) {
if(expander.family==AF_INET6 && (refine>128)) {
sx_report(SX_FATAL, "Invalid value for refinement: %u (1-128 for"
" IPv6)\n", refine);
} else if(expander.family==AF_INET && refine>32) {
sx_report(SX_FATAL, "Invalid value for refinement: %u (1-32 for"
" IPv4)\n", refine);
};
if(expander.vendor==V_JUNIPER && expander.generation==T_PREFIXLIST) {
sx_report(SX_FATAL, "Sorry, more-specific filter (-R %u) "
"is not supported for Juniper prefix-lists\n", refine);
};
if(expander.generation<T_PREFIXLIST) {
sx_report(SX_FATAL, "Sorry, more-specific filter (-R %u) "
"supported only with prefix-list generation\n", refine);
};
};
if(maxlen) {
if((expander.family==AF_INET6 && maxlen>128) ||
(expander.family==AF_INET && maxlen>32)) {
sx_report(SX_FATAL, "Invalid value for max-prefixlen: %lu (1-128 "
"for IPv6, 1-32 for IPv4)\n", maxlen);
exit(1);
} else if((expander.family==AF_INET6 && maxlen<128) ||
(expander.family==AF_INET && maxlen<32)) {
/* inet6/128 and inet4/32 does not make sense - all routes will
* be accepted, so save some CPU cycles :) */
expander.maxlen = maxlen;
};
};
if(expander.generation==T_EACL && expander.vendor==V_CISCO &&
expander.family==AF_INET6) {
sx_report(SX_FATAL,"Sorry, ipv6 access-lists not supported for Cisco"
" yet.\n");
};
if(!argv[0]) usage(1);
while(argv[0]) {
if(!strncasecmp(argv[0],"AS-",3)) {
bgpq_expander_add_asset(&expander,argv[0]);
} else if(!strncasecmp(argv[0],"RS-",3)) {
bgpq_expander_add_rset(&expander,argv[0]);
} else if(!strncasecmp(argv[0],"AS",2)) {
char* c;
if((c=strchr(argv[0],':'))) {
if(!strncasecmp(c+1,"AS-",3)) {
bgpq_expander_add_asset(&expander,argv[0]);
} else if(!strncasecmp(c+1,"RS-",3)) {
bgpq_expander_add_rset(&expander,argv[0]);
} else {
SX_DEBUG(debug_expander,"Unknown sub-as object %s\n",
argv[0]);
};
} else {
bgpq_expander_add_as(&expander,argv[0]);
};
} else {
if(!bgpq_expander_add_prefix(&expander,argv[0]))
exit(1);
};
argv++;
argc--;
};
if(!bgpq_expand(&expander)) {
exit(1);
};
if(refine)
sx_radix_tree_refine(expander.tree,refine);
if(aggregate)
sx_radix_tree_aggregate(expander.tree);
switch(expander.generation) {
default :
case T_NONE: sx_report(SX_FATAL,"Unreachable point... call snar\n");
exit(1);
case T_ASPATH: bgpq3_print_aspath(stdout,&expander);
break;
case T_OASPATH: bgpq3_print_oaspath(stdout,&expander);
break;
case T_PREFIXLIST: bgpq3_print_prefixlist(stdout,&expander);
break;
case T_EACL: bgpq3_print_eacl(stdout,&expander);
break;
};
return 0;
};

69
bgpq3.h Normal file
View File

@@ -0,0 +1,69 @@
#ifndef BGPQ3_H_
#define BGPQ3_H_
#include "sx_prefix.h"
#include "sx_slentry.h"
typedef enum {
V_CISCO = 0,
V_JUNIPER,
V_CISCO_XR,
V_JSON
} bgpq_vendor_t;
typedef enum {
T_NONE = 0,
T_ASPATH,
T_OASPATH,
T_PREFIXLIST,
T_EACL
} bgpq_gen_t;
struct bgpq_prequest {
struct bgpq_prequest* next;
char request[128];
int (*callback)(char*, void*);
void *udata;
};
struct bgpq_expander {
struct sx_radix_tree* tree;
struct sx_slentry* macroses;
struct sx_slentry* rsets;
int family;
char* sources;
uint32_t asnumber;
int aswidth, asdot;
char* name;
bgpq_vendor_t vendor;
bgpq_gen_t generation;
int identify;
unsigned char asn32;
unsigned char* asn32s[65536];
struct bgpq_prequest* firstpipe, *lastpipe;
int piped;
char* match;
char* server;
unsigned maxlen;
};
int bgpq_expander_init(struct bgpq_expander* b, int af);
int bgpq_expander_add_asset(struct bgpq_expander* b, char* set);
int bgpq_expander_add_rset(struct bgpq_expander* b, char* set);
int bgpq_expander_add_as(struct bgpq_expander* b, char* as);
int bgpq_expander_add_prefix(struct bgpq_expander* b, char* prefix);
int bgpq_expand(struct bgpq_expander* b);
int bgpq3_print_prefixlist(FILE* f, struct bgpq_expander* b);
int bgpq3_print_eacl(FILE* f, struct bgpq_expander* b);
int bgpq3_print_aspath(FILE* f, struct bgpq_expander* b);
int bgpq3_print_oaspath(FILE* f, struct bgpq_expander* b);
#ifndef HAVE_STRLCPY
size_t strlcpy(char* dst, const char* src, size_t size);
#endif
#endif

292
bgpq3.html Normal file
View File

@@ -0,0 +1,292 @@
<p><!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.or
g/TR/html4/loose.dtd">
<html><head><style type='text/css'>
h1 { color: #3c78b5; border-bottom: 3px solid #3c78b5; font-size: 180%; }
h2 { color: #3c78b5; border-bottom: 2px solid #3c78b5; font-size: 140%; }
h3 { color: #3c78b5; border-bottom: 1px dotted #3c78b5; font-size: 129%; }
em { color: #0000FF; }
code { font-size:12px; background-color:#f8f8ff; border:1px; }
pre { border: 1px dotted #3c78b5; background-color: #f8f8ff; margin: 1em 1em;}
body { width: 80%; margin: 0 3em; }
ul { list-style: none; }
</style></head><body></p>
<h2>NAME</h2>
<p><code>bgpq3</code> - bgp filtering automation for Cisco and Juniper routers</p>
<h2>SYNOPSIS</h2>
<pre><code>bgpq3 [-h host] [-S sources] [-EP] [-f asn | -G asn] [-36ADdJjX] [-R len] [-m max] OBJECTS [...]
</code></pre>
<h2>DESCRIPTION</h2>
<p>The bgpq3 utility used to generate Cisco and Juniper prefix-lists,
extended access-lists, policy-statement terms and as-path lists based on
RADB data.</p>
<p>The options are as follows:</p>
<ul>
<li><p>-3 </p>
<blockquote>
<p>assume that your device is asn32-capable.</p>
</blockquote></li>
<li><p>-6 </p>
<blockquote>
<p>generate IPv6 prefix/access-lists (IPv4 by default).</p>
</blockquote></li>
<li><p>-A </p>
<blockquote>
<p>try to aggregate generated filters as much as possible (not all
output formats supported).</p>
</blockquote></li>
<li><p>-d </p>
<blockquote>
<p>enable some debugging output.</p>
</blockquote></li>
<li><p>-D </p>
<blockquote>
<p>use asdot notation for Cisco as-path access-lists.</p>
</blockquote></li>
<li><p>-E </p>
<blockquote>
<p>generate extended access-list (Cisco) or policy-statement term using
route-filters (Juniper).</p>
</blockquote></li>
<li><p>-f number</p>
<blockquote>
<p>generate input as-path access-list.</p>
</blockquote></li>
<li><p>-G number</p>
<blockquote>
<p>generate output as-path access-list.</p>
</blockquote></li>
<li><p>-h host</p>
<blockquote>
<p>host running IRRD database (default: whois.radb.net).</p>
</blockquote></li>
<li><p>-J </p>
<blockquote>
<p>generate config for Juniper (default: Cisco).</p>
</blockquote></li>
<li><p>-j </p>
<blockquote>
<p>generate output in JSON format (default: Cisco).</p>
</blockquote></li>
<li><p>-m len </p>
<blockquote>
<p>maximum length of accepted prefixes (default: 32 for IPv4, 128 for IPv6).</p>
</blockquote></li>
<li><p>-M match </p>
<blockquote>
<p>extra match conditions for Juniper route-filters.</p>
</blockquote></li>
<li><p>-l name </p>
<blockquote>
<p>name of generated entry.</p>
</blockquote></li>
<li><p>-P </p>
<blockquote>
<p>generate prefix-list (default behaviour, flag added for backward
compatibility only).</p>
</blockquote></li>
<li><p>-R len </p>
<blockquote>
<p>allow more-specific routes up to specified masklen too.
(Please, note: objects with prefix-length greater than specified length
will be always allowed.)</p>
</blockquote></li>
<li><p>-S sources </p>
<blockquote>
<p>use specified sources only (default: RADB,RIPE,APNIC).</p>
</blockquote></li>
<li><p>-T </p>
<blockquote>
<p>disable pipelining.</p>
</blockquote></li>
<li><p>-X </p>
<blockquote>
<p>generate config for Cisco IOS XR devices (plain IOS by default).</p>
</blockquote></li>
</ul>
<p><code>OBJECTS</code> means networks (in prefix format), autonomous systems and as-macros.</p>
<h2>EXAMPLES</h2>
<p>Generating named Juniper prefix-filter for <code>AS20597</code>:</p>
<pre><code> user@host:~&gt;bgpq3 -Jl eltel AS20597
policy-options {
replace:
prefix-list eltel {
81.9.0.0/20;
81.9.32.0/20;
81.9.96.0/20;
81.222.128.0/20;
81.222.192.0/18;
85.249.8.0/21;
85.249.224.0/19;
89.112.0.0/19;
89.112.4.0/22;
89.112.32.0/19;
89.112.64.0/19;
217.170.64.0/20;
217.170.80.0/20;
}
}
</code></pre>
<p>For Cisco we can use aggregation (-A) flag to make this prefix-filter
more compact:</p>
<pre><code> user@host:~&gt;bgpq3 -Al eltel AS20597
no ip prefix-list eltel
ip prefix-list eltel permit 81.9.0.0/20
ip prefix-list eltel permit 81.9.32.0/20
ip prefix-list eltel permit 81.9.96.0/20
ip prefix-list eltel permit 81.222.128.0/20
ip prefix-list eltel permit 81.222.192.0/18
ip prefix-list eltel permit 85.249.8.0/21
ip prefix-list eltel permit 85.249.224.0/19
ip prefix-list eltel permit 89.112.0.0/18 ge 19 le 19
ip prefix-list eltel permit 89.112.4.0/22
ip prefix-list eltel permit 89.112.64.0/19
ip prefix-list eltel permit 217.170.64.0/19 ge 20 le 20
</code></pre>
<p>and, as you see, prefixes <code>89.112.0.0/19</code> and <code>89.112.32.0/19</code> now aggregated
into single entry </p>
<pre><code>ip prefix-list eltel permit 89.112.0.0/18 ge 19 le 19.
</code></pre>
<p>Well, for Juniper we can generate even more interesting policy-statement,
using <code>-M &lt;extra match conditions&gt;</code>, <code>-R &lt;len&gt;</code> and hierarchical names:</p>
<pre><code> user@host:~&gt;bgpq3 -AJEl eltel/specifics -R 32 -M "community blackhole" AS20597
policy-options {
policy-statement eltel {
term specifics {
replace:
from {
community blackhole;
route-filter 81.9.0.0/20 upto /32;
route-filter 81.9.32.0/20 upto /32;
route-filter 81.9.96.0/20 upto /32;
route-filter 81.222.128.0/20 upto /32;
route-filter 81.222.192.0/18 upto /32;
route-filter 85.249.8.0/21 upto /32;
route-filter 85.249.224.0/19 upto /32;
route-filter 89.112.0.0/18 prefix-length-range /19-/32;
route-filter 89.112.64.0/19 upto /32;
route-filter 217.170.64.0/19 prefix-length-range /20-/32;
}
}
}
}
</code></pre>
<p>generated policy-option term now allows all more-specific routes
for eltel networks if they marked with community 'blackhole' (defined
elsewhere in configuration).</p>
<p>Of course, <code>bgpq3</code> supports IPv6 (-6):</p>
<pre><code> user@host:~&gt;bgpq3 -6l as-retn-6 AS-RETN6
no ipv6 prefix-list as-retn-6
ipv6 prefix-list as-retn-6 permit 2001:7fb:fe00::/48
ipv6 prefix-list as-retn-6 permit 2001:7fb:fe01::/48
[....]
</code></pre>
<p>and ASN32</p>
<pre><code> user@host:~&gt;bgpq3 -J3f 112 AS-SPACENET
policy-options {
replace:
as-path-group NN {
as-path a0 "^112(112)*$";
as-path a1 "^112(.)*(1898|5539|8495|8763|8878|12136|12931|15909)$";
as-path a2 "^112(.)*(21358|23456|23600|24151|25152|31529|34127|34906)$";
as-path a3 "^112(.)*(35052|41720|43628|44450|196611)$";
}
}
</code></pre>
<p>see <code>AS196611</code> in the end of the list ? That's <code>AS3.3</code> in 'asplain' notation.</p>
<p>If your router does not support ASN32 (yet) you should not use switch -3,
and the result will be next:</p>
<pre><code> user@host:~&gt;bgpq3 -f 112 AS-SPACENET
no ip as-path access-list NN
ip as-path access-list NN permit ^112( 112)*$
ip as-path access-list NN permit ^112( [0-9]+)* (1898|5539|8495|8763)$
ip as-path access-list NN permit ^112( [0-9]+)* (8878|12136|12931|15909)$
ip as-path access-list NN permit ^112( [0-9]+)* (21358|23456|23600|24151)$
ip as-path access-list NN permit ^112( [0-9]+)* (25152|31529|34127|34906)$
ip as-path access-list NN permit ^112( [0-9]+)* (35052|41720|43628|44450)$
</code></pre>
<p><code>AS196611</code> is no more in the list, however, <code>AS23456</code> (transition AS) would
have been added to list if it were not present.</p>
<h2>DIAGNOSTICS</h2>
<p>When everything is OK, <code>bgpq3</code> generates result to standard output and
exits with status == 0. In case of errors they are printed to stderr and
program exits with non-zero status.</p>
<h2>NOTES ON ULTRA-LARGE PREFIX-LISTS</h2>
<p>When using <code>bgpq3</code> to expand extra-large AS-SETs, <code>bgpq3</code> may stuck
due to lacking tcp buffer size. To avoid this, tune your OS.
FreeBSD can be tuned in the following way:</p>
<pre><code>sysctl -w net.inet.tcp.sendbuf_max=16777216
</code></pre>
<p>Please note that generated prefix-lists may not fit your router's
limitations. For example, JunOS supports only 85,325 prefixes in
each prefix-list <a href="http://www.juniper.net/techpubs/en_US/junos11.4/topics/reference/configuration-statement/prefix-list-edit-policy-options.html">4</a>. </p>
<h2>SEE ALSO</h2>
<ol>
<li><a href="http://www.radb.net/">Routing Arbiter</a></li>
<li><a href="http://www.ietf.org/internet-drafts/draft-michaelson-4byte-as-representation-05.txt">draft-michaelson-4byte-as-representation-05.txt</a>
for information on 'asdot' and 'asplain' notations.</li>
<li><a href="http://www.cisco.com/en/US/prod/collateral/iosswrel/ps6537/ps6554/ps6599/data_sheet_C78-521821.html">Cisco documentation</a>
for information on Cisco implementation of ASN32.</li>
<li><a href="http://www.juniper.net/techpubs/en_US/junos11.4/topics/reference/configuration-statement/prefix-list-edit-policy-options.html">JunOS prefix-lists limitation</a></li>
</ol>
<h2>AUTHOR</h2>
<p>Alexandre Snarskii <a href="mailto:snar@snar.spb.ru">snar@snar.spb.ru</a></p>
<h2>Program Homepage</h2>
<p><a href="http://snar.spb.ru/prog/bgpq3/">http://snar.spb.ru/prog/bgpq3/</a></p>

48
bgpq3.spec Normal file
View File

@@ -0,0 +1,48 @@
Name: bgpq3
Version: 0.1.19
Release: 0%{?dist}
Group: System/Utilities
Summary: Automate BGP filter generation based on routing database information
URL: http://snar.spb.ru/prog/bgpq3/
License: BSD
Source0: http://snar.spb.ru/prog/bgpq3/bgpq3-0.1.19.tgz
BuildRoot: %{_tmppath}/%{name}-%{version}-%{release}-root-%(%{__id_u} -n)
%description
You are running BGP in your network and want to automate filter generation for your routers? Well, with BGPQ3 it's easy.
%prep
%setup -q
%build
./configure --prefix=$RPM_BUILD_ROOT%{_prefix} --mandir=%{_mandir}
make
%install
rm -rf $RPM_BUILD_ROOT
mkdir -p $RPM_BUILD_ROOT/usr/bin
make install
%clean
rm -rf $RPM_BUILD_ROOT
%files
%defattr(-,root,root,-)
/usr/bin/bgpq3
/usr/man/man8/bgpq3.8.gz
%doc COPYRIGHT CHANGES
%changelog
* Thu May 9 2013 Alexandre Snarskii <snar@snar.spb.ru> 0.1.19-0.snar
- Version updated
* Sun Feb 24 2013 Alexandre Snarskii <snar@snar.spb.ru> 0.1.18-3.snar
- License corrected
* Wed Feb 20 2013 Arnoud Vermeer <arnoud@tumblr.com> 0.1.18-2.tumblr
- Adding missing group info (arnoud@tumblr.com)
* Wed Feb 20 2013 Arnoud Vermeer <arnoud@tumblr.com> 0.1.18-1.tumblr
- new package built with tito

270
bgpq3.txt Normal file
View File

@@ -0,0 +1,270 @@
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.or
g/TR/html4/loose.dtd">
<html><head><style type='text/css'>
h1 { color: #3c78b5; border-bottom: 3px solid #3c78b5; font-size: 180%; }
h2 { color: #3c78b5; border-bottom: 2px solid #3c78b5; font-size: 140%; }
h3 { color: #3c78b5; border-bottom: 1px dotted #3c78b5; font-size: 129%; }
em { color: #0000FF; }
code { font-size:12px; background-color:#f8f8ff; border:1px; }
pre { border: 1px dotted #3c78b5; background-color: #f8f8ff; margin: 1em 1em;}
body { width: 80%; margin: 0 3em; }
ul { list-style: none; }
</style></head><body>
NAME
----
`bgpq3` - bgp filtering automation for Cisco and Juniper routers
SYNOPSIS
--------
bgpq3 [-h host] [-S sources] [-EP] [-f asn | -G asn] [-36ADdJjX] [-R len] [-m max] OBJECTS [...]
DESCRIPTION
-----------
The bgpq3 utility used to generate Cisco and Juniper prefix-lists,
extended access-lists, policy-statement terms and as-path lists based on
RADB data.
The options are as follows:
- -3
> assume that your device is asn32-capable.
- -6
> generate IPv6 prefix/access-lists (IPv4 by default).
- -A
> try to aggregate generated filters as much as possible (not all
output formats supported).
- -d
> enable some debugging output.
- -D
> use asdot notation for Cisco as-path access-lists.
- -E
> generate extended access-list (Cisco) or policy-statement term using
route-filters (Juniper).
- -f number
> generate input as-path access-list.
- -G number
> generate output as-path access-list.
- -h host
> host running IRRD database (default: whois.radb.net).
- -J
> generate config for Juniper (default: Cisco).
- -j
> generate output in JSON format (default: Cisco).
- -m len
> maximum length of accepted prefixes (default: 32 for IPv4, 128 for IPv6).
- -M match
> extra match conditions for Juniper route-filters.
- -l name
> name of generated entry.
- -P
> generate prefix-list (default behaviour, flag added for backward
compatibility only).
- -R len
> allow more-specific routes up to specified masklen too.
(Please, note: objects with prefix-length greater than specified length
will be always allowed.)
- -S sources
> use specified sources only (default: RADB,RIPE,APNIC).
- -T
> disable pipelining.
- -X
> generate config for Cisco IOS XR devices (plain IOS by default).
`OBJECTS` means networks (in prefix format), autonomous systems and as-macros.
EXAMPLES
--------
Generating named Juniper prefix-filter for `AS20597`:
user@host:~>bgpq3 -Jl eltel AS20597
policy-options {
replace:
prefix-list eltel {
81.9.0.0/20;
81.9.32.0/20;
81.9.96.0/20;
81.222.128.0/20;
81.222.192.0/18;
85.249.8.0/21;
85.249.224.0/19;
89.112.0.0/19;
89.112.4.0/22;
89.112.32.0/19;
89.112.64.0/19;
217.170.64.0/20;
217.170.80.0/20;
}
}
For Cisco we can use aggregation (-A) flag to make this prefix-filter
more compact:
user@host:~>bgpq3 -Al eltel AS20597
no ip prefix-list eltel
ip prefix-list eltel permit 81.9.0.0/20
ip prefix-list eltel permit 81.9.32.0/20
ip prefix-list eltel permit 81.9.96.0/20
ip prefix-list eltel permit 81.222.128.0/20
ip prefix-list eltel permit 81.222.192.0/18
ip prefix-list eltel permit 85.249.8.0/21
ip prefix-list eltel permit 85.249.224.0/19
ip prefix-list eltel permit 89.112.0.0/18 ge 19 le 19
ip prefix-list eltel permit 89.112.4.0/22
ip prefix-list eltel permit 89.112.64.0/19
ip prefix-list eltel permit 217.170.64.0/19 ge 20 le 20
and, as you see, prefixes `89.112.0.0/19` and `89.112.32.0/19` now aggregated
into single entry
ip prefix-list eltel permit 89.112.0.0/18 ge 19 le 19.
Well, for Juniper we can generate even more interesting policy-statement,
using `-M <extra match conditions>`, `-R <len>` and hierarchical names:
user@host:~>bgpq3 -AJEl eltel/specifics -R 32 -M "community blackhole" AS20597
policy-options {
policy-statement eltel {
term specifics {
replace:
from {
community blackhole;
route-filter 81.9.0.0/20 upto /32;
route-filter 81.9.32.0/20 upto /32;
route-filter 81.9.96.0/20 upto /32;
route-filter 81.222.128.0/20 upto /32;
route-filter 81.222.192.0/18 upto /32;
route-filter 85.249.8.0/21 upto /32;
route-filter 85.249.224.0/19 upto /32;
route-filter 89.112.0.0/18 prefix-length-range /19-/32;
route-filter 89.112.64.0/19 upto /32;
route-filter 217.170.64.0/19 prefix-length-range /20-/32;
}
}
}
}
generated policy-option term now allows all more-specific routes
for eltel networks if they marked with community 'blackhole' (defined
elsewhere in configuration).
Of course, `bgpq3` supports IPv6 (-6):
user@host:~>bgpq3 -6l as-retn-6 AS-RETN6
no ipv6 prefix-list as-retn-6
ipv6 prefix-list as-retn-6 permit 2001:7fb:fe00::/48
ipv6 prefix-list as-retn-6 permit 2001:7fb:fe01::/48
[....]
and ASN32
user@host:~>bgpq3 -J3f 112 AS-SPACENET
policy-options {
replace:
as-path-group NN {
as-path a0 "^112(112)*$";
as-path a1 "^112(.)*(1898|5539|8495|8763|8878|12136|12931|15909)$";
as-path a2 "^112(.)*(21358|23456|23600|24151|25152|31529|34127|34906)$";
as-path a3 "^112(.)*(35052|41720|43628|44450|196611)$";
}
}
see `AS196611` in the end of the list ? That's `AS3.3` in 'asplain' notation.
If your router does not support ASN32 (yet) you should not use switch -3,
and the result will be next:
user@host:~>bgpq3 -f 112 AS-SPACENET
no ip as-path access-list NN
ip as-path access-list NN permit ^112( 112)*$
ip as-path access-list NN permit ^112( [0-9]+)* (1898|5539|8495|8763)$
ip as-path access-list NN permit ^112( [0-9]+)* (8878|12136|12931|15909)$
ip as-path access-list NN permit ^112( [0-9]+)* (21358|23456|23600|24151)$
ip as-path access-list NN permit ^112( [0-9]+)* (25152|31529|34127|34906)$
ip as-path access-list NN permit ^112( [0-9]+)* (35052|41720|43628|44450)$
`AS196611` is no more in the list, however, `AS23456` (transition AS) would
have been added to list if it were not present.
DIAGNOSTICS
-----------
When everything is OK, `bgpq3` generates result to standard output and
exits with status == 0. In case of errors they are printed to stderr and
program exits with non-zero status.
NOTES ON ULTRA-LARGE PREFIX-LISTS
---------------------------------
When using `bgpq3` to expand extra-large AS-SETs, `bgpq3` may stuck
due to lacking tcp buffer size. To avoid this, tune your OS.
FreeBSD can be tuned in the following way:
sysctl -w net.inet.tcp.sendbuf_max=16777216
Please note that generated prefix-lists may not fit your router's
limitations. For example, JunOS supports only 85,325 prefixes in
each prefix-list [4](http://www.juniper.net/techpubs/en_US/junos11.4/topics/reference/configuration-statement/prefix-list-edit-policy-options.html).
SEE ALSO
--------
1. [Routing Arbiter](http://www.radb.net/)
2. [draft-michaelson-4byte-as-representation-05.txt](http://www.ietf.org/internet-drafts/draft-michaelson-4byte-as-representation-05.txt)
for information on 'asdot' and 'asplain' notations.
3. [Cisco documentation](http://www.cisco.com/en/US/prod/collateral/iosswrel/ps6537/ps6554/ps6599/data_sheet_C78-521821.html)
for information on Cisco implementation of ASN32.
4. [JunOS prefix-lists limitation](http://www.juniper.net/techpubs/en_US/junos11.4/topics/reference/configuration-statement/prefix-list-edit-policy-options.html)
AUTHOR
------
Alexandre Snarskii [snar@snar.spb.ru](mailto:snar@snar.spb.ru)
Program Homepage
----------------
[http://snar.spb.ru/prog/bgpq3/](http://snar.spb.ru/prog/bgpq3/)

514
bgpq3_printer.c Normal file
View File

@@ -0,0 +1,514 @@
#if HAVE_CONFIG_H
#include <config.h>
#endif
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <stdio.h>
#include <string.h>
#include <strings.h>
#include "bgpq3.h"
#include "sx_report.h"
int
bgpq3_print_cisco_aspath(FILE* f, struct bgpq_expander* b)
{
int nc=0, i, j, k;
fprintf(f,"no ip as-path access-list %s\n", b->name?b->name:"NN");
if(b->asn32s[b->asnumber/65536] &&
b->asn32s[b->asnumber/65536][(b->asnumber%65536)/8]&
(0x80>>(b->asnumber%8))) {
if(b->asdot && b->asnumber>65535) {
fprintf(f,"ip as-path access-list %s permit ^%i.%i(_%i.%i)*$\n",
b->name?b->name:"NN",b->asnumber/65536,b->asnumber%65536,
b->asnumber/65536,b->asnumber%65536);
} else {
fprintf(f,"ip as-path access-list %s permit ^%i(_%i)*$\n",
b->name?b->name:"NN",b->asnumber,b->asnumber);
};
};
for(k=0;k<65536;k++) {
if(!b->asn32s[k]) continue;
for(i=0;i<8192;i++) {
for(j=0;j<8;j++) {
if(b->asn32s[k][i]&(0x80>>j)) {
if(k*65536+i*8+j==b->asnumber) continue;
if(!nc) {
if(b->asdot && k>0) {
fprintf(f,"ip as-path access-list %s permit"
" ^%i(_[0-9]+)*_(%i.%i", b->name?b->name:"NN",
b->asnumber,k,i*8+j);
} else {
fprintf(f,"ip as-path access-list %s permit"
" ^%i(_[0-9]+)*_(%i", b->name?b->name:"NN",
b->asnumber,k*65536+i*8+j);
};
} else {
if(b->asdot && k>0) {
fprintf(f,"|%i.%i",k,i*8+j);
} else {
fprintf(f,"|%i",k*65536+i*8+j);
};
}
nc++;
if(nc==b->aswidth) {
fprintf(f,")$\n");
nc=0;
};
};
};
};
};
if(nc) fprintf(f,")$\n");
return 0;
};
int
bgpq3_print_cisco_oaspath(FILE* f, struct bgpq_expander* b)
{
int nc=0, i, j, k;
fprintf(f,"no ip as-path access-list %s\n", b->name?b->name:"NN");
if(b->asn32s[b->asnumber/65536] &&
b->asn32s[b->asnumber/65536][(b->asnumber%65536)/8]&
(0x80>>(b->asnumber%8))) {
if(b->asdot && b->asnumber>65535) {
fprintf(f,"ip as-path access-list %s permit ^(_%i.%i)*$\n",
b->name?b->name:"NN",b->asnumber/65536,b->asnumber%65536);
} else {
fprintf(f,"ip as-path access-list %s permit ^(_%i)*$\n",
b->name?b->name:"NN",b->asnumber);
};
};
for(k=0;k<65536;k++) {
if(!b->asn32s[k]) continue;
for(i=0;i<8192;i++) {
for(j=0;j<8;j++) {
if(b->asn32s[k][i]&(0x80>>j)) {
if(k*65536+i*8+j==b->asnumber) continue;
if(!nc) {
if(b->asdot && k>0) {
fprintf(f,"ip as-path access-list %s permit"
" ^(_[0-9]+)*_(%i.%i", b->name?b->name:"NN",
k,i*8+j);
} else {
fprintf(f,"ip as-path access-list %s permit"
" ^(_[0-9]+)*_(%i", b->name?b->name:"NN",
k*65536+i*8+j);
};
} else {
if(b->asdot && k>0) {
fprintf(f,"|%i.%i",k,i*8+j);
} else {
fprintf(f,"|%i",k*65536+i*8+j);
};
}
nc++;
if(nc==b->aswidth) {
fprintf(f,")$\n");
nc=0;
};
};
};
};
};
if(nc) fprintf(f,")$\n");
return 0;
};
int
bgpq3_print_juniper_aspath(FILE* f, struct bgpq_expander* b)
{
int nc=0, lineNo=0, i, j, k;
fprintf(f,"policy-options {\nreplace:\n as-path-group %s {\n",
b->name?b->name:"NN");
if(b->asn32s[b->asnumber/65536] &&
b->asn32s[b->asnumber/65535][(b->asnumber%65536)/8]&
(0x80>>(b->asnumber%8))) {
fprintf(f," as-path a%i \"^%u(%u)*$\";\n", lineNo, b->asnumber,
b->asnumber);
lineNo++;
};
for(k=0;k<65536;k++) {
if(!b->asn32s[k]) continue;
for(i=0;i<8192;i++) {
for(j=0;j<8;j++) {
if(b->asn32s[k][i]&(0x80>>j)) {
if(k*65536+i*8+j==b->asnumber) continue;
if(!nc) {
fprintf(f," as-path a%i \"^%u(.)*(%u",
lineNo,b->asnumber,k*65536+i*8+j);
} else {
fprintf(f,"|%u",k*65536+i*8+j);
};
nc++;
if(nc==b->aswidth) {
fprintf(f,")$\";\n");
nc=0;
lineNo++;
};
};
};
};
};
if(nc) fprintf(f,")$\";\n");
fprintf(f," }\n}\n");
return 0;
};
int
bgpq3_print_juniper_oaspath(FILE* f, struct bgpq_expander* b)
{
int nc=0, lineNo=0, i, j, k;
fprintf(f,"policy-options {\nreplace:\n as-path-group %s {\n",
b->name?b->name:"NN");
if(b->asn32s[b->asnumber/65536] &&
b->asn32s[b->asnumber/65536][(b->asnumber%65536)/8]&
(0x80>>(b->asnumber%8))) {
fprintf(f," as-path a%i \"^%u(%u)*$\";\n", lineNo, b->asnumber,
b->asnumber);
lineNo++;
};
for(k=0;k<65536;k++) {
if(!b->asn32s[k]) continue;
for(i=0;i<8192;i++) {
for(j=0;j<8;j++) {
if(b->asn32s[k][i]&(0x80>>j)) {
if(k*65536+i*8+j==b->asnumber) continue;
if(!nc) {
fprintf(f," as-path a%i \"^(.)*(%u",
lineNo,k*65536+i*8+j);
} else {
fprintf(f,"|%u",k*65536+i*8+j);
}
nc++;
if(nc==b->aswidth) {
fprintf(f,")$\";\n");
nc=0;
lineNo++;
};
};
};
};
};
if(nc) fprintf(f,")$\";\n");
fprintf(f," }\n}\n");
return 0;
};
int
bgpq3_print_aspath(FILE* f, struct bgpq_expander* b)
{
if(b->vendor==V_JUNIPER) {
return bgpq3_print_juniper_aspath(f,b);
} else if(b->vendor==V_CISCO) {
return bgpq3_print_cisco_aspath(f,b);
} else {
sx_report(SX_FATAL,"Unknown vendor %i\n", b->vendor);
};
return 0;
};
int
bgpq3_print_oaspath(FILE* f, struct bgpq_expander* b)
{
if(b->vendor==V_JUNIPER) {
return bgpq3_print_juniper_oaspath(f,b);
} else if(b->vendor==V_CISCO) {
return bgpq3_print_cisco_oaspath(f,b);
} else {
sx_report(SX_FATAL,"Unknown vendor %i\n", b->vendor);
};
return 0;
};
void
bgpq3_print_jprefix(struct sx_radix_node* n, void* ff)
{
char prefix[128];
FILE* f=(FILE*)ff;
if(n->isGlue) return;
if(!f) f=stdout;
sx_prefix_snprintf(&n->prefix,prefix,sizeof(prefix));
fprintf(f," %s;\n",prefix);
};
static int needscomma=0;
void
bgpq3_print_json_prefix(struct sx_radix_node* n, void* ff)
{
char prefix[128];
FILE* f=(FILE*)ff;
if(n->isGlue)
goto checkSon;
if(!f)
f=stdout;
sx_prefix_jsnprintf(&n->prefix, prefix, sizeof(prefix));
if (!n->isAggregate) {
fprintf(f, "%s\n { \"prefix\": \"%s\", \"exact\": true }",
needscomma?",":"", prefix);
} else if (n->aggregateLow > n->prefix.masklen) {
fprintf(f, "%s\n { \"prefix\": \"%s\", \"exact\": false,\n "
"\"greater-equal\": %u, \"less-equal\": %u }",
needscomma?",":"", prefix,
n->aggregateLow, n->aggregateHi);
} else {
fprintf(f, "%s\n { \"prefix\": \"%s\", \"exact\": false, "
"\"less-equal\": %u }",
needscomma?",":"", prefix, n->aggregateHi);
};
needscomma=1;
checkSon:
if(n->son)
bgpq3_print_json_prefix(n->son, ff);
};
void
bgpq3_print_jrfilter(struct sx_radix_node* n, void* ff)
{
char prefix[128];
FILE* f=(FILE*)ff;
if(n->isGlue) goto checkSon;
if(!f) f=stdout;
sx_prefix_snprintf(&n->prefix,prefix,sizeof(prefix));
if(!n->isAggregate) {
fprintf(f," route-filter %s exact;\n", prefix);
} else {
if(n->aggregateLow>n->prefix.masklen) {
fprintf(f," route-filter %s prefix-length-range /%u-/%u;\n",
prefix,n->aggregateLow,n->aggregateHi);
} else {
fprintf(f," route-filter %s upto /%u;\n", prefix,n->aggregateHi);
};
};
checkSon:
if(n->son)
bgpq3_print_jrfilter(n->son, ff);
};
static char* bname=NULL;
void
bgpq3_print_cprefix(struct sx_radix_node* n, void* ff)
{
char prefix[128];
FILE* f=(FILE*)ff;
if(!f) f=stdout;
if(n->isGlue) goto checkSon;
sx_prefix_snprintf(&n->prefix,prefix,sizeof(prefix));
if(n->isAggregate) {
if(n->aggregateLow>n->prefix.masklen) {
fprintf(f,"%s prefix-list %s permit %s ge %u le %u\n",
n->prefix.family==AF_INET?"ip":"ipv6",bname?bname:"NN",prefix,
n->aggregateLow,n->aggregateHi);
} else {
fprintf(f,"%s prefix-list %s permit %s le %u\n",
n->prefix.family==AF_INET?"ip":"ipv6",bname?bname:"NN",prefix,
n->aggregateHi);
};
} else {
fprintf(f,"%s prefix-list %s permit %s\n",
(n->prefix.family==AF_INET)?"ip":"ipv6",bname?bname:"NN",prefix);
};
checkSon:
if(n->son)
bgpq3_print_cprefix(n->son,ff);
};
void
bgpq3_print_cprefixxr(struct sx_radix_node* n, void* ff)
{
char prefix[128];
FILE* f=(FILE*)ff;
if(!f) f=stdout;
if(n->isGlue) goto checkSon;
sx_prefix_snprintf(&n->prefix,prefix,sizeof(prefix));
if(n->isAggregate) {
if(n->aggregateLow>n->prefix.masklen) {
fprintf(f,"%s%s ge %u le %u",
needscomma?",\n ":" ", prefix, n->aggregateLow,n->aggregateHi);
} else {
fprintf(f,"%s%s le %u", needscomma?",\n ":" ", prefix,
n->aggregateHi);
};
} else {
fprintf(f,"%s%s", needscomma?",\n ":" ", prefix);
};
needscomma=1;
checkSon:
if(n->son)
bgpq3_print_cprefixxr(n->son,ff);
};
void
bgpq3_print_ceacl(struct sx_radix_node* n, void* ff)
{
char prefix[128];
FILE* f=(FILE*)ff;
char* c;
uint32_t netmask=0xfffffffful;
if(!f) f=stdout;
if(n->isGlue) goto checkSon;
sx_prefix_snprintf(&n->prefix,prefix,sizeof(prefix));
c=strchr(prefix,'/');
if(c) *c=0;
if(n->prefix.masklen==32) {
netmask=0;
} else {
netmask<<=(32-n->prefix.masklen);
netmask&=0xfffffffful;
};
netmask=htonl(netmask);
if(n->isAggregate) {
unsigned long mask=0xfffffffful, wildaddr, wild2addr, wildmask;
int masklen=n->aggregateLow;
wildaddr=0xfffffffful>>n->prefix.masklen;
if(n->aggregateHi==32) {
wild2addr=0;
} else {
wild2addr=0xfffffffful>>n->aggregateHi;
};
wildaddr=wildaddr&(~wild2addr);
if(masklen==32) mask=0xfffffffful;
else mask=0xfffffffful & (0xfffffffful<<(32-masklen));
if(n->aggregateHi==32) wild2addr=0;
else wild2addr=0xfffffffful>>n->aggregateHi;
wildmask=(0xfffffffful>>n->aggregateLow)&(~wild2addr);
mask=htonl(mask);
wildaddr=htonl(wildaddr);
wildmask=htonl(wildmask);
if(wildaddr) {
fprintf(f," permit ip %s ", inet_ntoa(n->prefix.addr.addr));
fprintf(f,"%s ", inet_ntoa(*(struct in_addr*)&wildaddr));
} else {
fprintf(f," permit ip host %s ",inet_ntoa(n->prefix.addr.addr));
};
if(wildmask) {
fprintf(f,"%s ", inet_ntoa(*(struct in_addr*)&mask));
fprintf(f,"%s\n", inet_ntoa(*(struct in_addr*)&wildmask));
} else {
fprintf(f,"host %s\n", inet_ntoa(*(struct in_addr*)&mask));
};
} else {
fprintf(f," permit ip host %s host %s\n",prefix,
inet_ntoa(*(struct in_addr*)&netmask));
};
checkSon:
if(n->son)
bgpq3_print_ceacl(n->son,ff);
};
int
bgpq3_print_juniper_prefixlist(FILE* f, struct bgpq_expander* b)
{
fprintf(f,"policy-options {\nreplace:\n prefix-list %s {\n",
b->name?b->name:"NN");
sx_radix_tree_foreach(b->tree,bgpq3_print_jprefix,f);
fprintf(f," }\n}\n");
return 0;
};
int
bgpq3_print_juniper_routefilter(FILE* f, struct bgpq_expander* b)
{
char* c;
if(b->name && (c=strchr(b->name,'/'))) {
*c=0;
fprintf(f,"policy-options {\n policy-statement %s {\n term %s {\n"
"replace:\n from {\n", b->name, c+1);
if(b->match)
fprintf(f," %s;\n",b->match);
} else {
fprintf(f,"policy-options {\n policy-statement %s { \n"
"replace:\n from {\n", b->name?b->name:"NN");
if(b->match)
fprintf(f," %s;\n",b->match);
};
sx_radix_tree_foreach(b->tree,bgpq3_print_jrfilter,f);
if(c) {
fprintf(f, " }\n }\n }\n}\n");
} else {
fprintf(f, " }\n }\n}\n");
};
return 0;
};
int
bgpq3_print_cisco_prefixlist(FILE* f, struct bgpq_expander* b)
{
bname=b->name;
fprintf(f,"no %s prefix-list %s\n",
(b->family==AF_INET)?"ip":"ipv6",bname?bname:"NN");
sx_radix_tree_foreach(b->tree,bgpq3_print_cprefix,f);
return 0;
};
int
bgpq3_print_ciscoxr_prefixlist(FILE* f, struct bgpq_expander* b)
{
bname=b->name;
fprintf(f,"no prefix-set %s\nprefix-set %s\n", bname?bname:"NN",
bname?bname:"NN");
sx_radix_tree_foreach(b->tree,bgpq3_print_cprefixxr,f);
fprintf(f, "\nend-set\n");
return 0;
};
int
bgpq3_print_json_prefixlist(FILE* f, struct bgpq_expander* b)
{
fprintf(f,"{ \"%s\": [",
b->name?b->name:"NN");
sx_radix_tree_foreach(b->tree,bgpq3_print_json_prefix,f);
fprintf(f,"\n] }\n");
return 0;
};
int
bgpq3_print_cisco_eacl(FILE* f, struct bgpq_expander* b)
{
bname=b->name;
fprintf(f,"no ip access-list extended %s\n", bname?bname:"NN");
fprintf(f,"ip access-list extended %s\n", bname?bname:"NN");
sx_radix_tree_foreach(b->tree,bgpq3_print_ceacl,f);
return 0;
};
int
bgpq3_print_prefixlist(FILE* f, struct bgpq_expander* b)
{
switch(b->vendor) {
case V_JUNIPER: return bgpq3_print_juniper_prefixlist(f,b);
case V_CISCO: return bgpq3_print_cisco_prefixlist(f,b);
case V_CISCO_XR: return bgpq3_print_ciscoxr_prefixlist(f,b);
case V_JSON: return bgpq3_print_json_prefixlist(f,b);
};
return 0;
};
int
bgpq3_print_eacl(FILE* f, struct bgpq_expander* b)
{
switch(b->vendor) {
case V_JUNIPER: return bgpq3_print_juniper_routefilter(f,b);
case V_CISCO: return bgpq3_print_cisco_eacl(f,b);
case V_CISCO_XR: sx_report(SX_FATAL, "unreachable point\n");
case V_JSON: sx_report(SX_FATAL, "unreachable point\n");
};
return 0;
};

701
bgpq_expander.c Normal file
View File

@@ -0,0 +1,701 @@
#if HAVE_CONFIG_H
#include "config.h"
#endif
#include <sys/types.h>
#include <sys/socket.h>
#include <ctype.h>
#include <errno.h>
#include <netdb.h>
#include <string.h>
#include <strings.h>
#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include "bgpq3.h"
#include "sx_report.h"
#include "sx_maxsockbuf.h"
int debug_expander=0;
int pipelining=1;
int
bgpq_expander_init(struct bgpq_expander* b, int af)
{
if(!af) af=AF_INET;
if(!b) return 0;
memset(b,0,sizeof(struct bgpq_expander));
b->tree=sx_radix_tree_new(af);
if(!b->tree) goto fixups;
b->family=af;
b->sources="ripe,radb,apnic";
b->name="NN";
b->aswidth=8;
b->asn32s[0]=malloc(8192);
if(!b->asn32s[0]) {
sx_report(SX_FATAL,"Unable to allocate 8192 bytes: %s\n",
strerror(errno));
exit(1);
};
memset(b->asn32s[0],0,8192);
b->identify=1;
b->server="whois.radb.net";
return 1;
fixups:
/* if(b->tree) XXXXXXXXXXXXX sx_radix_tree_destroy(b->tree); */
b->tree=NULL;
free(b);
return 0;
};
int
bgpq_expander_add_asset(struct bgpq_expander* b, char* as)
{
struct sx_slentry* le;
if(!b || !as) return 0;
le=sx_slentry_new(as);
if(!le) return 0;
if(!b->macroses) {
b->macroses=le;
} else {
struct sx_slentry* ln=b->macroses;
while(ln->next) ln=ln->next;
ln->next=le;
};
return 1;
};
int
bgpq_expander_add_rset(struct bgpq_expander* b, char* rs)
{
struct sx_slentry* le;
if(!b || !rs) return 0;
le=sx_slentry_new(rs);
if(!le) return 0;
if(!b->rsets) {
b->rsets=le;
} else {
struct sx_slentry* ln=b->rsets;
while(ln->next) ln=ln->next;
ln->next=le;
};
return 1;
};
int
bgpq_expander_add_as(struct bgpq_expander* b, char* as)
{
char* eoa;
uint32_t asno;
if(!b || !as) return 0;
asno=strtoul(as+2,&eoa,10);
if(eoa && (*eoa!='.' && *eoa!=0)) {
sx_report(SX_ERROR,"Invalid symbol in AS number: '%c' in %s\n",
*eoa, as);
return 0;
};
if(*eoa=='.' || asno>65535) {
if(b->asn32 || b->generation>=T_PREFIXLIST) {
uint32_t asn1;
if(asno>65535) {
asn1=asno%65536;
asno/=65536;
} else
asn1=strtoul(eoa+1,&eoa,10);
if(eoa && *eoa!=0) {
sx_report(SX_ERROR,"Invalid symbol in AS number: '%c' in %s\n",
*eoa, as);
return 0;
};
if(asn1>65535) {
sx_report(SX_ERROR,"Invalid AS number in %s\n", as);
return 0;
};
if(!b->asn32s[asno]) {
b->asn32s[asno]=malloc(8192);
if(!b->asn32s[asno]) {
sx_report(SX_FATAL, "Unable to allocate 8192 bytes: %s."
" Unable to add asn32 %s to future expansion\n",
strerror(errno), as);
return 0;
};
memset(b->asn32s[asno],0,8192);
};
b->asn32s[asno][asn1/8]|=(0x80>>(asn1%8));
} else if(!b->asn32) {
b->asn32s[0][23456/8]|=(0x80>>(23456%8));
};
return 0;
};
if(asno<1 || asno>65535) {
sx_report(SX_ERROR,"Invalid AS number in %s\n", as);
return 0;
};
b->asn32s[0][asno/8]|=(0x80>>(asno%8));
return 1;
};
int
bgpq_expander_add_prefix(struct bgpq_expander* b, char* prefix)
{
struct sx_prefix p;
if(!sx_prefix_parse(&p,0,prefix)) {
sx_report(SX_ERROR,"Unable to parse prefix %s\n", prefix);
return 0;
} else if(p.family!=b->family) {
SX_DEBUG(debug_expander,"Ignoring prefix %s with wrong address family\n"
,prefix);
return 0;
};
if(b->maxlen && p.masklen>b->maxlen) {
SX_DEBUG(debug_expander, "Ignoring prefix %s: masklen %i > max "
"masklen %u\n", prefix, p.masklen, b->maxlen);
return 0;
};
sx_radix_tree_insert(b->tree,&p);
return 1;
};
int
bgpq_expanded_macro(char* as, void* udata)
{
struct bgpq_expander* ex=(struct bgpq_expander*)udata;
if(!ex) return 0;
bgpq_expander_add_as(ex,as);
return 1;
};
int
bgpq_expanded_prefix(char* as, void* udata)
{
struct bgpq_expander* ex=(struct bgpq_expander*)udata;
if(!ex) return 0;
bgpq_expander_add_prefix(ex,as);
return 1;
};
int
bgpq_expanded_v6prefix(char* prefix, void* udata)
{
struct bgpq_expander* ex=(struct bgpq_expander*)udata;
if(!ex) return 0;
bgpq_expander_add_prefix(ex,prefix);
return 1;
};
int
bgpq_pipeline_dequeue_ripe(FILE* f, struct bgpq_expander* b)
{
int sawNL=0;
char buffer[128];
char* otype=NULL, *object=NULL;
if(!f || !b) {
sx_report(SX_FATAL, "Invalid arguments\n");
exit(1);
};
if(!b->firstpipe) {
sx_report(SX_FATAL, "No piped requests\n");
exit(1);
};
while(fgets(buffer,sizeof(buffer),f)) {
if(buffer[0]=='\n') {
if(b->family==AF_INET && otype && !strcmp(otype,"route")) {
SX_DEBUG(debug_expander,"dequeuer(ripe): got route %s\n",
object);
if(b->firstpipe->callback)
b->firstpipe->callback(object,b->firstpipe->udata);
} else if(b->family==AF_INET6 && otype && !strcmp(otype,"route6")){
SX_DEBUG(debug_expander,"dequeuer(ripe): got route6 %s\n",
object);
if(b->firstpipe->callback)
b->firstpipe->callback(object,b->firstpipe->udata);
};
if(otype) free(otype); otype=NULL;
if(object) free(object); object=NULL;
sawNL++;
if(sawNL==2) {
/* end of object */
struct bgpq_prequest* p=b->firstpipe;
b->firstpipe=b->firstpipe->next;
free(p);
b->piped--;
if(!b->piped) {
return 0;
};
};
} else {
sawNL=0;
if(!otype) {
char* c=strchr(buffer,':');
if(c) {
*c=0;
otype=strdup(buffer);
c++;
while(isspace((int)*c)) c++;
object=strdup(c);
c=strchr(object,'\n');
if(c) *c=0;
};
};
};
};
if(feof(f)) {
sx_report(SX_FATAL,"EOF from RADB (dequeue, ripe)\n");
} else {
sx_report(SX_FATAL,"Error from RADB: %s (dequeue, ripe)\n",
strerror(errno));
};
return 0;
};
int
bgpq_expand_ripe(FILE* f, int (*callback)(char*, void*), void* udata,
char* fmt, ...)
{
char request[128];
char* otype=NULL, *object=NULL, *origin=NULL;
int sawNL=0, nObjects=0;
va_list ap;
struct bgpq_expander* b=(struct bgpq_expander*)udata;
if(!f) {
sx_report(SX_FATAL,"Invalid argments\n");
exit(1);
};
va_start(ap,fmt);
vsnprintf(request,sizeof(request),fmt,ap);
va_end(ap);
SX_DEBUG(debug_expander,"expander(ripe): sending '%s'\n", request);
fwrite(request,1,strlen(request),f);
fflush(f);
sawNL=0;
while(fgets(request,sizeof(request),f)) {
if(request[0]=='\n') {
if(b->family==AF_INET && otype && !strcmp(otype,"route")) {
SX_DEBUG(debug_expander,"expander(ripe): got route: %s\n",
object);
callback(object,udata);
} else if(b->family==AF_INET6 && otype&&!strcmp(otype,"route6")) {
SX_DEBUG(debug_expander,"expander(ripe): got route6: %s\n",
object);
callback(object,udata);
};
if(otype) free(otype); otype=NULL;
if(object) free(object); object=NULL;
if(origin) free(origin); origin=NULL;
nObjects++;
sawNL++;
if(sawNL==2) {
/* ok, that's end of input */
return nObjects;
};
} else {
sawNL=0;
if(!otype) {
/* that's the first line of object */
char* c=strchr(request,':');
if(c) {
*c=0;
otype=strdup(request);
c++;
while((isspace((int)*c))) c++;
object=strdup(c);
c=strchr(object,'\n');
if(c) *c=0;
};
} else if(!strncmp(request,"origin",6)) {
if(origin) free(origin);
origin=strdup(request);
};
};
};
if(feof(f)) {
sx_report(SX_FATAL,"EOF from server (expand, ripe)\n");
} else {
sx_report(SX_FATAL,"Error reading server: %s (expand, ripe)\n",
strerror(errno));
};
return 0;
};
int
bgpq_pipeline(FILE* f, int (*callback)(char*, void*), void* udata,
char* fmt, ...)
{
char request[128];
int ret;
struct bgpq_prequest* bp=NULL;
struct bgpq_expander* d=(struct bgpq_expander*)udata;
va_list ap;
va_start(ap,fmt);
vsnprintf(request,sizeof(request),fmt,ap);
va_end(ap);
SX_DEBUG(debug_expander,"expander: sending '%s'\n", request);
bp=malloc(sizeof(struct bgpq_prequest));
if(!bp) {
sx_report(SX_FATAL,"Unable to allocate %lu bytes: %s\n",
(unsigned long)sizeof(struct bgpq_prequest),strerror(errno));
exit(1);
};
memset(bp,0,sizeof(struct bgpq_prequest));
ret=fwrite(request,1,strlen(request),f);
if(ret!=strlen(request)) {
sx_report(SX_FATAL,"Partial write to radb, only %i bytes written: %s\n",
ret,strerror(errno));
exit(1);
};
strlcpy(bp->request,request,sizeof(bp->request));
bp->callback=callback;
bp->udata=udata;
if(d->lastpipe) {
d->lastpipe->next=bp;
d->lastpipe=bp;
} else {
d->firstpipe=d->lastpipe=bp;
};
d->piped++;
return 0;
};
int
bgpq_pipeline_dequeue(FILE* f, struct bgpq_expander* b)
{
while(b->piped>0) {
char request[128];
struct bgpq_prequest* pipe;
memset(request,0,sizeof(request));
if(!fgets(request,sizeof(request),f)) {
if(ferror(f)) {
sx_report(SX_FATAL,"Error reading data from RADB: %s (dequeue)"
"\n", strerror(errno));
} else {
sx_report(SX_FATAL,"EOF from RADB (dequeue)\n");
};
exit(1);
};
if(request[0]=='A') {
char* eon, *c;
unsigned long togot=strtoul(request+1,&eon,10);
char recvbuffer[togot+2];
memset(recvbuffer,0,togot+2);
if(eon && *eon!='\n') {
sx_report(SX_ERROR,"A-code finished with wrong char '%c'(%s)\n",
*eon,request);
exit(1);
};
if(fgets(recvbuffer,togot+1,f)==NULL) {
if(ferror(f)) {
sx_report(SX_FATAL,"Error reading RADB: %s (dequeue, "
"result)\n", strerror(errno));
} else {
sx_report(SX_FATAL,"EOF from RADB (dequeue, result)\n");
};
exit(1);
};
SX_DEBUG(debug_expander>=3,"Got %s in response to %s",recvbuffer,
b->firstpipe->request);
for(c=recvbuffer; c<recvbuffer+togot;) {
size_t spn=strcspn(c," \n");
if(spn) c[spn]=0;
if(c[0]==0) break;
if(b->firstpipe->callback) {
b->firstpipe->callback(c,b->firstpipe->udata);
};
c+=spn+1;
};
/* Final code */
if(fgets(recvbuffer,togot,f)==NULL) {
if(ferror(f)) {
sx_report(SX_FATAL,"Error reading RADB: %s (dequeue,final)"
")\n", strerror(errno));
} else {
sx_report(SX_FATAL,"EOF from RADB (dequeue,final)\n");
};
exit(1);
};
} else if(request[0]=='C') {
/* No data */
} else if(request[0]=='D') {
/* .... */
} else if(request[0]=='E') {
/* XXXXX */
} else if(request[0]=='F') {
/* XXXXX */
} else {
sx_report(SX_ERROR,"Wrong reply: %s to %s\n", request,
b->firstpipe->request);
};
pipe=b->firstpipe;
b->firstpipe=b->firstpipe->next;
b->piped--;
free(pipe);
};
return 0;
};
int
bgpq_expand_radb(FILE* f, int (*callback)(char*, void*), void* udata,
char* fmt, ...)
{
char request[128];
va_list ap;
int ret;
va_start(ap,fmt);
vsnprintf(request,sizeof(request),fmt,ap);
va_end(ap);
SX_DEBUG(debug_expander,"expander: sending '%s'\n", request);
ret=fwrite(request,1,strlen(request),f);
if(ret!=strlen(request)) {
sx_report(SX_FATAL,"Partial write to radb, only %i bytes written: %s\n",
ret,strerror(errno));
exit(1);
};
memset(request,0,sizeof(request));
if(!fgets(request,sizeof(request),f)) {
if(ferror(f)) {
sx_report(SX_FATAL,"Error reading data from radb: %s (expand,radb)"
"\n", strerror(errno));
exit(1);
};
sx_report(SX_FATAL,"EOF from radb (expand,radb)\n");
exit(1);
};
SX_DEBUG(debug_expander>2,"expander: initially got %lu bytes, '%s'\n",
(unsigned long)strlen(request),request);
if(request[0]=='A') {
char* eon, *c;
long togot=strtoul(request+1,&eon,10);
char recvbuffer[togot+1];
if(eon && *eon!='\n') {
sx_report(SX_ERROR,"A-code finised with wrong char '%c' (%s)\n",
*eon,request);
exit(1);
};
if(fgets(recvbuffer,togot+1,f)==NULL) {
if(feof(f)) {
sx_report(SX_FATAL,"EOF from radb (expand,radb,result)\n");
} else {
sx_report(SX_FATAL,"Error reading radb: %s (expand,radb,"
"result)\n", strerror(errno));
};
exit(1);
};
SX_DEBUG(debug_expander>2,"expander: final reply of %lu bytes, '%s'\n",
(unsigned long)strlen(recvbuffer),recvbuffer);
for(c=recvbuffer; c<recvbuffer+togot;) {
size_t spn=strcspn(c," \n");
if(spn) c[spn]=0;
if(c[0]==0) break;
if(callback) callback(c,udata);
c+=spn+1;
};
if(fgets(recvbuffer,togot,f)==NULL) {
if(feof(f)) {
sx_report(SX_FATAL,"EOF from radb (expand,radb,final)\n");
} else {
sx_report(SX_FATAL,"ERROR from radb: %s\n", strerror(errno));
};
exit(1);
};
} else if(request[0]=='C') {
/* no data */
} else if(request[0]=='D') {
/* ... */
} else if(request[0]=='E') {
/* XXXXXX */
} else if(request[0]=='F') {
/* XXXXXX */
} else {
sx_report(SX_ERROR,"Wrong reply: %s\n", request);
exit(0);
};
return 0;
};
int
bgpq_expand(struct bgpq_expander* b)
{
int fd=-1, err, ret;
struct sx_slentry* mc;
struct addrinfo hints, *res=NULL, *rp;
FILE* f=NULL;
memset(&hints,0,sizeof(struct addrinfo));
hints.ai_socktype=SOCK_STREAM;
err=getaddrinfo(b->server,"43",&hints,&res);
if(err) {
sx_report(SX_ERROR,"Unable to resolve %s: %s\n",
b->server, gai_strerror(err));
exit(1);
};
for(rp=res; rp; rp=rp->ai_next) {
fd=socket(rp->ai_family,rp->ai_socktype,0);
if(fd==-1) {
if(errno==EPROTONOSUPPORT) continue;
sx_report(SX_ERROR,"Unable to create socket: %s\n",
strerror(errno));
exit(1);
};
err=connect(fd,rp->ai_addr,rp->ai_addrlen);
if(err) {
shutdown(fd,SHUT_RDWR);
close(fd);
fd=-1;
continue;
};
sx_maxsockbuf(fd,SO_SNDBUF);
f=fdopen(fd,"a+");
if(!f) {
shutdown(fd,SHUT_RDWR);
close(fd);
fd=-1;
f=NULL;
continue;
};
break;
};
freeaddrinfo(res);
if(!f) {
/* all our attempts to connect failed */
sx_report(SX_ERROR,"All attempts to connect %s failed, last"
" error: %s\n", b->server, strerror(errno));
exit(1);
};
if((ret=fwrite("!!\n",1,3,f))!=3) {
sx_report(SX_ERROR,"Partial fwrite to radb: %i bytes, %s\n",
ret, strerror(errno));
exit(1);
};
if(b->sources && b->sources[0]!=0) {
char sources[128];
snprintf(sources,sizeof(sources),"!s%s\n", b->sources);
fwrite(sources,strlen(sources),1,f);
fgets(sources,sizeof(sources),f);
};
if(b->identify) {
char ident[128];
snprintf(ident,sizeof(ident),"!n" PACKAGE_STRING "\n");
fwrite(ident,strlen(ident),1,f);
fgets(ident,sizeof(ident),f);
};
for(mc=b->macroses;mc;mc=mc->next) {
bgpq_expand_radb(f,bgpq_expanded_macro,b,"!i%s,1\n",mc->text);
};
if(b->generation>=T_PREFIXLIST) {
unsigned i, j, k;
for(mc=b->rsets;mc;mc=mc->next) {
if(b->family==AF_INET) {
bgpq_expand_radb(f,bgpq_expanded_prefix,b,"!i%s,1\n",mc->text);
} else {
if(!pipelining) {
bgpq_expand_ripe(f,bgpq_expanded_v6prefix,b,
"-T route6 -i member-of %s\n",mc->text);
} else {
bgpq_pipeline(f,bgpq_expanded_v6prefix,b,
"-T route6 -i member-of %s\n", mc->text);
};
};
};
for(k=0;k<sizeof(b->asn32s)/sizeof(unsigned char*);k++) {
if(!b->asn32s[k]) continue;
for(i=0;i<8192;i++) {
for(j=0;j<8;j++) {
if(b->asn32s[k][i]&(0x80>>j)) {
if(b->family==AF_INET6) {
if(!pipelining) {
if(k>0)
bgpq_expand_ripe(f,bgpq_expanded_v6prefix,b,
"-T route6 -i origin as%u.%u\r\n", k,
i*8+j);
else
bgpq_expand_ripe(f,bgpq_expanded_v6prefix,b,
"-T route6 -i origin as%u\r\n", i*8+j);
} else {
if(k>0)
bgpq_pipeline(f,bgpq_expanded_v6prefix,b,
"-T route6 -i origin as%u.%u\r\n", k,
i*8+j);
else
bgpq_pipeline(f,bgpq_expanded_v6prefix,b,
"-T route6 -i origin as%u\r\n", i*8+j);
};
} else {
if(!pipelining) {
if(k>0)
bgpq_expand_radb(f,bgpq_expanded_prefix,b,
"!gas%u.%u\n", k, i*8+j);
else
bgpq_expand_radb(f,bgpq_expanded_prefix,b,
"!gas%u\n", i*8+j);
} else {
if(k>0)
bgpq_pipeline(f,bgpq_expanded_prefix,b,
"!gas%u.%u\n", k, i*8+j);
else
bgpq_pipeline(f,bgpq_expanded_prefix,b,
"!gas%u\n", i*8+j);
};
};
};
};
};
};
if(pipelining && b->firstpipe) {
if(b->family==AF_INET6) {
bgpq_pipeline_dequeue_ripe(f,b);
} else {
bgpq_pipeline_dequeue(f,b);
};
};
};
fwrite("!q\n",1,3,f);
fclose(f);
return 1;
};

25
config.h.in Normal file
View File

@@ -0,0 +1,25 @@
/* config.h.in. Generated from configure.in by autoheader. */
/* Define to 1 if you have the `nsl' library (-lnsl). */
#undef HAVE_LIBNSL
/* Define to 1 if you have the `socket' library (-lsocket). */
#undef HAVE_LIBSOCKET
/* Define to 1 if you have the `strlcpy' function. */
#undef HAVE_STRLCPY
/* Define to the address where bug reports for this package should be sent. */
#undef PACKAGE_BUGREPORT
/* Define to the full name of this package. */
#undef PACKAGE_NAME
/* Define to the full name and version of this package. */
#undef PACKAGE_STRING
/* Define to the one symbol short name of this package. */
#undef PACKAGE_TARNAME
/* Define to the version of this package. */
#undef PACKAGE_VERSION

4143
configure vendored Executable file
View File

File diff suppressed because it is too large Load Diff

12
configure.in Normal file
View File

@@ -0,0 +1,12 @@
AC_INIT(bgpq3,0.1.20,snar@snar.spb.ru)
AC_CONFIG_HEADER(config.h)
AC_PROG_CC
AC_PROG_INSTALL
AC_CHECK_FUNCS(strlcpy)
AC_CHECK_LIB(socket,socket)
AC_CHECK_LIB(nsl,getaddrinfo)
AC_OUTPUT(Makefile)

251
install-sh Executable file
View File

@@ -0,0 +1,251 @@
#!/bin/sh
#
# install - install a program, script, or datafile
# This comes from X11R5 (mit/util/scripts/install.sh).
#
# Copyright 1991 by the Massachusetts Institute of Technology
#
# Permission to use, copy, modify, distribute, and sell this software and its
# documentation for any purpose is hereby granted without fee, provided that
# the above copyright notice appear in all copies and that both that
# copyright notice and this permission notice appear in supporting
# documentation, and that the name of M.I.T. not be used in advertising or
# publicity pertaining to distribution of the software without specific,
# written prior permission. M.I.T. makes no representations about the
# suitability of this software for any purpose. It is provided "as is"
# without express or implied warranty.
#
# Calling this script install-sh is preferred over install.sh, to prevent
# `make' implicit rules from creating a file called install from it
# when there is no Makefile.
#
# This script is compatible with the BSD install script, but was written
# from scratch. It can only install one file at a time, a restriction
# shared with many OS's install programs.
# set DOITPROG to echo to test this script
# Don't use :- since 4.3BSD and earlier shells don't like it.
doit="${DOITPROG-}"
# put in absolute paths if you don't have them in your path; or use env. vars.
mvprog="${MVPROG-mv}"
cpprog="${CPPROG-cp}"
chmodprog="${CHMODPROG-chmod}"
chownprog="${CHOWNPROG-chown}"
chgrpprog="${CHGRPPROG-chgrp}"
stripprog="${STRIPPROG-strip}"
rmprog="${RMPROG-rm}"
mkdirprog="${MKDIRPROG-mkdir}"
transformbasename=""
transform_arg=""
instcmd="$mvprog"
chmodcmd="$chmodprog 0755"
chowncmd=""
chgrpcmd=""
stripcmd=""
rmcmd="$rmprog -f"
mvcmd="$mvprog"
src=""
dst=""
dir_arg=""
while [ x"$1" != x ]; do
case $1 in
-c) instcmd="$cpprog"
shift
continue;;
-d) dir_arg=true
shift
continue;;
-m) chmodcmd="$chmodprog $2"
shift
shift
continue;;
-o) chowncmd="$chownprog $2"
shift
shift
continue;;
-g) chgrpcmd="$chgrpprog $2"
shift
shift
continue;;
-s) stripcmd="$stripprog"
shift
continue;;
-t=*) transformarg=`echo $1 | sed 's/-t=//'`
shift
continue;;
-b=*) transformbasename=`echo $1 | sed 's/-b=//'`
shift
continue;;
*) if [ x"$src" = x ]
then
src=$1
else
# this colon is to work around a 386BSD /bin/sh bug
:
dst=$1
fi
shift
continue;;
esac
done
if [ x"$src" = x ]
then
echo "install: no input file specified"
exit 1
else
true
fi
if [ x"$dir_arg" != x ]; then
dst=$src
src=""
if [ -d $dst ]; then
instcmd=:
chmodcmd=""
else
instcmd=mkdir
fi
else
# Waiting for this to be detected by the "$instcmd $src $dsttmp" command
# might cause directories to be created, which would be especially bad
# if $src (and thus $dsttmp) contains '*'.
if [ -f $src -o -d $src ]
then
true
else
echo "install: $src does not exist"
exit 1
fi
if [ x"$dst" = x ]
then
echo "install: no destination specified"
exit 1
else
true
fi
# If destination is a directory, append the input filename; if your system
# does not like double slashes in filenames, you may need to add some logic
if [ -d $dst ]
then
dst="$dst"/`basename $src`
else
true
fi
fi
## this sed command emulates the dirname command
dstdir=`echo $dst | sed -e 's,[^/]*$,,;s,/$,,;s,^$,.,'`
# Make sure that the destination directory exists.
# this part is taken from Noah Friedman's mkinstalldirs script
# Skip lots of stat calls in the usual case.
if [ ! -d "$dstdir" ]; then
defaultIFS='
'
IFS="${IFS-${defaultIFS}}"
oIFS="${IFS}"
# Some sh's can't handle IFS=/ for some reason.
IFS='%'
set - `echo ${dstdir} | sed -e 's@/@%@g' -e 's@^%@/@'`
IFS="${oIFS}"
pathcomp=''
while [ $# -ne 0 ] ; do
pathcomp="${pathcomp}${1}"
shift
if [ ! -d "${pathcomp}" ] ;
then
$mkdirprog "${pathcomp}"
else
true
fi
pathcomp="${pathcomp}/"
done
fi
if [ x"$dir_arg" != x ]
then
$doit $instcmd $dst &&
if [ x"$chowncmd" != x ]; then $doit $chowncmd $dst; else true ; fi &&
if [ x"$chgrpcmd" != x ]; then $doit $chgrpcmd $dst; else true ; fi &&
if [ x"$stripcmd" != x ]; then $doit $stripcmd $dst; else true ; fi &&
if [ x"$chmodcmd" != x ]; then $doit $chmodcmd $dst; else true ; fi
else
# If we're going to rename the final executable, determine the name now.
if [ x"$transformarg" = x ]
then
dstfile=`basename $dst`
else
dstfile=`basename $dst $transformbasename |
sed $transformarg`$transformbasename
fi
# don't allow the sed command to completely eliminate the filename
if [ x"$dstfile" = x ]
then
dstfile=`basename $dst`
else
true
fi
# Make a temp file name in the proper directory.
dsttmp=$dstdir/#inst.$$#
# Move or copy the file name to the temp name
$doit $instcmd $src $dsttmp &&
trap "rm -f ${dsttmp}" 0 &&
# and set any options; do chmod last to preserve setuid bits
# If any of these fail, we abort the whole thing. If we want to
# ignore errors from any of these, just make sure not to ignore
# errors from the above "$doit $instcmd $src $dsttmp" command.
if [ x"$chowncmd" != x ]; then $doit $chowncmd $dsttmp; else true;fi &&
if [ x"$chgrpcmd" != x ]; then $doit $chgrpcmd $dsttmp; else true;fi &&
if [ x"$stripcmd" != x ]; then $doit $stripcmd $dsttmp; else true;fi &&
if [ x"$chmodcmd" != x ]; then $doit $chmodcmd $dsttmp; else true;fi &&
# Now rename the file to the real destination.
$doit $rmcmd -f $dstdir/$dstfile &&
$doit $mvcmd $dsttmp $dstdir/$dstfile
fi &&
exit 0

68
strlcpy.c Normal file
View File

@@ -0,0 +1,68 @@
/* $OpenBSD: strlcpy.c,v 1.4 1999/05/01 18:56:41 millert Exp $ */
/*
* Copyright (c) 1998 Todd C. Miller <Todd.Miller@courtesan.com>
* 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.
* 3. The name of the author may not be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED ``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
* THE AUTHOR 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.
*/
#ifndef HAVE_STRLCPY
#include <sys/types.h>
#include <string.h>
/*
* Copy src to string dst of size siz. At most siz-1 characters
* will be copied. Always NUL terminates (unless siz == 0).
* Returns strlen(src); if retval >= siz, truncation occurred.
*/
size_t
strlcpy(dst, src, siz)
char *dst;
const char *src;
size_t siz;
{
char *d = dst;
const char *s = src;
size_t n = siz;
/* Copy as many bytes as will fit */
if (n != 0 && --n != 0) {
do {
if ((*d++ = *s++) == 0)
break;
} while (--n != 0);
}
/* Not enough room in dst, add NUL and traverse rest of src */
if (n == 0) {
if (siz != 0)
*d = '\0'; /* NUL-terminate dst */
while (*s++)
;
}
return(s - src - 1); /* count does not include NUL */
}
#endif

89
sx_maxsockbuf.c Normal file
View File

@@ -0,0 +1,89 @@
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include "sx_report.h"
#ifndef SX_MAXSOCKBUF_MAX
#define SX_MAXSOCKBUF_MAX (2*1024*1024)
#endif
int
sx_maxsockbuf(int s, int dir)
{
int optval=0, voptval;
int hiconf=-1, loconf=-1;
unsigned int voptlen;
int phase=0, iterations=0;
if(s<0) {
sx_report(SX_FATAL,"Unable to maximize sockbuf on invalid socket %i\n",
s);
exit(1);
};
voptlen=sizeof(optval);
if(getsockopt(s,SOL_SOCKET,dir,(void*)&optval,&voptlen)==-1) {
sx_report(SX_ERROR,"initial getsockopt failed: %s\n", strerror(errno));
return -1;
};
for(;;) {
iterations++;
if(phase==0) optval<<=1;
else {
if(optval==(hiconf+loconf)/2) break;
optval=(hiconf+loconf)/2;
};
if(optval>SX_MAXSOCKBUF_MAX && phase==0)
break;
if(setsockopt(s,SOL_SOCKET,dir,(void*)&optval,sizeof(optval))==-1)
{
if(phase==0) phase=1;
hiconf=optval;
continue;
} else {
loconf=optval;
};
voptlen=sizeof(voptval);
if(getsockopt(s,SOL_SOCKET,dir,(void*)&voptval,&voptlen)==-1) {
sx_report(SX_ERROR,"getsockopt failed: %s\n", strerror(errno));
return -1;
} else if(voptval<optval) {
if(phase==0) {
phase=1; optval>>=1; continue;
} else if(phase==1) {
phase=2; optval-=2048; continue;
} else break;
} else if(voptval>=SX_MAXSOCKBUF_MAX) {
/* ... and getsockopt not failed and voptval>=optval. Do not allow
* to increase sockbuf too much even in case OS permits it */
break;
};
};
voptlen=sizeof(voptval);
if(getsockopt(s,SOL_SOCKET,dir,(void*)&voptval,&voptlen)==-1) {
sx_report(SX_ERROR,"getsockopt(final stage) failed: %s\n",
strerror(errno));
return -1;
} else {
/*
printf("Finally got %i bytes of recvspace in %i interations\n",
voptval, iterations);
*/
};
return 0;
};

8
sx_maxsockbuf.h Normal file
View File

@@ -0,0 +1,8 @@
#ifndef SX_MAXSOCKBUF_H_
#define SX_MAXSOCKBUF_H_
/* s - number of opened socket, dir is either SO_SNDBUF or SO_RCVBUF */
int sx_maxsockbuf(int s, int dir);
#endif

867
sx_prefix.c Normal file
View File

@@ -0,0 +1,867 @@
#if HAVE_CONFIG_H
#include "config.h"
#endif
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <strings.h>
#include "sx_prefix.h"
#include "sx_report.h"
int debug_aggregation=0;
struct sx_prefix*
sx_prefix_alloc(struct sx_prefix* p)
{
struct sx_prefix* sp=malloc(sizeof(struct sx_prefix));
if(!sp) return NULL;
if(p) {
*sp=*p;
} else {
memset(sp,0,sizeof(struct sx_prefix));
};
return sp;
};
void
sx_prefix_destroy(struct sx_prefix* p)
{
if(p) free(p);
};
void
sx_prefix_adjust_masklen(struct sx_prefix* p)
{
int nbytes=(p->family==AF_INET?4:16);
int i;
if(p->masklen==nbytes*8) return ; /* mask is all ones */
for(i=nbytes-1;i>p->masklen/8;i--) {
p->addr.addrs[i]=0;
};
for(i=1;i<=8-p->masklen%8;i++) {
p->addr.addrs[p->masklen/8]&=(0xff<<i);
};
};
int
sx_prefix_parse(struct sx_prefix* p, int af, char* text)
{
char* c=NULL;
int masklen;
c=strchr(text,'/');
if(c) {
char* eod;
*c=0;
masklen=strtol(c+1,&eod,10);
if(eod[0]) {
*c='/';
sx_report(SX_ERROR,"Invalid masklen in prefix %s\n", text);
goto fixups;
};
} else {
masklen=-1;
};
if(!af) {
if(strchr(text,':')) af=AF_INET6;
else
af=AF_INET;
};
if(inet_pton(af,text,&p->addr)!=1) {
if(c) *c='/';
sx_report(SX_ERROR,"Unable to parse prefix %s, af=%i\n",text,af);
goto fixups;
};
if(af==AF_INET) {
if(masklen==-1) p->masklen=32;
else {
if(masklen<0 || masklen>32) {
p->masklen=32;
} else {
p->masklen=masklen;
};
};
} else if(af==AF_INET6) {
if(masklen==-1) p->masklen=128;
else {
if(masklen<0 || masklen>128) {
p->masklen=128;
} else {
p->masklen=masklen;
};
};
} else {
sx_report(SX_ERROR,"Invalid address family %i\n", af);
goto fixups;
};
p->family=af;
sx_prefix_adjust_masklen(p);
if(c) *c='/';
return 1;
fixups:
return 0;
};
struct sx_prefix*
sx_prefix_new(int af, char* text)
{
struct sx_prefix* p=NULL;
if(!text) return NULL;
p=sx_prefix_alloc(NULL);
if(!p) return NULL;
if(!sx_prefix_parse(p,af,text)) {
sx_prefix_destroy(p);
return NULL;
};
return p;
};
int
sx_prefix_fprint(FILE* f, struct sx_prefix* p)
{
char buffer[128];
if(!p) {
fprintf(f?f:stdout,"(null)");
return 0;
};
inet_ntop(p->family,&p->addr,buffer,sizeof(buffer));
return fprintf(f?f:stdout,"%s/%i",buffer,p->masklen);
};
int
sx_prefix_snprintf(struct sx_prefix* p, char* rbuffer, int srb)
{
char buffer[128];
if(!p) {
snprintf(rbuffer,srb,"(null)");
return 0;
};
inet_ntop(p->family,&p->addr,buffer,sizeof(buffer));
return snprintf(rbuffer,srb,"%s/%i",buffer,p->masklen);
};
int
sx_prefix_jsnprintf(struct sx_prefix* p, char* rbuffer, int srb)
{
char buffer[128];
if(!p) {
snprintf(rbuffer,srb,"(null)");
return 0;
};
inet_ntop(p->family,&p->addr,buffer,sizeof(buffer));
return snprintf(rbuffer,srb,"%s\\/%i",buffer,p->masklen);
};
struct sx_radix_tree*
sx_radix_tree_new(int af)
{
struct sx_radix_tree* rt=malloc(sizeof(struct sx_radix_tree));
if(!rt) {
return NULL;
};
memset(rt,0,sizeof(struct sx_radix_tree));
rt->family=af;
return rt;
};
struct sx_radix_node*
sx_radix_node_new(struct sx_prefix* prefix)
{
struct sx_radix_node* rn=malloc(sizeof(struct sx_radix_node));
if(!rn) return NULL;
memset(rn,0,sizeof(struct sx_radix_node));
if(prefix) {
rn->prefix=*prefix; /* structure copy */
};
return rn;
};
int
sx_prefix_eqbits(struct sx_prefix* a, struct sx_prefix* b)
{
int i;
int nbytes=(a->family==AF_INET?4:16);
for(i=0;i<nbytes;i++) {
if(a->addr.addrs[i]==b->addr.addrs[i]) continue;
else {
int j;
for(j=0;j<8 && i*8+j<=a->masklen && i*8+j<=b->masklen;j++) {
if((a->addr.addrs[i]&(0x80>>j))!=(b->addr.addrs[i]&(0x80>>j)))
return i*8+j;
};
};
};
if(a->masklen<b->masklen) return a->masklen;
return b->masklen;
};
int
sx_prefix_isbitset(struct sx_prefix* p, int n)
{
unsigned char s;
/* bits outside the prefix considered unset */
if(p->family==AF_INET && (n<0 || n>32)) return 0;
else if(p->family==AF_INET6 && (n<0 || n>128)) return 0;
s=p->addr.addrs[(n-1)/8];
return (s&(0x80>>((n-1)%8)))?1:0;
};
struct sx_prefix*
sx_prefix_overlay(struct sx_prefix* p, int n)
{
struct sx_prefix* sp=sx_prefix_alloc(p);
sp->masklen=n;
sx_prefix_adjust_masklen(sp);
return sp;
};
void
sx_radix_tree_unlink(struct sx_radix_tree* tree, struct sx_radix_node* node)
{
next:
if(node->r && node->l) {
node->isGlue=1;
} else if(node->r) {
if(node->parent) {
if(node->parent->r==node) {
node->parent->r=node->r;
node->r->parent=node->parent;
} else if(node->parent->l==node) {
node->parent->l=node->r;
node->r->parent=node->parent;
} else {
sx_report(SX_ERROR,"Unlinking node which is not descendant "
"of its parent\n");
};
} else if(tree->head==node) {
/* only one case, really */
tree->head=node->r;
node->r->parent=NULL;
} else {
sx_report(SX_ERROR,"Unlinking node with no parent and not root\n");
};
return;
} else if(node->l) {
if(node->parent) {
if(node->parent->r==node) {
node->parent->r=node->l;
node->l->parent=node->parent;
} else if(node->parent->l==node) {
node->parent->l=node->l;
node->l->parent=node->parent;
} else {
sx_report(SX_ERROR,"Unlinking node which is not descendant "
"of its parent\n");
};
} else if(tree->head==node) {
tree->head=node->l;
node->l->parent=NULL;
} else {
sx_report(SX_ERROR,"Unlinking node with no parent and not root\n");
};
return;
} else {
/* the only case - node does not have descendants */
if(node->parent) {
if(node->parent->l==node) node->parent->l=NULL;
else if(node->parent->r==node) node->parent->r=NULL;
else {
sx_report(SX_ERROR,"Unlinking node which is not descendant "
"of its parent\n");
};
if(node->parent->isGlue) {
node=node->parent;
goto next;
};
} else if(tree->head==node) {
tree->head=NULL;
} else {
sx_report(SX_ERROR,"Unlinking node with no parent and not root\n");
};
return;
};
};
struct sx_radix_node*
sx_radix_tree_lookup(struct sx_radix_tree* tree, struct sx_prefix* prefix)
{
int eb;
struct sx_radix_node* candidate=NULL, *chead;
if(!tree || !prefix) return NULL;
if(tree->family!=prefix->family) return NULL;
if(!tree->head) return NULL;
chead=tree->head;
next:
eb=sx_prefix_eqbits(&chead->prefix,prefix);
if(eb==chead->prefix.masklen && eb==prefix->masklen) {
/* they are equal */
if(chead->isGlue) return candidate;
return chead;
} else if(eb<chead->prefix.masklen) {
return candidate;
} else if(eb<prefix->masklen) {
/* it equals chead->masklen */
if(sx_prefix_isbitset(prefix,eb+1)) {
if(chead->r) {
if(!chead->isGlue) {
candidate=chead;
};
chead=chead->r;
goto next;
} else {
if(chead->isGlue) return candidate;
return chead;
};
} else {
if(chead->l) {
if(!chead->isGlue) {
candidate=chead;
};
chead=chead->l;
goto next;
} else {
if(chead->isGlue) return candidate;
return chead;
};
};
} else {
char pbuffer[128], cbuffer[128];
sx_prefix_snprintf(prefix,pbuffer,sizeof(pbuffer));
sx_prefix_snprintf(&chead->prefix,cbuffer,sizeof(cbuffer));
printf("Unreachible point... eb=%i, prefix=%s, chead=%s\n", eb,
pbuffer, cbuffer);
abort();
};
};
struct sx_radix_node*
sx_radix_tree_insert(struct sx_radix_tree* tree, struct sx_prefix* prefix)
{
int eb;
struct sx_radix_node** candidate=NULL, *chead;
if(!tree || !prefix) return NULL;
if(tree->family!=prefix->family) {
return NULL;
};
if(!tree->head) {
tree->head=sx_radix_node_new(prefix);
return tree->head;
};
candidate=&tree->head;
chead=tree->head;
next:
eb=sx_prefix_eqbits(prefix,&chead->prefix);
if(eb<prefix->masklen && eb<chead->prefix.masklen) {
struct sx_prefix neoRoot=*prefix;
struct sx_radix_node* rn, *ret=sx_radix_node_new(prefix);
neoRoot.masklen=eb;
sx_prefix_adjust_masklen(&neoRoot);
rn=sx_radix_node_new(&neoRoot);
if(!rn) {
sx_report(SX_ERROR,"Unable to create node: %s\n", strerror(errno));
return NULL;
};
if(sx_prefix_isbitset(prefix,eb+1)) {
rn->l=chead;
rn->r=ret;
} else {
rn->l=ret;
rn->r=chead;
};
rn->parent=chead->parent;
chead->parent=rn;
ret->parent=rn;
rn->isGlue=1;
*candidate=rn;
return ret;
} else if(eb==prefix->masklen && eb<chead->prefix.masklen) {
struct sx_radix_node* ret=sx_radix_node_new(prefix);
if(sx_prefix_isbitset(&chead->prefix,eb+1)) {
ret->r=chead;
} else {
ret->l=chead;
};
ret->parent=chead->parent;
chead->parent=ret;
*candidate=ret;
return ret;
} else if(eb==chead->prefix.masklen && eb<prefix->masklen) {
if(sx_prefix_isbitset(prefix,eb+1)) {
if(chead->r) {
candidate=&chead->r;
chead=chead->r;
goto next;
} else {
chead->r=sx_radix_node_new(prefix);
chead->r->parent=chead;
return chead->r;
};
} else {
if(chead->l) {
candidate=&chead->l;
chead=chead->l;
goto next;
} else {
chead->l=sx_radix_node_new(prefix);
chead->l->parent=chead;
return chead->l;
};
};
} else if(eb==chead->prefix.masklen && eb==prefix->masklen) {
/* equal routes... */
if(chead->isGlue) {
chead->isGlue=0;
};
return chead;
} else {
char pbuffer[128], cbuffer[128];
sx_prefix_snprintf(prefix,pbuffer,sizeof(pbuffer));
sx_prefix_snprintf(&chead->prefix,cbuffer,sizeof(cbuffer));
printf("Unreachible point... eb=%i, prefix=%s, chead=%s\n", eb,
pbuffer, cbuffer);
abort();
};
};
void
sx_radix_node_fprintf(struct sx_radix_node* node, void* udata)
{
FILE* out=(udata?udata:stdout);
char buffer[128];
if(!node) {
fprintf(out,"(null)\n");
} else {
sx_prefix_snprintf(&node->prefix,buffer,sizeof(buffer));
fprintf(out,"%s %s\n", buffer, node->isGlue?"(glue)":"");
};
};
int
sx_radix_node_foreach(struct sx_radix_node* node,
void (*func)(struct sx_radix_node*, void*), void* udata)
{
func(node,udata);
if(node->l) sx_radix_node_foreach(node->l,func,udata);
if(node->r) sx_radix_node_foreach(node->r,func,udata);
return 0;
};
int
sx_radix_tree_foreach(struct sx_radix_tree* tree,
void (*func)(struct sx_radix_node*, void*), void* udata)
{
if(!func || !tree || !tree->head) return 0;
sx_radix_node_foreach(tree->head,func,udata);
return 0;
};
int
sx_radix_node_aggregate(struct sx_radix_node* node)
{
if(node->l)
sx_radix_node_aggregate(node->l);
if(node->r)
sx_radix_node_aggregate(node->r);
if(debug_aggregation) {
printf("Aggregating on node: ");
sx_prefix_fprint(stdout,&node->prefix);
printf(" %s%s%u,%u\n", node->isGlue?"Glue ":"",
node->isAggregate?"Aggregate ":"",node->aggregateLow,
node->aggregateHi);
if(node->r) {
printf("R-Tree: ");
sx_prefix_fprint(stdout,&node->r->prefix);
printf(" %s%s%u,%u\n", (node->r->isGlue)?"Glue ":"",
(node->r->isAggregate)?"Aggregate ":"",
node->r->aggregateLow,node->r->aggregateHi);
if(node->r->son) {
printf("R-Son: ");
sx_prefix_fprint(stdout,&node->r->son->prefix);
printf(" %s%s%u,%u\n",node->r->son->isGlue?"Glue ":"",
node->r->son->isAggregate?"Aggregate ":"",
node->r->son->aggregateLow,node->r->son->aggregateHi);
};
};
if(node->l) {
printf("L-Tree: ");
sx_prefix_fprint(stdout,&node->l->prefix);
printf(" %s%s%u,%u\n",node->l->isGlue?"Glue ":"",
node->l->isAggregate?"Aggregate ":"",
node->l->aggregateLow,node->l->aggregateHi);
if(node->l->son) {
printf("L-Son: ");
sx_prefix_fprint(stdout,&node->l->son->prefix);
printf(" %s%s%u,%u\n",node->l->son->isGlue?"Glue ":"",
node->l->son->isAggregate?"Aggregate ":"",
node->l->son->aggregateLow,node->l->son->aggregateHi);
};
};
};
if(node->r && node->l) {
if(!node->r->isAggregate && !node->l->isAggregate &&
!node->r->isGlue && !node->l->isGlue &&
node->r->prefix.masklen==node->l->prefix.masklen) {
if(node->r->prefix.masklen==node->prefix.masklen+1) {
node->isAggregate=1;
node->r->isGlue=1;
node->l->isGlue=1;
node->aggregateHi=node->r->prefix.masklen;
if(node->isGlue) {
node->isGlue=0;
node->aggregateLow=node->r->prefix.masklen;
} else {
node->aggregateLow=node->prefix.masklen;
};
};
if(node->r->son && node->l->son &&
node->r->son->isAggregate && node->l->son->isAggregate &&
node->r->son->aggregateHi==node->l->son->aggregateHi &&
node->r->son->aggregateLow==node->l->son->aggregateLow &&
node->r->prefix.masklen==node->prefix.masklen+1 &&
node->l->prefix.masklen==node->prefix.masklen+1)
{
node->son=sx_radix_node_new(&node->prefix);
node->son->isGlue=0;
node->son->isAggregate=1;
node->son->aggregateHi=node->r->son->aggregateHi;
node->son->aggregateLow=node->r->son->aggregateLow;
node->r->son->isGlue=1;
node->l->son->isGlue=1;
};
} else if(node->r->isAggregate && node->l->isAggregate &&
node->r->aggregateHi==node->l->aggregateHi &&
node->r->aggregateLow==node->l->aggregateLow) {
if(node->r->prefix.masklen==node->prefix.masklen+1 &&
node->l->prefix.masklen==node->prefix.masklen+1) {
if(node->isGlue) {
node->r->isGlue=1;
node->l->isGlue=1;
node->isAggregate=1;
node->isGlue=0;
node->aggregateHi=node->r->aggregateHi;
node->aggregateLow=node->r->aggregateLow;
} else if(node->r->prefix.masklen==node->r->aggregateLow) {
node->r->isGlue=1;
node->l->isGlue=1;
node->isAggregate=1;
node->aggregateHi=node->r->aggregateHi;
node->aggregateLow=node->prefix.masklen;
} else {
node->son=sx_radix_node_new(&node->prefix);
node->son->isGlue=0;
node->son->isAggregate=1;
node->son->aggregateHi=node->r->aggregateHi;
node->son->aggregateLow=node->r->aggregateLow;
node->r->isGlue=1;
node->l->isGlue=1;
if(node->r->son && node->l->son &&
node->r->son->aggregateHi==node->l->son->aggregateHi &&
node->r->son->aggregateLow==node->l->son->aggregateLow)
{
node->son->son=sx_radix_node_new(&node->prefix);
node->son->son->isGlue=0;
node->son->son->isAggregate=1;
node->son->son->aggregateHi=node->r->son->aggregateHi;
node->son->son->aggregateLow=node->r->son->aggregateLow;
node->r->son->isGlue=1;
node->l->son->isGlue=1;
};
};
};
} else if(node->l->son &&
node->r->isAggregate && node->l->son->isAggregate &&
node->r->aggregateHi==node->l->son->aggregateHi &&
node->r->aggregateLow==node->l->son->aggregateLow) {
if(node->r->prefix.masklen==node->prefix.masklen+1 &&
node->l->prefix.masklen==node->prefix.masklen+1) {
if(node->isGlue) {
node->r->isGlue=1;
node->l->son->isGlue=1;
node->isAggregate=1;
node->isGlue=0;
node->aggregateHi=node->r->aggregateHi;
node->aggregateLow=node->r->aggregateLow;
} else {
node->son=sx_radix_node_new(&node->prefix);
node->son->isGlue=0;
node->son->isAggregate=1;
node->son->aggregateHi=node->r->aggregateHi;
node->son->aggregateLow=node->r->aggregateLow;
node->r->isGlue=1;
node->l->son->isGlue=1;
};
};
} else if(node->r->son &&
node->l->isAggregate && node->r->son->isAggregate &&
node->l->aggregateHi==node->r->son->aggregateHi &&
node->l->aggregateLow==node->r->son->aggregateLow) {
if(node->l->prefix.masklen==node->prefix.masklen+1 &&
node->r->prefix.masklen==node->prefix.masklen+1) {
if(node->isGlue) {
node->l->isGlue=1;
node->r->son->isGlue=1;
node->isAggregate=1;
node->isGlue=0;
node->aggregateHi=node->l->aggregateHi;
node->aggregateLow=node->l->aggregateLow;
} else {
node->son=sx_radix_node_new(&node->prefix);
node->son->isGlue=0;
node->son->isAggregate=1;
node->son->aggregateHi=node->l->aggregateHi;
node->son->aggregateLow=node->l->aggregateLow;
node->l->isGlue=1;
node->r->son->isGlue=1;
};
};
};
};
return 0;
};
int
sx_radix_tree_aggregate(struct sx_radix_tree* tree)
{
if(tree && tree->head) return sx_radix_node_aggregate(tree->head);
return 0;
};
static void
setGlueUpTo(struct sx_radix_node* node, void* udata)
{
unsigned refine=*(unsigned*)udata;
if(node && node->prefix.masklen <= refine) {
node->isGlue=1;
};
};
int
sx_radix_node_refine(struct sx_radix_node* node, unsigned refine)
{
if(!node->isGlue && node->prefix.masklen<refine) {
node->isAggregate=1;
node->aggregateLow=node->prefix.masklen;
node->aggregateHi=refine;
if(node->l) {
sx_radix_node_foreach(node->l, setGlueUpTo, &refine);
sx_radix_node_refine(node->l, refine);
};
if(node->r) {
sx_radix_node_foreach(node->r, setGlueUpTo, &refine);
sx_radix_node_refine(node->r, refine);
};
} else if(!node->isGlue && node->prefix.masklen==refine) {
/* not setting aggregate in this case */
if(node->l) sx_radix_node_refine(node->l, refine);
if(node->r) sx_radix_node_refine(node->r, refine);
} else if(node->isGlue) {
if(node->r) sx_radix_node_refine(node->r, refine);
if(node->l) sx_radix_node_refine(node->l, refine);
} else {
/* node->prefix.masklen > refine */
/* do nothing, should pass specifics 'as is'. Also, do not
process any embedded routes, their masklen is bigger, too...
node->isGlue=1;
if(node->l) sx_radix_node_foreach(node->l, setGlue, NULL);
if(node->r) sx_radix_node_foreach(node->r, setGlue, NULL);
*/
};
return 0;
};
int
sx_radix_tree_refine(struct sx_radix_tree* tree, unsigned refine)
{
if(tree && tree->head) return sx_radix_node_refine(tree->head, refine);
return 0;
};
#if SX_PTREE_TEST
int
main() {
struct sx_prefix* p;
int n;
struct sx_radix_tree* tree;
struct sx_radix_node* node;
p=sx_prefix_new(0,strdup("10.11.12.13/24"));
sx_prefix_fprint(stdout,p);
printf("\n");
p=sx_prefix_new(0,strdup("10.11.12.13/33"));
sx_prefix_fprint(stdout,p);
printf("\n");
p=sx_prefix_new(0,strdup("10.11.12.13/-133"));
sx_prefix_fprint(stdout,p);
printf("\n");
p=sx_prefix_new(AF_INET,strdup("10.11.12.14/24"));
sx_prefix_fprint(stdout,p);
printf("\n");
p=sx_prefix_new(AF_INET,strdup("10.11.12.14/33"));
sx_prefix_fprint(stdout,p);
printf("\n");
p=sx_prefix_new(AF_INET,strdup("10.11.12.14/-133"));
sx_prefix_fprint(stdout,p);
printf("\n");
p=sx_prefix_new(AF_INET6,strdup("10.11.12.15/24"));
sx_prefix_fprint(stdout,p);
printf("\n");
p=sx_prefix_new(AF_INET6,strdup("10.11.12.15/33"));
sx_prefix_fprint(stdout,p);
printf("\n");
p=sx_prefix_new(AF_INET6,strdup("10.11.12.15/-133"));
sx_prefix_fprint(stdout,p);
printf("\n");
p=sx_prefix_new(0,strdup("2001:1b00::/24"));
sx_prefix_fprint(stdout,p);
printf("\n");
p=sx_prefix_new(0,strdup("2001:1b00::/33"));
sx_prefix_fprint(stdout,p);
printf("\n");
p=sx_prefix_new(0,strdup("2001:1b00::/-133"));
sx_prefix_fprint(stdout,p);
printf("\n");
p=sx_prefix_new(AF_INET6,strdup("2001:1b01::/24"));
sx_prefix_fprint(stdout,p);
printf("\n");
p=sx_prefix_new(AF_INET6,strdup("2001:1b01::/33"));
sx_prefix_fprint(stdout,p);
printf("\n");
p=sx_prefix_new(AF_INET6,strdup("2001:1b01::/-133"));
sx_prefix_fprint(stdout,p);
printf("\n");
#define SX_TEST_EBITS(a,b,susp) n=sx_prefix_eqbits(sx_prefix_new(0,strdup(a)),\
sx_prefix_new(0,strdup(b))); \
if(n!=susp) printf("FAILED: %s eqbits %s=%i, not %i\n", a, b, n, susp);\
else printf("OK, %s eqbits %s=%i, as suspected\n", a, b, n);
SX_TEST_EBITS("192.168.0.0/24","192.168.1.0/24",23);
SX_TEST_EBITS("192.168.0.0/32","192.168.0.1/32",31);
#if SX_LIBPTREE_IPV6
SX_TEST_EBITS("2001:1b00::/32","2001:1b01::/32",31);
#endif
p=sx_prefix_new(0,strdup("10.11.12.255/32"));
sx_prefix_fprint(stdout,p);
printf("\n31'th bit is %i\n",sx_prefix_isbitset(p,31));
printf("32'th bit is %i\n",sx_prefix_isbitset(p,32));
printf("33'th bit is %i\n",sx_prefix_isbitset(p,33));
p=sx_prefix_new(0,strdup("10.11.12.255/31"));
sx_prefix_fprint(stdout,p);
printf("\n31'th bit is %i\n",sx_prefix_isbitset(p,31));
printf("32'th bit is %i\n",sx_prefix_isbitset(p,32));
printf("33'th bit is %i\n",sx_prefix_isbitset(p,33));
p=sx_prefix_new(0,strdup("10.11.12.255/30"));
sx_prefix_fprint(stdout,p);
printf("\n31'th bit is %i\n",sx_prefix_isbitset(p,31));
p=sx_prefix_new(0,strdup("10.11.12.255/29"));
sx_prefix_fprint(stdout,p);
printf("\n");
p=sx_prefix_new(0,strdup("10.11.12.255/28"));
sx_prefix_fprint(stdout,p);
printf("\n");
p=sx_prefix_new(0,strdup("10.11.12.255/27"));
sx_prefix_fprint(stdout,p);
printf("\n");
p=sx_prefix_new(0,strdup("10.11.12.255/26"));
sx_prefix_fprint(stdout,p);
printf("\n");
p=sx_prefix_new(0,strdup("10.11.12.255/25"));
sx_prefix_fprint(stdout,p);
printf("\n25'th bit is %i\n",sx_prefix_isbitset(p,25));
p=sx_prefix_new(0,strdup("10.11.12.255/24"));
sx_prefix_fprint(stdout,p);
printf("\n25'th bit is %i\n",sx_prefix_isbitset(p,25));
tree=sx_radix_tree_new(AF_INET);
sx_radix_tree_insert(tree,sx_prefix_new(0,"81.9.100.10/32"));
sx_radix_tree_insert(tree,sx_prefix_new(0,"217.170.80.83/32"));
sx_radix_tree_foreach(tree,sx_radix_node_fprintf,NULL);
tree=sx_radix_tree_new(AF_INET);
sx_radix_tree_insert(tree,sx_prefix_new(0,"81.9.100.10/32"));
sx_radix_tree_insert(tree,sx_prefix_new(0,"217.170.80.83/32"));
sx_radix_tree_insert(tree,sx_prefix_new(0,"217.170.80.84/32"));
sx_radix_tree_insert(tree,sx_prefix_new(0,"217.170.80.85/32"));
sx_radix_tree_insert(tree,sx_prefix_new(0,"217.170.80.86/32"));
sx_radix_tree_insert(tree,sx_prefix_new(0,"217.170.80.87/32"));
sx_radix_tree_insert(tree,sx_prefix_new(0,"217.170.80.90/32"));
sx_radix_tree_insert(tree,sx_prefix_new(0,"217.170.80.90/32"));
sx_radix_tree_insert(tree,sx_prefix_new(0,"127.0.0.1/32"));
sx_radix_tree_insert(tree,sx_prefix_new(0,"127.0.0.1/24"));
sx_radix_tree_insert(tree,sx_prefix_new(0,"127.0.0.0/24"));
sx_radix_tree_insert(tree,sx_prefix_new(0,"128.0.0.0/1"));
sx_radix_tree_foreach(tree,sx_radix_node_fprintf,NULL);
printf("lookup 1.1.1.1: ");
node=sx_radix_tree_lookup(tree,sx_prefix_new(0,"1.1.1.1"));
sx_radix_node_fprintf(node,NULL);
printf("lookup 217.170.80.90: ");
node=sx_radix_tree_lookup(tree,sx_prefix_new(0,"217.170.80.90"));
sx_radix_node_fprintf(node,NULL);
sx_radix_tree_unlink(tree,node);
printf("lookup 217.170.80.90 after delete: ");
node=sx_radix_tree_lookup(tree,sx_prefix_new(0,"217.170.80.90"));
sx_radix_node_fprintf(node,NULL);
sx_radix_tree_insert(tree,sx_prefix_new(0,"217.170.80.90/32"));
printf("lookup 217.170.80.90 after reinsert: ");
node=sx_radix_tree_lookup(tree,sx_prefix_new(0,"217.170.80.90"));
sx_radix_node_fprintf(node,NULL);
printf("lookup 217.170.80.81: ");
node=sx_radix_tree_lookup(tree,sx_prefix_new(0,"217.170.80.81"));
sx_radix_node_fprintf(node,NULL);
printf("lookup 127.0.0.1/24: ");
node=sx_radix_tree_lookup(tree,sx_prefix_new(0,"127.0.0.1/24"));
sx_radix_node_fprintf(node,NULL);
printf("lookup 127.0.0.1/26: ");
node=sx_radix_tree_lookup(tree,sx_prefix_new(0,"127.0.0.1/26"));
sx_radix_node_fprintf(node,NULL);
printf("lookup 127.0.0.1/23: ");
node=sx_radix_tree_lookup(tree,sx_prefix_new(0,"127.0.0.1/23"));
sx_radix_node_fprintf(node,NULL);
tree=sx_radix_tree_new(AF_INET6);
sx_radix_tree_insert(tree,sx_prefix_new(0,"2100:1b00::/32"));
sx_radix_tree_insert(tree,sx_prefix_new(0,"2100:1b01::/32"));
sx_radix_tree_insert(tree,sx_prefix_new(0,"2100:1b00::/33"));
sx_radix_tree_insert(tree,sx_prefix_new(0,"2100:1b00::1/128"));
sx_radix_tree_foreach(tree,sx_radix_node_fprintf,NULL);
return 0;
};
#endif

64
sx_prefix.h Normal file
View File

@@ -0,0 +1,64 @@
#ifndef _SX_PREFIX_H_
#define _SX_PREFIX_H_
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
typedef struct sx_prefix {
int family;
int masklen;
union {
struct in_addr addr;
struct in6_addr addr6;
unsigned char addrs[sizeof(struct in6_addr)];
} addr;
} sx_prefix_t;
typedef struct sx_radix_node {
struct sx_radix_node* parent, *l, *r, *son;
void* payload;
unsigned int isGlue:1;
unsigned int isAggregated:1;
unsigned int isAggregate:1;
unsigned int aggregateLow;
unsigned int aggregateHi;
struct sx_prefix prefix;
} sx_radix_node_t;
typedef struct sx_radix_tree {
int family;
struct sx_radix_node* head;
} sx_radix_tree_t;
/* most common operations with the tree is to: lookup/insert/unlink */
struct sx_radix_node* sx_radix_tree_lookup(struct sx_radix_tree* tree,
struct sx_prefix* prefix);
struct sx_radix_node* sx_radix_tree_insert(struct sx_radix_tree* tree,
struct sx_prefix* prefix);
void sx_radix_tree_unlink(struct sx_radix_tree* t, struct sx_radix_node* n);
struct sx_radix_node* sx_radix_tree_lookup_exact(struct sx_radix_tree* tree,
struct sx_prefix* prefix);
struct sx_prefix* sx_prefix_alloc(struct sx_prefix* p);
void sx_prefix_destroy(struct sx_prefix* p);
void sx_prefix_adjust_masklen(struct sx_prefix* p);
struct sx_prefix* sx_prefix_new(int af, char* text);
int sx_prefix_parse(struct sx_prefix* p, int af, char* text);
int sx_prefix_fprint(FILE* f, struct sx_prefix* p);
int sx_prefix_snprintf(struct sx_prefix* p, char* rbuffer, int srb);
int sx_prefix_jsnprintf(struct sx_prefix* p, char* rbuffer, int srb);
struct sx_radix_tree* sx_radix_tree_new(int af);
struct sx_radix_node* sx_radix_node_new(struct sx_prefix* prefix);
struct sx_prefix* sx_prefix_overlay(struct sx_prefix* p, int n);
void sx_radix_node_fprintf(struct sx_radix_node* node, void* udata);
int sx_radix_node_foreach(struct sx_radix_node* node,
void (*func)(struct sx_radix_node*, void*), void* udata);
int sx_radix_tree_foreach(struct sx_radix_tree* tree,
void (*func)(struct sx_radix_node*, void*), void* udata);
int sx_radix_tree_aggregate(struct sx_radix_tree* tree);
int sx_radix_tree_refine(struct sx_radix_tree* tree, unsigned refine);
#endif

97
sx_report.c Normal file
View File

@@ -0,0 +1,97 @@
#if HAVE_CONFIG_H
#include <config.h>
#endif
#include <errno.h>
#include <inttypes.h>
#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <strings.h>
#include <syslog.h>
#include "sx_report.h"
static int reportStderr=1;
static char const*
sx_report_name(sx_report_t t)
{
switch(t) {
case SX_MISFEATURE: return "MISSING FEATURE:";
case SX_FATAL: return "FATAL ERROR:";
case SX_ERROR: return "ERROR:";
case SX_NOTICE: return "Notice:";
case SX_DEBUG: return "Debug:";
};
return "...... HMMMMM.... ERROR... \n";
};
int
sx_report(sx_report_t t, char* fmt, ...)
{
char buffer[65536];
va_list ap;
va_start(ap,fmt);
vsnprintf(buffer,sizeof(buffer),fmt,ap);
va_end(ap);
if(reportStderr) {
fputs(sx_report_name(t),stderr);
fputs(buffer,stderr);
} else {
switch(t) {
case SX_FATAL:
syslog(LOG_ERR,"FATAL ERROR: %s", buffer);
break;
case SX_MISFEATURE:
case SX_ERROR:
syslog(LOG_ERR,"ERROR: %s", buffer);
break;
case SX_NOTICE:
syslog(LOG_WARNING,"Notice: %s", buffer);
break;
case SX_DEBUG:
syslog(LOG_DEBUG,"Debug: %s", buffer);
break;
};
};
if(t==SX_FATAL) exit(-1);
return 0;
};
int
sx_debug(char const* const file, char const* const func, int const line,
char* fmt, ...)
{
char buffer[65536];
char bline[65536];
va_list ap;
va_start(ap,fmt);
vsnprintf(buffer,sizeof(buffer),fmt,ap);
va_end(ap);
snprintf(bline,sizeof(bline),"DEBUG: %s:%i %s ", file, line, func);
if(reportStderr) {
fputs(bline,stderr);
fputs(buffer,stderr);
} else {
syslog(LOG_DEBUG,"%s %s", bline, buffer);
};
return 0;
};
void
sx_openlog(char* progname)
{
openlog(progname?progname:"<unknown>",LOG_PID,LOG_DAEMON);
reportStderr=0;
};

24
sx_report.h Normal file
View File

@@ -0,0 +1,24 @@
#ifndef SX_REPORT_H_
#define SX_REPORT_H_
typedef enum {
SX_DEBUG = 0,
SX_NOTICE,
SX_ERROR,
SX_MISFEATURE,
SX_FATAL
} sx_report_t;
/* opens syslog and disables logging to stderr */
void sx_openlog(char* progname);
int sx_report(sx_report_t, char* fmt, ...)
__attribute__ ((format (printf, 2, 3)));
int sx_debug(char const* const, char const* const, int const, char* fmt, ...)
__attribute__ ((format (printf, 4, 5)));
#define SX_DEBUG(a,b,c...) if(a) sx_debug(__FILE__,__FUNCTION__,__LINE__,\
b, ## c);
#endif

16
sx_slentry.c Normal file
View File

@@ -0,0 +1,16 @@
#include <stdlib.h>
#include <string.h>
#include <strings.h>
#include "sx_slentry.h"
struct sx_slentry*
sx_slentry_new(char* t)
{
struct sx_slentry* e=malloc(sizeof(struct sx_slentry));
if(!e) return NULL;
memset(e,0,sizeof(struct sx_slentry));
if(t) e->text=strdup(t);
return e;
};

11
sx_slentry.h Normal file
View File

@@ -0,0 +1,11 @@
#ifndef SX_SLENTRY_H_
#define SX_SLENTRY_H_
struct sx_slentry {
struct sx_slentry* next;
char* text;
};
struct sx_slentry* sx_slentry_new(char* text);
#endif