pax_global_header00006660000000000000000000000064141077052770014524gustar00rootroot0000000000000052 comment=b1074667e8852a728dd554f339f18d85eaf558bf bgpq4-1.4/000077500000000000000000000000001410770527700124055ustar00rootroot00000000000000bgpq4-1.4/.github/000077500000000000000000000000001410770527700137455ustar00rootroot00000000000000bgpq4-1.4/.github/images/000077500000000000000000000000001410770527700152125ustar00rootroot00000000000000bgpq4-1.4/.github/images/centos.Dockerfile000066400000000000000000000004601410770527700204760ustar00rootroot00000000000000ARG image=centos:8 FROM $image # Install dependencies RUN yum update -y RUN yum groupinstall -y 'Development Tools' RUN yum install -y autoconf automake findutils libtool # Add source code ADD . /src WORKDIR /src # Run steps RUN ./bootstrap RUN ./configure RUN make RUN make check RUN make distcheck bgpq4-1.4/.github/images/centos:7.Dockerfile000077700000000000000000000000001410770527700241362centos.Dockerfileustar00rootroot00000000000000bgpq4-1.4/.github/images/centos:8.Dockerfile000077700000000000000000000000001410770527700241372centos.Dockerfileustar00rootroot00000000000000bgpq4-1.4/.github/images/debian.Dockerfile000066400000000000000000000012321410770527700204230ustar00rootroot00000000000000ARG image=debian:buster FROM $image # From https://github.com/docker-library/postgres/blob/69bc540ecfffecce72d49fa7e4a46680350037f9/9.6/Dockerfile#L21-L24 RUN apt-get update && apt-get install -y locales && rm -rf /var/lib/apt/lists/* \ && localedef -i en_US -c -f UTF-8 -A /usr/share/locale/locale.alias en_US.UTF-8 ENV LANG en_US.utf8 # Install dependencies RUN apt-get update \ && apt-get dist-upgrade -y \ && apt-get install -y build-essential autoconf libtool automake markdown \ && rm -rf /var/lib/apt/lists/* # Add source code ADD . /src WORKDIR /src # Run steps RUN ./bootstrap RUN ./configure RUN make RUN make check RUN make distcheck bgpq4-1.4/.github/images/debian:buster.Dockerfile000077700000000000000000000000001410770527700251522debian.Dockerfileustar00rootroot00000000000000bgpq4-1.4/.github/images/debian:stretch.Dockerfile000077700000000000000000000000001410770527700253222debian.Dockerfileustar00rootroot00000000000000bgpq4-1.4/.github/images/fedora:30.Dockerfile000077700000000000000000000000001410770527700241572centos.Dockerfileustar00rootroot00000000000000bgpq4-1.4/.github/images/fedora:31.Dockerfile000077700000000000000000000000001410770527700241602centos.Dockerfileustar00rootroot00000000000000bgpq4-1.4/.github/images/ubuntu:bionic.Dockerfile000077700000000000000000000000001410770527700252112debian.Dockerfileustar00rootroot00000000000000bgpq4-1.4/.github/images/ubuntu:xenial.Dockerfile000077700000000000000000000000001410770527700252262debian.Dockerfileustar00rootroot00000000000000bgpq4-1.4/.github/workflows/000077500000000000000000000000001410770527700160025ustar00rootroot00000000000000bgpq4-1.4/.github/workflows/build.yml000066400000000000000000000005711410770527700176270ustar00rootroot00000000000000name: Build and test (single linux distro) on: [push, pull_request] jobs: build: runs-on: ubuntu-latest steps: - uses: actions/checkout@v1 - name: bootstrap run: ./bootstrap - name: configure run: ./configure - name: make run: make - name: make check run: make check - name: make distcheck run: make distcheck bgpq4-1.4/.github/workflows/matrixbuild.yml000066400000000000000000000010631410770527700210510ustar00rootroot00000000000000name: Build and test (linux matrix) on: [push] jobs: build: runs-on: ubuntu-latest strategy: fail-fast: false matrix: dockerenv: - debian:buster - debian:stretch - ubuntu:bionic - ubuntu:xenial - centos:8 - centos:7 - fedora:31 - fedora:30 steps: - uses: actions/checkout@v1 - name: Run build on ${{matrix.dockerenv}} run: docker build . --file .github/images/${{matrix.dockerenv}}.Dockerfile --build-arg image=${{matrix.dockerenv}} bgpq4-1.4/CHANGES000066400000000000000000000326651410770527700134140ustar00rootroot000000000000001.4 (2021-08-20) - Fix BIRD aspath output 1.3 (2021-08-20) - Change versioning from X.Y.Z to Y.Z - The repository file hierachy has been reorganized (compat/ include/) - Man page has been extended - Large portions of code have been reformatted to adhere to OpenBSD's source file style guide to improve readability. - Refactor: replace two-dimensional array for ASN storage with Red-Black tree - Reduced memory usage 0.0.9 (2021-08-18) - Fix various memory errors 0.0.8 (2021-08-17) - Reorganize automake files and includes - Normalize code to adhere to KNF - Fix all compiler warnings 0.0.6 (2020-03-12): - Bugfixes - Thanks Chris Caputo! 0.0.5 (2020-01-01): - Bugfixes 0.0.4 (2019-12-31): - Remove the '-3' command line option, assume all devices are 32-bit ASN safe 0.0.3 (2019-12-30): - Remove the '-2' command line option - Significant code reformating - Improve performance by using IRRd 4's A query when available 0.0.2 (2019-12-14): - Add Mikrotik support - Remove ASDDOT support 0.0.1 (2019-12-14): - Fork bgpq3 into bgpq4 0.1.36-pre (2019-11-08): - minor documentation cleanup: bgpq3 supports much more vendors than just Cisco and Juniper. Copyright years updated. - Nokia SR OS "classic" actually supports aggregation and and more-specific filtering in prefix-lists. Thanks to mfisher128 for reporting. - change log level for prefixes with wrong address family from error to debugging: it's perfectly correct to have prefixes of different families in route-sets. Thanks to Jay Ford for suggestion. 0.1.35 (2018-11-30): - initial support for Juniper route-filter-lists (JunOS 16.2+). - too large (>124bytes) sources list was not handled correctly. Reported by Pier Carlo Chiodi. - initial support for Huawei format (prefix-lists and as-path filters) New flag -U. Requested by Alexander Wagberg. - fix ipv6 prefix-ranges. Reported by Jay Ford. - OpenBGPd change: -E now generates prefix-set instead of prefix-list. Based on submission by Denis Fondras - new option -w, allowing to 'validate' AS numbers included in as-path access-lists: only those AS having registered route-objects are allowed. By default checks route[4] presence, to check route6 objects shall be used together with -6. - cleanup OpenBGPd prefix-sets. Submitted by Claudio Jeker. - new flag -t: generate as-sets for OpenBGPD (OpenBSD 6.4+), BIRD and JSON formats. Based on submission by Claudio Jeker. - new flag -n: support for Nokia SR OS MD-CLI. Based on examples provided by Greg Hankins. - irrd queries for asn32 changed from asdot to asplain notation. Thanks to Troy2914 for heads up. 0.1.35-rc2 (2017-06-14) - OpenBSD need . Reported by Denis Fondras. - OpenBGPD output shall not emit 'deny any from any' in case of empty prefix-list. New flag -a introduced to allow peer-as indication. When this flag is not specified, empty prefix-list is generated (will not be accepted by OpenBGPD). Reported by Denis Fondras 0.1.35-rc (2017-30-05) - Nokia SR OS (formerly Alcatel-Lucent) support. Based on submission by Michail Litvak. - sync man-page with readme.md - socket() EAFNOSUPPORT error handling 0.1.33 (2016-10-14) - OpenBGPD support (-B). Submitted by Peter Hessler. 0.1.32 (2016-08-28) - rollback 0.1.32-rc2 (2015-07-01) change: by default all IRRD sources are allowed by default. Documentation updated to mark radb,ripe,apnic as 'recommended', not as 'preset default'. untagged yet (2016-05-10) - fix: was not able to build on Solaris. Thanks to Mansoor Ali Khan. - feature: IOS XR mode now supports as-paths (ios-regexs). Thanks to Tassos Chatzithomaoglou for examples and proofreading. (additions from 2015-09-23) - bugfix: stoplist shall be able to catch AS numbers as promised. - bugfix: bgpq3 shall not hang at unknown escapes in -M.. - gotcha: "ANY" object in recursive mode ignored: shut complaints on "ERROR:unexpected object 'ANY' in expanded_macro_limit (in response to !iAS-SET-SCOPESKY)" (object contains mbrs-by-ref: ANY). (additions from 2015-08-30) - bugfix: OpenBSD sys/queue.h does not have STAILQ_ interface. Thanks to Pedro Caetano for reporting and testing. - feature: alternate whois port can be configured with -h host[:port] - feature: new format char %N (object name) in formatted output. Thanks to Denis Fondras. - feature: new format chars %m (prefix mask) and %i (inverse mask) in formatted output. 0.1.32-rc5 (2015-07-12) - feature: -L : limit recursion depth when expanding as-sets (default: not limited). Based on idea by Eugene Demidov. - feature: stoplist. Now you can add EXCEPT Object... at the end of bgpq3 command line and corresponding as-sets and asns will not be expanded (does not works for prefixes and prefix-sets yet). - internals: major pipelining rewrite and some code cleanup. 0.1.32-rc4 (2015-07-06) - change: BIRD can't handle empty lists (NAME = []), so they are not generated at all. 0.1.32-rc3 (2015-07-01) - feature: option -s can be used to generate sequence numbers in IOS prefix-lists - feature: option -F can be used to generate output in user-defined format. Only prefix-lists supported for now. 0.1.32-rc2 (2015-07-01) - bugfix: when no sources provided in command line and via IRRD_SOURCES env, no source limitation were sent to IRRd. Thanks to Mikhail A. Grishin. 0.1.32-rc (2015-06-28) - bugfix: F source(s) unavailable message from IRRD was ignored. Please note: this error is caught only when all the specified sources are invalid. For example, 'bgpq3 -s nonexistant' will fail, however, 'bgpq3 -s nonexistant,ripe' will not fail and will use only ripe source. Thanks to Mikhail A. Grishin for reporting. - RIPE-style queries (-T route6 -i origin asNNN) replaced with IRRd-style !6asNNN queries. 0.1.31 (2015-06-23) - pipelining mode now counts buffered requests and issues dequeue when new request can overflow allocated buffer. So, bgpq3 shall no more require TCP tuning (it is still recomended, though). - tcp tuning parameters decreased in README (sx_maxsockbuf will not allow buffer over 2Mb anyway). 0.1.30 (2015-06-16) - bugfix: private asns with number > 2^31 were printed as negative integers. Thanks to Henrik Thostrup Jensen. - do not use ASNs reserved for documentation purposes and private use: 64496-64511 For documentation and sample code; reserved by [RFC5398] 64512-65534 For private use; reserved by [RFC6996] 65535 Reserved by [RFC7300] 65536-65551 For documentation and sample code; reserved by [RFC5398] 4200000000-4294967294 For private use; reserved by [RFC6996] 4294967295 Reserved by [RFC7300] Please, use new -p flag to include these asn's. Suggested by Henrik Thostrup Jensen and Job Snijders. - allow as-path generation with BIRD output. Suggested by Jiri Mikulas. - merge README.md changes by Job Snijders. - bugfix: incorrect asdot representation (as101. without symbols after dot) is not allowed anymore. 0.1.29 (2015-05-04) - do not include routes registered for AS23456 (transition-as) by default. Use new option -2 to restore old behaviour. 0.1.28 (2015-03-10) - minor changes: .spec update, non-silent failure on wrong af, more room for masklen... 0.1.27 (2015-03-10) - bugfix: some ipv6 prefixes were not parsed correctly since 0.1.26. Thanks to Job Snijders. 0.1.26 (2015-02-19) - RPSL support, can be found in rs-esnetcustomers. Thanks to Kris O'Connell for reporting. 0.1.25 (2014-10-29) - JSON support extended to handle "as-paths" too. Well, actually, as there are no defined format for as-path in json, bgpq3 just creates simple object like following: snar@fri:~/compile/bgpq3>./bgpq3 -j3f 20597 as-eltel {"NN": [ 112,5495,6857,8377,20597,34102,35357,43951, 52007,56764,197759,197888,198610,201499 ]} Based on suggestion by Henrik Thostrup Jensen. - -W len option documented. 0.1.24 (2014-07-31) - empty prefix-lists (Cisco), extended access-lists (Cisco), as-path filters (Cisco and Juniper) and route-filters (Juniper) handling: explicit 'deny any' entry now generated instead of implicit 'permit-any'. Based on suggestion by Tore Anderson. 0.1.23 (2014-07-30) - bugfix: use of -M option caused major slowdown as it turned off request pipelining... Thanks to Tore Anderson. 0.1.22 (2014-07-27) - bugfix: allow network object with stray spaces after prefix length. Found by Tom Eichhorn in 2620:74:14::/48 (VeriSign Route6, RADB). - bugfix: networks with leading zeros (02.51.252.0/22, as4787) are not parsed correctly in inet_ntop.. Found by Tom Eichhorn. 0.1.21 (2014-06-05) - new flag -b: generate prefix-filters for BIRD (http://bird.network.cz), contributed by Job Snijders. 0.1.20-todo2 (2014-05-01) - new flag -r , allowing bgpq to generate limited set of more-specific routes - only routes with prefix-length >= are accepted. Thanks to Pavel Gulchouck for suggesion. 0.1.20-todo (2013-10-07) - socket close code fixed. Thanks to Martin J. Levy. - new flag -4, "force ipv4". Actually does a little more than allowing for pedantic checks. 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 : 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 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.. bgpq4-1.4/COPYRIGHT000066400000000000000000000025531410770527700137050ustar00rootroot00000000000000/*- * Copyright (c) 2007-2019 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. * */ bgpq4-1.4/IDEAS000066400000000000000000000015771410770527700131670ustar00rootroot00000000000000Ben Maddison taught me another aggregation trick: route-set: AS37271:RS-EXAMPLE mp-members: 192.0.2.0/27 mp-members: 192.0.2.32/27 mp-members: 192.0.2.64/27 mp-members: 192.0.2.96/27 mp-members: 192.0.2.128/26 mp-members: 192.0.2.128/27 mp-members: 192.0.2.160/27 mp-members: 192.0.2.192/27 mp-members: 192.0.2.224/27 descr: Example route-set mnt-by: MAINT-AS37271 changed: benm@workonline.africa 20210819 source: RADB BGPQ4 produces the following: $ bgpq4 -A AS37271:RS-EXAMPLE no ip prefix-list NN ip prefix-list NN permit 192.0.2.0/25 ge 27 le 27 ip prefix-list NN permit 192.0.2.128/26 le 27 ip prefix-list NN permit 192.0.2.192/26 ge 27 le 27 But the following aggregation also is valid, and shorter: ip prefix-list NN permit 192.0.2.0/24 ge 27 le 27 ip prefix-list NN permit 192.0.2.128/26 bgpq4-1.4/Makefile.am000066400000000000000000000014711410770527700144440ustar00rootroot00000000000000SUBDIRS = include ACLOCAL_AMFLAGS = -I m4 AM_CPPFLAGS = -I$(top_srcdir)/include AM_CPPFLAGS += -I$(top_srcdir)/compat AUTOMAKE_OPTIONS=foreign subdir-objects bin_PROGRAMS=bgpq4 dist_man8_MANS=bgpq4.8 bgpq4_LDADD = $(PLATFORM_LDADD) $(PROG_LDADD) if !HAVE_STRLCPY SUBDIRS += compat bgpq4_LDADD += $(top_builddir)/compat/libcompat.la endif bgpq4_SOURCES=main.c extern.h printer.c expander.c \ sx_maxsockbuf.c \ sx_prefix.c sx_prefix.h \ sx_report.c sx_report.h \ sx_slentry.c EXTRA_DIST=bootstrap README.md CHANGES MAINTAINERCLEANFILES=configure aclocal.m4 compile \ install-sh missing Makefile.in depcomp \ stamp-h1 compat/Makefile.in \ config.guess config.sub include/Makefile.in \ ltmain.sh maintainer-clean-local: -rm -rf m4 autom4te.cache bgpq4-1.4/README.md000066400000000000000000000235721410770527700136750ustar00rootroot00000000000000# NAME **bgpq4** - bgp filtering automation tool # SYNOPSIS **bgpq4** \[**-h** *host\[:port]*] \[**-S** *sources*] \[**-EPz**] \[**-f** *asn* | **-F** *fmt* | **-G** *asn* **-t**] \[**-46ABbDdJjNnsXU**] \[**-a** *asn*] \[**-r** *len*] \[**-R** *len*] \[**-m** *max*] \[**-W** *len*] *OBJECTS* \[...] \[EXCEPT OBJECTS] # DESCRIPTION The **bgpq4** utility used to generate configurations (prefix-lists, extended access-lists, policy-statement terms and as-path lists) based on RADB data. The options are as follows: **-4** > generate IPv4 prefix/access-lists (default). **-6** > generate IPv6 prefix/access-lists (IPv4 by default). **-A** > try to aggregate prefix-lists as much as possible (not all output > formats supported). **-a** *asn* > specify what asn shall be denied in case of empty prefix-list (OpenBGPD) **-B** > generate output in OpenBGPD format (default: Cisco) **-b** > generate output in BIRD format (default: Cisco). **-d** > enable some debugging output. **-e** > generate output in Arista EOS format (default: Cisco). **-E** > generate extended access-list (Cisco), policy-statement term using > route-filters (Juniper), \[ip|ipv6]-prefix-list (Nokia) or prefix-sets > (OpenBGPd). **-f** *number* > generate input as-path access-list. **-F** *fmt* > generate output in user-defined format. **-G** *number* > generate output as-path access-list. **-h** *host\[:port]* > host running IRRD database (default: rr.ntt.net). **-J** > generate config for Juniper (default: Cisco). **-j** > generate output in JSON format (default: Cisco). **-K** > generate config for Mikrotik (default: Cisco). **-l** *name* > name of generated entry. **-L** *limit* > limit recursion depth when expanding as-sets. **-m** *len* > maximum prefix-length of accepted prefixes (default: 32 for IPv4 and > 128 for IPv6). **-M** *match* > extra match conditions for Juniper route-filters. **-n** > generate config for Nokia SR OS MD-CLI (Cisco IOS by default) **-N** > generate config for Nokia SR OS classic CLI (Cisco IOS by default). **-p** > accept routes registered for private ASNs (default: disabled) **-P** > generate prefix-list (default, backward compatibility). **-r** *len* > allow more specific routes starting with specified masklen too. **-R** *len* > allow more specific routes up to specified masklen too. **-s** > generate sequence numbers in IOS-style prefix-lists. **-S** *sources* > use specified sources only (recommended: RADB,RIPE,APNIC). **-t** > generate as-sets for OpenBGPd, BIRD and JSON formats. **-T** > disable pipelining (not recommended). **-W** *len* > generate as-path strings of no more than len items (use 0 for inifinity). **-U** > generate config for Huawei devices (Cisco IOS by default) **-X** > generate config for Cisco IOS XR devices (plain IOS by default). **-z** > generate route-filter-lists (JunOS 16.2+). *OBJECTS* > means networks (in prefix format), autonomous systems, as-sets and route-sets. *EXCEPT OBJECTS* > those objects will be excluded from expansion. # EXAMPLES Generating named juniper prefix-filter for AS20597: $ bgpq4 -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: $ bgpq4 -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 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. Well, for Juniper we can generate even more interesting policy-options, using -M <extra match conditions>, -R <len> and hierarchical names: $ bgpq4 -AJEl eltel/specifics -r 29 -R 32 -M "community blackhole" AS20597 policy-options { policy-statement eltel { term specifics { replace: from { community blackhole; route-filter 81.9.0.0/20 prefix-length-range /29-/32; route-filter 81.9.32.0/20 prefix-length-range /29-/32; route-filter 81.9.96.0/20 prefix-length-range /29-/32; route-filter 81.222.128.0/20 prefix-length-range /29-/32; route-filter 81.222.192.0/18 prefix-length-range /29-/32; route-filter 85.249.8.0/21 prefix-length-range /29-/32; route-filter 85.249.224.0/19 prefix-length-range /29-/32; route-filter 89.112.0.0/17 prefix-length-range /29-/32; route-filter 217.170.64.0/19 prefix-length-range /29-/32; } } } } generated policy-option term now allows all specifics with prefix-length between /29 and /32 for eltel networks if they match with special community blackhole (defined elsewhere in configuration). Of course, this version supports IPv6 (-6): $ bgpq4 -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 assumes your device supports 32-bit ASNs $ bgpq4 -Jf 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 a 32-bit ASN. # USER-DEFINED FORMAT If you want to generate configuration not for routers, but for some other programs/systems, you may use user-defined formatting, like in example below: $ bgpq4 -F "ipfw add pass all from %n/%l to any\n" as3254 ipfw add pass all from 62.244.0.0/18 to any ipfw add pass all from 91.219.29.0/24 to any ipfw add pass all from 91.219.30.0/24 to any ipfw add pass all from 193.193.192.0/19 to any Recognized format sequences are: **%n** > network **%l** > mask length **%a** > aggregate low mask length **%A** > aggregate high mask length **%N** > object name **%m** > object mask **%i** > inversed mask **\n** > new line **\t** > tabulation Please note that no new lines inserted automatically after each sentence, you have to add them into format string manually, elsewhere output will be in one line (sometimes it makes sense): $ bgpq4 -6F "%n/%l; " as-eltel 2001:1b00::/32; 2620:4f:8000::/48; 2a04:bac0::/29; 2a05:3a80::/48; # NOTES ON SOURCES By default *bgpq4* trusts to data from all databases mirrored into NTT's IRR service. Unfortunately, not all these databases are equal in how much can we trust their data. RIR maintained databases (AFRINIC, ARIN, APNIC, LACNIC and RIPE) shall be trusted more than the others because they are indeed have the knowledge about which address space allocated to this or that ASn, other databases lack this knowledge and can (and, actually, do) contain some stale data: noone but RIRs care to remove outdated route-objects when address space revoked from one ASn and allocated to another. In order to keep their filters both compact and actual, *bgpq4 users* are encouraged to use '-S' flag to limit database sources to only ones they trust. General recommendations: Use minimal set of RIR databases (only those in which you and your customers have registered route-objects). Avoid using ARIN-NONAUTH and RIPE-NONAUTH as trusted source: these records were created in database but for address space allocated to different RIR, so the NONAUTH databases have no chance to confirm validity of this route object. $ bgpq4 -S RIPE,RADB as-space no ip prefix-list NN ip prefix-list NN permit 195.190.32.0/19 $ bgpq4 -S RADB,RIPE as-space no ip prefix-list NN ip prefix-list NN permit 45.4.4.0/22 ip prefix-list NN permit 45.4.132.0/22 ip prefix-list NN permit 45.6.128.0/22 ip prefix-list NN permit 45.65.184.0/22 [...] # PERFORMANCE To improve \`bgpq4\` performance when expanding extra-large AS-SETs you shall tune OS settings to enlarge TCP send buffer. FreeBSD can be tuned in the following way: sysctl -w net.inet.tcp.sendbuf_max=2097152 Linux can be tuned in the following way: sysctl -w net.ipv4.tcp_window_scaling=1 sysctl -w net.core.rmem_max=2097152 sysctl -w net.core.wmem_max=2097152 sysctl -w net.ipv4.tcp_rmem="4096 87380 2097152" sysctl -w net.ipv4.tcp_wmem="4096 65536 2097152" # BUILDING This project uses autotools. If you are building from the repository, run the following command to prepare the build system: ./bootstrap In order to compile the software, run: ./configure make make install If you wish to remove the generated build system files from your working tree, run: make maintainer-clean In order to create a distribution archive, run: make dist # DIAGNOSTICS When everything is OK, **bgpq4** 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. # AUTHORS Alexandre Snarskii, Christian David, Claudio Jeker, Job Snijders, Massimiliano Stucchi, Michail Litvak, Peter Schoenmaker, Roelf Wichertjes, and contributions from many others. # SEE ALSO **https://github.com/bgp/bgpq4** BGPQ4 on Github. **http://bgpfilterguide.nlnog.net/** NLNOG's BGP Filter Guide. **https://tcp0.com/cgi-bin/mailman/listinfo/bgpq4** Users and interested parties can subscribe to the BGPQ4 mailing list bgpq4@tcp0.com # PROJECT MAINTAINER Job Snijders <job@sobornost.net> bgpq4-1.4/VERSION000066400000000000000000000000041410770527700134470ustar00rootroot000000000000001.4 bgpq4-1.4/bgpq4.8000066400000000000000000000265641410770527700135300ustar00rootroot00000000000000.\" Copyright (c) 2007-2019 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. .\" .Dd December 23, 2020 .Dt BGPQ4 8 .Os .Sh NAME .Nm bgpq4 .Nd "bgp filtering automation tool" .Sh SYNOPSIS .Nm .Op Fl h Ar host[:port] .Op Fl S Ar sources .Op Fl EPz .Oo .Fl f Ar asn | .Fl F Ar fmt | .Fl G Ar asn .Fl t .Oc .Op Fl 46ABbDdJjNnsXU .Op Fl a Ar asn .Op Fl r Ar len .Op Fl R Ar len .Op Fl m Ar max .Op Fl W Ar len .Ar OBJECTS .Op "..." .Op EXCEPT OBJECTS .Sh DESCRIPTION The .Nm utility used to generate configurations (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 4 generate IPv4 prefix/access-lists (default). .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 a Ar asn specify what asn shall be denied in case of empty prefix-list (OpenBGPD) .It Fl B generate output in OpenBGPD format (default: Cisco) .It Fl b generate output in BIRD format (default: Cisco). .It Fl d enable some debugging output. .It Fl e generate output in Arista EOS format (default: Cisco). .It Fl E generate extended access-list (Cisco), policy-statement term using route-filters (Juniper), [ip|ipv6]-prefix-list (Nokia) or prefix-sets (OpenBGPd). .It Fl f Ar number generate input as-path access-list. .It Fl F Ar fmt generate output in user-defined format. .It Fl G Ar number generate output as-path access-list. .It Fl h Ar host[:port] host running IRRD database (default: rr.ntt.net). .It Fl J generate config for Juniper (default: Cisco). .It Fl j generate output in JSON format (default: Cisco). .It Fl K generate config for Mikrotik (default: Cisco). .It Fl l Ar name name of generated entry. .It Fl L Ar limit limit recursion depth when expanding as-sets. .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 n generate config for Nokia SR OS MD-CLI (Cisco IOS by default) .It Fl N generate config for Nokia SR OS classic CLI (Cisco IOS by default). .It Fl p accept routes registered for private ASNs (default: disabled) .It Fl P generate prefix-list (default, backward compatibility). .It Fl r Ar len allow more specific routes starting with specified masklen too. .It Fl R Ar len allow more specific routes up to specified masklen too. .It Fl s generate sequence numbers in IOS-style prefix-lists. .It Fl S Ar sources use specified sources only (recommended: RADB,RIPE,APNIC). .It Fl t generate as-sets for OpenBGPd, BIRD and JSON formats. .It Fl T disable pipelining (not recommended). .It Fl W Ar len generate as-path strings of no more than len items (use 0 for inifinity). .It Fl U generate config for Huawei devices (Cisco IOS by default) .It Fl X generate config for Cisco IOS XR devices (plain IOS by default). .It Fl z generate route-filter-lists (JunOS 16.2+). .It Ar OBJECTS means networks (in prefix format), autonomous systems, as-sets and route-sets. .It Ar EXCEPT OBJECTS those objects will be excluded from expansion. .El .Sh EXAMPLES Generating named juniper prefix-filter for AS20597: .nf .Bd -literal $ bgpq4 -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; } } .Ed .fi .Pp For Cisco we can use aggregation (-A) flag to make this prefix-filter more compact: .nf .Bd -literal $ bgpq4 -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 .Ed .fi .Pp 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 , -R and hierarchical names: .nf .Bd -literal $ bgpq4 -AJEl eltel/specifics -r 29 -R 32 -M "community blackhole" AS20597 policy-options { policy-statement eltel { term specifics { replace: from { community blackhole; route-filter 81.9.0.0/20 prefix-length-range /29-/32; route-filter 81.9.32.0/20 prefix-length-range /29-/32; route-filter 81.9.96.0/20 prefix-length-range /29-/32; route-filter 81.222.128.0/20 prefix-length-range /29-/32; route-filter 81.222.192.0/18 prefix-length-range /29-/32; route-filter 85.249.8.0/21 prefix-length-range /29-/32; route-filter 85.249.224.0/19 prefix-length-range /29-/32; route-filter 89.112.0.0/17 prefix-length-range /29-/32; route-filter 217.170.64.0/19 prefix-length-range /29-/32; } } } } .Ed .fi generated policy-option term now allows all specifics with prefix-length between /29 and /32 for eltel networks if they match with special community blackhole (defined elsewhere in configuration). .Pp Of course, this version supports IPv6 (-6): .nf .Bd -literal $ bgpq4 -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 [....] .Ed .fi and assumes your device supports 32-bit ASNs .nf .Bd -literal $ bgpq4 -Jf 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)$"; } } .Ed .fi see `AS196611` in the end of the list ? That's a 32-bit ASN. .Sh USER-DEFINED FORMAT If you want to generate configuration not for routers, but for some other programs/systems, you may use user-defined formatting, like in example below: .nf .Bd -literal $ bgpq4 -F "ipfw add pass all from %n/%l to any\\n" as3254 ipfw add pass all from 62.244.0.0/18 to any ipfw add pass all from 91.219.29.0/24 to any ipfw add pass all from 91.219.30.0/24 to any ipfw add pass all from 193.193.192.0/19 to any .Ed .fi .Pp Recognized format sequences are: .Pp .Bl -tag -width Ds -offset indent -compact .It Cm %n network .It Cm %l mask length .It Cm %a aggregate low mask length .It Cm \&%A aggregate high mask length .It Cm \&%N object name .It Cm %m object mask .It Cm %i inversed mask .It Cm \en new line .It Cm \et tabulation .El .Pp Please note that no new lines inserted automatically after each sentence, you have to add them into format string manually, elsewhere output will be in one line (sometimes it makes sense): .nf .Bd -literal $ bgpq4 -6F "%n/%l; " as-eltel 2001:1b00::/32; 2620:4f:8000::/48; 2a04:bac0::/29; 2a05:3a80::/48; .Ed .fi .Sh NOTES ON SOURCES By default .Em bgpq4 trusts to data from all databases mirrored into NTT's IRR service. Unfortunately, not all these databases are equal in how much can we trust their data. RIR maintained databases (AFRINIC, ARIN, APNIC, LACNIC and RIPE) shall be trusted more than the others because they are indeed have the knowledge about which address space allocated to this or that ASn, other databases lack this knowledge and can (and, actually, do) contain some stale data: noone but RIRs care to remove outdated route-objects when address space revoked from one ASn and allocated to another. In order to keep their filters both compact and actual, .Em bgpq4 users are encouraged to use '-S' flag to limit database sources to only ones they trust. .Pp General recommendations: .Pp Use minimal set of RIR databases (only those in which you and your customers have registered route-objects). .Pp Avoid using ARIN-NONAUTH and RIPE-NONAUTH as trusted source: these records were created in database but for address space allocated to different RIR, so the NONAUTH databases have no chance to confirm validity of this route object. .Bd -literal $ bgpq4 -S RIPE,RADB as-space no ip prefix-list NN ip prefix-list NN permit 195.190.32.0/19 $ bgpq4 -S RADB,RIPE as-space no ip prefix-list NN ip prefix-list NN permit 45.4.4.0/22 ip prefix-list NN permit 45.4.132.0/22 ip prefix-list NN permit 45.6.128.0/22 ip prefix-list NN permit 45.65.184.0/22 [...] .Sh PERFORMANCE To improve `bgpq4` performance when expanding extra-large AS-SETs you shall tune OS settings to enlarge TCP send buffer. .Pp FreeBSD can be tuned in the following way: .Pp .Dl sysctl -w net.inet.tcp.sendbuf_max=2097152 .Pp Linux can be tuned in the following way: .Pp .Dl sysctl -w net.ipv4.tcp_window_scaling=1 .Dl sysctl -w net.core.rmem_max=2097152 .Dl sysctl -w net.core.wmem_max=2097152 .Dl sysctl -w net.ipv4.tcp_rmem="4096 87380 2097152" .Dl sysctl -w net.ipv4.tcp_wmem="4096 65536 2097152" .Sh BUILDING This project uses autotools. If you are building from the repository, run the following command to prepare the build system: .Pp .Dl ./bootstrap .Pp In order to compile the software, run: .Pp .Dl ./configure .Dl make .Dl make install .Pp If you wish to remove the generated build system files from your working tree, run: .Pp .Dl make maintainer-clean .Pp In order to create a distribution archive, run: .Pp .Dl make dist .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 AUTHORS Alexandre Snarskii, Christian David, Claudio Jeker, Job Snijders, Massimiliano Stucchi, Michail Litvak, Peter Schoenmaker, Roelf Wichertjes, and contributions from many others. .Sh SEE ALSO .Sy https://github.com/bgp/bgpq4 BGPQ4 on Github. .Pp .Sy http://bgpfilterguide.nlnog.net/ NLNOG's BGP Filter Guide. .Pp .Sy https://tcp0.com/cgi-bin/mailman/listinfo/bgpq4 Users and interested parties can subscribe to the BGPQ4 mailing list bgpq4@tcp0.com .Sh PROJECT MAINTAINER .An Job Snijders Aq job@sobornost.net bgpq4-1.4/bootstrap000077500000000000000000000017701410770527700143550ustar00rootroot00000000000000#!/bin/sh # # Script to help bootstrap the build system when checked out from git # bsd_environment() { # Based on https://github.com/rvm/rvm/blob/59fe3b39f0fb5ae01ed5b9aa187201080815ac16/scripts/functions/build_config_system#L123 if [[ -z "${AUTOCONF_VERSION:-}" ]] then export AUTOCONF_VERSION AUTOCONF_VERSION="$( ls -1 /usr/local/bin/autoreconf-* | awk -F- '{print $NF}' | sort | tail -n 1 )" echo "Using autoconf version: $AUTOCONF_VERSION" fi if [[ -z "${AUTOMAKE_VERSION:-}" ]] then export AUTOMAKE_VERSION # FreeBSD might have automake-wrapper AUTOMAKE_VERSION="$( ls -1 /usr/local/bin/automake-1* | awk -F- '{print $NF}' | sort | tail -n 1 )" echo "Using automake version: $AUTOMAKE_VERSION" fi } # Use the uname string to figure out if this is a BSD case "$(uname)" in *BSD*) bsd_environment ;; esac test -n "$srcdir" || srcdir="$(dirname "$0")" test -n "$srcdir" || srcdir=. autoreconf --force --install --verbose "$srcdir" bgpq4-1.4/compat/000077500000000000000000000000001410770527700136705ustar00rootroot00000000000000bgpq4-1.4/compat/Makefile.am000066400000000000000000000002241410770527700157220ustar00rootroot00000000000000AM_CPPFLAGS = -I$(top_srcdir)/include noinst_LTLIBRARIES = libcompat.la libcompat_la_LIBADD = $(PLATFORM_LDADD) libcompat_la_SOURCES = strlcpy.c bgpq4-1.4/compat/strlcpy.c000066400000000000000000000030711410770527700155350ustar00rootroot00000000000000/* $OpenBSD: strlcpy.c,v 1.16 2019/01/25 00:19:25 millert Exp $ */ /* * Copyright (c) 1998, 2015 Todd C. Miller * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include #include /* * Copy string src to buffer dst of size dsize. At most dsize-1 * chars will be copied. Always NUL terminates (unless dsize == 0). * Returns strlen(src); if retval >= dsize, truncation occurred. */ size_t strlcpy(char *dst, const char *src, size_t dsize) { const char *osrc = src; size_t nleft = dsize; /* Copy as many bytes as will fit. */ if (nleft != 0) { while (--nleft != 0) { if ((*dst++ = *src++) == '\0') break; } } /* Not enough room in dst, add NUL and traverse rest of src. */ if (nleft == 0) { if (dsize != 0) *dst = '\0'; /* NUL-terminate dst */ while (*src++) ; } return(src - osrc - 1); /* count does not include NUL */ } bgpq4-1.4/configure.ac000066400000000000000000000065551410770527700147060ustar00rootroot00000000000000# # Copyright (c) 2019 Brent Cook # # Permission to use, copy, modify, and distribute this software for any # purpose with or without fee is hereby granted, provided that the above # copyright notice and this permission notice appear in all copies. # # THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES # WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF # MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR # ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES # WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN # ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF # OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. AC_INIT([bgpq4], m4_esyscmd([tr -d '\n' < VERSION]), job@sobornost.net) AC_CANONICAL_HOST AM_INIT_AUTOMAKE([subdir-objects foreign]) AC_CONFIG_MACRO_DIR([m4]) m4_ifdef([AM_SILENT_RULES], [AM_SILENT_RULES([yes])]) AC_PROG_CC([cc gcc]) case $host_os in *darwin*) HOST_OS=darwin AC_DEFINE([SPT_TYPE], [SPT_REUSEARGV]) ;; *freebsd*) HOST_OS=freebsd ;; *linux*) HOST_OS=linux CFLAGS="$CFLAGS -D_DEFAULT_SOURCE -D_BSD_SOURCE -D_GNU_SOURCE" AC_DEFINE([SPT_TYPE], [SPT_REUSEARGV]) ;; *netbsd*) HOST_OS=netbsd ;; *openbsd*) HOST_OS=openbsd AC_DEFINE([HAVE_ATTRIBUTE__BOUNDED__], [1], [OpenBSD has __bounded__]) AC_DEFINE([HAVE_ATTRIBUTE__DEAD], [1], [OpenBSD has __dead]) ;; *solaris*) HOST_OS=solaris CFLAGS="$CFLAGS -D__EXTENSIONS__ -D_XOPEN_SOURCE=600 -DBSD_COMP" ;; *) ;; esac AM_CONDITIONAL([HOST_DARWIN], [test x$HOST_OS = xdarwin]) AM_CONDITIONAL([HOST_FREEBSD], [test x$HOST_OS = xfreebsd]) AM_CONDITIONAL([HOST_LINUX], [test x$HOST_OS = xlinux]) AM_CONDITIONAL([HOST_NETBSD], [test x$HOST_OS = xnetbsd]) AM_CONDITIONAL([HOST_SOLARIS], [test x$HOST_OS = xsolaris]) AC_PROG_CC AC_PROG_CC_STDC AM_PROG_CC_C_O AC_PROG_LIBTOOL AC_PROG_INSTALL AC_ARG_ENABLE(warnings, AS_HELP_STRING([--disable-warnings], [ enable compiler warnings [default=enabled]]), [case $enableval in yes) enable_warnings=yes;; no) enable_warnings=no;; *) enable_warnings=yes;; esac], enable_warnings=yes) if test "$enable_warnings" = yes; then AM_CFLAGS="$AM_CFLAGS -Wall -Wstrict-prototypes -Wmissing-prototypes -Wmissing-declarations -Wshadow -Wpointer-arith -Wsign-compare -Werror-implicit-function-declaration" save_cflags="$CFLAGS" CFLAGS=-Wno-pointer-sign AC_MSG_CHECKING([whether CC supports -Wno-pointer-sign]) AC_COMPILE_IFELSE([AC_LANG_PROGRAM([])], [AC_MSG_RESULT([yes])] [WARN_CFLAGS=-Wno-pointer-sign], [AC_MSG_RESULT([no])] ) AM_CFLAGS="$AM_CFLAGS $WARN_CFLAGS" CFLAGS="$save_cflags" fi AC_MSG_CHECKING([if compiling with clang]) AC_COMPILE_IFELSE([AC_LANG_PROGRAM([], [[ #ifndef __clang__ not clang #endif ]])], [AC_MSG_RESULT([yes])] [CLANG_FLAGS=-Qunused-arguments], [AC_MSG_RESULT([no])] ) AM_CFLAGS="$AM_CFLAGS $CLANG_FLAGS" AM_LDFLAGS="$LDFLAGS $CLANG_FLAGS" AC_SUBST(AM_CFLAGS) AC_SUBST(AM_LDFLAGS) AC_CHECK_FUNCS(strlcpy) AC_CHECK_FUNCS(pledge) AC_CHECK_LIB(socket,socket) AC_CHECK_LIB(nsl,getaddrinfo) AC_CHECK_HEADERS([sys/cdefs.h sys/queue.h sys/tree.h sys/select.h]) AM_CONDITIONAL([HAVE_PLEDGE], [test "x$ac_cv_func_pledge" = xyes]) AM_CONDITIONAL([HAVE_STRLCPY], [test "x$ac_cv_func_strlcpy" = xyes]) AC_CONFIG_FILES([ Makefile include/Makefile compat/Makefile ]) AC_OUTPUT bgpq4-1.4/expander.c000066400000000000000000000665251410770527700143750ustar00rootroot00000000000000/* * Copyright (c) 2019-2021 Job Snijders * Copyright (c) 2018 Peter Schoenmaker * Copyright (c) 2007-2019 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. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "extern.h" #include "sx_report.h" int debug_expander = 0; int pipelining = 1; int expand_special_asn = 0; static inline int tentry_cmp(struct sx_tentry *a, struct sx_tentry *b) { return strcasecmp(a->text, b->text); } RB_GENERATE_STATIC(tentree, sx_tentry, entry, tentry_cmp); int asn_cmp(struct asn_entry *a, struct asn_entry *b) { return (a->asn < b->asn ? -1 : a->asn > b->asn); } RB_GENERATE(asn_tree, asn_entry, entry, asn_cmp); 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 = ""; b->name = "NN"; b->aswidth = 8; b->identify = 1; b->server = "rr.ntt.net"; b->port = "43"; RB_INIT(&b->asnlist); STAILQ_INIT(&b->wq); STAILQ_INIT(&b->rq); STAILQ_INIT(&b->rsets); STAILQ_INIT(&b->macroses); return 1; fixups: if (b->tree) sx_radix_tree_freeall(b->tree); b->tree = NULL; free(b); return 0; } int bgpq_expander_add_asset(struct bgpq_expander *b, char *as) { struct slentry *le; if (!b || !as) return 0; le = sx_slentry_new(as); STAILQ_INSERT_TAIL(&b->macroses, le, entry); return 1; } int bgpq_expander_add_rset(struct bgpq_expander *b, char *rs) { struct slentry *le; if (!b || !rs) return 0; le = sx_slentry_new(rs); if (!le) return 0; STAILQ_INSERT_TAIL(&b->rsets, le, entry); return 1; } static int bgpq_expander_add_already(struct bgpq_expander *b, char *rs) { struct sx_tentry *le, lkey; lkey.text = rs; if (RB_FIND(tentree, &b->already, &lkey)) return 1; le = sx_tentry_new(rs); RB_INSERT(tentree, &b->already, le); return 1; } int bgpq_expander_add_stop(struct bgpq_expander *b, char *rs) { struct sx_tentry *le, lkey; lkey.text = rs; if (RB_FIND(tentree, &b->stoplist, &lkey)) return 1; le = sx_tentry_new(rs); RB_INSERT(tentree, &b->stoplist, le); return 1; } int bgpq_expander_add_as(struct bgpq_expander *b, char *as) { char *eoa; uint32_t asno = 0; struct asn_entry *asne; if (!b || !as) return 0; asno = strtoul(as + 2, &eoa, 10); if (eoa && *eoa != 0) { sx_report(SX_ERROR,"Invalid symbol in AS number: '%c' in %s\n", *eoa, as); return 0; } if (!expand_special_asn && ((asno >= 4200000000ul) || (asno >= 64496 && asno <= 65551))) return 0; if ((asne = malloc(sizeof(struct asn_entry))) == NULL) err(1, NULL); asne->asn = asno; RB_INSERT(asn_tree, &b->asnlist, asne); return 1; } int bgpq_expander_add_prefix(struct bgpq_expander *b, char *prefix) { struct sx_prefix *p = sx_prefix_alloc(NULL); 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); if (p) sx_prefix_destroy(p); return 1; } int bgpq_expander_add_prefix_range(struct bgpq_expander *b, char *prefix) { return sx_prefix_range_parse(b->tree, b->family, b->maxlen, prefix); } static int bgpq_expanded_macro(char *as, struct bgpq_expander *ex, struct request *req) { bgpq_expander_add_as(ex, as); return 1; } struct request *bgpq_pipeline(struct bgpq_expander *b, int (*callback)(char *, struct bgpq_expander *b, struct request *req), void *udata, char *fmt, ...); int bgpq_expand_irrd(struct bgpq_expander *b, int (*callback)(char*, struct bgpq_expander *b, struct request *req), void *udata, char *fmt, ...); static int bgpq_expanded_macro_limit(char *as, struct bgpq_expander *b, struct request *req) { if (!strncasecmp(as, "AS-", 3) || strchr(as, '-') || strchr(as, ':')) { struct sx_tentry tkey = { .text = as }; if (RB_FIND(tentree, &b->already, &tkey)) { SX_DEBUG(debug_expander > 2, "%s is already expanding, " "ignore\n", as); return 0; } if (RB_FIND(tentree, &b->stoplist, &tkey)) { SX_DEBUG(debug_expander > 2, "%s is in the stoplist, " "ignore\n", as); return 0; } if (!b->maxdepth || (b->cdepth + 1 < b->maxdepth && req->depth + 1 < b->maxdepth)) { bgpq_expander_add_already(b, as); if (pipelining) { struct request *req1 = bgpq_pipeline(b, bgpq_expanded_macro_limit, NULL, "!i%s\n", as); req1->depth = req->depth + 1; } else { b->cdepth++; bgpq_expand_irrd(b, bgpq_expanded_macro_limit, NULL, "!i%s\n", as); b->cdepth--; } } else { SX_DEBUG(debug_expander > 2, "ignoring %s at depth %i\n", as, b->cdepth ? (b->cdepth + 1) : (req->depth + 1)); } } else if (!strncasecmp(as, "AS", 2)) { struct sx_tentry tkey = { .text = as }; if (RB_FIND(tentree, &b->stoplist, &tkey)) { SX_DEBUG(debug_expander > 2, "%s is in the stoplist, ignore\n", as); return 0; } if (bgpq_expander_add_as(b, as)) { SX_DEBUG(debug_expander > 2, ".. added asn %s\n", as); } else { SX_DEBUG(debug_expander, ".. some error adding as %s " "(in response to %s)\n", as, req->request); } } else if (!strcasecmp(as, "ANY")) return 0; else sx_report(SX_ERROR, "unexpected object '%s' in " "expanded_macro_limit (in response to %s)\n", as, req->request); return 1; } static int bgpq_expanded_prefix(char *as, struct bgpq_expander *ex, struct request *req __attribute__((unused))) { char *d = strchr(as, '^'); if (!d) bgpq_expander_add_prefix(ex, as); else bgpq_expander_add_prefix_range(ex, as); return 1; } static int bgpq_expanded_v6prefix(char *prefix, struct bgpq_expander *ex, struct request *req) { char *d = strchr(prefix, '^'); if (!d) bgpq_expander_add_prefix(ex, prefix); else bgpq_expander_add_prefix_range(ex, prefix); return 1; } int bgpq_pipeline_dequeue(int fd, struct bgpq_expander *b); static struct request * request_alloc(char *request, int (*callback)(char *, struct bgpq_expander *, struct request *), void *udata) { struct request *bp = malloc(sizeof(struct request)); if (!bp) return NULL; memset(bp, 0, sizeof(struct request)); bp->request = strdup(request); bp->offset = 0; bp->size = strlen(bp->request); bp->callback = callback; bp->udata = udata; return bp; } static void request_free(struct request *req) { if (req->request) free(req->request); free(req); } struct request * bgpq_pipeline(struct bgpq_expander *b, int (*callback)(char *, struct bgpq_expander *, struct request *), void *udata, char *fmt, ...) { char request[128]; int ret; struct request *bp = NULL; va_list ap; va_start(ap, fmt); vsnprintf(request, sizeof(request), fmt, ap); va_end(ap); SX_DEBUG(debug_expander,"expander: sending %s", request); bp = request_alloc(request, callback, udata); if (!bp) { sx_report(SX_FATAL,"Unable to allocate %lu bytes: %s\n", (unsigned long)sizeof(struct request), strerror(errno)); exit(1); } if (STAILQ_EMPTY(&b->wq)) { ret = write(b->fd, request, bp->size); if (ret < 0) { if (errno == EAGAIN) { STAILQ_INSERT_TAIL(&b->wq, bp, next); return bp; } sx_report(SX_FATAL, "Error writing request: %s\n", strerror(errno)); } bp->offset=ret; if (ret == bp->size) { STAILQ_INSERT_TAIL(&b->rq, bp, next); } else { STAILQ_INSERT_TAIL(&b->wq, bp, next); } } else STAILQ_INSERT_TAIL(&b->wq, bp, next); return bp; } static void bgpq_expander_invalidate_asn(struct bgpq_expander *b, const char *q) { char *eptr; unsigned long asn = 0; struct asn_entry find, *res; if (!strncmp(q, "!gas", 4) || !strncmp(q, "!6as", 4)) { errno = 0; asn = strtoul(q + 4, &eptr, 10); if (*eptr != '\n') { sx_report(SX_ERROR, "not a number: %s\n", q); return; } if (errno == ERANGE && asn == ULONG_MAX) { sx_report(SX_ERROR, "overflow: %s\n", q); return; } if (asn == 0 || asn >= 4294967295) { sx_report(SX_ERROR, "out of range: %s\n", q); return; } if (eptr && *eptr != '\n') { sx_report(SX_ERROR, "no number? %s\n", q); return; } find.asn = asn; if ((res = RB_FIND(asn_tree, &b->asnlist, &find)) == NULL) { RB_REMOVE(asn_tree, &b->asnlist, res); free(res); } } } static void bgpq_write(struct bgpq_expander *b) { while(!STAILQ_EMPTY(&b->wq)) { struct request *req = STAILQ_FIRST(&b->wq); int ret = write(b->fd, req->request + req->offset, req->size-req->offset); if (ret < 0) { if (errno == EAGAIN) return; sx_report(SX_FATAL, "error writing data: %s\n", strerror(errno)); } if (ret == req->size - req->offset) { /* this request was dequeued */ STAILQ_REMOVE_HEAD(&b->wq, next); STAILQ_INSERT_TAIL(&b->rq, req, next); } else { req->offset += ret; break; } } } static int bgpq_selread(struct bgpq_expander *b, char *buffer, int size) { fd_set rfd, wfd; int ret; repeat: FD_ZERO(&rfd); FD_SET(b->fd, &rfd); FD_ZERO(&wfd); if (!STAILQ_EMPTY(&b->wq)) FD_SET(b->fd, &wfd); ret = select(b->fd + 1, &rfd, &wfd, NULL, NULL); if (ret == 0) sx_report(SX_FATAL, "select failed\n"); else if (ret == -1 && errno == EINTR) goto repeat; else if (ret == -1) sx_report(SX_FATAL, "select error %i: %s\n", errno, strerror(errno)); if (!STAILQ_EMPTY(&b->wq) && FD_ISSET(b->fd, &wfd)) bgpq_write(b); if (FD_ISSET(b->fd, &rfd)) return read(b->fd, buffer, size); goto repeat; } static int bgpq_read(struct bgpq_expander *b) { static char response[256]; static int off = 0; if (!STAILQ_EMPTY(&b->wq)) bgpq_write(b); while(!STAILQ_EMPTY(&b->rq)) { int ret = 0; char *cres; struct request *req = STAILQ_FIRST(&b->rq); SX_DEBUG(debug_expander > 2, "waiting for answer to %s," "init %i '%.*s'\n", req->request, off, off, response); if ((cres=strchr(response, '\n')) != NULL) goto have; repeat: ret = bgpq_selread(b, response + off, sizeof(response) - off); if (ret < 0) { if (errno == EAGAIN) goto repeat; sx_report(SX_FATAL,"Error reading data from IRRd: " "%s (dequeue)\n", strerror(errno)); } else if (ret == 0) { sx_report(SX_FATAL,"EOF from IRRd (dequeue)\n"); } off += ret; if (!(cres = strchr(response, '\n'))) goto repeat; have: SX_DEBUG(debug_expander > 5, "got response of %.*s\n", off, response); if (response[0] == 'A') { char *eon, *c; unsigned long offset = 0; unsigned long togot = strtoul(response + 1, &eon, 10); char *recvbuffer = malloc(togot + 2); if (!recvbuffer) { sx_report(SX_FATAL, "error allocating %lu " "bytes: %s\n", togot + 2, strerror(errno)); } memset(recvbuffer, 0, togot + 2); if (!eon || *eon != '\n') { sx_report(SX_ERROR,"A-code finished with wrong" " char '%c'(%s)\n", eon ? *eon : '0', response); exit(1); } if ((unsigned)(off - ((eon + 1) - response)) > togot) { // full response and more data is already in buffer memcpy(recvbuffer, eon + 1, togot); offset = togot; memmove(response, eon + 1 + togot, off - ((eon + 1) - response) - togot); off -= togot + ((eon + 1) - response); memset(response + off, 0, sizeof(response) - off); } else { /* response is not yet fully buffered */ memcpy(recvbuffer, eon + 1, off - ((eon + 1) - response)); offset = off - ((eon + 1) - response); memset(response, 0, sizeof(response)); off = 0; } SX_DEBUG(debug_expander > 5, "starting read with ready '%.*s', waiting for " "%lu\n", (int)offset, recvbuffer, togot - offset); if (off > 0) goto have3; if (offset == togot) goto reread2; reread: ret = bgpq_selread(b, recvbuffer + offset, togot - offset); if (ret < 0) { if (errno == EAGAIN) goto reread; sx_report(SX_FATAL,"Error reading IRRd: %s " "(dequeue, result)\n", strerror(errno)); } else if (ret == 0) { sx_report(SX_FATAL,"EOF from IRRd (dequeue, " "result)\n"); } SX_DEBUG(debug_expander > 5, "Read1: got '%.*s'\n", ret, recvbuffer + offset); offset += ret; if (offset < togot) { SX_DEBUG(debug_expander > 5, "expected %lu, got " "%lu expanding %s", togot, strlen(recvbuffer), req->request); goto reread; } reread2: ret = bgpq_selread(b, response + off, sizeof(response) - off); if (ret < 0) { if (errno == EAGAIN) goto reread2; sx_report(SX_FATAL,"Error reading IRRd: %s " "(dequeue,final)\n", strerror(errno)); } else if (ret == 0) { sx_report(SX_FATAL,"EOF from IRRd (dequeue," "final)\n"); } SX_DEBUG(debug_expander > 5, "Read2: got '%.*s'\n", ret, response + off); off += ret; have3: if (!(cres = strchr(response, '\n'))) goto reread2; SX_DEBUG(debug_expander>=3,"Got %s (%lu bytes of %lu) " "in response to %sfinal code: %.*s", recvbuffer, strlen(recvbuffer), togot, req->request, off, response); for (c = recvbuffer; c < recvbuffer + togot;) { size_t spn=strcspn(c," \n"); if (spn) c[spn] = 0; if (c[0] == 0) break; req->callback(c, b, req); c += spn + 1; } assert(c == recvbuffer + togot); memset(recvbuffer, 0, togot + 2); free(recvbuffer); } else if (response[0] == 'C') { /* No data */ SX_DEBUG(debug_expander,"No data expanding %s\n", req->request); if (b->validate_asns) bgpq_expander_invalidate_asn(b, req->request); } else if (response[0] == 'D') { /* .... */ SX_DEBUG(debug_expander,"Key not found expanding %s\n", req->request); if (b->validate_asns) bgpq_expander_invalidate_asn(b, req->request); } else if (response[0] == 'E') { sx_report(SX_ERROR, "Multiple keys expanding %s: %s\n", req->request, response); } else if ( response[0] == 'F') { sx_report(SX_ERROR, "Error expanding %s: %s\n", req->request, response); } else { sx_report(SX_ERROR,"Wrong reply: %s to %s\n", response, req->request); exit(1); } memmove(response, cres + 1, off - ((cres + 1) - response)); off -= (cres + 1) - response; memset(response + off, 0, sizeof(response) - off); SX_DEBUG(debug_expander > 5, "fixed response of %i, %.*s\n", off, off, response); STAILQ_REMOVE_HEAD(&b->rq, next); b->piped--; request_free(req); } return 0; } int bgpq_expand_irrd(struct bgpq_expander *b, int (*callback)(char *, struct bgpq_expander *, struct request *), void *udata, char *fmt, ...) { char request[128], response[128]; va_list ap; ssize_t ret; int off = 0; struct request *req; va_start(ap, fmt); vsnprintf(request, sizeof(request), fmt, ap); va_end(ap); req = request_alloc(request, callback, udata); SX_DEBUG(debug_expander, "expander sending: %s", request); if ((ret = write(b->fd, request, strlen(request)) == 0) || ret == -1) err(1, "write"); memset(response, 0, sizeof(response)); repeat: ret = bgpq_selread(b, response+off, sizeof(response)-off); if (ret < 0) { sx_report(SX_ERROR, "Error reading IRRd: %s\n", strerror(errno)); exit(1); } else if (ret == 0) { sx_report(SX_FATAL, "EOF reading IRRd\n"); exit(1); } off += ret; if (strchr(response, '\n') == NULL) goto repeat; SX_DEBUG(debug_expander > 2, "expander: initially got %lu bytes, " "'%s'\n", (unsigned long)strlen(response), response); if (response[0] == 'A') { char *eon, *c; long togot = strtoul(response + 1, &eon, 10); char *recvbuffer = malloc(togot + 2); int offset = 0; if (!recvbuffer) { sx_report(SX_FATAL, "Error allocating %lu bytes: %s\n", togot + 2, strerror(errno)); } if (eon && *eon != '\n') { sx_report(SX_ERROR,"A-code finished with wrong char " "'%c' (%s)\n", *eon, response); exit(1); } if (off - ((eon + 1) - response) > togot) { memcpy(recvbuffer, eon + 1, togot); offset = togot; memmove(response, eon + 1 + togot, off - ((eon + 1) - response) - togot); off -= togot + ((eon + 1) - response); memset(response+off, 0, sizeof(response) - off); } else { memcpy(recvbuffer, eon + 1, off - ((eon + 1) - response)); offset = off - ((eon + 1) - response); memset(response, 0, sizeof(response)); off = 0; } if (off > 0) goto have3; if (offset == togot) goto reread2; reread: ret = bgpq_selread(b, recvbuffer + offset, togot - offset); if (ret == 0) { sx_report(SX_FATAL,"EOF from IRRd (expand,result)\n"); } else if (ret < 0) { sx_report(SX_FATAL,"Error reading IRRd: %s " "(expand,result)\n", strerror(errno)); } offset += ret; if (offset < togot) goto reread; reread2: ret = bgpq_selread(b, response+off, sizeof(response) - off); if (ret < 0) { sx_report(SX_FATAL, "error reading IRRd: %s\n", strerror(errno)); exit(1); } else if (ret == 0) { sx_report(SX_FATAL, "eof reading IRRd\n"); exit(1); } off += ret; have3: if (!strchr(response, '\n')) goto reread2; SX_DEBUG(debug_expander > 2,"expander: final reply of %lu bytes," " %.*sreturn code %.*s", (unsigned long)strlen(recvbuffer), offset, recvbuffer, off, response); 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, b, req); c += spn + 1; } memset(recvbuffer, 0, togot + 2); free(recvbuffer); } else if (response[0] == 'C') { /* no data */ if (b->validate_asns) bgpq_expander_invalidate_asn(b, request); } else if (response[0] == 'D') { /* ... */ if (b->validate_asns) bgpq_expander_invalidate_asn(b, request); } else if (response[0] == 'E') { /* XXXXXX */ } else if (response[0] == 'F') { /* XXXXXX */ } else { sx_report(SX_ERROR,"Wrong reply: %s\n", response); exit(0); } request_free(req); return 0; } int bgpq_expand(struct bgpq_expander *b) { int fd = -1, err, ret, aquery = 0; struct slentry *mc; struct addrinfo hints, *res = NULL, *rp; struct linger sl; struct asn_entry *asne; sl.l_onoff = 1; sl.l_linger = 5; memset(&hints, 0, sizeof(struct addrinfo)); hints.ai_socktype = SOCK_STREAM; err=getaddrinfo(b->server, b->port, &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 || errno == EAFNOSUPPORT) continue; sx_report(SX_ERROR,"Unable to create socket: %s\n", strerror(errno)); exit(1); } if (setsockopt(fd, SOL_SOCKET, SO_LINGER, &sl, sizeof(struct linger))) { sx_report(SX_ERROR,"Unable to set linger on socket: " "%s\n", strerror(errno)); close(fd); exit(1); } err = connect(fd, rp->ai_addr, rp->ai_addrlen); if (err) { close(fd); fd = -1; continue; } err = sx_maxsockbuf(fd, SO_SNDBUF); if (err > 0) { SX_DEBUG(debug_expander, "Acquired sendbuf of %i " "bytes\n", err); } else { close(fd); fd = -1; continue; } break; } freeaddrinfo(res); if (fd == -1) { /* 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); } b->fd = fd; SX_DEBUG(debug_expander, "Sending '!!' to server to request for the" " connection to remain open\n"); if ((ret = write(fd, "!!\n", 3)) != 3) { sx_report(SX_ERROR, "Partial write of multiple command mode " "to IRRd: %i bytes, %s\n", ret, strerror(errno)); exit(1); } if (b->identify) { SX_DEBUG(debug_expander, "b->identify: Sending '!n " PACKAGE_STRING "' to server.\n"); char ident[128]; int ilen = snprintf(ident, sizeof(ident), "!n" PACKAGE_STRING "\n"); if (ilen > 0) { if ((ret = write(fd, ident, ilen)) != ilen) { sx_report(SX_ERROR, "Partial write of " "identifier to IRRd: %i bytes, %s\n", ret, strerror(errno)); exit(1); } memset(ident, 0, sizeof(ident)); if (0 < read(fd, ident, sizeof(ident))) { SX_DEBUG(debug_expander, "Got answer %s", ident); } else { sx_report(SX_ERROR, "ident, failed read from IRRd\n"); exit(1); } } else { sx_report(SX_ERROR, "snprintf(ident) failed\n"); exit(1); } } /* Test whether the server has support for the A query */ if (b->generation >= T_PREFIXLIST && !STAILQ_EMPTY(&b->macroses)) { char aret[128]; char aresp[] = "F Missing required set name for A query"; SX_DEBUG(debug_expander, "Testing support for A queries\n"); if ((ret = write(fd, "!a\n", 3)) != 3) { sx_report(SX_ERROR, "Partial write of '!a' test query " "to IRRd: %i bytes, %s\n", ret, strerror(errno)); exit(1); } memset(aret, 0, sizeof(aret)); if (0 < read(fd, aret, sizeof(aret))) { if (strncmp(aret, aresp, strlen(aresp)) == 0) { SX_DEBUG(debug_expander, "Server supports A query\n"); aquery = 1; } else { SX_DEBUG(debug_expander, "No support for A query\n"); } } else { sx_report(SX_ERROR, "A query test failed read from IRRd\n"); exit(1); } } if (b->sources && b->sources[0] != 0) { int slen = strlen(b->sources) + 4; if (slen < 128) slen = 128; char sources[slen]; slen = snprintf(sources, sizeof(sources), "!s%s\n", b->sources); if (slen > 0) { SX_DEBUG(debug_expander, "Requesting sources %s", sources); if ((ret = write(fd, sources, slen)) != slen) { sx_report(SX_ERROR, "Partial write of sources to " "IRRd: %i bytes, %s\n", ret, strerror(errno)); exit(1); } memset(sources, 0, sizeof(sources)); if (0 < read(fd, sources, sizeof(sources))) { SX_DEBUG(debug_expander, "Got answer %s", sources); if (sources[0] != 'C') { sx_report(SX_ERROR, "Invalid source(s) " "'%s': %s\n", b->sources, sources); exit(1); } } else { sx_report(SX_ERROR, "failed to read sources\n"); exit(1); } } else { sx_report(SX_ERROR, "snprintf(sources) failed\n"); exit(1); } } if (pipelining) fcntl(fd, F_SETFL, O_NONBLOCK|(fcntl(fd, F_GETFL))); STAILQ_FOREACH(mc, &b->macroses, entry) { if (!b->maxdepth && RB_EMPTY(&b->stoplist)) { if (aquery) bgpq_expand_irrd(b, bgpq_expanded_prefix, b, "!a%s%s\n", b->family == AF_INET ? "4" : "6", mc->text); else bgpq_expand_irrd(b, bgpq_expanded_macro, b, "!i%s,1\n", mc->text); } else { bgpq_expander_add_already(b, mc->text); if (pipelining) bgpq_pipeline(b, bgpq_expanded_macro_limit, NULL, "!i%s\n", mc->text); else bgpq_expand_irrd(b, bgpq_expanded_macro_limit, NULL, "!i%s\n", mc->text); } } if (pipelining) { if (!STAILQ_EMPTY(&b->wq)) bgpq_write(b); if (!STAILQ_EMPTY(&b->rq)) bgpq_read(b); } if (b->generation >= T_PREFIXLIST || b->validate_asns) { STAILQ_FOREACH(mc, &b->rsets, entry) { if (b->family == AF_INET) bgpq_expand_irrd(b, bgpq_expanded_prefix, NULL, "!i%s,1\n", mc->text); else bgpq_expand_irrd(b, bgpq_expanded_v6prefix, NULL, "!i%s,1\n", mc->text); } RB_FOREACH(asne, asn_tree, &b->asnlist) { if (b->family == AF_INET6) { if (!pipelining) { bgpq_expand_irrd(b, bgpq_expanded_v6prefix, NULL, "!6as%" PRIu32 "\n", asne->asn); } else { bgpq_pipeline(b, bgpq_expanded_v6prefix, NULL, "!6as%" PRIu32 "\n", asne->asn); } } else { if (!pipelining) { bgpq_expand_irrd(b, bgpq_expanded_prefix, NULL, "!gas%" PRIu32 "\n", asne->asn); } else { bgpq_pipeline(b, bgpq_expanded_prefix, NULL, "!gas%" PRIu32 "\n", asne->asn); } } } if (pipelining) { if (!STAILQ_EMPTY(&b->wq)) bgpq_write(b); if (!STAILQ_EMPTY(&b->rq)) bgpq_read(b); } } if ((ret = write(fd, "!q\n", 3)) != 3) { sx_report(SX_ERROR, "Partial write of quit to IRRd: %i bytes, %s\n", ret, strerror(errno)); // not worth exiting due to this } if (pipelining) { int fl = fcntl(fd, F_GETFL); fl &= ~O_NONBLOCK; fcntl(fd, F_SETFL, fl); } close(fd); return 1; } void sx_radix_node_freeall(struct sx_radix_node *n) { if (n->l != NULL) sx_radix_node_freeall(n->l); if (n->r != NULL) sx_radix_node_freeall(n->r); if (n->son != NULL) sx_radix_node_freeall(n->son); if (n->payload) free(n->payload); sx_prefix_destroy(n->prefix); free(n); } void sx_radix_tree_freeall(struct sx_radix_tree *t) { if (t->head != NULL) sx_radix_node_freeall(t->head); free(t); } void bgpq_prequest_freeall(struct bgpq_prequest *bpr) { } void expander_freeall(struct bgpq_expander *expander) { struct sx_tentry *var, *nxt; struct asn_entry *asne, *asne_next; while (!STAILQ_EMPTY(&expander->macroses)) { struct slentry *n1 = STAILQ_FIRST(&expander->macroses); STAILQ_REMOVE_HEAD(&expander->macroses, entry); free(n1->text); free(n1); } while (!STAILQ_EMPTY(&expander->rsets)) { struct slentry *n1 = STAILQ_FIRST(&expander->rsets); STAILQ_REMOVE_HEAD(&expander->rsets, entry); free(n1->text); free(n1); } for (var = RB_MIN(tentree, &expander->already); var != NULL; var = nxt) { nxt = RB_NEXT(tentree, &expander->already, var); RB_REMOVE(tentree, &expander->already, var); free(var->text); free(var); } for (var = RB_MIN(tentree, &expander->stoplist); var != NULL; var = nxt) { nxt = RB_NEXT(tentree, &expander->stoplist, var); RB_REMOVE(tentree, &expander->stoplist, var); free(var->text); free(var); } for (asne = RB_MIN(asn_tree, &expander->asnlist); asne != NULL; asne = asne_next) { asne_next = RB_NEXT(asn_tree, &expander->asnlist, asne); RB_REMOVE(asn_tree, &expander->asnlist, asne); free(asne); } sx_radix_tree_freeall(expander->tree); bgpq_prequest_freeall(expander->firstpipe); bgpq_prequest_freeall(expander->lastpipe); } bgpq4-1.4/extern.h000066400000000000000000000103711410770527700140650ustar00rootroot00000000000000/* * Copyright (c) 2007-2019 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. */ #include #include #include "sx_prefix.h" struct slentry { STAILQ_ENTRY(slentry) entry; char *text; }; struct slentry *sx_slentry_new(char *text); struct sx_tentry { RB_ENTRY(sx_tentry) entry; char *text; }; struct sx_tentry *sx_tentry_new(char *text); struct asn_entry { RB_ENTRY(asn_entry) entry; uint32_t asn; }; typedef enum { V_CISCO = 0, V_JUNIPER, V_CISCO_XR, V_JSON, V_BIRD, V_OPENBGPD, V_FORMAT, V_NOKIA, V_HUAWEI, V_MIKROTIK, V_NOKIA_MD, V_ARISTA } bgpq_vendor_t; typedef enum { T_NONE = 0, T_ASPATH, T_OASPATH, T_ASSET, T_PREFIXLIST, T_EACL, T_ROUTE_FILTER_LIST } bgpq_gen_t; struct bgpq_expander; struct request { STAILQ_ENTRY(request) next; char *request; int size, offset; void *udata; unsigned int depth; int (*callback)(char *, struct bgpq_expander *, struct request *); }; struct bgpq_expander { struct sx_radix_tree *tree; int family; char *sources; uint32_t asnumber; int aswidth; char *name; bgpq_vendor_t vendor; bgpq_gen_t generation; int identify; int sequence; unsigned int maxdepth; unsigned int cdepth; int validate_asns; struct bgpq_prequest *firstpipe, *lastpipe; int piped; char *match; char *server; char *port; char *format; unsigned int maxlen; int fd; RB_HEAD(asn_tree, asn_entry) asnlist; STAILQ_HEAD(requests, request) wq, rq; STAILQ_HEAD(slentries, slentry) macroses, rsets; RB_HEAD(tentree, sx_tentry) already, stoplist; }; int asn_cmp(struct asn_entry *, struct asn_entry *); RB_PROTOTYPE(asn_tree, asn_entry, entry, asn_cmp); 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_expander_add_prefix_range(struct bgpq_expander *b, char *prefix); int bgpq_expander_add_stop(struct bgpq_expander *b, char *object); int bgpq_expand(struct bgpq_expander *b); void bgpq4_print_prefixlist(FILE *f, struct bgpq_expander *b); void bgpq4_print_eacl(FILE *f, struct bgpq_expander *b); void bgpq4_print_aspath(FILE *f, struct bgpq_expander *b); void bgpq4_print_asset(FILE *f, struct bgpq_expander *b); void bgpq4_print_oaspath(FILE *f, struct bgpq_expander *b); void bgpq4_print_route_filter_list(FILE *f, struct bgpq_expander *b); void sx_radix_node_freeall(struct sx_radix_node *n); void sx_radix_tree_freeall(struct sx_radix_tree *t); void bgpq_prequest_freeall(struct bgpq_prequest *bpr); void expander_freeall(struct bgpq_expander *expander); /* s - number of opened socket, dir is either SO_SNDBUF or SO_RCVBUF */ int sx_maxsockbuf(int s, int dir); #ifndef HAVE_STRLCPY size_t strlcpy(char *dst, const char *src, size_t size); #endif bgpq4-1.4/include/000077500000000000000000000000001410770527700140305ustar00rootroot00000000000000bgpq4-1.4/include/Makefile.am000066400000000000000000000002431410770527700160630ustar00rootroot00000000000000noinst_HEADERS = noinst_HEADERS += sys/_null.h noinst_HEADERS += sys/queue.h noinst_HEADERS += sys/tree.h noinst_HEADERS += sys/types.h noinst_HEADERS += string.h bgpq4-1.4/include/string.h000066400000000000000000000007511410770527700155120ustar00rootroot00000000000000/* * Public domain * string.h compatibility shim */ #include_next #ifndef LIBCOMPAT_STRING_H #define LIBCOMPAT_STRING_H #include #if defined(__sun) || defined(__hpux) /* Some functions historically defined in string.h were placed in strings.h by * SUS. Use the same hack as OS X and FreeBSD use to work around on Solaris and HPUX. */ #include #endif #ifndef HAVE_STRLCPY size_t strlcpy(char *dst, const char *src, size_t siz); #endif #endif bgpq4-1.4/include/sys/000077500000000000000000000000001410770527700146465ustar00rootroot00000000000000bgpq4-1.4/include/sys/_null.h000066400000000000000000000005151410770527700161310ustar00rootroot00000000000000/* $OpenBSD: _null.h,v 1.2 2016/09/09 22:07:58 millert Exp $ */ /* * Written by Todd C. Miller, September 9, 2016 * Public domain. */ #ifndef NULL #if !defined(__cplusplus) #define NULL ((void *)0) #elif __cplusplus >= 201103L #define NULL nullptr #elif defined(__GNUG__) #define NULL __null #else #define NULL 0L #endif #endif bgpq4-1.4/include/sys/queue.h000066400000000000000000000517271410770527700161570ustar00rootroot00000000000000/* $OpenBSD: queue.h,v 1.46 2020/12/30 13:33:12 millert Exp $ */ /* $NetBSD: queue.h,v 1.11 1996/05/16 05:17:14 mycroft Exp $ */ /* * Copyright (c) 1991, 1993 * The Regents of the University of California. 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. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS 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 REGENTS 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. * * @(#)queue.h 8.5 (Berkeley) 8/20/94 */ #ifndef _SYS_QUEUE_H_ #define _SYS_QUEUE_H_ #include /* * This file defines five types of data structures: singly-linked lists, * lists, simple queues, tail queues and XOR simple queues. * * * A singly-linked list is headed by a single forward pointer. The elements * are singly linked for minimum space and pointer manipulation overhead at * the expense of O(n) removal for arbitrary elements. New elements can be * added to the list after an existing element or at the head of the list. * Elements being removed from the head of the list should use the explicit * macro for this purpose for optimum efficiency. A singly-linked list may * only be traversed in the forward direction. Singly-linked lists are ideal * for applications with large datasets and few or no removals or for * implementing a LIFO queue. * * A list is headed by a single forward pointer (or an array of forward * pointers for a hash table header). The elements are doubly linked * so that an arbitrary element can be removed without a need to * traverse the list. New elements can be added to the list before * or after an existing element or at the head of the list. A list * may only be traversed in the forward direction. * * A simple queue is headed by a pair of pointers, one to the head of the * list and the other to the tail of the list. The elements are singly * linked to save space, so elements can only be removed from the * head of the list. New elements can be added to the list before or after * an existing element, at the head of the list, or at the end of the * list. A simple queue may only be traversed in the forward direction. * * A tail queue is headed by a pair of pointers, one to the head of the * list and the other to the tail of the list. The elements are doubly * linked so that an arbitrary element can be removed without a need to * traverse the list. New elements can be added to the list before or * after an existing element, at the head of the list, or at the end of * the list. A tail queue may be traversed in either direction. * * An XOR simple queue is used in the same way as a regular simple queue. * The difference is that the head structure also includes a "cookie" that * is XOR'd with the queue pointer (first, last or next) to generate the * real pointer value. * * For details on the use of these macros, see the queue(3) manual page. */ #if defined(QUEUE_MACRO_DEBUG) || (defined(_KERNEL) && defined(DIAGNOSTIC)) #define _Q_INVALID ((void *)-1) #define _Q_INVALIDATE(a) (a) = _Q_INVALID #else #define _Q_INVALIDATE(a) #endif /* * Singly-linked List definitions. */ #define SLIST_HEAD(name, type) \ struct name { \ struct type *slh_first; /* first element */ \ } #define SLIST_HEAD_INITIALIZER(head) \ { NULL } #define SLIST_ENTRY(type) \ struct { \ struct type *sle_next; /* next element */ \ } /* * Singly-linked List access methods. */ #define SLIST_FIRST(head) ((head)->slh_first) #define SLIST_END(head) NULL #define SLIST_EMPTY(head) (SLIST_FIRST(head) == SLIST_END(head)) #define SLIST_NEXT(elm, field) ((elm)->field.sle_next) #define SLIST_FOREACH(var, head, field) \ for((var) = SLIST_FIRST(head); \ (var) != SLIST_END(head); \ (var) = SLIST_NEXT(var, field)) #define SLIST_FOREACH_SAFE(var, head, field, tvar) \ for ((var) = SLIST_FIRST(head); \ (var) && ((tvar) = SLIST_NEXT(var, field), 1); \ (var) = (tvar)) /* * Singly-linked List functions. */ #define SLIST_INIT(head) { \ SLIST_FIRST(head) = SLIST_END(head); \ } #define SLIST_INSERT_AFTER(slistelm, elm, field) do { \ (elm)->field.sle_next = (slistelm)->field.sle_next; \ (slistelm)->field.sle_next = (elm); \ } while (0) #define SLIST_INSERT_HEAD(head, elm, field) do { \ (elm)->field.sle_next = (head)->slh_first; \ (head)->slh_first = (elm); \ } while (0) #define SLIST_REMOVE_AFTER(elm, field) do { \ (elm)->field.sle_next = (elm)->field.sle_next->field.sle_next; \ } while (0) #define SLIST_REMOVE_HEAD(head, field) do { \ (head)->slh_first = (head)->slh_first->field.sle_next; \ } while (0) #define SLIST_REMOVE(head, elm, type, field) do { \ if ((head)->slh_first == (elm)) { \ SLIST_REMOVE_HEAD((head), field); \ } else { \ struct type *curelm = (head)->slh_first; \ \ while (curelm->field.sle_next != (elm)) \ curelm = curelm->field.sle_next; \ curelm->field.sle_next = \ curelm->field.sle_next->field.sle_next; \ } \ _Q_INVALIDATE((elm)->field.sle_next); \ } while (0) /* * List definitions. */ #define LIST_HEAD(name, type) \ struct name { \ struct type *lh_first; /* first element */ \ } #define LIST_HEAD_INITIALIZER(head) \ { NULL } #define LIST_ENTRY(type) \ struct { \ struct type *le_next; /* next element */ \ struct type **le_prev; /* address of previous next element */ \ } /* * List access methods. */ #define LIST_FIRST(head) ((head)->lh_first) #define LIST_END(head) NULL #define LIST_EMPTY(head) (LIST_FIRST(head) == LIST_END(head)) #define LIST_NEXT(elm, field) ((elm)->field.le_next) #define LIST_FOREACH(var, head, field) \ for((var) = LIST_FIRST(head); \ (var)!= LIST_END(head); \ (var) = LIST_NEXT(var, field)) #define LIST_FOREACH_SAFE(var, head, field, tvar) \ for ((var) = LIST_FIRST(head); \ (var) && ((tvar) = LIST_NEXT(var, field), 1); \ (var) = (tvar)) /* * List functions. */ #define LIST_INIT(head) do { \ LIST_FIRST(head) = LIST_END(head); \ } while (0) #define LIST_INSERT_AFTER(listelm, elm, field) do { \ if (((elm)->field.le_next = (listelm)->field.le_next) != NULL) \ (listelm)->field.le_next->field.le_prev = \ &(elm)->field.le_next; \ (listelm)->field.le_next = (elm); \ (elm)->field.le_prev = &(listelm)->field.le_next; \ } while (0) #define LIST_INSERT_BEFORE(listelm, elm, field) do { \ (elm)->field.le_prev = (listelm)->field.le_prev; \ (elm)->field.le_next = (listelm); \ *(listelm)->field.le_prev = (elm); \ (listelm)->field.le_prev = &(elm)->field.le_next; \ } while (0) #define LIST_INSERT_HEAD(head, elm, field) do { \ if (((elm)->field.le_next = (head)->lh_first) != NULL) \ (head)->lh_first->field.le_prev = &(elm)->field.le_next;\ (head)->lh_first = (elm); \ (elm)->field.le_prev = &(head)->lh_first; \ } while (0) #define LIST_REMOVE(elm, field) do { \ if ((elm)->field.le_next != NULL) \ (elm)->field.le_next->field.le_prev = \ (elm)->field.le_prev; \ *(elm)->field.le_prev = (elm)->field.le_next; \ _Q_INVALIDATE((elm)->field.le_prev); \ _Q_INVALIDATE((elm)->field.le_next); \ } while (0) #define LIST_REPLACE(elm, elm2, field) do { \ if (((elm2)->field.le_next = (elm)->field.le_next) != NULL) \ (elm2)->field.le_next->field.le_prev = \ &(elm2)->field.le_next; \ (elm2)->field.le_prev = (elm)->field.le_prev; \ *(elm2)->field.le_prev = (elm2); \ _Q_INVALIDATE((elm)->field.le_prev); \ _Q_INVALIDATE((elm)->field.le_next); \ } while (0) /* * Simple queue definitions. */ #define SIMPLEQ_HEAD(name, type) \ struct name { \ struct type *sqh_first; /* first element */ \ struct type **sqh_last; /* addr of last next element */ \ } #define SIMPLEQ_HEAD_INITIALIZER(head) \ { NULL, &(head).sqh_first } #define SIMPLEQ_ENTRY(type) \ struct { \ struct type *sqe_next; /* next element */ \ } /* * Simple queue access methods. */ #define SIMPLEQ_FIRST(head) ((head)->sqh_first) #define SIMPLEQ_END(head) NULL #define SIMPLEQ_EMPTY(head) (SIMPLEQ_FIRST(head) == SIMPLEQ_END(head)) #define SIMPLEQ_NEXT(elm, field) ((elm)->field.sqe_next) #define SIMPLEQ_FOREACH(var, head, field) \ for((var) = SIMPLEQ_FIRST(head); \ (var) != SIMPLEQ_END(head); \ (var) = SIMPLEQ_NEXT(var, field)) #define SIMPLEQ_FOREACH_SAFE(var, head, field, tvar) \ for ((var) = SIMPLEQ_FIRST(head); \ (var) && ((tvar) = SIMPLEQ_NEXT(var, field), 1); \ (var) = (tvar)) /* * Simple queue functions. */ #define SIMPLEQ_INIT(head) do { \ (head)->sqh_first = NULL; \ (head)->sqh_last = &(head)->sqh_first; \ } while (0) #define SIMPLEQ_INSERT_HEAD(head, elm, field) do { \ if (((elm)->field.sqe_next = (head)->sqh_first) == NULL) \ (head)->sqh_last = &(elm)->field.sqe_next; \ (head)->sqh_first = (elm); \ } while (0) #define SIMPLEQ_INSERT_TAIL(head, elm, field) do { \ (elm)->field.sqe_next = NULL; \ *(head)->sqh_last = (elm); \ (head)->sqh_last = &(elm)->field.sqe_next; \ } while (0) #define SIMPLEQ_INSERT_AFTER(head, listelm, elm, field) do { \ if (((elm)->field.sqe_next = (listelm)->field.sqe_next) == NULL)\ (head)->sqh_last = &(elm)->field.sqe_next; \ (listelm)->field.sqe_next = (elm); \ } while (0) #define SIMPLEQ_REMOVE_HEAD(head, field) do { \ if (((head)->sqh_first = (head)->sqh_first->field.sqe_next) == NULL) \ (head)->sqh_last = &(head)->sqh_first; \ } while (0) #define SIMPLEQ_REMOVE_AFTER(head, elm, field) do { \ if (((elm)->field.sqe_next = (elm)->field.sqe_next->field.sqe_next) \ == NULL) \ (head)->sqh_last = &(elm)->field.sqe_next; \ } while (0) #define SIMPLEQ_CONCAT(head1, head2) do { \ if (!SIMPLEQ_EMPTY((head2))) { \ *(head1)->sqh_last = (head2)->sqh_first; \ (head1)->sqh_last = (head2)->sqh_last; \ SIMPLEQ_INIT((head2)); \ } \ } while (0) /* * XOR Simple queue definitions. */ #define XSIMPLEQ_HEAD(name, type) \ struct name { \ struct type *sqx_first; /* first element */ \ struct type **sqx_last; /* addr of last next element */ \ unsigned long sqx_cookie; \ } #define XSIMPLEQ_ENTRY(type) \ struct { \ struct type *sqx_next; /* next element */ \ } /* * XOR Simple queue access methods. */ #define XSIMPLEQ_XOR(head, ptr) ((__typeof(ptr))((head)->sqx_cookie ^ \ (unsigned long)(ptr))) #define XSIMPLEQ_FIRST(head) XSIMPLEQ_XOR(head, ((head)->sqx_first)) #define XSIMPLEQ_END(head) NULL #define XSIMPLEQ_EMPTY(head) (XSIMPLEQ_FIRST(head) == XSIMPLEQ_END(head)) #define XSIMPLEQ_NEXT(head, elm, field) XSIMPLEQ_XOR(head, ((elm)->field.sqx_next)) #define XSIMPLEQ_FOREACH(var, head, field) \ for ((var) = XSIMPLEQ_FIRST(head); \ (var) != XSIMPLEQ_END(head); \ (var) = XSIMPLEQ_NEXT(head, var, field)) #define XSIMPLEQ_FOREACH_SAFE(var, head, field, tvar) \ for ((var) = XSIMPLEQ_FIRST(head); \ (var) && ((tvar) = XSIMPLEQ_NEXT(head, var, field), 1); \ (var) = (tvar)) /* * XOR Simple queue functions. */ #define XSIMPLEQ_INIT(head) do { \ arc4random_buf(&(head)->sqx_cookie, sizeof((head)->sqx_cookie)); \ (head)->sqx_first = XSIMPLEQ_XOR(head, NULL); \ (head)->sqx_last = XSIMPLEQ_XOR(head, &(head)->sqx_first); \ } while (0) #define XSIMPLEQ_INSERT_HEAD(head, elm, field) do { \ if (((elm)->field.sqx_next = (head)->sqx_first) == \ XSIMPLEQ_XOR(head, NULL)) \ (head)->sqx_last = XSIMPLEQ_XOR(head, &(elm)->field.sqx_next); \ (head)->sqx_first = XSIMPLEQ_XOR(head, (elm)); \ } while (0) #define XSIMPLEQ_INSERT_TAIL(head, elm, field) do { \ (elm)->field.sqx_next = XSIMPLEQ_XOR(head, NULL); \ *(XSIMPLEQ_XOR(head, (head)->sqx_last)) = XSIMPLEQ_XOR(head, (elm)); \ (head)->sqx_last = XSIMPLEQ_XOR(head, &(elm)->field.sqx_next); \ } while (0) #define XSIMPLEQ_INSERT_AFTER(head, listelm, elm, field) do { \ if (((elm)->field.sqx_next = (listelm)->field.sqx_next) == \ XSIMPLEQ_XOR(head, NULL)) \ (head)->sqx_last = XSIMPLEQ_XOR(head, &(elm)->field.sqx_next); \ (listelm)->field.sqx_next = XSIMPLEQ_XOR(head, (elm)); \ } while (0) #define XSIMPLEQ_REMOVE_HEAD(head, field) do { \ if (((head)->sqx_first = XSIMPLEQ_XOR(head, \ (head)->sqx_first)->field.sqx_next) == XSIMPLEQ_XOR(head, NULL)) \ (head)->sqx_last = XSIMPLEQ_XOR(head, &(head)->sqx_first); \ } while (0) #define XSIMPLEQ_REMOVE_AFTER(head, elm, field) do { \ if (((elm)->field.sqx_next = XSIMPLEQ_XOR(head, \ (elm)->field.sqx_next)->field.sqx_next) \ == XSIMPLEQ_XOR(head, NULL)) \ (head)->sqx_last = \ XSIMPLEQ_XOR(head, &(elm)->field.sqx_next); \ } while (0) /* * Tail queue definitions. */ #define TAILQ_HEAD(name, type) \ struct name { \ struct type *tqh_first; /* first element */ \ struct type **tqh_last; /* addr of last next element */ \ } #define TAILQ_HEAD_INITIALIZER(head) \ { NULL, &(head).tqh_first } #define TAILQ_ENTRY(type) \ struct { \ struct type *tqe_next; /* next element */ \ struct type **tqe_prev; /* address of previous next element */ \ } /* * Tail queue access methods. */ #define TAILQ_FIRST(head) ((head)->tqh_first) #define TAILQ_END(head) NULL #define TAILQ_NEXT(elm, field) ((elm)->field.tqe_next) #define TAILQ_LAST(head, headname) \ (*(((struct headname *)((head)->tqh_last))->tqh_last)) /* XXX */ #define TAILQ_PREV(elm, headname, field) \ (*(((struct headname *)((elm)->field.tqe_prev))->tqh_last)) #define TAILQ_EMPTY(head) \ (TAILQ_FIRST(head) == TAILQ_END(head)) #define TAILQ_FOREACH(var, head, field) \ for((var) = TAILQ_FIRST(head); \ (var) != TAILQ_END(head); \ (var) = TAILQ_NEXT(var, field)) #define TAILQ_FOREACH_SAFE(var, head, field, tvar) \ for ((var) = TAILQ_FIRST(head); \ (var) != TAILQ_END(head) && \ ((tvar) = TAILQ_NEXT(var, field), 1); \ (var) = (tvar)) #define TAILQ_FOREACH_REVERSE(var, head, headname, field) \ for((var) = TAILQ_LAST(head, headname); \ (var) != TAILQ_END(head); \ (var) = TAILQ_PREV(var, headname, field)) #define TAILQ_FOREACH_REVERSE_SAFE(var, head, headname, field, tvar) \ for ((var) = TAILQ_LAST(head, headname); \ (var) != TAILQ_END(head) && \ ((tvar) = TAILQ_PREV(var, headname, field), 1); \ (var) = (tvar)) /* * Tail queue functions. */ #define TAILQ_INIT(head) do { \ (head)->tqh_first = NULL; \ (head)->tqh_last = &(head)->tqh_first; \ } while (0) #define TAILQ_INSERT_HEAD(head, elm, field) do { \ if (((elm)->field.tqe_next = (head)->tqh_first) != NULL) \ (head)->tqh_first->field.tqe_prev = \ &(elm)->field.tqe_next; \ else \ (head)->tqh_last = &(elm)->field.tqe_next; \ (head)->tqh_first = (elm); \ (elm)->field.tqe_prev = &(head)->tqh_first; \ } while (0) #define TAILQ_INSERT_TAIL(head, elm, field) do { \ (elm)->field.tqe_next = NULL; \ (elm)->field.tqe_prev = (head)->tqh_last; \ *(head)->tqh_last = (elm); \ (head)->tqh_last = &(elm)->field.tqe_next; \ } while (0) #define TAILQ_INSERT_AFTER(head, listelm, elm, field) do { \ if (((elm)->field.tqe_next = (listelm)->field.tqe_next) != NULL)\ (elm)->field.tqe_next->field.tqe_prev = \ &(elm)->field.tqe_next; \ else \ (head)->tqh_last = &(elm)->field.tqe_next; \ (listelm)->field.tqe_next = (elm); \ (elm)->field.tqe_prev = &(listelm)->field.tqe_next; \ } while (0) #define TAILQ_INSERT_BEFORE(listelm, elm, field) do { \ (elm)->field.tqe_prev = (listelm)->field.tqe_prev; \ (elm)->field.tqe_next = (listelm); \ *(listelm)->field.tqe_prev = (elm); \ (listelm)->field.tqe_prev = &(elm)->field.tqe_next; \ } while (0) #define TAILQ_REMOVE(head, elm, field) do { \ if (((elm)->field.tqe_next) != NULL) \ (elm)->field.tqe_next->field.tqe_prev = \ (elm)->field.tqe_prev; \ else \ (head)->tqh_last = (elm)->field.tqe_prev; \ *(elm)->field.tqe_prev = (elm)->field.tqe_next; \ _Q_INVALIDATE((elm)->field.tqe_prev); \ _Q_INVALIDATE((elm)->field.tqe_next); \ } while (0) #define TAILQ_REPLACE(head, elm, elm2, field) do { \ if (((elm2)->field.tqe_next = (elm)->field.tqe_next) != NULL) \ (elm2)->field.tqe_next->field.tqe_prev = \ &(elm2)->field.tqe_next; \ else \ (head)->tqh_last = &(elm2)->field.tqe_next; \ (elm2)->field.tqe_prev = (elm)->field.tqe_prev; \ *(elm2)->field.tqe_prev = (elm2); \ _Q_INVALIDATE((elm)->field.tqe_prev); \ _Q_INVALIDATE((elm)->field.tqe_next); \ } while (0) #define TAILQ_CONCAT(head1, head2, field) do { \ if (!TAILQ_EMPTY(head2)) { \ *(head1)->tqh_last = (head2)->tqh_first; \ (head2)->tqh_first->field.tqe_prev = (head1)->tqh_last; \ (head1)->tqh_last = (head2)->tqh_last; \ TAILQ_INIT((head2)); \ } \ } while (0) /* * Singly-linked Tail queue declarations. */ #define STAILQ_HEAD(name, type) \ struct name { \ struct type *stqh_first; /* first element */ \ struct type **stqh_last; /* addr of last next element */ \ } #define STAILQ_HEAD_INITIALIZER(head) \ { NULL, &(head).stqh_first } #define STAILQ_ENTRY(type) \ struct { \ struct type *stqe_next; /* next element */ \ } /* * Singly-linked Tail queue access methods. */ #define STAILQ_FIRST(head) ((head)->stqh_first) #define STAILQ_END(head) NULL #define STAILQ_EMPTY(head) (STAILQ_FIRST(head) == STAILQ_END(head)) #define STAILQ_NEXT(elm, field) ((elm)->field.stqe_next) #define STAILQ_FOREACH(var, head, field) \ for ((var) = STAILQ_FIRST(head); \ (var) != STAILQ_END(head); \ (var) = STAILQ_NEXT(var, field)) #define STAILQ_FOREACH_SAFE(var, head, field, tvar) \ for ((var) = STAILQ_FIRST(head); \ (var) && ((tvar) = STAILQ_NEXT(var, field), 1); \ (var) = (tvar)) /* * Singly-linked Tail queue functions. */ #define STAILQ_INIT(head) do { \ STAILQ_FIRST((head)) = NULL; \ (head)->stqh_last = &STAILQ_FIRST((head)); \ } while (0) #define STAILQ_INSERT_HEAD(head, elm, field) do { \ if ((STAILQ_NEXT((elm), field) = STAILQ_FIRST((head))) == NULL) \ (head)->stqh_last = &STAILQ_NEXT((elm), field); \ STAILQ_FIRST((head)) = (elm); \ } while (0) #define STAILQ_INSERT_TAIL(head, elm, field) do { \ STAILQ_NEXT((elm), field) = NULL; \ *(head)->stqh_last = (elm); \ (head)->stqh_last = &STAILQ_NEXT((elm), field); \ } while (0) #define STAILQ_INSERT_AFTER(head, listelm, elm, field) do { \ if ((STAILQ_NEXT((elm), field) = STAILQ_NEXT((elm), field)) == NULL)\ (head)->stqh_last = &STAILQ_NEXT((elm), field); \ STAILQ_NEXT((elm), field) = (elm); \ } while (0) #define STAILQ_REMOVE_HEAD(head, field) do { \ if ((STAILQ_FIRST((head)) = \ STAILQ_NEXT(STAILQ_FIRST((head)), field)) == NULL) \ (head)->stqh_last = &STAILQ_FIRST((head)); \ } while (0) #define STAILQ_REMOVE_AFTER(head, elm, field) do { \ if ((STAILQ_NEXT(elm, field) = \ STAILQ_NEXT(STAILQ_NEXT(elm, field), field)) == NULL) \ (head)->stqh_last = &STAILQ_NEXT((elm), field); \ } while (0) #define STAILQ_REMOVE(head, elm, type, field) do { \ if (STAILQ_FIRST((head)) == (elm)) { \ STAILQ_REMOVE_HEAD((head), field); \ } else { \ struct type *curelm = (head)->stqh_first; \ while (STAILQ_NEXT(curelm, field) != (elm)) \ curelm = STAILQ_NEXT(curelm, field); \ STAILQ_REMOVE_AFTER(head, curelm, field); \ } \ } while (0) #define STAILQ_CONCAT(head1, head2) do { \ if (!STAILQ_EMPTY((head2))) { \ *(head1)->stqh_last = (head2)->stqh_first; \ (head1)->stqh_last = (head2)->stqh_last; \ STAILQ_INIT((head2)); \ } \ } while (0) #define STAILQ_LAST(head, type, field) \ (STAILQ_EMPTY((head)) ? NULL : \ ((struct type *)(void *) \ ((char *)((head)->stqh_last) - offsetof(struct type, field)))) #endif /* !_SYS_QUEUE_H_ */ bgpq4-1.4/include/sys/tree.h000066400000000000000000001021171410770527700157600ustar00rootroot00000000000000/* $OpenBSD: tree.h,v 1.30 2020/10/10 18:03:41 otto Exp $ */ /* * Copyright 2002 Niels Provos * 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 ``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 _SYS_TREE_H_ #define _SYS_TREE_H_ #include /* * This file defines data structures for different types of trees: * splay trees and red-black trees. * * A splay tree is a self-organizing data structure. Every operation * on the tree causes a splay to happen. The splay moves the requested * node to the root of the tree and partly rebalances it. * * This has the benefit that request locality causes faster lookups as * the requested nodes move to the top of the tree. On the other hand, * every lookup causes memory writes. * * The Balance Theorem bounds the total access time for m operations * and n inserts on an initially empty tree as O((m + n)lg n). The * amortized cost for a sequence of m accesses to a splay tree is O(lg n); * * A red-black tree is a binary search tree with the node color as an * extra attribute. It fulfills a set of conditions: * - every search path from the root to a leaf consists of the * same number of black nodes, * - each red node (except for the root) has a black parent, * - each leaf node is black. * * Every operation on a red-black tree is bounded as O(lg n). * The maximum height of a red-black tree is 2lg (n+1). */ #define SPLAY_HEAD(name, type) \ struct name { \ struct type *sph_root; /* root of the tree */ \ } #define SPLAY_INITIALIZER(root) \ { NULL } #define SPLAY_INIT(root) do { \ (root)->sph_root = NULL; \ } while (0) #define SPLAY_ENTRY(type) \ struct { \ struct type *spe_left; /* left element */ \ struct type *spe_right; /* right element */ \ } #define SPLAY_LEFT(elm, field) (elm)->field.spe_left #define SPLAY_RIGHT(elm, field) (elm)->field.spe_right #define SPLAY_ROOT(head) (head)->sph_root #define SPLAY_EMPTY(head) (SPLAY_ROOT(head) == NULL) /* SPLAY_ROTATE_{LEFT,RIGHT} expect that tmp hold SPLAY_{RIGHT,LEFT} */ #define SPLAY_ROTATE_RIGHT(head, tmp, field) do { \ SPLAY_LEFT((head)->sph_root, field) = SPLAY_RIGHT(tmp, field); \ SPLAY_RIGHT(tmp, field) = (head)->sph_root; \ (head)->sph_root = tmp; \ } while (0) #define SPLAY_ROTATE_LEFT(head, tmp, field) do { \ SPLAY_RIGHT((head)->sph_root, field) = SPLAY_LEFT(tmp, field); \ SPLAY_LEFT(tmp, field) = (head)->sph_root; \ (head)->sph_root = tmp; \ } while (0) #define SPLAY_LINKLEFT(head, tmp, field) do { \ SPLAY_LEFT(tmp, field) = (head)->sph_root; \ tmp = (head)->sph_root; \ (head)->sph_root = SPLAY_LEFT((head)->sph_root, field); \ } while (0) #define SPLAY_LINKRIGHT(head, tmp, field) do { \ SPLAY_RIGHT(tmp, field) = (head)->sph_root; \ tmp = (head)->sph_root; \ (head)->sph_root = SPLAY_RIGHT((head)->sph_root, field); \ } while (0) #define SPLAY_ASSEMBLE(head, node, left, right, field) do { \ SPLAY_RIGHT(left, field) = SPLAY_LEFT((head)->sph_root, field); \ SPLAY_LEFT(right, field) = SPLAY_RIGHT((head)->sph_root, field);\ SPLAY_LEFT((head)->sph_root, field) = SPLAY_RIGHT(node, field); \ SPLAY_RIGHT((head)->sph_root, field) = SPLAY_LEFT(node, field); \ } while (0) /* Generates prototypes and inline functions */ #define SPLAY_PROTOTYPE(name, type, field, cmp) \ void name##_SPLAY(struct name *, struct type *); \ void name##_SPLAY_MINMAX(struct name *, int); \ struct type *name##_SPLAY_INSERT(struct name *, struct type *); \ struct type *name##_SPLAY_REMOVE(struct name *, struct type *); \ \ /* Finds the node with the same key as elm */ \ static __unused __inline struct type * \ name##_SPLAY_FIND(struct name *head, struct type *elm) \ { \ if (SPLAY_EMPTY(head)) \ return(NULL); \ name##_SPLAY(head, elm); \ if ((cmp)(elm, (head)->sph_root) == 0) \ return (head->sph_root); \ return (NULL); \ } \ \ static __unused __inline struct type * \ name##_SPLAY_NEXT(struct name *head, struct type *elm) \ { \ name##_SPLAY(head, elm); \ if (SPLAY_RIGHT(elm, field) != NULL) { \ elm = SPLAY_RIGHT(elm, field); \ while (SPLAY_LEFT(elm, field) != NULL) { \ elm = SPLAY_LEFT(elm, field); \ } \ } else \ elm = NULL; \ return (elm); \ } \ \ static __unused __inline struct type * \ name##_SPLAY_MIN_MAX(struct name *head, int val) \ { \ name##_SPLAY_MINMAX(head, val); \ return (SPLAY_ROOT(head)); \ } /* Main splay operation. * Moves node close to the key of elm to top */ #define SPLAY_GENERATE(name, type, field, cmp) \ struct type * \ name##_SPLAY_INSERT(struct name *head, struct type *elm) \ { \ if (SPLAY_EMPTY(head)) { \ SPLAY_LEFT(elm, field) = SPLAY_RIGHT(elm, field) = NULL; \ } else { \ int __comp; \ name##_SPLAY(head, elm); \ __comp = (cmp)(elm, (head)->sph_root); \ if(__comp < 0) { \ SPLAY_LEFT(elm, field) = SPLAY_LEFT((head)->sph_root, field);\ SPLAY_RIGHT(elm, field) = (head)->sph_root; \ SPLAY_LEFT((head)->sph_root, field) = NULL; \ } else if (__comp > 0) { \ SPLAY_RIGHT(elm, field) = SPLAY_RIGHT((head)->sph_root, field);\ SPLAY_LEFT(elm, field) = (head)->sph_root; \ SPLAY_RIGHT((head)->sph_root, field) = NULL; \ } else \ return ((head)->sph_root); \ } \ (head)->sph_root = (elm); \ return (NULL); \ } \ \ struct type * \ name##_SPLAY_REMOVE(struct name *head, struct type *elm) \ { \ struct type *__tmp; \ if (SPLAY_EMPTY(head)) \ return (NULL); \ name##_SPLAY(head, elm); \ if ((cmp)(elm, (head)->sph_root) == 0) { \ if (SPLAY_LEFT((head)->sph_root, field) == NULL) { \ (head)->sph_root = SPLAY_RIGHT((head)->sph_root, field);\ } else { \ __tmp = SPLAY_RIGHT((head)->sph_root, field); \ (head)->sph_root = SPLAY_LEFT((head)->sph_root, field);\ name##_SPLAY(head, elm); \ SPLAY_RIGHT((head)->sph_root, field) = __tmp; \ } \ return (elm); \ } \ return (NULL); \ } \ \ void \ name##_SPLAY(struct name *head, struct type *elm) \ { \ struct type __node, *__left, *__right, *__tmp; \ int __comp; \ \ SPLAY_LEFT(&__node, field) = SPLAY_RIGHT(&__node, field) = NULL;\ __left = __right = &__node; \ \ while ((__comp = (cmp)(elm, (head)->sph_root))) { \ if (__comp < 0) { \ __tmp = SPLAY_LEFT((head)->sph_root, field); \ if (__tmp == NULL) \ break; \ if ((cmp)(elm, __tmp) < 0){ \ SPLAY_ROTATE_RIGHT(head, __tmp, field); \ if (SPLAY_LEFT((head)->sph_root, field) == NULL)\ break; \ } \ SPLAY_LINKLEFT(head, __right, field); \ } else if (__comp > 0) { \ __tmp = SPLAY_RIGHT((head)->sph_root, field); \ if (__tmp == NULL) \ break; \ if ((cmp)(elm, __tmp) > 0){ \ SPLAY_ROTATE_LEFT(head, __tmp, field); \ if (SPLAY_RIGHT((head)->sph_root, field) == NULL)\ break; \ } \ SPLAY_LINKRIGHT(head, __left, field); \ } \ } \ SPLAY_ASSEMBLE(head, &__node, __left, __right, field); \ } \ \ /* Splay with either the minimum or the maximum element \ * Used to find minimum or maximum element in tree. \ */ \ void name##_SPLAY_MINMAX(struct name *head, int __comp) \ { \ struct type __node, *__left, *__right, *__tmp; \ \ SPLAY_LEFT(&__node, field) = SPLAY_RIGHT(&__node, field) = NULL;\ __left = __right = &__node; \ \ while (1) { \ if (__comp < 0) { \ __tmp = SPLAY_LEFT((head)->sph_root, field); \ if (__tmp == NULL) \ break; \ if (__comp < 0){ \ SPLAY_ROTATE_RIGHT(head, __tmp, field); \ if (SPLAY_LEFT((head)->sph_root, field) == NULL)\ break; \ } \ SPLAY_LINKLEFT(head, __right, field); \ } else if (__comp > 0) { \ __tmp = SPLAY_RIGHT((head)->sph_root, field); \ if (__tmp == NULL) \ break; \ if (__comp > 0) { \ SPLAY_ROTATE_LEFT(head, __tmp, field); \ if (SPLAY_RIGHT((head)->sph_root, field) == NULL)\ break; \ } \ SPLAY_LINKRIGHT(head, __left, field); \ } \ } \ SPLAY_ASSEMBLE(head, &__node, __left, __right, field); \ } #define SPLAY_NEGINF -1 #define SPLAY_INF 1 #define SPLAY_INSERT(name, x, y) name##_SPLAY_INSERT(x, y) #define SPLAY_REMOVE(name, x, y) name##_SPLAY_REMOVE(x, y) #define SPLAY_FIND(name, x, y) name##_SPLAY_FIND(x, y) #define SPLAY_NEXT(name, x, y) name##_SPLAY_NEXT(x, y) #define SPLAY_MIN(name, x) (SPLAY_EMPTY(x) ? NULL \ : name##_SPLAY_MIN_MAX(x, SPLAY_NEGINF)) #define SPLAY_MAX(name, x) (SPLAY_EMPTY(x) ? NULL \ : name##_SPLAY_MIN_MAX(x, SPLAY_INF)) #define SPLAY_FOREACH(x, name, head) \ for ((x) = SPLAY_MIN(name, head); \ (x) != NULL; \ (x) = SPLAY_NEXT(name, head, x)) /* Macros that define a red-black tree */ #define RB_HEAD(name, type) \ struct name { \ struct type *rbh_root; /* root of the tree */ \ } #define RB_INITIALIZER(root) \ { NULL } #define RB_INIT(root) do { \ (root)->rbh_root = NULL; \ } while (0) #define RB_BLACK 0 #define RB_RED 1 #define RB_ENTRY(type) \ struct { \ struct type *rbe_left; /* left element */ \ struct type *rbe_right; /* right element */ \ struct type *rbe_parent; /* parent element */ \ int rbe_color; /* node color */ \ } #define RB_LEFT(elm, field) (elm)->field.rbe_left #define RB_RIGHT(elm, field) (elm)->field.rbe_right #define RB_PARENT(elm, field) (elm)->field.rbe_parent #define RB_COLOR(elm, field) (elm)->field.rbe_color #define RB_ROOT(head) (head)->rbh_root #define RB_EMPTY(head) (RB_ROOT(head) == NULL) #define RB_SET(elm, parent, field) do { \ RB_PARENT(elm, field) = parent; \ RB_LEFT(elm, field) = RB_RIGHT(elm, field) = NULL; \ RB_COLOR(elm, field) = RB_RED; \ } while (0) #define RB_SET_BLACKRED(black, red, field) do { \ RB_COLOR(black, field) = RB_BLACK; \ RB_COLOR(red, field) = RB_RED; \ } while (0) #ifndef RB_AUGMENT #define RB_AUGMENT(x) do {} while (0) #endif #define RB_ROTATE_LEFT(head, elm, tmp, field) do { \ (tmp) = RB_RIGHT(elm, field); \ if ((RB_RIGHT(elm, field) = RB_LEFT(tmp, field))) { \ RB_PARENT(RB_LEFT(tmp, field), field) = (elm); \ } \ RB_AUGMENT(elm); \ if ((RB_PARENT(tmp, field) = RB_PARENT(elm, field))) { \ if ((elm) == RB_LEFT(RB_PARENT(elm, field), field)) \ RB_LEFT(RB_PARENT(elm, field), field) = (tmp); \ else \ RB_RIGHT(RB_PARENT(elm, field), field) = (tmp); \ } else \ (head)->rbh_root = (tmp); \ RB_LEFT(tmp, field) = (elm); \ RB_PARENT(elm, field) = (tmp); \ RB_AUGMENT(tmp); \ if ((RB_PARENT(tmp, field))) \ RB_AUGMENT(RB_PARENT(tmp, field)); \ } while (0) #define RB_ROTATE_RIGHT(head, elm, tmp, field) do { \ (tmp) = RB_LEFT(elm, field); \ if ((RB_LEFT(elm, field) = RB_RIGHT(tmp, field))) { \ RB_PARENT(RB_RIGHT(tmp, field), field) = (elm); \ } \ RB_AUGMENT(elm); \ if ((RB_PARENT(tmp, field) = RB_PARENT(elm, field))) { \ if ((elm) == RB_LEFT(RB_PARENT(elm, field), field)) \ RB_LEFT(RB_PARENT(elm, field), field) = (tmp); \ else \ RB_RIGHT(RB_PARENT(elm, field), field) = (tmp); \ } else \ (head)->rbh_root = (tmp); \ RB_RIGHT(tmp, field) = (elm); \ RB_PARENT(elm, field) = (tmp); \ RB_AUGMENT(tmp); \ if ((RB_PARENT(tmp, field))) \ RB_AUGMENT(RB_PARENT(tmp, field)); \ } while (0) /* Generates prototypes and inline functions */ #define RB_PROTOTYPE(name, type, field, cmp) \ RB_PROTOTYPE_INTERNAL(name, type, field, cmp,) #define RB_PROTOTYPE_STATIC(name, type, field, cmp) \ RB_PROTOTYPE_INTERNAL(name, type, field, cmp, __attribute__((__unused__)) static) #define RB_PROTOTYPE_INTERNAL(name, type, field, cmp, attr) \ attr void name##_RB_INSERT_COLOR(struct name *, struct type *); \ attr void name##_RB_REMOVE_COLOR(struct name *, struct type *, struct type *);\ attr struct type *name##_RB_REMOVE(struct name *, struct type *); \ attr struct type *name##_RB_INSERT(struct name *, struct type *); \ attr struct type *name##_RB_FIND(struct name *, struct type *); \ attr struct type *name##_RB_NFIND(struct name *, struct type *); \ attr struct type *name##_RB_NEXT(struct type *); \ attr struct type *name##_RB_PREV(struct type *); \ attr struct type *name##_RB_MINMAX(struct name *, int); \ \ /* Main rb operation. * Moves node close to the key of elm to top */ #define RB_GENERATE(name, type, field, cmp) \ RB_GENERATE_INTERNAL(name, type, field, cmp,) #define RB_GENERATE_STATIC(name, type, field, cmp) \ RB_GENERATE_INTERNAL(name, type, field, cmp, __attribute__((__unused__)) static) #define RB_GENERATE_INTERNAL(name, type, field, cmp, attr) \ attr void \ name##_RB_INSERT_COLOR(struct name *head, struct type *elm) \ { \ struct type *parent, *gparent, *tmp; \ while ((parent = RB_PARENT(elm, field)) && \ RB_COLOR(parent, field) == RB_RED) { \ gparent = RB_PARENT(parent, field); \ if (parent == RB_LEFT(gparent, field)) { \ tmp = RB_RIGHT(gparent, field); \ if (tmp && RB_COLOR(tmp, field) == RB_RED) { \ RB_COLOR(tmp, field) = RB_BLACK; \ RB_SET_BLACKRED(parent, gparent, field);\ elm = gparent; \ continue; \ } \ if (RB_RIGHT(parent, field) == elm) { \ RB_ROTATE_LEFT(head, parent, tmp, field);\ tmp = parent; \ parent = elm; \ elm = tmp; \ } \ RB_SET_BLACKRED(parent, gparent, field); \ RB_ROTATE_RIGHT(head, gparent, tmp, field); \ } else { \ tmp = RB_LEFT(gparent, field); \ if (tmp && RB_COLOR(tmp, field) == RB_RED) { \ RB_COLOR(tmp, field) = RB_BLACK; \ RB_SET_BLACKRED(parent, gparent, field);\ elm = gparent; \ continue; \ } \ if (RB_LEFT(parent, field) == elm) { \ RB_ROTATE_RIGHT(head, parent, tmp, field);\ tmp = parent; \ parent = elm; \ elm = tmp; \ } \ RB_SET_BLACKRED(parent, gparent, field); \ RB_ROTATE_LEFT(head, gparent, tmp, field); \ } \ } \ RB_COLOR(head->rbh_root, field) = RB_BLACK; \ } \ \ attr void \ name##_RB_REMOVE_COLOR(struct name *head, struct type *parent, struct type *elm) \ { \ struct type *tmp; \ while ((elm == NULL || RB_COLOR(elm, field) == RB_BLACK) && \ elm != RB_ROOT(head)) { \ if (RB_LEFT(parent, field) == elm) { \ tmp = RB_RIGHT(parent, field); \ if (RB_COLOR(tmp, field) == RB_RED) { \ RB_SET_BLACKRED(tmp, parent, field); \ RB_ROTATE_LEFT(head, parent, tmp, field);\ tmp = RB_RIGHT(parent, field); \ } \ if ((RB_LEFT(tmp, field) == NULL || \ RB_COLOR(RB_LEFT(tmp, field), field) == RB_BLACK) &&\ (RB_RIGHT(tmp, field) == NULL || \ RB_COLOR(RB_RIGHT(tmp, field), field) == RB_BLACK)) {\ RB_COLOR(tmp, field) = RB_RED; \ elm = parent; \ parent = RB_PARENT(elm, field); \ } else { \ if (RB_RIGHT(tmp, field) == NULL || \ RB_COLOR(RB_RIGHT(tmp, field), field) == RB_BLACK) {\ struct type *oleft; \ if ((oleft = RB_LEFT(tmp, field)))\ RB_COLOR(oleft, field) = RB_BLACK;\ RB_COLOR(tmp, field) = RB_RED; \ RB_ROTATE_RIGHT(head, tmp, oleft, field);\ tmp = RB_RIGHT(parent, field); \ } \ RB_COLOR(tmp, field) = RB_COLOR(parent, field);\ RB_COLOR(parent, field) = RB_BLACK; \ if (RB_RIGHT(tmp, field)) \ RB_COLOR(RB_RIGHT(tmp, field), field) = RB_BLACK;\ RB_ROTATE_LEFT(head, parent, tmp, field);\ elm = RB_ROOT(head); \ break; \ } \ } else { \ tmp = RB_LEFT(parent, field); \ if (RB_COLOR(tmp, field) == RB_RED) { \ RB_SET_BLACKRED(tmp, parent, field); \ RB_ROTATE_RIGHT(head, parent, tmp, field);\ tmp = RB_LEFT(parent, field); \ } \ if ((RB_LEFT(tmp, field) == NULL || \ RB_COLOR(RB_LEFT(tmp, field), field) == RB_BLACK) &&\ (RB_RIGHT(tmp, field) == NULL || \ RB_COLOR(RB_RIGHT(tmp, field), field) == RB_BLACK)) {\ RB_COLOR(tmp, field) = RB_RED; \ elm = parent; \ parent = RB_PARENT(elm, field); \ } else { \ if (RB_LEFT(tmp, field) == NULL || \ RB_COLOR(RB_LEFT(tmp, field), field) == RB_BLACK) {\ struct type *oright; \ if ((oright = RB_RIGHT(tmp, field)))\ RB_COLOR(oright, field) = RB_BLACK;\ RB_COLOR(tmp, field) = RB_RED; \ RB_ROTATE_LEFT(head, tmp, oright, field);\ tmp = RB_LEFT(parent, field); \ } \ RB_COLOR(tmp, field) = RB_COLOR(parent, field);\ RB_COLOR(parent, field) = RB_BLACK; \ if (RB_LEFT(tmp, field)) \ RB_COLOR(RB_LEFT(tmp, field), field) = RB_BLACK;\ RB_ROTATE_RIGHT(head, parent, tmp, field);\ elm = RB_ROOT(head); \ break; \ } \ } \ } \ if (elm) \ RB_COLOR(elm, field) = RB_BLACK; \ } \ \ attr struct type * \ name##_RB_REMOVE(struct name *head, struct type *elm) \ { \ struct type *child, *parent, *old = elm; \ int color; \ if (RB_LEFT(elm, field) == NULL) \ child = RB_RIGHT(elm, field); \ else if (RB_RIGHT(elm, field) == NULL) \ child = RB_LEFT(elm, field); \ else { \ struct type *left; \ elm = RB_RIGHT(elm, field); \ while ((left = RB_LEFT(elm, field))) \ elm = left; \ child = RB_RIGHT(elm, field); \ parent = RB_PARENT(elm, field); \ color = RB_COLOR(elm, field); \ if (child) \ RB_PARENT(child, field) = parent; \ if (parent) { \ if (RB_LEFT(parent, field) == elm) \ RB_LEFT(parent, field) = child; \ else \ RB_RIGHT(parent, field) = child; \ RB_AUGMENT(parent); \ } else \ RB_ROOT(head) = child; \ if (RB_PARENT(elm, field) == old) \ parent = elm; \ (elm)->field = (old)->field; \ if (RB_PARENT(old, field)) { \ if (RB_LEFT(RB_PARENT(old, field), field) == old)\ RB_LEFT(RB_PARENT(old, field), field) = elm;\ else \ RB_RIGHT(RB_PARENT(old, field), field) = elm;\ RB_AUGMENT(RB_PARENT(old, field)); \ } else \ RB_ROOT(head) = elm; \ RB_PARENT(RB_LEFT(old, field), field) = elm; \ if (RB_RIGHT(old, field)) \ RB_PARENT(RB_RIGHT(old, field), field) = elm; \ if (parent) { \ left = parent; \ do { \ RB_AUGMENT(left); \ } while ((left = RB_PARENT(left, field))); \ } \ goto color; \ } \ parent = RB_PARENT(elm, field); \ color = RB_COLOR(elm, field); \ if (child) \ RB_PARENT(child, field) = parent; \ if (parent) { \ if (RB_LEFT(parent, field) == elm) \ RB_LEFT(parent, field) = child; \ else \ RB_RIGHT(parent, field) = child; \ RB_AUGMENT(parent); \ } else \ RB_ROOT(head) = child; \ color: \ if (color == RB_BLACK) \ name##_RB_REMOVE_COLOR(head, parent, child); \ return (old); \ } \ \ /* Inserts a node into the RB tree */ \ attr struct type * \ name##_RB_INSERT(struct name *head, struct type *elm) \ { \ struct type *tmp; \ struct type *parent = NULL; \ int comp = 0; \ tmp = RB_ROOT(head); \ while (tmp) { \ parent = tmp; \ comp = (cmp)(elm, parent); \ if (comp < 0) \ tmp = RB_LEFT(tmp, field); \ else if (comp > 0) \ tmp = RB_RIGHT(tmp, field); \ else \ return (tmp); \ } \ RB_SET(elm, parent, field); \ if (parent != NULL) { \ if (comp < 0) \ RB_LEFT(parent, field) = elm; \ else \ RB_RIGHT(parent, field) = elm; \ RB_AUGMENT(parent); \ } else \ RB_ROOT(head) = elm; \ name##_RB_INSERT_COLOR(head, elm); \ return (NULL); \ } \ \ /* Finds the node with the same key as elm */ \ attr struct type * \ name##_RB_FIND(struct name *head, struct type *elm) \ { \ struct type *tmp = RB_ROOT(head); \ int comp; \ while (tmp) { \ comp = cmp(elm, tmp); \ if (comp < 0) \ tmp = RB_LEFT(tmp, field); \ else if (comp > 0) \ tmp = RB_RIGHT(tmp, field); \ else \ return (tmp); \ } \ return (NULL); \ } \ \ /* Finds the first node greater than or equal to the search key */ \ attr struct type * \ name##_RB_NFIND(struct name *head, struct type *elm) \ { \ struct type *tmp = RB_ROOT(head); \ struct type *res = NULL; \ int comp; \ while (tmp) { \ comp = cmp(elm, tmp); \ if (comp < 0) { \ res = tmp; \ tmp = RB_LEFT(tmp, field); \ } \ else if (comp > 0) \ tmp = RB_RIGHT(tmp, field); \ else \ return (tmp); \ } \ return (res); \ } \ \ /* ARGSUSED */ \ attr struct type * \ name##_RB_NEXT(struct type *elm) \ { \ if (RB_RIGHT(elm, field)) { \ elm = RB_RIGHT(elm, field); \ while (RB_LEFT(elm, field)) \ elm = RB_LEFT(elm, field); \ } else { \ if (RB_PARENT(elm, field) && \ (elm == RB_LEFT(RB_PARENT(elm, field), field))) \ elm = RB_PARENT(elm, field); \ else { \ while (RB_PARENT(elm, field) && \ (elm == RB_RIGHT(RB_PARENT(elm, field), field)))\ elm = RB_PARENT(elm, field); \ elm = RB_PARENT(elm, field); \ } \ } \ return (elm); \ } \ \ /* ARGSUSED */ \ attr struct type * \ name##_RB_PREV(struct type *elm) \ { \ if (RB_LEFT(elm, field)) { \ elm = RB_LEFT(elm, field); \ while (RB_RIGHT(elm, field)) \ elm = RB_RIGHT(elm, field); \ } else { \ if (RB_PARENT(elm, field) && \ (elm == RB_RIGHT(RB_PARENT(elm, field), field))) \ elm = RB_PARENT(elm, field); \ else { \ while (RB_PARENT(elm, field) && \ (elm == RB_LEFT(RB_PARENT(elm, field), field)))\ elm = RB_PARENT(elm, field); \ elm = RB_PARENT(elm, field); \ } \ } \ return (elm); \ } \ \ attr struct type * \ name##_RB_MINMAX(struct name *head, int val) \ { \ struct type *tmp = RB_ROOT(head); \ struct type *parent = NULL; \ while (tmp) { \ parent = tmp; \ if (val < 0) \ tmp = RB_LEFT(tmp, field); \ else \ tmp = RB_RIGHT(tmp, field); \ } \ return (parent); \ } #define RB_NEGINF -1 #define RB_INF 1 #define RB_INSERT(name, x, y) name##_RB_INSERT(x, y) #define RB_REMOVE(name, x, y) name##_RB_REMOVE(x, y) #define RB_FIND(name, x, y) name##_RB_FIND(x, y) #define RB_NFIND(name, x, y) name##_RB_NFIND(x, y) #define RB_NEXT(name, x, y) name##_RB_NEXT(y) #define RB_PREV(name, x, y) name##_RB_PREV(y) #define RB_MIN(name, x) name##_RB_MINMAX(x, RB_NEGINF) #define RB_MAX(name, x) name##_RB_MINMAX(x, RB_INF) #define RB_FOREACH(x, name, head) \ for ((x) = RB_MIN(name, head); \ (x) != NULL; \ (x) = name##_RB_NEXT(x)) #define RB_FOREACH_SAFE(x, name, head, y) \ for ((x) = RB_MIN(name, head); \ ((x) != NULL) && ((y) = name##_RB_NEXT(x), 1); \ (x) = (y)) #define RB_FOREACH_REVERSE(x, name, head) \ for ((x) = RB_MAX(name, head); \ (x) != NULL; \ (x) = name##_RB_PREV(x)) #define RB_FOREACH_REVERSE_SAFE(x, name, head, y) \ for ((x) = RB_MAX(name, head); \ ((x) != NULL) && ((y) = name##_RB_PREV(x), 1); \ (x) = (y)) /* * Copyright (c) 2016 David Gwynne * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ struct rb_type { int (*t_compare)(const void *, const void *); void (*t_augment)(void *); unsigned int t_offset; /* offset of rb_entry in type */ }; struct rb_tree { struct rb_entry *rbt_root; }; struct rb_entry { struct rb_entry *rbt_parent; struct rb_entry *rbt_left; struct rb_entry *rbt_right; unsigned int rbt_color; }; #define RBT_HEAD(_name, _type) \ struct _name { \ struct rb_tree rbh_root; \ } #define RBT_ENTRY(_type) struct rb_entry static inline void _rb_init(struct rb_tree *rbt) { rbt->rbt_root = NULL; } static inline int _rb_empty(struct rb_tree *rbt) { return (rbt->rbt_root == NULL); } void *_rb_insert(const struct rb_type *, struct rb_tree *, void *); void *_rb_remove(const struct rb_type *, struct rb_tree *, void *); void *_rb_find(const struct rb_type *, struct rb_tree *, const void *); void *_rb_nfind(const struct rb_type *, struct rb_tree *, const void *); void *_rb_root(const struct rb_type *, struct rb_tree *); void *_rb_min(const struct rb_type *, struct rb_tree *); void *_rb_max(const struct rb_type *, struct rb_tree *); void *_rb_next(const struct rb_type *, void *); void *_rb_prev(const struct rb_type *, void *); void *_rb_left(const struct rb_type *, void *); void *_rb_right(const struct rb_type *, void *); void *_rb_parent(const struct rb_type *, void *); void _rb_set_left(const struct rb_type *, void *, void *); void _rb_set_right(const struct rb_type *, void *, void *); void _rb_set_parent(const struct rb_type *, void *, void *); void _rb_poison(const struct rb_type *, void *, unsigned long); int _rb_check(const struct rb_type *, void *, unsigned long); #define RBT_INITIALIZER(_head) { { NULL } } #define RBT_PROTOTYPE(_name, _type, _field, _cmp) \ extern const struct rb_type *const _name##_RBT_TYPE; \ \ __unused static inline void \ _name##_RBT_INIT(struct _name *head) \ { \ _rb_init(&head->rbh_root); \ } \ \ __unused static inline struct _type * \ _name##_RBT_INSERT(struct _name *head, struct _type *elm) \ { \ return _rb_insert(_name##_RBT_TYPE, &head->rbh_root, elm); \ } \ \ __unused static inline struct _type * \ _name##_RBT_REMOVE(struct _name *head, struct _type *elm) \ { \ return _rb_remove(_name##_RBT_TYPE, &head->rbh_root, elm); \ } \ \ __unused static inline struct _type * \ _name##_RBT_FIND(struct _name *head, const struct _type *key) \ { \ return _rb_find(_name##_RBT_TYPE, &head->rbh_root, key); \ } \ \ __unused static inline struct _type * \ _name##_RBT_NFIND(struct _name *head, const struct _type *key) \ { \ return _rb_nfind(_name##_RBT_TYPE, &head->rbh_root, key); \ } \ \ __unused static inline struct _type * \ _name##_RBT_ROOT(struct _name *head) \ { \ return _rb_root(_name##_RBT_TYPE, &head->rbh_root); \ } \ \ __unused static inline int \ _name##_RBT_EMPTY(struct _name *head) \ { \ return _rb_empty(&head->rbh_root); \ } \ \ __unused static inline struct _type * \ _name##_RBT_MIN(struct _name *head) \ { \ return _rb_min(_name##_RBT_TYPE, &head->rbh_root); \ } \ \ __unused static inline struct _type * \ _name##_RBT_MAX(struct _name *head) \ { \ return _rb_max(_name##_RBT_TYPE, &head->rbh_root); \ } \ \ __unused static inline struct _type * \ _name##_RBT_NEXT(struct _type *elm) \ { \ return _rb_next(_name##_RBT_TYPE, elm); \ } \ \ __unused static inline struct _type * \ _name##_RBT_PREV(struct _type *elm) \ { \ return _rb_prev(_name##_RBT_TYPE, elm); \ } \ \ __unused static inline struct _type * \ _name##_RBT_LEFT(struct _type *elm) \ { \ return _rb_left(_name##_RBT_TYPE, elm); \ } \ \ __unused static inline struct _type * \ _name##_RBT_RIGHT(struct _type *elm) \ { \ return _rb_right(_name##_RBT_TYPE, elm); \ } \ \ __unused static inline struct _type * \ _name##_RBT_PARENT(struct _type *elm) \ { \ return _rb_parent(_name##_RBT_TYPE, elm); \ } \ \ __unused static inline void \ _name##_RBT_SET_LEFT(struct _type *elm, struct _type *left) \ { \ _rb_set_left(_name##_RBT_TYPE, elm, left); \ } \ \ __unused static inline void \ _name##_RBT_SET_RIGHT(struct _type *elm, struct _type *right) \ { \ _rb_set_right(_name##_RBT_TYPE, elm, right); \ } \ \ __unused static inline void \ _name##_RBT_SET_PARENT(struct _type *elm, struct _type *parent) \ { \ _rb_set_parent(_name##_RBT_TYPE, elm, parent); \ } \ \ __unused static inline void \ _name##_RBT_POISON(struct _type *elm, unsigned long poison) \ { \ _rb_poison(_name##_RBT_TYPE, elm, poison); \ } \ \ __unused static inline int \ _name##_RBT_CHECK(struct _type *elm, unsigned long poison) \ { \ return _rb_check(_name##_RBT_TYPE, elm, poison); \ } #define RBT_GENERATE_INTERNAL(_name, _type, _field, _cmp, _aug) \ static int \ _name##_RBT_COMPARE(const void *lptr, const void *rptr) \ { \ const struct _type *l = lptr, *r = rptr; \ return _cmp(l, r); \ } \ static const struct rb_type _name##_RBT_INFO = { \ _name##_RBT_COMPARE, \ _aug, \ offsetof(struct _type, _field), \ }; \ const struct rb_type *const _name##_RBT_TYPE = &_name##_RBT_INFO #define RBT_GENERATE_AUGMENT(_name, _type, _field, _cmp, _aug) \ static void \ _name##_RBT_AUGMENT(void *ptr) \ { \ struct _type *p = ptr; \ return _aug(p); \ } \ RBT_GENERATE_INTERNAL(_name, _type, _field, _cmp, _name##_RBT_AUGMENT) #define RBT_GENERATE(_name, _type, _field, _cmp) \ RBT_GENERATE_INTERNAL(_name, _type, _field, _cmp, NULL) #define RBT_INIT(_name, _head) _name##_RBT_INIT(_head) #define RBT_INSERT(_name, _head, _elm) _name##_RBT_INSERT(_head, _elm) #define RBT_REMOVE(_name, _head, _elm) _name##_RBT_REMOVE(_head, _elm) #define RBT_FIND(_name, _head, _key) _name##_RBT_FIND(_head, _key) #define RBT_NFIND(_name, _head, _key) _name##_RBT_NFIND(_head, _key) #define RBT_ROOT(_name, _head) _name##_RBT_ROOT(_head) #define RBT_EMPTY(_name, _head) _name##_RBT_EMPTY(_head) #define RBT_MIN(_name, _head) _name##_RBT_MIN(_head) #define RBT_MAX(_name, _head) _name##_RBT_MAX(_head) #define RBT_NEXT(_name, _elm) _name##_RBT_NEXT(_elm) #define RBT_PREV(_name, _elm) _name##_RBT_PREV(_elm) #define RBT_LEFT(_name, _elm) _name##_RBT_LEFT(_elm) #define RBT_RIGHT(_name, _elm) _name##_RBT_RIGHT(_elm) #define RBT_PARENT(_name, _elm) _name##_RBT_PARENT(_elm) #define RBT_SET_LEFT(_name, _elm, _l) _name##_RBT_SET_LEFT(_elm, _l) #define RBT_SET_RIGHT(_name, _elm, _r) _name##_RBT_SET_RIGHT(_elm, _r) #define RBT_SET_PARENT(_name, _elm, _p) _name##_RBT_SET_PARENT(_elm, _p) #define RBT_POISON(_name, _elm, _p) _name##_RBT_POISON(_elm, _p) #define RBT_CHECK(_name, _elm, _p) _name##_RBT_CHECK(_elm, _p) #define RBT_FOREACH(_e, _name, _head) \ for ((_e) = RBT_MIN(_name, (_head)); \ (_e) != NULL; \ (_e) = RBT_NEXT(_name, (_e))) #define RBT_FOREACH_SAFE(_e, _name, _head, _n) \ for ((_e) = RBT_MIN(_name, (_head)); \ (_e) != NULL && ((_n) = RBT_NEXT(_name, (_e)), 1); \ (_e) = (_n)) #define RBT_FOREACH_REVERSE(_e, _name, _head) \ for ((_e) = RBT_MAX(_name, (_head)); \ (_e) != NULL; \ (_e) = RBT_PREV(_name, (_e))) #define RBT_FOREACH_REVERSE_SAFE(_e, _name, _head, _n) \ for ((_e) = RBT_MAX(_name, (_head)); \ (_e) != NULL && ((_n) = RBT_PREV(_name, (_e)), 1); \ (_e) = (_n)) #endif /* _SYS_TREE_H_ */ bgpq4-1.4/include/sys/types.h000066400000000000000000000006741410770527700161720ustar00rootroot00000000000000/* * Public domain * sys/types.h compatibility shim */ #include_next #ifndef LIBCOMPAT_SYS_TYPES_H #define LIBCOMPAT_SYS_TYPES_H #include #ifdef __MINGW32__ #include <_bsd_types.h> #endif #if !defined(HAVE_ATTRIBUTE__DEAD) && !defined(__dead) #define __dead __attribute__((__noreturn__)) #endif #if !defined(HAVE_ATTRIBUTE__BOUNDED__) && !defined(__bounded__) # define __bounded__(x, y, z) #endif #endif bgpq4-1.4/main.c000066400000000000000000000514321410770527700135020ustar00rootroot00000000000000/* * Copyright (c) 2019-2021 Job Snijders * Copyright (c) 2007-2019 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. */ #if HAVE_CONFIG_H #include #endif #include #include #include #include #include #include #include #include #include #include #include "extern.h" #include "sx_report.h" extern int debug_expander; extern int debug_aggregation; extern int pipelining; extern int expand_special_asn; static int usage(int ecode) { printf("\nUsage: bgpq4 [-h host[:port]] [-S sources] [-E|G " "|f |t] [-46ABbdJjKNnwXz] [-R len] ... " "[EXCEPT ...]\n"); printf("\nVendor targets:\n"); printf(" no option : Cisco IOS Classic (default)\n"); printf(" -X : Cisco IOS XR\n"); printf(" -U : Huawei\n"); printf(" -j : JSON\n"); printf(" -J : Juniper Junos\n"); printf(" -K : MikroTik RouterOS\n"); printf(" -b : NIC.CZ BIRD\n"); printf(" -N : Nokia SR OS (Classic CLI)\n"); printf(" -n : Nokia SR OS (MD-CLI)\n"); printf(" -B : OpenBSD OpenBGPD\n"); printf(" -e : Arista EOS\n"); printf(" -F fmt : User defined format (example: '-F %%n/%%l')\n"); printf("\nInput filters:\n"); printf(" -4 : generate IPv4 prefix-lists (default)\n"); printf(" -6 : generate IPv6 prefix-lists\n"); printf(" -m len : maximum prefix length (default: 32 for IPv4, " "128 for IPv6)\n"); printf(" -L depth : limit recursion depth (default: unlimited)\n"), printf(" -S sources: only use specified IRR sources, in the specified " "order (comma separated)\n"); printf(" -w : 'validate' AS numbers: accept only ones with " "registered routes\n"); printf("\nOutput modifiers:\n"); printf(" -A : try to aggregate prefix-lists/route-filters\n"); printf(" -E : generate extended access-list (Cisco), " "route-filter (Juniper)\n" " [ip|ipv6]-prefix-list (Nokia) or prefix-set " "(OpenBGPD)\n"); printf(" -f number : generate input as-path access-list\n"); printf(" -G number : generate output as-path access-list\n"); printf(" -M match : extra match conditions for JunOS route-filters\n"); printf(" -l name : use specified name for generated access/prefix/.." " list\n"); printf(" -R len : allow more specific routes up to specified masklen\n"); printf(" -r len : allow more specific routes from masklen specified\n"); printf(" -s : generate sequence numbers in prefix-lists (IOS only)\n"); printf(" -t : generate as-sets for OpenBGPD (OpenBGPD 6.4+), BIRD " "and JSON formats\n"); printf(" -z : generate route-filter-list (Junos only)\n"); printf(" -W len : specify max-entries on as-path line (use 0 for " "infinity)\n"); printf("\nUtility operations:\n"); printf(" -d : generate some debugging output\n"); printf(" -h host : host running IRRD software (default: rr.ntt.net)\n" " use 'host:port' to specify alternate port\n"); printf(" -T : disable pipelining (not recommended)\n"); printf(" -v : print version and exit\n"); printf("\n" PACKAGE_NAME " version: " PACKAGE_VERSION " " "(https://github.com/bgp/bgpq4)\n"); exit(ecode); } static void version(void) { printf(PACKAGE_NAME " - a versatile utility to generate BGP filters\n" "version: " PACKAGE_VERSION "\n" "website: https://github.com/bgp/bgpq4\n" "maintainer: Job Snijders \n"); exit(0); } static void exclusive(void) { fprintf(stderr,"-E, -F, -K , -f , -G , and -t are mutually" " exclusive\n"); exit(1); } static void vendor_exclusive(void) { fprintf(stderr, "-b (BIRD), -B (OpenBGPD), -F (formatted), -J (Junos)," " -j (JSON), -N (Nokia SR OS Classic), -n (Nokia SR OS MD-CLI)," " -U (Huawei), -e (Arista) and -X (IOS XR) options are mutually" " exclusive\n"); exit(1); } static int parseasnumber(struct bgpq_expander *expander, char *asnstr) { char *eon = NULL; expander->asnumber = strtoul(asnstr, &eon, 10); if (expander->asnumber < 1 || expander->asnumber > (65535ul * 65535)) { sx_report(SX_FATAL, "Invalid AS number: %s\n", asnstr); 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", asnstr); exit(1); } if (loas < 1 || loas > 65535) { sx_report(SX_FATAL,"Invalid AS number: %s\n", asnstr); exit(1); } if (eon && *eon) { sx_report(SX_FATAL,"Invalid symbol in AS number: " "%c (%s)\n", *eon, asnstr); 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, asnstr); exit(1); } return 0; } int main(int argc, char* argv[]) { int c; struct bgpq_expander expander; int af = AF_INET, selectedipv4 = 0, exceptmode = 0; int widthSet = 0, aggregate = 0, refine = 0, refineLow = 0; unsigned long maxlen = 0; #ifdef HAVE_PLEDGE if (pledge("stdio inet dns", NULL) == -1) { sx_report(SX_ERROR, "pledge() failed"); exit(1); } #endif bgpq_expander_init(&expander, af); if (getenv("IRRD_SOURCES")) expander.sources=getenv("IRRD_SOURCES"); while ((c = getopt(argc, argv, "346a:AbBdDEeF:S:jJKf:l:L:m:M:NnW:pr:R:G:tTh:UwXsvz")) !=EOF) { switch (c) { case '3': /* * No-op, left for backwards compatibility with bgpq3 */ break; case '4': /* do nothing, expander already configured for IPv4 */ if (expander.family == AF_INET6) { sx_report(SX_FATAL, "-4 and -6 are mutually " "exclusive\n"); exit(1); } selectedipv4=1; break; case '6': if (selectedipv4) { sx_report(SX_FATAL, "-4 and -6 are mutually " "exclusive\n"); exit(1); } af = AF_INET6; expander.family = AF_INET6; expander.tree->family = AF_INET6; break; case 'a': parseasnumber(&expander, optarg); break; case 'A': if (aggregate) debug_aggregation++; aggregate = 1; break; case 'b': if (expander.vendor) vendor_exclusive(); expander.vendor = V_BIRD; break; case 'B': if (expander.vendor) vendor_exclusive(); expander.vendor = V_OPENBGPD; break; case 'd': debug_expander++; break; case 'E': if (expander.generation) exclusive(); expander.generation = T_EACL; break; case 'e': if (expander.vendor) vendor_exclusive(); expander.vendor = V_ARISTA; expander.sequence = 1; break; case 'F': if (expander.vendor) exclusive(); expander.vendor = V_FORMAT; expander.format = optarg; 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 'h': { char* d = strchr(optarg, ':'); expander.server = optarg; if (d) { *d = 0; expander.port = d + 1; } } 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 'K': if (expander.vendor) vendor_exclusive(); expander.vendor = V_MIKROTIK; break; case 'p': expand_special_asn = 1; break; case 'r': refineLow = strtoul(optarg, NULL, 10); if (!refineLow) { sx_report(SX_FATAL, "Invalid refineLow value:" " %s\n", optarg); exit(1); } 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 'L': expander.maxdepth = strtol(optarg, NULL, 10); if (expander.maxdepth < 1) { sx_report(SX_FATAL, "Invalid maximum recursion" " (-L): %s\n", optarg); exit(1); } 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 *mc, *md; expander.match = strdup(optarg); mc = md = expander.match; while (*mc) { if (*mc == '\\') { if (*(mc + 1) == '\n') { *md = '\n'; md++; mc += 2; } else if (*(mc + 1) == 'r') { *md = '\r'; md++; mc += 2; } else if (*(mc + 1) == 't') { *md = '\t'; md++; mc += 2; } else if (*(mc + 1) == '\\') { *md = '\\'; md++; mc += 2; } else { sx_report(SX_FATAL, "Unsupported" " escape \%c (0x%2.2x) in " "'%s'\n", isprint(*mc) ? *mc : 20, *mc, optarg); exit(1); } } else { if (mc != md) { *md = *mc; } md++; mc++; } } *md = 0; } break; case 'N': if (expander.vendor) vendor_exclusive(); expander.vendor = V_NOKIA; break; case 'n': if (expander.vendor) vendor_exclusive(); expander.vendor = V_NOKIA_MD; break; case 't': if (expander.generation) exclusive(); expander.generation = T_ASSET; break; case 'T': pipelining = 0; break; case 's': expander.sequence = 1; break; case 'S': expander.sources = optarg; break; case 'U': if (expander.vendor) exclusive(); expander.vendor = V_HUAWEI; break; case 'W': expander.aswidth = atoi(optarg); if (expander.aswidth < 0) { sx_report(SX_FATAL,"Invalid as-width: %s\n", optarg); exit(1); } widthSet = 1; break; case 'w': expander.validate_asns = 1; break; case 'X': if (expander.vendor) vendor_exclusive(); expander.vendor = V_CISCO_XR; break; case 'v': version(); break; case 'z': if (expander.generation) exclusive(); expander.generation = T_ROUTE_FILTER_LIST; break; default: usage(1); } } argc -= optind; argv += optind; if (!widthSet) { if (expander.generation == T_ASPATH) { int vendor = expander.vendor; switch (vendor) { case V_ARISTA: case V_CISCO: case V_MIKROTIK: expander.aswidth = 4; break; case V_CISCO_XR: expander.aswidth = 6; break; case V_JUNIPER: case V_NOKIA: case V_NOKIA_MD: expander.aswidth = 8; break; case V_BIRD: expander.aswidth = 10; break; } } else if (expander.generation == T_OASPATH) { int vendor = expander.vendor; switch (vendor) { case V_ARISTA: case V_CISCO: expander.aswidth = 5; break; case V_CISCO_XR: expander.aswidth = 7; break; case V_JUNIPER: case V_NOKIA: case V_NOKIA_MD: expander.aswidth = 8; break; } } } if (!expander.generation) expander.generation = T_PREFIXLIST; if (expander.vendor == V_CISCO_XR && expander.generation != T_PREFIXLIST && expander.generation != T_ASPATH && expander.generation != T_OASPATH) { sx_report(SX_FATAL, "Sorry, only prefix-sets and as-paths " "supported for IOS XR\n"); } if (expander.vendor == V_BIRD && expander.generation != T_PREFIXLIST && expander.generation != T_ASPATH && expander.generation != T_ASSET) { sx_report(SX_FATAL, "Sorry, only prefix-lists and as-paths/as-sets " "supported for BIRD output\n"); } if (expander.vendor == V_JSON && expander.generation != T_PREFIXLIST && expander.generation != T_ASPATH && expander.generation != T_ASSET) { sx_report(SX_FATAL, "Sorry, only prefix-lists and as-paths/as-sets " "supported for JSON output\n"); } if (expander.vendor == V_FORMAT && expander.generation != T_PREFIXLIST) sx_report(SX_FATAL, "Sorry, only prefix-lists supported in formatted " "output\n"); if (expander.vendor == V_HUAWEI && expander.generation != T_ASPATH && expander.generation != T_OASPATH && expander.generation != T_PREFIXLIST) sx_report(SX_FATAL, "Sorry, only as-paths and prefix-lists supported " "for Huawei output\n"); if (expander.generation == T_ROUTE_FILTER_LIST && expander.vendor != V_JUNIPER) sx_report(SX_FATAL, "Route-filter-lists (-z) supported for Juniper (-J)" " output only\n"); if (expander.generation == T_ASSET && expander.vendor != V_JSON && expander.vendor != V_OPENBGPD && expander.vendor != V_BIRD) sx_report(SX_FATAL, "As-Sets (-t) supported for JSON (-j), OpenBGPD " "(-B) and BIRD (-b) output only\n"); 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) " "or route-filter-lists (-z) instead of prefix-lists\n."); exit(1); } if (aggregate && (expander.vendor == V_NOKIA_MD || expander.vendor == V_NOKIA) && expander.generation != T_PREFIXLIST) { sx_report(SX_FATAL, "Sorry, aggregation (-A) is not supported with " "ip-prefix-lists (-E) on Nokia.\n"); exit(1); } if (refine && (expander.vendor == V_NOKIA_MD || expander.vendor == V_NOKIA) && expander.generation != T_PREFIXLIST) { sx_report(SX_FATAL, "Sorry, more-specifics (-R) is not supported with " "ip-prefix-lists (-E) on Nokia.\n"); exit(1); } if (refineLow && (expander.vendor == V_NOKIA_MD || expander.vendor == V_NOKIA) && expander.generation != T_PREFIXLIST) { sx_report(SX_FATAL, "Sorry, more-specifics (-r) is not supported with " "ip-prefix-lists (-E) on Nokia.\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 (expander.sequence && (expander.vendor != V_CISCO && expander.vendor != V_ARISTA)) { sx_report(SX_FATAL, "Sorry, prefix-lists sequencing (-s) supported" " only for IOS and EOS\n"); exit(1); } if (expander.sequence && expander.generation < T_PREFIXLIST) { sx_report(SX_FATAL, "Sorry, prefix-lists sequencing (-s) can't be " " used for non prefix-list\n"); exit(1); } if (refineLow && !refine) { if (expander.family == AF_INET) refine = 32; else refine = 128; } if (refineLow && refineLow > refine) sx_report(SX_FATAL, "Incompatible values for -r %u and -R %u\n", refineLow, refine); if (refine || refineLow) { if (expander.family == AF_INET6 && refine > 128) { sx_report(SX_FATAL, "Invalid value for refine(-R): %u (1-128 for" " IPv6)\n", refine); } else if (expander.family == AF_INET6 && refineLow > 128) { sx_report(SX_FATAL, "Invalid value for refineLow(-r): %u (1-128 for" " IPv6)\n", refineLow); } else if (expander.family == AF_INET && refine > 32) { sx_report(SX_FATAL, "Invalid value for refine(-R): %u (1-32 for" " IPv4)\n", refine); } else if (expander.family == AF_INET && refineLow > 32) { sx_report(SX_FATAL, "Invalid value for refineLow(-r): %u (1-32 for" " IPv4)\n", refineLow); } if (expander.vendor == V_JUNIPER && expander.generation == T_PREFIXLIST) { if (refine) { sx_report(SX_FATAL, "Sorry, more-specific filters (-R %u) " "is not supported for Juniper prefix-lists.\n" "Use router-filters (-E) or route-filter-lists (-z) " "instead\n", refine); } else { sx_report(SX_FATAL, "Sorry, more-specific filters (-r %u) " "is not supported for Juniper prefix-lists.\n" "Use route-filters (-E) or route-filter-lists (-z) " "instead\n", refineLow); } } if (expander.generation < T_PREFIXLIST) { if (refine) sx_report(SX_FATAL, "Sorry, more-specific filter (-R %u) " "supported only with prefix-list generation\n", refine); else sx_report(SX_FATAL, "Sorry, more-specific filter (-r %u) " "supported only with prefix-list generation\n", refineLow); } } 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; } } else if (expander.family == AF_INET) expander.maxlen = 32; else if (expander.family == AF_INET6) expander.maxlen = 128; 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 (expander.match != NULL && (expander.vendor != V_JUNIPER || expander.generation != T_EACL)) { sx_report(SX_FATAL, "Sorry, extra match conditions (-M) can be used " "only with Juniper route-filters\n"); } if ((expander.generation == T_ASPATH || expander.generation == T_OASPATH) && af != AF_INET && !expander.validate_asns) { sx_report(SX_FATAL, "Sorry, -6 makes no sense with as-path (-f/-G) " "generation\n"); } if (expander.validate_asns && expander.generation != T_ASPATH && expander.generation != T_OASPATH) { sx_report(SX_FATAL, "Sorry, -w makes sense only for as-path " "(-f/-G) generation\n"); } if (!argv[0]) usage(1); while (argv[0]) { if (!strcmp(argv[0], "EXCEPT")) { exceptmode = 1; } else if (exceptmode) { bgpq_expander_add_stop(&expander, argv[0]); } else 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 *ec; if ((ec = strchr(argv[0], ':'))) { if (!strncasecmp(ec + 1, "AS-", 3)) { bgpq_expander_add_asset(&expander, argv[0]); } else if (!strncasecmp(ec + 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 { char *ec = strchr(argv[0], '^'); if (!ec && !bgpq_expander_add_prefix(&expander, argv[0])) { sx_report(SX_ERROR, "Unable to add prefix %s " "(bad prefix or address-family)\n", argv[0]); exit(1); } else if (ec && !bgpq_expander_add_prefix_range(&expander, argv[0])) { sx_report(SX_ERROR, "Unable to add prefix-range " "%s (bad range or address-family)\n", argv[0]); exit(1); } } argv++; argc--; } if (!bgpq_expand(&expander)) exit(1); if (refine) sx_radix_tree_refine(expander.tree, refine); if (refineLow) sx_radix_tree_refineLow(expander.tree, refineLow); if (aggregate) sx_radix_tree_aggregate(expander.tree); switch (expander.generation) { case T_NONE: sx_report(SX_FATAL,"Unreachable point... call snar\n"); exit(1); case T_ASPATH: bgpq4_print_aspath(stdout, &expander); break; case T_OASPATH: bgpq4_print_oaspath(stdout, &expander); break; case T_ASSET: bgpq4_print_asset(stdout, &expander); break; case T_PREFIXLIST: bgpq4_print_prefixlist(stdout, &expander); break; case T_EACL: bgpq4_print_eacl(stdout, &expander); break; case T_ROUTE_FILTER_LIST: bgpq4_print_route_filter_list(stdout, &expander); break; } expander_freeall(&expander); return 0; } bgpq4-1.4/printer.c000066400000000000000000001104351410770527700142400ustar00rootroot00000000000000/* * Copyright (c) 2019-2021 Job Snijders * Copyright (c) 2007-2019 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. */ #include #include #include #include #include #include #include #include #include "extern.h" #include "sx_report.h" extern int debug_expander; static void bgpq4_print_cisco_aspath(FILE *f, struct bgpq_expander *b) { int nc = 0; struct asn_entry *asne, find, *res; fprintf(f, "no ip as-path access-list %s\n", b->name); if (RB_EMPTY(&b->asnlist)) { fprintf(f, "ip as-path access-list %s deny .*\n", b->name); return; } find.asn = b->asnumber; if ((res = RB_FIND(asn_tree, &b->asnlist, &find)) != NULL) { fprintf(f, "ip as-path access-list %s permit ^%u(_%u)*$\n", b->name, res->asn, res->asn); RB_REMOVE(asn_tree, &b->asnlist, res); } RB_FOREACH(asne, asn_tree, &b->asnlist) { if (!nc) fprintf(f, "ip as-path access-list %s permit" " ^%u(_[0-9]+)*_(%u", b->name, b->asnumber, asne->asn); else fprintf(f,"|%u", asne->asn); nc++; if (nc == b->aswidth) { fprintf(f, ")$\n"); nc = 0; } } if (nc) fprintf(f,")$\n"); } static void bgpq4_print_cisco_xr_aspath(FILE *f, struct bgpq_expander *b) { int nc = 0, comma = 1; struct asn_entry *asne, find, *res; fprintf(f, "as-path-set %s", b->name); find.asn = b->asnumber; if ((res = RB_FIND(asn_tree, &b->asnlist, &find)) != NULL) { fprintf(f, "\n ios-regex '^%u(_%u)*$'", res->asn, res->asn); RB_REMOVE(asn_tree, &b->asnlist, res); } RB_FOREACH(asne, asn_tree, &b->asnlist) { if (!nc) { fprintf(f, "%s\n ios-regex '^%u(_[0-9]+)*_(%u", comma ? "," : "", b->asnumber, asne->asn); comma = 1; } else fprintf(f, "|%u", asne->asn); nc++; if (nc == b->aswidth) { fprintf(f, ")$'"); nc = 0; } } if (nc) fprintf(f, ")$'"); fprintf(f, "\nend-set\n"); } static void bgpq4_print_cisco_oaspath(FILE *f, struct bgpq_expander *b) { int nc = 0; struct asn_entry *asne, find, *res; fprintf(f, "no ip as-path access-list %s\n", b->name); if (RB_EMPTY(&b->asnlist)) { fprintf(f, "ip as-path access-list %s deny .*\n", b->name); return; } find.asn = b->asnumber; if ((res = RB_FIND(asn_tree, &b->asnlist, &find)) != NULL) { fprintf(f, "ip as-path access-list %s permit ^(_%u)*$\n", b->name, res->asn); RB_REMOVE(asn_tree, &b->asnlist, res); } RB_FOREACH(asne, asn_tree, &b->asnlist) { if (!nc) fprintf(f,"ip as-path access-list %s permit" " ^(_[0-9]+)*_(%u", b->name, asne->asn); else fprintf(f,"|%u",asne->asn); nc++; if (nc == b->aswidth) { fprintf(f,")$\n"); nc = 0; } } if (nc) fprintf(f, ")$\n"); } static void bgpq4_print_cisco_xr_oaspath(FILE *f, struct bgpq_expander *b) { int nc = 0, comma = 0; struct asn_entry *asne, find, *res; fprintf(f, "as-path-set %s", b->name); find.asn = b->asnumber; if ((res = RB_FIND(asn_tree, &b->asnlist, &find)) != NULL) { fprintf(f, "\n ios-regex '^(_%u)*$'", res->asn); RB_REMOVE(asn_tree, &b->asnlist, res); comma = 1; } RB_FOREACH(asne, asn_tree, &b->asnlist) { if (!nc) { fprintf(f,"%s\n ios-regex '^(_[0-9]+)*_(%u", comma ? "," : "", asne->asn); comma = 1; } else fprintf(f,"|%u",asne->asn); nc++; if (nc == b->aswidth) { fprintf(f,")$'"); nc = 0; } } if (nc) fprintf(f,")$'"); fprintf(f,"\nend-set\n"); } static void bgpq4_print_juniper_aspath(FILE *f, struct bgpq_expander *b) { int nc = 0, lineNo = 0; struct asn_entry *asne, find, *res; fprintf(f,"policy-options {\nreplace:\n as-path-group %s {\n", b->name); find.asn = b->asnumber; if ((res = RB_FIND(asn_tree, &b->asnlist, &find)) != NULL) { fprintf(f, " as-path a0 \"^%u(%u)*$\";\n", res->asn, res->asn); RB_REMOVE(asn_tree, &b->asnlist, res); lineNo++; } RB_FOREACH(asne, asn_tree, &b->asnlist) { if (!nc) { fprintf(f, " as-path a%u \"^%u(.)*(%u", lineNo, b->asnumber, asne->asn); } else { fprintf(f,"|%u", asne->asn); } nc++; if (nc == b->aswidth) { fprintf(f, ")$\";\n"); nc = 0; lineNo++; } } if (nc) fprintf(f, ")$\";\n"); else if (lineNo == 0) fprintf(f, " as-path aNone \"!.*\";\n"); fprintf(f, " }\n}\n"); } static void bgpq4_print_juniper_oaspath(FILE *f, struct bgpq_expander *b) { int nc = 0, lineNo = 0; struct asn_entry *asne, find, *res; fprintf(f,"policy-options {\nreplace:\n as-path-group %s {\n", b->name); find.asn = b->asnumber; if ((res = RB_FIND(asn_tree, &b->asnlist, &find)) != NULL) { fprintf(f, " as-path a%u \"^%u(%u)*$\";\n", lineNo, res->asn, res->asn); RB_REMOVE(asn_tree, &b->asnlist, res); lineNo++; } RB_FOREACH(asne, asn_tree, &b->asnlist) { if (!nc) { fprintf(f," as-path a%u \"^(.)*(%u", lineNo, asne->asn); } else { fprintf(f, "|%u", asne->asn); } nc++; if (nc == b->aswidth) { fprintf(f, ")$\";\n"); nc = 0; lineNo++; } } if (nc) fprintf(f, ")$\";\n"); else if (lineNo == 0) fprintf(f, " as-path aNone \"!.*\";\n"); fprintf(f, " }\n}\n"); } static void bgpq4_print_openbgpd_oaspath(FILE *f, struct bgpq_expander *b) { struct asn_entry *asne; if (RB_EMPTY(&b->asnlist)) { fprintf(f, "deny to AS %u\n", b->asnumber); return; } RB_FOREACH(asne, asn_tree, &b->asnlist) fprintf(f, "allow to AS %u AS %u\n", b->asnumber, asne->asn); } static void bgpq4_print_nokia_aspath(FILE *f, struct bgpq_expander *b) { int nc = 0, lineNo = 1; struct asn_entry *asne, find, *res; fprintf(f, "configure router policy-options\n" "begin\nno as-path-group \"%s\"\n", b->name); fprintf(f, "as-path-group \"%s\"\n", b->name); find.asn = b->asnumber; if ((res = RB_FIND(asn_tree, &b->asnlist, &find)) != NULL) { fprintf(f, " entry 1 expression \"%u+\"\n", res->asn); RB_REMOVE(asn_tree, &b->asnlist, res); lineNo++; } RB_FOREACH(asne, asn_tree, &b->asnlist) { if (!nc) { fprintf(f," entry %u expression \"%u.*[%u", lineNo, b->asnumber, asne->asn); } else { fprintf(f, " %u", asne->asn); } nc++; if (nc == b->aswidth) { fprintf(f, "]\"\n"); nc = 0; lineNo++; } } if (nc) fprintf(f, "]\"\n"); fprintf(f,"exit\ncommit\n"); } static void bgpq4_print_nokia_md_aspath(FILE *f, struct bgpq_expander *b) { int nc = 0, lineNo = 1; struct asn_entry *asne, find, *res; fprintf(f,"/configure policy-options\ndelete as-path-group \"%s\"\n", b->name); fprintf(f,"as-path-group \"%s\" {\n", b->name); find.asn = b->asnumber; if ((res = RB_FIND(asn_tree, &b->asnlist, &find)) != NULL) { fprintf(f," entry 1 {\n expression \"%u+\"\n }\n", res->asn); lineNo++; RB_REMOVE(asn_tree, &b->asnlist, res); } RB_FOREACH(asne, asn_tree, &b->asnlist) { if (!nc) { fprintf(f," entry %u {\n expression \"%u.*[%u", lineNo, b->asnumber, asne->asn); } else { fprintf(f, " %u", asne->asn); } nc++; if (nc == b->aswidth) { fprintf(f,"]\"\n }\n"); nc = 0; lineNo++; } } if (nc) fprintf(f,"]\"\n }\n"); fprintf(f, "}\n"); } static void bgpq4_print_huawei_aspath(FILE *f, struct bgpq_expander *b) { int nc = 0; struct asn_entry *asne, find, *res; fprintf(f, "undo ip as-path-filter %s\n", b->name); if (RB_EMPTY(&b->asnlist)) { fprintf(f,"ip as-path-filter %s deny .*\n", b->name); return; } find.asn = b->asnumber; if ((res = RB_FIND(asn_tree, &b->asnlist, &find)) != NULL) { fprintf(f, "ip as-path-filter %s permit ^%u(_%u)*$\n", b->name, res->asn, res->asn); RB_REMOVE(asn_tree, &b->asnlist, res); } RB_FOREACH(asne, asn_tree, &b->asnlist) { if (!nc) fprintf(f, "ip as-path-filter %s permit ^%u(_[0-9]+)*" "_(%u", b->name, b->asnumber, asne->asn); else fprintf(f, "|%u", asne->asn); nc++; if (nc == b->aswidth) { fprintf(f, ")$\n"); nc = 0; } } if (nc) fprintf(f, ")$\n"); } static void bgpq4_print_huawei_oaspath(FILE *f, struct bgpq_expander *b) { int nc = 0; struct asn_entry *asne, find, *res; fprintf(f,"undo ip as-path-filter %s\n", b->name); find.asn = b->asnumber; if ((res = RB_FIND(asn_tree, &b->asnlist, &find)) != NULL) { fprintf(f,"ip as-path-filter %s permit ^(_%u)*$\n", b->name, res->asn); RB_REMOVE(asn_tree, &b->asnlist, res); } if (RB_EMPTY(&b->asnlist)) { fprintf(f, "ip as-path-filter %s deny .*\n", b->name); return; } RB_FOREACH(asne, asn_tree, &b->asnlist) { if (!nc) { fprintf(f, "ip as-path-filter %s permit ^(_[0-9]+)*_(%u", b->name, asne->asn); } else { fprintf(f, "|%u", asne->asn); } nc++; if (nc == b->aswidth) { fprintf(f, ")$\n"); nc = 0; } } if (nc) fprintf(f, ")$\n"); } static void bgpq4_print_nokia_oaspath(FILE *f, struct bgpq_expander *b) { int nc = 0, lineNo = 1; struct asn_entry *asne, find, *res; fprintf(f, "configure router policy-options\nbegin\nno as-path-group" "\"%s\"\n", b->name); fprintf(f, "as-path-group \"%s\"\n", b->name); find.asn = b->asnumber; if ((res = RB_FIND(asn_tree, &b->asnlist, &find)) != NULL) { fprintf(f, " entry %u expression \"%u+\"\n", lineNo, b->asnumber); RB_REMOVE(asn_tree, &b->asnlist, res); lineNo++; } RB_FOREACH(asne, asn_tree, &b->asnlist) { if (!nc) { fprintf(f," entry %u expression \".*[%u", lineNo, asne->asn); } else { fprintf(f," %u", asne->asn); } nc++; if (nc == b->aswidth) { fprintf(f,"]\"\n"); nc = 0; lineNo++; } } if (nc) fprintf(f, "]\"\n"); } static void bgpq4_print_nokia_md_oaspath(FILE *f, struct bgpq_expander *b) { int nc = 0, lineNo = 1; struct asn_entry *asne, find, *res; fprintf(f, "/configure policy-options\ndelete as-path-group \"%s\"\n", b->name); fprintf(f, "as-path-group \"%s\" {\n", b->name); find.asn = b->asnumber; if ((res = RB_FIND(asn_tree, &b->asnlist, &find)) != NULL) { fprintf(f, " entry %u {\n expression \"%u+\"\n }\n", lineNo, res->asn); RB_REMOVE(asn_tree, &b->asnlist, res); lineNo++; } RB_FOREACH(asne, asn_tree, &b->asnlist) { if (!nc) { fprintf(f," entry %u {\n expression \".*[%u", lineNo, asne->asn); } else { fprintf(f, " %u", asne->asn); } nc++; if (nc == b->aswidth) { fprintf(f, "]\"\n }\n"); nc = 0; lineNo++; } } if (nc) fprintf(f,"]\"\n }\n"); fprintf(f, "}\n"); } static void bgpq4_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; static void bgpq4_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) bgpq4_print_json_prefix(n->son, ff); } static void bgpq4_print_json_aspath(FILE *f, struct bgpq_expander *b) { int nc = 0; struct asn_entry *asne; fprintf(f, "{\"%s\": [", b->name); RB_FOREACH(asne, asn_tree, &b->asnlist) { if (!nc) { fprintf(f, "%s\n %u", needscomma ? "," : "", asne->asn); needscomma = 1; } else { fprintf(f, "%s%u", needscomma ? "," : "", asne->asn); needscomma = 1; } nc++; if (nc == b->aswidth) nc = 0; } fprintf(f,"\n]}\n"); } static void bgpq4_print_bird_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_snprintf(n->prefix, prefix, sizeof(prefix)); if (!n->isAggregate) { fprintf(f, "%s\n %s", needscomma ? "," : "", prefix); } else if (n->aggregateLow > n->prefix->masklen) { fprintf(f, "%s\n %s{%u,%u}", needscomma ? "," : "", prefix, n->aggregateLow, n->aggregateHi); } else { fprintf(f, "%s\n %s{%u,%u}", needscomma ? "," : "", prefix, n->prefix->masklen, n->aggregateHi); } needscomma = 1; checkSon: if (n->son) bgpq4_print_bird_prefix(n->son, ff); } static void bgpq4_print_bird_aspath(FILE* f, struct bgpq_expander* b) { int nc = 0; struct asn_entry *asne; fprintf(f, "%s = [", b->name); if (RB_EMPTY(&b->asnlist)) { fprintf(f, "];\n"); return; } RB_FOREACH(asne, asn_tree, &b->asnlist) { if (!nc) { fprintf(f, "%s\n %u", needscomma ? "," : "", asne->asn); needscomma = 1; } else { fprintf(f, ", %u", asne->asn); needscomma = 1; } nc++; if (nc == b->aswidth) nc = 0; } fprintf(f, "];\n"); } static void bgpq4_print_openbgpd_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_snprintf(n->prefix, prefix, sizeof(prefix)); if (!n->isAggregate) { fprintf(f, "\n\t%s", prefix); } else if (n->aggregateLow == n->aggregateHi) { fprintf(f, "\n\t%s prefixlen = %u", prefix, n->aggregateHi); } else if (n->aggregateLow > n->prefix->masklen) { fprintf(f, "\n\t%s prefixlen %u - %u", prefix, n->aggregateLow, n->aggregateHi); } else { fprintf(f, "\n\t%s prefixlen %u - %u", prefix, n->prefix->masklen, n->aggregateHi); } checkSon: if (n->son) bgpq4_print_openbgpd_prefix(n->son, ff); } static void bgpq4_print_openbgpd_asset(FILE *f, struct bgpq_expander *b) { int nc = 0; struct asn_entry *asne; fprintf(f, "as-set %s {", b->name); RB_FOREACH(asne, asn_tree, &b->asnlist) { fprintf(f, "%s%u", nc == 0 ? "\n\t" : " ", asne->asn); nc++; if (nc == b->aswidth) nc = 0; } fprintf(f, "\n}\n"); } static void bgpq4_print_openbgpd_aspath(FILE *f, struct bgpq_expander *b) { struct asn_entry *asne; if (RB_EMPTY(&b->asnlist)) { fprintf(f, "deny from AS %u\n", b->asnumber); return; } RB_FOREACH(asne, asn_tree, &b->asnlist) fprintf(f, "allow from AS %u AS %u\n", b->asnumber, asne->asn); } void bgpq4_print_aspath(FILE *f, struct bgpq_expander *b) { switch (b->vendor) { case V_JUNIPER: bgpq4_print_juniper_aspath(f, b); break; case V_CISCO: case V_ARISTA: bgpq4_print_cisco_aspath(f, b); break; case V_CISCO_XR: bgpq4_print_cisco_xr_aspath(f, b); break; case V_JSON: bgpq4_print_json_aspath(f, b); break; case V_BIRD: bgpq4_print_bird_aspath(f, b); break; case V_OPENBGPD: bgpq4_print_openbgpd_aspath(f, b); break; case V_NOKIA: bgpq4_print_nokia_aspath(f, b); break; case V_NOKIA_MD: bgpq4_print_nokia_md_aspath(f, b); break; case V_HUAWEI: bgpq4_print_huawei_aspath(f, b); break; default: sx_report(SX_FATAL,"Unknown vendor %i\n", b->vendor); } } void bgpq4_print_oaspath(FILE *f, struct bgpq_expander *b) { switch (b->vendor) { case V_JUNIPER: bgpq4_print_juniper_oaspath(f, b); break; case V_CISCO: case V_ARISTA: bgpq4_print_cisco_oaspath(f, b); break; case V_CISCO_XR: bgpq4_print_cisco_xr_oaspath(f, b); break; case V_OPENBGPD: bgpq4_print_openbgpd_oaspath(f, b); break; case V_NOKIA: bgpq4_print_nokia_oaspath(f, b); break; case V_NOKIA_MD: bgpq4_print_nokia_md_oaspath(f, b); break; case V_HUAWEI: bgpq4_print_huawei_oaspath(f, b); break; default: sx_report(SX_FATAL,"Unknown vendor %i\n", b->vendor); } } void bgpq4_print_asset(FILE *f, struct bgpq_expander *b) { switch (b->vendor) { case V_JSON: bgpq4_print_json_aspath(f, b); break; case V_OPENBGPD: bgpq4_print_openbgpd_asset(f, b); break; case V_BIRD: bgpq4_print_bird_aspath(f, b); break; default: sx_report(SX_FATAL, "as-sets (-t) supported for JSON, " "OpenBGPD, and BIRD only\n"); } } static int jrfilter_prefixed = 1; static void bgpq4_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, " %s%s exact;\n", jrfilter_prefixed ? "route-filter " : "", prefix); } else { if (n->aggregateLow > n->prefix->masklen) { fprintf(f," %s%s prefix-length-range /%u-/%u;\n", jrfilter_prefixed ? "route-filter " : "", prefix, n->aggregateLow, n->aggregateHi); } else { fprintf(f," %s%s upto /%u;\n", jrfilter_prefixed ? "route-filter " : "", prefix, n->aggregateHi); } } checkSon: if (n->son) bgpq4_print_jrfilter(n->son, ff); } static char* bname = NULL; static int seq = 0; static void bgpq4_print_cprefix(struct sx_radix_node *n, void *ff) { char prefix[128], seqno[16] = ""; FILE *f = (FILE*)ff; if (!f) f = stdout; if (n->isGlue) goto checkSon; sx_prefix_snprintf(n->prefix, prefix, sizeof(prefix)); if (seq) snprintf(seqno, sizeof(seqno), " seq %i", seq++); if (n->isAggregate) { if (n->aggregateLow > n->prefix->masklen) { fprintf(f,"%s prefix-list %s%s permit %s ge %u le %u\n", n->prefix->family == AF_INET ? "ip" : "ipv6", bname ? bname : "NN", seqno, prefix, n->aggregateLow, n->aggregateHi); } else { fprintf(f,"%s prefix-list %s%s permit %s le %u\n", n->prefix->family == AF_INET ? "ip" : "ipv6", bname?bname:"NN", seqno, prefix, n->aggregateHi); } } else { fprintf(f,"%s prefix-list %s%s permit %s\n", (n->prefix->family == AF_INET) ? "ip" : "ipv6", bname ? bname : "NN", seqno, prefix); } checkSon: if (n->son) bgpq4_print_cprefix(n->son, ff); } static void bgpq4_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) bgpq4_print_cprefixxr(n->son, ff); } static void bgpq4_print_hprefix(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_sep(n->prefix, prefix, sizeof(prefix), " "); if (n->isAggregate) { if (n->aggregateLow > n->prefix->masklen) { fprintf(f,"ip %s-prefix %s permit %s greater-equal %u " "less-equal %u\n", n->prefix->family == AF_INET ? "ip" : "ipv6", bname ? bname : "NN", prefix, n->aggregateLow, n->aggregateHi); } else { fprintf(f,"ip %s-prefix %s permit %s less-equal %u\n", n->prefix->family == AF_INET ? "ip" : "ipv6", bname ? bname : "NN", prefix, n->aggregateHi); } } else { fprintf(f,"ip %s-prefix %s permit %s\n", n->prefix->family == AF_INET ? "ip" : "ipv6", bname ? bname : "NN", prefix); } checkSon: if (n->son) bgpq4_print_hprefix(n->son, ff); } static void bgpq4_print_eprefix(struct sx_radix_node *n, void *ff) { char prefix[128], seqno[16] = ""; FILE *f = (FILE*)ff; if (!f) f = stdout; if (n->isGlue) goto checkSon; sx_prefix_snprintf(n->prefix, prefix, sizeof(prefix)); snprintf(seqno, sizeof(seqno), " seq %i", seq++); if (n->isAggregate) { if (n->aggregateLow > n->prefix->masklen) { fprintf(f," %s permit %s ge %u le %u\n", seqno, prefix, n->aggregateLow, n->aggregateHi); } else { fprintf(f," %s permit %s le %u\n", seqno, prefix, n->aggregateHi); } } else { fprintf(f," %s permit %s\n", seqno, prefix); } checkSon: if (n->son) bgpq4_print_eprefix(n->son, ff); } static void bgpq4_print_ceacl(struct sx_radix_node *n, void *ff) { char prefix[128]; FILE *f = (FILE*)ff; char *c; struct in_addr netmask; netmask.s_addr = 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.s_addr = 0; else { netmask.s_addr <<= (32 - n->prefix->masklen); netmask.s_addr &= 0xfffffffful; } netmask.s_addr = htonl(netmask.s_addr); if (n->isAggregate) { struct in_addr mask, wildaddr, wild2addr, wildmask; int masklen = n->aggregateLow; mask.s_addr = 0xfffffffful; wildaddr.s_addr = 0xfffffffful >> n->prefix->masklen; if (n->aggregateHi == 32) wild2addr.s_addr = 0; else wild2addr.s_addr = 0xfffffffful >> n->aggregateHi; wildaddr.s_addr = wildaddr.s_addr & (~wild2addr.s_addr); if (masklen == 32) mask.s_addr = 0xfffffffful; else mask.s_addr = 0xfffffffful & (0xfffffffful << (32 - masklen)); if (n->aggregateHi == 32) wild2addr.s_addr = 0; else wild2addr.s_addr = 0xfffffffful >> n->aggregateHi; wildmask.s_addr = (0xfffffffful >> n->aggregateLow) & (~wild2addr.s_addr); mask.s_addr = htonl(mask.s_addr); wildaddr.s_addr = htonl(wildaddr.s_addr); wildmask.s_addr = htonl(wildmask.s_addr); if (wildaddr.s_addr) { fprintf(f, "permit ip %s ", inet_ntoa(n->prefix->addr.addr)); fprintf(f, "%s ", inet_ntoa(wildaddr)); } else { fprintf(f, "permit ip host %s ", inet_ntoa(n->prefix->addr.addr)); } if (wildmask.s_addr) { fprintf(f, "%s ", inet_ntoa(mask)); fprintf(f, "%s\n", inet_ntoa(wildmask)); } else { fprintf(f, "host %s\n", inet_ntoa(mask)); } } else { fprintf(f, "permit ip host %s host %s\n", prefix, inet_ntoa(netmask)); } checkSon: if (n->son) bgpq4_print_ceacl(n->son, ff); } static void bgpq4_print_nokia_ipfilter(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)); fprintf(f, " prefix %s\n", prefix); checkSon: if (n->son) bgpq4_print_nokia_ipfilter(n->son, ff); } static void bgpq4_print_nokia_md_ipfilter(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)); fprintf(f, " prefix %s { }\n", prefix); checkSon: if (n->son) bgpq4_print_nokia_md_ipfilter(n->son, ff); } static void bgpq4_print_nokia_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_snprintf(n->prefix, prefix, sizeof(prefix)); if (!n->isAggregate) { fprintf(f, " prefix %s exact\n", prefix); } else { if (n->aggregateLow > n->prefix->masklen) { fprintf(f," prefix %s prefix-length-range %u-%u\n", prefix, n->aggregateLow, n->aggregateHi); } else { fprintf(f," prefix %s prefix-length-range %u-%u\n", prefix, n->prefix->masklen, n->aggregateHi); } } checkSon: if (n->son) bgpq4_print_nokia_prefix(n->son, ff); } static void bgpq4_print_nokia_md_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_snprintf(n->prefix, prefix, sizeof(prefix)); if (!n->isAggregate) { fprintf(f, " prefix %s type exact {\n }\n", prefix); } else { if (n->aggregateLow > n->prefix->masklen) { fprintf(f," prefix %s type range {\n" " start-length %u\n" " end-length %u\n }\n", prefix, n->aggregateLow, n->aggregateHi); } else { fprintf(f," prefix %s type through {\n " "through-length %u\n }\n", prefix, n->aggregateHi); } } checkSon: if (n->son) bgpq4_print_nokia_md_prefix(n->son, ff); } static void bgpq4_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, bgpq4_print_jprefix, f); fprintf(f, " }\n}\n"); } static void bgpq4_print_juniper_routefilter(FILE *f, struct bgpq_expander *b) { char *c = NULL; 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); } if (!sx_radix_tree_empty(b->tree)) { jrfilter_prefixed = 1; sx_radix_tree_foreach(b->tree, bgpq4_print_jrfilter, f); } else { fprintf(f, " route-filter %s/0 orlonger reject;\n", b->tree->family == AF_INET ? "0.0.0.0" : "::"); } if (c) { fprintf(f, " }\n }\n }\n}\n"); } else { fprintf(f, " }\n }\n}\n"); } } static void bgpq4_print_openbgpd_prefixlist(FILE *f, struct bgpq_expander *b) { if (sx_radix_tree_empty(b->tree)) { fprintf(f, "# generated prefix-list %s (AS %u) is empty\n", b->name, b->asnumber); if (!b->asnumber) fprintf(f, "# use -a to generate \"deny from " "ASN \" instead of this list\n"); } if (!sx_radix_tree_empty(b->tree) || !b->asnumber) { if (b->name) { if (strcmp(b->name, "NN") != 0) { fprintf(f, "%s=\"", b->name); } } fprintf(f, "prefix { "); sx_radix_tree_foreach(b->tree, bgpq4_print_openbgpd_prefix, f); fprintf(f, "\n\t}"); if (b->name) { if (strcmp(b->name, "NN") != 0) { fprintf(f, "\""); } } fprintf(f, "\n"); } else { fprintf(f, "deny from AS %u\n", b->asnumber); } } static void bgpq4_print_openbgpd_prefixset(FILE *f, struct bgpq_expander *b) { bname = b->name ? b->name : "NN"; fprintf(f, "prefix-set %s {", bname); if (!sx_radix_tree_empty(b->tree)) sx_radix_tree_foreach(b->tree, bgpq4_print_openbgpd_prefix, f); fprintf(f, "\n}\n"); } static void bgpq4_print_cisco_prefixlist(FILE *f, struct bgpq_expander *b) { bname = b->name ? b->name : "NN"; seq = b->sequence; fprintf(f, "no %s prefix-list %s\n", b->family == AF_INET ? "ip" : "ipv6", bname); if (!sx_radix_tree_empty(b->tree)) { sx_radix_tree_foreach(b->tree, bgpq4_print_cprefix, f); } else { fprintf(f, "! generated prefix-list %s is empty\n", bname); fprintf(f, "%s prefix-list %s%s deny %s\n", (b->family == AF_INET) ? "ip" : "ipv6", bname, seq ? " seq 1" : "", (b->family == AF_INET) ? "0.0.0.0/0" : "::/0"); } } static void bgpq4_print_ciscoxr_prefixlist(FILE *f, struct bgpq_expander *b) { fprintf(f, "no prefix-set %s\n", b->name); fprintf(f, "prefix-set %s\n", b->name); sx_radix_tree_foreach(b->tree, bgpq4_print_cprefixxr, f); fprintf(f, "\nend-set\n"); } static void bgpq4_print_json_prefixlist(FILE *f, struct bgpq_expander *b) { fprintf(f, "{ \"%s\": [", b->name); sx_radix_tree_foreach(b->tree, bgpq4_print_json_prefix, f); fprintf(f,"\n] }\n"); } static void bgpq4_print_bird_prefixlist(FILE *f, struct bgpq_expander *b) { if (!sx_radix_tree_empty(b->tree)) { fprintf(f,"%s = [", b->name ? b->name : "NN"); sx_radix_tree_foreach(b->tree, bgpq4_print_bird_prefix, f); fprintf(f, "\n];\n"); } else { SX_DEBUG(debug_expander, "skip empty prefix-list in BIRD format\n"); } } static void bgpq4_print_huawei_prefixlist(FILE *f, struct bgpq_expander *b) { bname = b->name ? b->name : "NN"; seq = b->sequence; fprintf(f,"undo ip %s-prefix %s\n", (b->family == AF_INET) ? "ip" : "ipv6", bname); if (!sx_radix_tree_empty(b->tree)) { sx_radix_tree_foreach(b->tree, bgpq4_print_hprefix, f); } else { fprintf(f, "ip %s-prefix %s%s deny %s\n", (b->family == AF_INET) ? "ip" : "ipv6", bname, seq ? " seq 1" : "", (b->family == AF_INET) ? "0.0.0.0/0" : "::/0"); } } static void bgpq4_print_arista_prefixlist(FILE *f, struct bgpq_expander *b) { bname = b->name ? b->name : "NN"; seq = b->sequence; fprintf(f, "no %s prefix-list %s\n", b->family == AF_INET ? "ip" : "ipv6", bname); if (!sx_radix_tree_empty(b->tree)) { fprintf(f,"%s prefix-list %s\n", b->family == AF_INET ? "ip" : "ipv6", bname); sx_radix_tree_foreach(b->tree, bgpq4_print_eprefix, f); } else { fprintf(f, "! generated prefix-list %s is empty\n", bname); fprintf(f, "%s prefix-list %s\n seq %i deny %s\n", (b->family == AF_INET) ? "ip" : "ipv6", bname, seq, (b->family == AF_INET) ? "0.0.0.0/0" : "::/0"); } } struct fpcbdata { FILE *f; struct bgpq_expander *b; }; static void bgpq4_print_format_prefix(struct sx_radix_node *n, void *ff) { struct fpcbdata *fpc = (struct fpcbdata*)ff; FILE *f = fpc->f; struct bgpq_expander *b = fpc->b; if (n->isGlue) goto checkSon; if (!f) f = stdout; if (!n->isAggregate) { sx_prefix_snprintf_fmt(n->prefix, f, b->name ? b->name : "NN", b->format, n->prefix->masklen, n->prefix->masklen); } else if (n->aggregateLow > n->prefix->masklen) { sx_prefix_snprintf_fmt(n->prefix, f, b->name ? b->name : "NN", b->format, n->aggregateLow, n->aggregateHi); } else { sx_prefix_snprintf_fmt(n->prefix, f, b->name ? b->name : "NN", b->format, n->prefix->masklen, n->aggregateHi); } checkSon: if (n->son) bgpq4_print_format_prefix(n->son, ff); } static void bgpq4_print_format_prefixlist(FILE *f, struct bgpq_expander *b) { struct fpcbdata ff = {.f=f, .b=b}; int len = strlen(b->format); sx_radix_tree_foreach(b->tree, bgpq4_print_format_prefix, &ff); // Add newline if format doesn't already end with one. if (len < 2 || !(b->format[len-2] == '\\' && b->format[len-1] == 'n')) fprintf(f, "\n"); } static void bgpq4_print_nokia_prefixlist(FILE *f, struct bgpq_expander *b) { bname = b->name ? b->name : "NN"; fprintf(f,"configure router policy-options\nbegin\nno prefix-list \"%s\"\n", bname); fprintf(f,"prefix-list \"%s\"\n", bname); sx_radix_tree_foreach(b->tree, bgpq4_print_nokia_prefix, f); fprintf(f,"exit\ncommit\n"); } static void bgpq4_print_cisco_eacl(FILE *f, struct bgpq_expander *b) { bname = b->name ? b->name : "NN"; fprintf(f,"no ip access-list extended %s\n", bname); if (!sx_radix_tree_empty(b->tree)) { fprintf(f, "ip access-list extended %s\n", bname); sx_radix_tree_foreach(b->tree, bgpq4_print_ceacl, f); } else { fprintf(f, "! generated access-list %s is empty\n", bname); fprintf(f, "ip access-list extended %s deny any any\n", bname); } } static void bgpq4_print_nokia_ipprefixlist(FILE *f, struct bgpq_expander *b) { bname = b->name ? b->name : "NN"; fprintf(f, "configure filter match-list\nno %s-prefix-list \"%s\"\n", (b->tree->family == AF_INET) ? "ip" : "ipv6", bname); fprintf(f, "%s-prefix-list \"%s\" create\n", b->tree->family == AF_INET ? "ip":"ipv6", bname); if (!sx_radix_tree_empty(b->tree)) { sx_radix_tree_foreach(b->tree, bgpq4_print_nokia_ipfilter, f); } else { fprintf(f, "# generated ip-prefix-list %s is empty\n", bname); } fprintf(f,"exit\n"); } static void bgpq4_print_nokia_md_prefixlist(FILE *f, struct bgpq_expander *b) { bname = b->name ? b->name : "NN"; fprintf(f,"/configure filter match-list\ndelete %s-prefix-list \"%s\"\n", b->tree->family == AF_INET ? "ip" : "ipv6", bname); fprintf(f,"%s-prefix-list \"%s\" {\n", b->tree->family == AF_INET ? "ip" : "ipv6", bname); if (!sx_radix_tree_empty(b->tree)) { sx_radix_tree_foreach(b->tree, bgpq4_print_nokia_md_ipfilter, f); } else { fprintf(f,"# generated %s-prefix-list %s is empty\n", b->tree->family == AF_INET ? "ip" : "ipv6", bname); } fprintf(f,"}\n"); } static void bgpq4_print_nokia_md_ipprefixlist(FILE *f, struct bgpq_expander *b) { bname = b->name ? b->name : "NN"; fprintf(f, "/configure policy-options\ndelete prefix-list \"%s\"\n", bname); fprintf(f, "prefix-list \"%s\" {\n", bname); if (!sx_radix_tree_empty(b->tree)) { sx_radix_tree_foreach(b->tree, bgpq4_print_nokia_md_prefix, f); } fprintf(f,"}\n"); } static void bgpq4_print_kprefix(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_sep(n->prefix, prefix, sizeof(prefix), "/"); if (n->isAggregate) fprintf(f,"/routing filter add action=accept chain=\"" "%s-%s\" prefix=%s prefix-length=%d-%d\n", bname ? bname : "NN", n->prefix->family == AF_INET ? "V4" : "V6", prefix, n->aggregateLow, n->aggregateHi); else fprintf(f,"/routing filter add action=accept chain=\"" "%s-%s\" prefix=%s\n", bname ? bname : "NN", n->prefix->family == AF_INET ? "V4" : "V6", prefix); checkSon: if (n->son) bgpq4_print_kprefix(n->son, ff); } static void bgpq4_print_mikrotik_prefixlist(FILE *f, struct bgpq_expander *b) { bname = b->name ? b->name : "NN"; if (!sx_radix_tree_empty(b->tree)) { sx_radix_tree_foreach(b->tree, bgpq4_print_kprefix, f); } else { fprintf(f, "# generated prefix-list %s is empty\n", bname); } } void bgpq4_print_prefixlist(FILE *f, struct bgpq_expander *b) { switch (b->vendor) { case V_JUNIPER: bgpq4_print_juniper_prefixlist(f, b); break; case V_CISCO: bgpq4_print_cisco_prefixlist(f, b); break; case V_CISCO_XR: bgpq4_print_ciscoxr_prefixlist(f, b); break; case V_JSON: bgpq4_print_json_prefixlist(f, b); break; case V_BIRD: bgpq4_print_bird_prefixlist(f, b); break; case V_OPENBGPD: bgpq4_print_openbgpd_prefixlist(f, b); break; case V_FORMAT: bgpq4_print_format_prefixlist(f, b); break; case V_NOKIA: bgpq4_print_nokia_prefixlist(f, b); break; case V_NOKIA_MD: bgpq4_print_nokia_md_ipprefixlist(f, b); break; case V_HUAWEI: bgpq4_print_huawei_prefixlist(f, b); break; case V_MIKROTIK: bgpq4_print_mikrotik_prefixlist(f, b); break; case V_ARISTA: bgpq4_print_arista_prefixlist(f, b); break; } } void bgpq4_print_eacl(FILE *f, struct bgpq_expander *b) { switch (b->vendor) { case V_JUNIPER: bgpq4_print_juniper_routefilter(f, b); break; case V_CISCO: bgpq4_print_cisco_eacl(f, b); break; case V_OPENBGPD: bgpq4_print_openbgpd_prefixset(f, b); break; case V_NOKIA: bgpq4_print_nokia_ipprefixlist(f, b); break; case V_NOKIA_MD: bgpq4_print_nokia_md_prefixlist(f, b); break; case V_ARISTA: bgpq4_print_cisco_eacl(f, b); break; default: sx_report(SX_FATAL, "unreachable point\n"); } } static void bgpq4_print_juniper_route_filter_list(FILE *f, struct bgpq_expander *b) { fprintf(f, "policy-options {\nreplace:\n route-filter-list %s {\n", b->name ? b->name : "NN"); if (sx_radix_tree_empty(b->tree)) { fprintf(f, " %s/0 orlonger reject;\n", b->tree->family == AF_INET ? "0.0.0.0" : "::"); } else { jrfilter_prefixed = 0; sx_radix_tree_foreach(b->tree, bgpq4_print_jrfilter, f); } fprintf(f, " }\n}\n"); } void bgpq4_print_route_filter_list(FILE *f, struct bgpq_expander *b) { switch(b->vendor) { case V_JUNIPER: bgpq4_print_juniper_route_filter_list(f, b); break; default: sx_report(SX_FATAL, "unreachable point\n"); } } bgpq4-1.4/sx_maxsockbuf.c000066400000000000000000000060771410770527700154370ustar00rootroot00000000000000/* * Copyright (c) 2019-2020 Job Snijders * Copyright (c) 2007-2019 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. */ #include #include #include #include #include #include #include #include #include #include "extern.h" #include "sx_report.h" 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 (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; } } 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 return voptval; } bgpq4-1.4/sx_prefix.c000066400000000000000000001026651410770527700145720ustar00rootroot00000000000000/* * Copyright (c) 2019-2020 Job Snijders * Copyright (c) 2007-2019 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. */ #include #include #include #include #include #include #include "sx_prefix.h" #include "sx_report.h" int debug_aggregation = 0; extern int debug_expander; 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) memcpy(sp, p, sizeof(struct sx_prefix)); else memset(sp, 0, sizeof(struct sx_prefix)); return sp; } void sx_prefix_destroy(struct sx_prefix *p) { if (p) free(p); } void sx_radix_node_destroy(struct sx_radix_node *n) { if (n) { if (n->payload) free(n->payload); if (n->prefix) free(n->prefix); free(n); } } void sx_prefix_adjust_masklen(struct sx_prefix *p) { unsigned int nbytes = (p->family == AF_INET ? 4 : 16); unsigned 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); } } static void sx_prefix_mask(struct sx_prefix *p, struct sx_prefix *q) { unsigned int i; memset(q->addr.addrs, 0, sizeof(q->addr.addrs)); q->family = p->family; q->masklen = p->masklen; for (i = 0; i < p->masklen / 8; i++) q->addr.addrs[i] = 0xff; for (i = 1; i <= p->masklen % 8; i++) q->addr.addrs[p->masklen / 8] |= (1 << (8 - i)); } static void sx_prefix_imask(struct sx_prefix *p, struct sx_prefix *q) { unsigned int i; memset(q->addr.addrs, 0xff, sizeof(q->addr.addrs)); q->family = p->family; q->masklen = p->masklen; for (i = 0; i < p->masklen / 8; i++) q->addr.addrs[i] = 0; for (i = 1;i <= p->masklen % 8; i++) q->addr.addrs[p->masklen / 8] &= ~(1 <<(8 - i)); } int sx_prefix_parse(struct sx_prefix *p, int af, char *text) { char *c = NULL; int masklen, ret; char mtext[INET6_ADDRSTRLEN + 5]; strlcpy(mtext, text, sizeof(mtext)); c = strchr(mtext,'/'); if (c) { char* eod; *c = 0; masklen = strtol(c + 1, &eod, 10); if (eod && eod[0] && !isspace(eod[0])) { *c = '/'; sx_report(SX_ERROR, "Invalid masklen in prefix %s\n", text); goto fixups; } } else { masklen = -1; } if (!af) { if (strchr(mtext, ':')) af = AF_INET6; else af = AF_INET; } ret = inet_pton(af, mtext, &p->addr); if (ret != 1) { int aparts[4]; /* * contrary to documentation (man inet_ntop on FreeBSD), * addresses with leading zeros are not parsed correctly. Try * to workaround this issue manually. */ if (af == AF_INET && sscanf(mtext, "%i.%i.%i.%i", aparts, aparts + 1, aparts + 2, aparts + 3) == 4 && aparts[0] >= 0 && aparts[0] < 256 && aparts[1] >= 0 && aparts[1] < 256 && aparts[2] >= 0 && aparts[2] < 256 && aparts[3] >= 0 && aparts[3] < 256) { p->addr.addr.s_addr = htonl((aparts[0] << 24) + (aparts[1] << 16) + (aparts[2] << 8) + aparts[3]); } else { if (c) *c = '/'; sx_report(SX_ERROR,"Unable to parse prefix '%s', af=%i" " (%s), ret=%i\n", mtext, af, af == AF_INET ? "inet" : "inet6", ret); 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; } static 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; } static void sx_prefix_setbit(struct sx_prefix *p, int n) { unsigned char *s; if (p->family == AF_INET && (n < 0 || n > 32)) return; else if (p->family == AF_INET6 && (n < 0 || n > 128)) return; s = p->addr.addrs + (n - 1) / 8; (*s) |= 0x80 >> ((n - 1) % 8); } static int sx_radix_tree_insert_specifics(struct sx_radix_tree *t, struct sx_prefix *p, unsigned min, unsigned max) { struct sx_prefix *np; np = sx_prefix_alloc(p); if (np->masklen >= min) { struct sx_radix_node *nn = sx_radix_tree_insert(t, np); sx_prefix_destroy(np); np = nn->prefix; } if (np->masklen + 1 > max) return 1; np->masklen += 1; sx_radix_tree_insert_specifics(t, np, min, max); sx_prefix_setbit(np, np->masklen); sx_radix_tree_insert_specifics(t, np, min, max); return 1; } int sx_prefix_range_parse(struct sx_radix_tree *tree, int af, unsigned int maxlen, char *text) { char *d = strchr(text, '^'); struct sx_prefix *p; unsigned long min, max = 0; p = sx_prefix_alloc(NULL); if (!d || !d[1]) return 0; *d = 0; if (!sx_prefix_parse(p, 0, text)) { sx_report(SX_ERROR, "Unable to parse prefix %s^%s\n", text, d + 1); return 0; } *d = '^'; if (af && p->family != af) { sx_report(SX_ERROR, "Ignoring prefix %s, wrong af %i\n", text, p->family); return 0; } if (maxlen && p->masklen > maxlen) { SX_DEBUG(debug_expander, "Ignoring prefix %s, masklen %i > max" " masklen %u\n", text, p->masklen, maxlen); return 0; } if (d[1] == '-') { min = p->masklen + 1; max = maxlen; } else if (d[1] == '+') { min = p->masklen; max = maxlen; } else if (isdigit(d[1])) { char *dm = NULL; min = strtoul(d + 1, &dm, 10); if (dm && *dm == '-' && isdigit(dm[1])) { max = strtoul(dm + 1, NULL, 10); } else if (dm && *dm) { sx_report(SX_ERROR, "Unable to parse prefix-range " "%s\n", text); return 0; } } else { sx_report(SX_ERROR, "Invalid prefix-range %s\n", text); return 0; } if (min < p->masklen) { sx_report(SX_ERROR, "Invalid prefix-range %s: min %lu < " "masklen %u\n", text, min, p->masklen); return 0; } if (af == AF_INET && max > 32) { sx_report(SX_ERROR, "Invalid prefix-range %s: max %lu > " "32\n", text, max); return 0; } else if (af == AF_INET6 && max > 128) { sx_report(SX_ERROR, "Invalid ipv6 prefix-range %s: max %lu > " "128\n", text, max); return 0; } if (max > maxlen) max = maxlen; SX_DEBUG(debug_expander, "parsed prefix-range %s as %lu-%lu (maxlen: " "%u)\n", text, min, max, maxlen); sx_radix_tree_insert_specifics(tree, p, min, max); return 1; } 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_sep(struct sx_prefix *p, char *rbuffer, int srb, char *sep) { char buffer[128]; if (!sep) sep="/"; if (!p) { snprintf(rbuffer, srb, "(null)"); return 0; } inet_ntop(p->family, &p->addr, buffer, sizeof(buffer)); return snprintf(rbuffer, srb, "%s%s%i", buffer, sep, p->masklen); } int sx_prefix_snprintf(struct sx_prefix *p, char *rbuffer, int srb) { return sx_prefix_snprintf_sep(p, rbuffer, srb, "/"); } void sx_prefix_snprintf_fmt(struct sx_prefix *p, FILE *f, const char *name, const char *format, unsigned int aggregateLow, unsigned int aggregateHi) { const char *c = format; struct sx_prefix *q = sx_prefix_alloc(NULL); char prefix[128]; while (*c) { if (*c == '%') { switch (*(c + 1)) { case 'r': case 'n': if (NULL != inet_ntop(p->family, &p->addr, prefix, sizeof(prefix))) { fprintf(f, "%s", prefix); } else { sx_report(SX_ERROR, "inet_ntop failed\n"); return; } break; case 'l': fprintf(f, "%i", p->masklen); break; case 'a': fprintf(f, "%u", aggregateLow); break; case 'A': fprintf(f, "%u", aggregateHi); break; case '%': fprintf(f, "%%"); break; case 'N': fprintf(f, "%s", name); break; case 'm': sx_prefix_mask(p, q); if (NULL != inet_ntop(p->family, &q->addr, prefix, sizeof(prefix))) { fprintf(f, "%s", prefix); } else { sx_report(SX_ERROR, "inet_ntop failed\n"); return; } break; case 'i': sx_prefix_imask(p, q); if (NULL != inet_ntop(p->family, &q->addr, prefix, sizeof(prefix))) { fprintf(f, "%s", prefix); } else { sx_report(SX_ERROR, "inet_ntop failed\n"); return; } break; default : sx_report(SX_ERROR, "Unknown format char " "'%c'\n", *(c + 1)); return; } c += 2; } else if (*c == '\\') { switch(*(c + 1)) { case 'n': fprintf(f, "\n"); break; case 't': fprintf(f, "\t"); break; case '\\': fprintf(f, "\\"); break; default: fprintf(f, "%c", *(c + 1)); break; } c += 2; } else { fprintf(f, "%c", *c); c++; } } } 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; } int sx_radix_tree_empty(struct sx_radix_tree *t) { return t->head == NULL; } 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 = sx_prefix_alloc(prefix); return rn; } static int sx_prefix_eqbits(struct sx_prefix *a, struct sx_prefix *b) { unsigned int i; unsigned 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 { unsigned 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; } 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"); } sx_radix_node_destroy(node); 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"); } sx_radix_node_destroy(node); 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"); } sx_radix_node_destroy(node); return; } } struct sx_radix_node* sx_radix_tree_lookup(struct sx_radix_tree *tree, struct sx_prefix *prefix) { unsigned 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) { unsigned 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 = sx_prefix_alloc(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); sx_prefix_destroy(neoRoot); neoRoot = rn->prefix; 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; } static 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; } static int sx_radix_node_refine(struct sx_radix_node *node, unsigned refine) { if (!node->isGlue && node->prefix->masklenisAggregate = 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; } static void setGlueFrom(struct sx_radix_node *node, void *udata) { unsigned refine = *(unsigned*)udata; if (node && node->prefix->masklen <= refine) node->isGlue = 1; } static int sx_radix_node_refineLow(struct sx_radix_node *node, unsigned refineLow) { if (!node->isGlue && node->prefix->masklen<=refineLow) { if (!node->isAggregate) { node->isAggregate = 1; node->aggregateLow = refineLow; if (node->prefix->family == AF_INET) node->aggregateHi = 32; else node->aggregateHi = 128; } else node->aggregateLow=refineLow; if (node->l) { sx_radix_node_foreach(node->l, setGlueFrom, &refineLow); sx_radix_node_refineLow(node->l, refineLow); } if (node->r) { sx_radix_node_foreach(node->r, setGlueFrom, &refineLow); sx_radix_node_refineLow(node->r, refineLow); } } else if (!node->isGlue && node->prefix->masklen == refineLow) { /* not setting aggregate in this case */ if (node->l) sx_radix_node_refineLow(node->l, refineLow); if (node->r) sx_radix_node_refineLow(node->r, refineLow); } else if (node->isGlue) { if (node->r) sx_radix_node_refineLow(node->r, refineLow); if (node->l) sx_radix_node_refineLow(node->l, refineLow); } 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_refineLow(struct sx_radix_tree *tree, unsigned refineLow) { if (tree && tree->head) return sx_radix_node_refineLow(tree->head, refineLow); return 0; } #if SX_PTREE_TEST int main() { struct sx_prefix *p; struct sx_radix_tree *tree; struct sx_radix_node *node; int n; p = sx_prefix_new(0, "10.11.12.13/24"); sx_prefix_fprint(stdout, p); printf("\n"); p = sx_prefix_new(0, "10.11.12.13/33"); sx_prefix_fprint(stdout, p); printf("\n"); p = sx_prefix_new(0, "10.11.12.13/-133"); sx_prefix_fprint(stdout, p); printf("\n"); p = sx_prefix_new(AF_INET, "10.11.12.14/24"); sx_prefix_fprint(stdout, p); printf("\n"); p = sx_prefix_new(AF_INET, "10.11.12.14/33"); sx_prefix_fprint(stdout, p); printf("\n"); p = sx_prefix_new(AF_INET, "10.11.12.14/-133"); sx_prefix_fprint(stdout, p); printf("\n"); p = sx_prefix_new(AF_INET6, "10.11.12.15/24"); sx_prefix_fprint(stdout, p); printf("\n"); p = sx_prefix_new(AF_INET6, "10.11.12.15/33"); sx_prefix_fprint(stdout, p); printf("\n"); p = sx_prefix_new(AF_INET6, "10.11.12.15/-133"); sx_prefix_fprint(stdout, p); printf("\n"); p = sx_prefix_new(0, "2001:1b00::/24"); sx_prefix_fprint(stdout, p); printf("\n"); p = sx_prefix_new(0, "2001:1b00::/33"); sx_prefix_fprint(stdout, p); printf("\n"); p = sx_prefix_new(0, "2001:1b00::/-133"); sx_prefix_fprint(stdout, p); printf("\n"); p = sx_prefix_new(AF_INET6, "2001:1b01::/24"); sx_prefix_fprint(stdout, p); printf("\n"); p = sx_prefix_new(AF_INET6, "2001:1b01::/33"); sx_prefix_fprint(stdout, p); printf("\n"); p = sx_prefix_new(AF_INET6, "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, a), sx_prefix_new(0, 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, "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, "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, "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, "10.11.12.255/29"); sx_prefix_fprint(stdout, p); printf("\n"); p = sx_prefix_new(0, "10.11.12.255/28"); sx_prefix_fprint(stdout, p); printf("\n"); p = sx_prefix_new(0, "10.11.12.255/27"); sx_prefix_fprint(stdout, p); printf("\n"); p = sx_prefix_new(0, "10.11.12.255/26"); sx_prefix_fprint(stdout, p); printf("\n"); p = sx_prefix_new(0, "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, "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 bgpq4-1.4/sx_prefix.h000066400000000000000000000102271410770527700145670ustar00rootroot00000000000000/* * Copyright (c) 2007-2019 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. */ #ifndef _SX_PREFIX_H_ #define _SX_PREFIX_H_ #include #include #include #include typedef struct sx_prefix { int family; unsigned 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_radix_node_destroy(struct sx_radix_node *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_range_parse(struct sx_radix_tree *t, int af, unsigned int ml, 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_snprintf_sep(struct sx_prefix *p, char *rbuffer, int srb, char *); void sx_prefix_snprintf_fmt(struct sx_prefix *p, FILE *f, const char *name, const char *fmt, unsigned int aggregateLow, unsigned int aggregateHi); 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); int sx_radix_tree_empty(struct sx_radix_tree *t); 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); int sx_radix_tree_refineLow(struct sx_radix_tree *tree, unsigned refineLow); #ifndef HAVE_STRLCPY size_t strlcpy(char *dst, const char *src, size_t size); #endif #endif bgpq4-1.4/sx_report.c000066400000000000000000000060571410770527700146060ustar00rootroot00000000000000/* * Copyright (c) 2007-2019 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. */ #include #include #include #include #include #include #include #include #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 : "", LOG_PID, LOG_DAEMON); reportStderr = 0; } bgpq4-1.4/sx_report.h000066400000000000000000000035661410770527700146150ustar00rootroot00000000000000/* * Copyright (c) 2007-2019 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. */ #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 bgpq4-1.4/sx_slentry.c000066400000000000000000000035471410770527700147740ustar00rootroot00000000000000/* * Copyright (c) 2007-2019 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. */ #include #include #include #include #include "extern.h" struct slentry * sx_slentry_new(char *t) { struct slentry *e = malloc(sizeof(struct slentry)); if (!e) return NULL; memset(e, 0, sizeof(struct slentry)); e->text = strdup(t); return e; } struct sx_tentry * sx_tentry_new(char *t) { struct sx_tentry *te = malloc(sizeof(struct sx_tentry)); if (!te) return NULL; memset(te, 0, sizeof(struct sx_tentry)); te->text = strdup(t); return te; }