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:
3
.gitignore
vendored
Normal file
3
.gitignore
vendored
Normal file
@@ -0,0 +1,3 @@
|
||||
CVS/
|
||||
Makefile
|
||||
config.h
|
||||
112
CHANGES
Normal file
112
CHANGES
Normal 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
26
COPYRIGHT
Normal 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
41
Makefile.in
Normal 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
234
bgpq3.8
Normal 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
379
bgpq3.c
Normal 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
69
bgpq3.h
Normal 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
292
bgpq3.html
Normal 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:~>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:~>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 <extra match conditions></code>, <code>-R <len></code> and hierarchical names:</p>
|
||||
|
||||
<pre><code> 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</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:~>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:~>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:~>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
48
bgpq3.spec
Normal 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
270
bgpq3.txt
Normal 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
514
bgpq3_printer.c
Normal 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
701
bgpq_expander.c
Normal 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
25
config.h.in
Normal 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
|
||||
12
configure.in
Normal file
12
configure.in
Normal 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
251
install-sh
Executable 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
68
strlcpy.c
Normal 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
89
sx_maxsockbuf.c
Normal 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
8
sx_maxsockbuf.h
Normal 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
867
sx_prefix.c
Normal 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
64
sx_prefix.h
Normal 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
97
sx_report.c
Normal 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
24
sx_report.h
Normal 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
16
sx_slentry.c
Normal 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
11
sx_slentry.h
Normal 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
|
||||
Reference in New Issue
Block a user