├── .github ├── images │ ├── alpine.Dockerfile │ ├── alpine:3.21.Dockerfile │ ├── alpine:3.Dockerfile │ ├── alpine:edge.Dockerfile │ ├── centos.Dockerfile │ ├── centos │ │ ├── centos:stream10.Dockerfile │ │ └── centos:stream9.Dockerfile │ ├── debian.Dockerfile │ ├── debian:bookworm.Dockerfile │ ├── debian:bullseye.Dockerfile │ ├── debian:trixie.Dockerfile │ ├── fedora │ │ ├── fedora:40.Dockerfile │ │ ├── fedora:41.Dockerfile │ │ └── fedora:42.Dockerfile │ ├── rockylinux │ │ ├── rockylinux:8.Dockerfile │ │ └── rockylinux:9.Dockerfile │ ├── ubuntu:focal.Dockerfile │ ├── ubuntu:jammy.Dockerfile │ └── ubuntu:noble.Dockerfile └── workflows │ ├── build-container.yml │ ├── build.yml │ ├── codeql-analysis.yml │ ├── matrixbuild.yml │ └── unit-tests.yml ├── CHANGES ├── COPYRIGHT ├── IDEAS ├── Makefile.am ├── README.md ├── VERSION ├── bgpq4.8 ├── bootstrap ├── compat ├── Makefile.am └── strlcpy.c ├── configure.ac ├── expander.c ├── extern.h ├── include ├── Makefile.am ├── string.h └── sys │ ├── _null.h │ ├── queue.h │ ├── tree.h │ └── types.h ├── main.c ├── printer.c ├── sx_prefix.c ├── sx_prefix.h ├── sx_report.c ├── sx_report.h ├── sx_slentry.c └── tests ├── generate_outputs.sh └── reference ├── as-as112-apnic-notation.txt ├── as-as112-ripe-notation.txt ├── as112-apnic.txt ├── as112-ripe-nonauth.txt ├── bird--4.txt ├── bird--6.txt ├── bird--asp.txt ├── eos--4.txt ├── eos--6.txt ├── formated--4.txt ├── formated--6.txt ├── huawei--4.txt ├── huawei--6.txt ├── huawei--asp.txt ├── huawei-xpl--4.txt ├── huawei-xpl--6.txt ├── huawei-xpl--asp.txt ├── ios--4.txt ├── ios--6.txt ├── ios--asp.txt ├── ios-xr--4.txt ├── ios-xr--6.txt ├── ios-xr--asp.txt ├── json--4.txt ├── json--6.txt ├── json--asp.txt ├── junos--4.txt ├── junos--6.txt ├── junos--asp.txt ├── openbgpd--4.txt ├── openbgpd--6.txt ├── openbgpd--asp.txt ├── routeros6--4.txt ├── routeros6--6.txt ├── routeros7--4.txt ├── routeros7--6.txt ├── srlinux--4.txt ├── srlinux--6.txt ├── sros--4.txt ├── sros--6.txt ├── sros--asp.txt ├── sros-mdcli--4.txt ├── sros-mdcli--6.txt └── sros-mdcli--asp.txt /.github/images/alpine.Dockerfile: -------------------------------------------------------------------------------- 1 | ARG image=alpine:latest 2 | FROM $image 3 | 4 | # Install dependencies 5 | RUN apk upgrade 6 | RUN apk add autoconf automake file gcc gzip libtool make musl-dev 7 | 8 | # Add source code 9 | ADD . /src 10 | WORKDIR /src 11 | 12 | # Run steps 13 | RUN ./bootstrap 14 | RUN ./configure 15 | RUN make 16 | RUN make check 17 | RUN make distcheck 18 | -------------------------------------------------------------------------------- /.github/images/alpine:3.21.Dockerfile: -------------------------------------------------------------------------------- 1 | alpine.Dockerfile -------------------------------------------------------------------------------- /.github/images/alpine:3.Dockerfile: -------------------------------------------------------------------------------- 1 | # to build the image locally tagged with the short commit hash: 2 | # docker build -t bgpq4:$(git rev-parse --short HEAD) -f .github/images/alpine:3.Dockerfile . 3 | ARG IMAGE=alpine:3 4 | FROM $IMAGE as builder 5 | 6 | # Install dependencies 7 | RUN apk upgrade 8 | RUN apk add autoconf automake file gcc gzip libtool make musl-dev 9 | 10 | # Add source code 11 | ADD . /src 12 | WORKDIR /src 13 | 14 | # Run steps 15 | RUN ./bootstrap 16 | RUN ./configure 17 | RUN make 18 | RUN make check 19 | RUN make distcheck 20 | 21 | FROM alpine:3 22 | COPY --from=builder /src/bgpq4 /bgp/ 23 | WORKDIR /bgp 24 | ENTRYPOINT [ "./bgpq4" ] -------------------------------------------------------------------------------- /.github/images/alpine:edge.Dockerfile: -------------------------------------------------------------------------------- 1 | alpine.Dockerfile -------------------------------------------------------------------------------- /.github/images/centos.Dockerfile: -------------------------------------------------------------------------------- 1 | ARG image=centos/centos:latest 2 | FROM quay.io/$image 3 | 4 | # Install dependencies 5 | RUN dnf -y update 6 | RUN dnf -y install autoconf automake gcc libtool make diffutils file gzip gawk 7 | 8 | # Add source code 9 | ADD . /src 10 | WORKDIR /src 11 | 12 | # Run steps 13 | RUN ./bootstrap 14 | RUN ./configure 15 | RUN make 16 | RUN make check 17 | RUN make distcheck 18 | -------------------------------------------------------------------------------- /.github/images/centos/centos:stream10.Dockerfile: -------------------------------------------------------------------------------- 1 | ../centos.Dockerfile -------------------------------------------------------------------------------- /.github/images/centos/centos:stream9.Dockerfile: -------------------------------------------------------------------------------- 1 | ../centos.Dockerfile -------------------------------------------------------------------------------- /.github/images/debian.Dockerfile: -------------------------------------------------------------------------------- 1 | ARG image=debian:buster 2 | FROM $image 3 | 4 | # From https://github.com/docker-library/postgres/blob/69bc540ecfffecce72d49fa7e4a46680350037f9/9.6/Dockerfile#L21-L24 5 | RUN apt-get update && apt-get install -y locales && rm -rf /var/lib/apt/lists/* \ 6 | && localedef -i en_US -c -f UTF-8 -A /usr/share/locale/locale.alias en_US.UTF-8 7 | ENV LANG en_US.utf8 8 | 9 | # Install dependencies 10 | RUN apt-get update \ 11 | && apt-get dist-upgrade -y \ 12 | && apt-get install -y build-essential autoconf libtool automake markdown \ 13 | && rm -rf /var/lib/apt/lists/* 14 | 15 | # Add source code 16 | ADD . /src 17 | WORKDIR /src 18 | 19 | # Run steps 20 | RUN ./bootstrap 21 | RUN ./configure 22 | RUN make 23 | RUN make check 24 | RUN make distcheck 25 | 26 | -------------------------------------------------------------------------------- /.github/images/debian:bookworm.Dockerfile: -------------------------------------------------------------------------------- 1 | debian.Dockerfile -------------------------------------------------------------------------------- /.github/images/debian:bullseye.Dockerfile: -------------------------------------------------------------------------------- 1 | debian.Dockerfile -------------------------------------------------------------------------------- /.github/images/debian:trixie.Dockerfile: -------------------------------------------------------------------------------- 1 | debian.Dockerfile -------------------------------------------------------------------------------- /.github/images/fedora/fedora:40.Dockerfile: -------------------------------------------------------------------------------- 1 | ../centos.Dockerfile -------------------------------------------------------------------------------- /.github/images/fedora/fedora:41.Dockerfile: -------------------------------------------------------------------------------- 1 | ../centos.Dockerfile -------------------------------------------------------------------------------- /.github/images/fedora/fedora:42.Dockerfile: -------------------------------------------------------------------------------- 1 | ../centos.Dockerfile -------------------------------------------------------------------------------- /.github/images/rockylinux/rockylinux:8.Dockerfile: -------------------------------------------------------------------------------- 1 | ../centos.Dockerfile -------------------------------------------------------------------------------- /.github/images/rockylinux/rockylinux:9.Dockerfile: -------------------------------------------------------------------------------- 1 | ../centos.Dockerfile -------------------------------------------------------------------------------- /.github/images/ubuntu:focal.Dockerfile: -------------------------------------------------------------------------------- 1 | debian.Dockerfile -------------------------------------------------------------------------------- /.github/images/ubuntu:jammy.Dockerfile: -------------------------------------------------------------------------------- 1 | debian.Dockerfile -------------------------------------------------------------------------------- /.github/images/ubuntu:noble.Dockerfile: -------------------------------------------------------------------------------- 1 | debian.Dockerfile -------------------------------------------------------------------------------- /.github/workflows/build-container.yml: -------------------------------------------------------------------------------- 1 | name: Container build 2 | "on": 3 | push: 4 | tags: 5 | - "*" # Push events to any tag 6 | branches: 7 | - "main" 8 | 9 | workflow_dispatch: 10 | inputs: 11 | tag: 12 | description: "Container tag to use for the build" 13 | required: true 14 | default: "test" 15 | 16 | jobs: 17 | test: 18 | uses: ./.github/workflows/unit-tests.yml 19 | 20 | build: 21 | name: Build container 22 | runs-on: ubuntu-24.04 23 | needs: test 24 | 25 | steps: 26 | - name: Checkout 27 | uses: actions/checkout@v4 28 | 29 | - # Add support for more platforms with QEMU 30 | # https://github.com/docker/setup-qemu-action 31 | name: Set up QEMU 32 | uses: docker/setup-qemu-action@v3 33 | 34 | - name: Set up Docker Buildx 35 | uses: docker/setup-buildx-action@v3 36 | 37 | - name: Docker meta 38 | id: meta 39 | uses: docker/metadata-action@v5 40 | with: 41 | images: ghcr.io/${{ github.repository_owner }}/bgpq4 42 | tags: | 43 | # pick up tag provided from workflow_dispatch user's input 44 | type=raw,value=${{ inputs.tag }} 45 | type=ref,event=tag 46 | type=ref,event=branch 47 | # git short commit 48 | type=sha 49 | 50 | - name: Login to GitHub Container Registry 51 | if: github.repository_owner == 'bgp' 52 | uses: docker/login-action@v3 53 | with: 54 | registry: ghcr.io 55 | username: ${{ github.repository_owner }} 56 | password: ${{ secrets.GITHUB_TOKEN }} 57 | 58 | - name: Build and push 59 | uses: docker/build-push-action@v6 60 | with: 61 | file: .github/images/alpine:3.Dockerfile 62 | context: . 63 | platforms: linux/amd64,linux/arm64 64 | push: ${{ github.repository_owner == 'bgp' }} 65 | tags: ${{ steps.meta.outputs.tags }} 66 | -------------------------------------------------------------------------------- /.github/workflows/build.yml: -------------------------------------------------------------------------------- 1 | name: Build and test (latest Ubuntu/macOS) 2 | 3 | on: [push, pull_request] 4 | 5 | jobs: 6 | build: 7 | runs-on: ${{ matrix.os }} 8 | strategy: 9 | matrix: 10 | os: [ubuntu-latest, macos-latest] 11 | steps: 12 | - uses: actions/checkout@v4 13 | - name: install macOS autogen prerequisites 14 | run: brew install autoconf automake libtool 15 | if: runner.os == 'macOS' 16 | - name: bootstrap 17 | run: ./bootstrap 18 | - name: configure 19 | run: ./configure 20 | - name: make 21 | run: make 22 | - name: make check 23 | run: make check 24 | - name: make distcheck 25 | run: make distcheck 26 | if: runner.os == 'Linux' 27 | 28 | -------------------------------------------------------------------------------- /.github/workflows/codeql-analysis.yml: -------------------------------------------------------------------------------- 1 | name: CodeQL analysis 2 | 3 | on: 4 | push: 5 | pull_request: 6 | schedule: 7 | # build the main branch every Tuesday morning 8 | - cron: '15 6 * * 2' 9 | workflow_dispatch: 10 | 11 | jobs: 12 | analyze: 13 | name: Analyze 14 | runs-on: ubuntu-latest 15 | permissions: 16 | actions: read 17 | contents: read 18 | security-events: write 19 | strategy: 20 | fail-fast: false 21 | matrix: 22 | language: [ 'cpp' ] 23 | steps: 24 | - name: Checkout repository 25 | uses: actions/checkout@v4 26 | - name: Initialize CodeQL 27 | uses: github/codeql-action/init@v3 28 | with: 29 | languages: ${{ matrix.language }} 30 | - name: Build Application using script 31 | run: | 32 | ./bootstrap 33 | ./configure 34 | make 35 | - name: Perform CodeQL Analysis 36 | uses: github/codeql-action/analyze@v3 37 | with: 38 | category: "/language:${{matrix.language}}" 39 | -------------------------------------------------------------------------------- /.github/workflows/matrixbuild.yml: -------------------------------------------------------------------------------- 1 | name: Build and test (linux matrix) 2 | 3 | on: [push] 4 | 5 | jobs: 6 | build: 7 | runs-on: ubuntu-latest 8 | strategy: 9 | fail-fast: false 10 | matrix: 11 | dockerenv: 12 | - debian:trixie 13 | - debian:bookworm 14 | - debian:bullseye 15 | - ubuntu:noble 16 | - ubuntu:jammy 17 | - ubuntu:focal 18 | - fedora/fedora:42 19 | - fedora/fedora:41 20 | - fedora/fedora:40 21 | - centos/centos:stream10 22 | - centos/centos:stream9 23 | - rockylinux/rockylinux:9 24 | - rockylinux/rockylinux:8 25 | - alpine:edge 26 | - alpine:3.21 27 | steps: 28 | - uses: actions/checkout@v4 29 | - name: Work around Docker BuildKit regression 30 | # https://github.com/moby/buildkit/issues/2119: `DOCKER_BUILDKIT=1 docker build` fails if Dockerfile is a symlink 31 | run: cp --remove-destination $(readlink -f .github/images/${{matrix.dockerenv}}.Dockerfile) .github/images/${{matrix.dockerenv}}.Dockerfile 32 | - name: Run build on ${{matrix.dockerenv}} 33 | run: docker build . --file .github/images/${{matrix.dockerenv}}.Dockerfile --build-arg image=${{matrix.dockerenv}} 34 | -------------------------------------------------------------------------------- /.github/workflows/unit-tests.yml: -------------------------------------------------------------------------------- 1 | name: basic unit tests 2 | 3 | on: 4 | pull_request: 5 | types: 6 | - opened 7 | - edited 8 | - reopened 9 | - ready_for_review 10 | - synchronize 11 | push: 12 | branches: 13 | - main 14 | workflow_call: 15 | 16 | jobs: 17 | output-unit-tests: 18 | name: output unit tests 19 | runs-on: ubuntu-24.04 20 | steps: 21 | - name: clone repo 22 | uses: actions/checkout@v4 23 | - name: install pre-reqs 24 | run: | 25 | sudo apt-get update 26 | DEBIAN_FRONTEND=noninteractive sudo apt-get -y --no-install-recommends install autoconf automake libtool make 27 | - name: build bgpq4 28 | run: | 29 | ./bootstrap 30 | ./configure 31 | make 32 | ./bgpq4 -v 33 | - name: generate output 34 | run: ./tests/generate_outputs.sh ./bgpq4 /tmp 35 | - name: check output 36 | run: > 37 | for file in tests/reference/*.txt; 38 | do 39 | echo "$(sha256sum "${file}" | awk '{print $1}') /tmp/$(basename "${file}")" | sha256sum --check; 40 | done 41 | -------------------------------------------------------------------------------- /CHANGES: -------------------------------------------------------------------------------- 1 | 1.15 (2024-05-15) 2 | - Apologies, debug code slipped into the last release 3 | 4 | 1.14 (2024-05-14) 5 | - Small performance gain: set TCP_NODELAY on the socket 6 | 7 | 1.13 (2024-05-01) 8 | - Fixed a bug for Mac users by removing sx_maxsockbuf() 9 | - Fixed a comma printing bug in IOS XR as-path-set output 10 | 11 | 1.12 (2024-02-12) 12 | - Fix a bug in the mikrotik printer 13 | 14 | 1.11 (2023-06-20) 15 | - disallow AS 23456 as origin (can be bypassed via -p) 16 | 17 | 1.10 (2023-06-03) 18 | - Add support for Nokia SR Linux IP prefix lists / ACL filters 19 | - Accept -3 as a no-op for bgpq3 compatibility 20 | 21 | 1.9 (2023-03-05) 22 | - Bugfix for -S problem (bgpq4#83) by James Bensley 23 | 24 | 1.8 (2023-01-20) 25 | - Downgrade 'key not found' to DEBUG level to reduce noise 26 | - Re-introduce -p for private ASN support option 27 | 28 | 1.7 (2022-11-03) 29 | - Support SOURCE:: syntax (contributed by James Bensley) 30 | 31 | 1.6 (2022-09-07) 32 | - Fix a bug in address prefix range parsing 33 | 34 | 1.5 (2022-07-25) 35 | - Add support for the new Junos as-path-origins feature 36 | 37 | 1.4 (2021-08-20) 38 | - Fix BIRD aspath output 39 | 40 | 1.3 (2021-08-20) 41 | - Change versioning from X.Y.Z to Y.Z 42 | - The repository file hierachy has been reorganized (compat/ include/) 43 | - Man page has been extended 44 | - Large portions of code have been reformatted to adhere to OpenBSD's source 45 | file style guide to improve readability. 46 | - Refactor: replace two-dimensional array for ASN storage with Red-Black tree 47 | - Reduced memory usage 48 | 49 | 0.0.9 (2021-08-18) 50 | - Fix various memory errors 51 | 52 | 0.0.8 (2021-08-17) 53 | - Reorganize automake files and includes 54 | - Normalize code to adhere to KNF 55 | - Fix all compiler warnings 56 | 57 | 0.0.6 (2020-03-12): 58 | - Bugfixes - Thanks Chris Caputo! 59 | 60 | 0.0.5 (2020-01-01): 61 | - Bugfixes 62 | 63 | 0.0.4 (2019-12-31): 64 | - Remove the '-3' command line option, assume all devices are 65 | 32-bit ASN safe 66 | 67 | 0.0.3 (2019-12-30): 68 | - Remove the '-2' command line option 69 | - Significant code reformating 70 | - Improve performance by using IRRd 4's A query when available 71 | 72 | 0.0.2 (2019-12-14): 73 | - Add Mikrotik support 74 | - Remove ASDDOT support 75 | 76 | 0.0.1 (2019-12-14): 77 | - Fork bgpq3 into bgpq4 78 | 79 | 0.1.36-pre (2019-11-08): 80 | - minor documentation cleanup: bgpq3 supports much more vendors 81 | than just Cisco and Juniper. Copyright years updated. 82 | - Nokia SR OS "classic" actually supports aggregation and 83 | and more-specific filtering in prefix-lists. Thanks to mfisher128 84 | for reporting. 85 | - change log level for prefixes with wrong address family from error 86 | to debugging: it's perfectly correct to have prefixes of different 87 | families in route-sets. Thanks to Jay Ford for suggestion. 88 | 89 | 0.1.35 (2018-11-30): 90 | - initial support for Juniper route-filter-lists (JunOS 16.2+). 91 | - too large (>124bytes) sources list was not handled correctly. 92 | Reported by Pier Carlo Chiodi. 93 | - initial support for Huawei format (prefix-lists and as-path filters) 94 | New flag -U. Requested by Alexander Wagberg. 95 | - fix ipv6 prefix-ranges. Reported by Jay Ford. 96 | - OpenBGPd change: -E now generates prefix-set instead of prefix-list. 97 | Based on submission by Denis Fondras 98 | - new option -w, allowing to 'validate' AS numbers included in as-path 99 | access-lists: only those AS having registered route-objects are allowed. 100 | By default checks route[4] presence, to check route6 objects shall be 101 | used together with -6. 102 | - cleanup OpenBGPd prefix-sets. Submitted by Claudio Jeker. 103 | - new flag -t: generate as-sets for OpenBGPD (OpenBSD 6.4+), BIRD and 104 | JSON formats. Based on submission by Claudio Jeker. 105 | - new flag -n: support for Nokia SR OS MD-CLI. Based on examples 106 | provided by Greg Hankins. 107 | - irrd queries for asn32 changed from asdot to asplain notation. 108 | Thanks to Troy2914 for heads up. 109 | 110 | 0.1.35-rc2 (2017-06-14) 111 | - OpenBSD need . Reported by Denis Fondras. 112 | - OpenBGPD output shall not emit 'deny any from any' in case of empty 113 | prefix-list. New flag -a introduced to allow peer-as indication. 114 | When this flag is not specified, empty prefix-list is generated (will 115 | not be accepted by OpenBGPD). 116 | Reported by Denis Fondras 117 | 118 | 0.1.35-rc (2017-30-05) 119 | - Nokia SR OS (formerly Alcatel-Lucent) support. Based on submission by 120 | Michail Litvak. 121 | - sync man-page with readme.md 122 | - socket() EAFNOSUPPORT error handling 123 | 124 | 0.1.33 (2016-10-14) 125 | - OpenBGPD support (-B). Submitted by Peter Hessler. 126 | 127 | 0.1.32 (2016-08-28) 128 | - rollback 0.1.32-rc2 (2015-07-01) change: by default all IRRD sources 129 | are allowed by default. Documentation updated to mark radb,ripe,apnic 130 | as 'recommended', not as 'preset default'. 131 | 132 | untagged yet (2016-05-10) 133 | - fix: was not able to build on Solaris. Thanks to Mansoor Ali Khan. 134 | - feature: IOS XR mode now supports as-paths (ios-regexs). Thanks 135 | to Tassos Chatzithomaoglou for examples and proofreading. 136 | (additions from 2015-09-23) 137 | - bugfix: stoplist shall be able to catch AS numbers as promised. 138 | - bugfix: bgpq3 shall not hang at unknown escapes in -M.. 139 | - gotcha: "ANY" object in recursive mode ignored: shut complaints on 140 | "ERROR:unexpected object 'ANY' in expanded_macro_limit (in response 141 | to !iAS-SET-SCOPESKY)" (object contains mbrs-by-ref: ANY). 142 | (additions from 2015-08-30) 143 | - bugfix: OpenBSD sys/queue.h does not have STAILQ_ interface. 144 | Thanks to Pedro Caetano for reporting and testing. 145 | - feature: alternate whois port can be configured with -h host[:port] 146 | - feature: new format char %N (object name) in formatted output. 147 | Thanks to Denis Fondras. 148 | - feature: new format chars %m (prefix mask) and %i (inverse mask) in 149 | formatted output. 150 | 151 | 0.1.32-rc5 (2015-07-12) 152 | - feature: -L : limit recursion depth when expanding as-sets 153 | (default: not limited). Based on idea by Eugene Demidov. 154 | - feature: stoplist. Now you can add EXCEPT Object... at the end of 155 | bgpq3 command line and corresponding as-sets and asns will not be 156 | expanded (does not works for prefixes and prefix-sets yet). 157 | - internals: major pipelining rewrite and some code cleanup. 158 | 159 | 0.1.32-rc4 (2015-07-06) 160 | - change: BIRD can't handle empty lists (NAME = []), so they are not 161 | generated at all. 162 | 163 | 0.1.32-rc3 (2015-07-01) 164 | - feature: option -s can be used to generate sequence numbers in IOS 165 | prefix-lists 166 | - feature: option -F can be used to generate output in user-defined 167 | format. Only prefix-lists supported for now. 168 | 169 | 0.1.32-rc2 (2015-07-01) 170 | - bugfix: when no sources provided in command line and via IRRD_SOURCES env, 171 | no source limitation were sent to IRRd. Thanks to Mikhail A. Grishin. 172 | 173 | 0.1.32-rc (2015-06-28) 174 | - bugfix: F source(s) unavailable message from IRRD was ignored. 175 | Please note: this error is caught only when all the specified sources 176 | are invalid. For example, 'bgpq3 -s nonexistant' will fail, however, 177 | 'bgpq3 -s nonexistant,ripe' will not fail and will use only ripe source. 178 | Thanks to Mikhail A. Grishin for reporting. 179 | - RIPE-style queries (-T route6 -i origin asNNN) replaced with IRRd-style 180 | !6asNNN queries. 181 | 182 | 0.1.31 (2015-06-23) 183 | - pipelining mode now counts buffered requests and issues dequeue 184 | when new request can overflow allocated buffer. So, bgpq3 shall no 185 | more require TCP tuning (it is still recomended, though). 186 | - tcp tuning parameters decreased in README (sx_maxsockbuf will not 187 | allow buffer over 2Mb anyway). 188 | 189 | 0.1.30 (2015-06-16) 190 | - bugfix: private asns with number > 2^31 were printed as negative integers. 191 | Thanks to Henrik Thostrup Jensen. 192 | - do not use ASNs reserved for documentation purposes and private use: 193 | 64496-64511 For documentation and sample code; reserved by [RFC5398] 194 | 64512-65534 For private use; reserved by [RFC6996] 195 | 65535 Reserved by [RFC7300] 196 | 65536-65551 For documentation and sample code; reserved by [RFC5398] 197 | 4200000000-4294967294 For private use; reserved by [RFC6996] 198 | 4294967295 Reserved by [RFC7300] 199 | Please, use new -p flag to include these asn's. 200 | Suggested by Henrik Thostrup Jensen and Job Snijders. 201 | - allow as-path generation with BIRD output. Suggested by Jiri Mikulas. 202 | - merge README.md changes by Job Snijders. 203 | - bugfix: incorrect asdot representation (as101. without symbols after dot) 204 | is not allowed anymore. 205 | 206 | 0.1.29 (2015-05-04) 207 | - do not include routes registered for AS23456 (transition-as) by default. 208 | Use new option -2 to restore old behaviour. 209 | 210 | 0.1.28 (2015-03-10) 211 | - minor changes: .spec update, non-silent failure on wrong af, 212 | more room for masklen... 213 | 214 | 0.1.27 (2015-03-10) 215 | - bugfix: some ipv6 prefixes were not parsed correctly since 0.1.26. 216 | Thanks to Job Snijders. 217 | 218 | 0.1.26 (2015-02-19) 219 | - RPSL support, can be found in rs-esnetcustomers. 220 | Thanks to Kris O'Connell for reporting. 221 | 222 | 0.1.25 (2014-10-29) 223 | - JSON support extended to handle "as-paths" too. Well, actually, as 224 | there are no defined format for as-path in json, bgpq3 just creates 225 | simple object like following: 226 | 227 | snar@fri:~/compile/bgpq3>./bgpq3 -j3f 20597 as-eltel 228 | {"NN": [ 229 | 112,5495,6857,8377,20597,34102,35357,43951, 230 | 52007,56764,197759,197888,198610,201499 231 | ]} 232 | 233 | Based on suggestion by Henrik Thostrup Jensen. 234 | - -W len option documented. 235 | 236 | 0.1.24 (2014-07-31) 237 | - empty prefix-lists (Cisco), extended access-lists (Cisco), as-path 238 | filters (Cisco and Juniper) and route-filters (Juniper) handling: 239 | explicit 'deny any' entry now generated instead of implicit 'permit-any'. 240 | Based on suggestion by Tore Anderson. 241 | 242 | 0.1.23 (2014-07-30) 243 | - bugfix: use of -M option caused major slowdown as it turned off 244 | request pipelining... Thanks to Tore Anderson. 245 | 246 | 0.1.22 (2014-07-27) 247 | - bugfix: allow network object with stray spaces after prefix length. 248 | Found by Tom Eichhorn in 2620:74:14::/48 (VeriSign Route6, RADB). 249 | - bugfix: networks with leading zeros (02.51.252.0/22, as4787) are not 250 | parsed correctly in inet_ntop.. Found by Tom Eichhorn. 251 | 252 | 0.1.21 (2014-06-05) 253 | - new flag -b: generate prefix-filters for BIRD (http://bird.network.cz), 254 | contributed by Job Snijders. 255 | 256 | 0.1.20-todo2 (2014-05-01) 257 | - new flag -r , allowing bgpq to generate limited set of more-specific 258 | routes - only routes with prefix-length >= are accepted. 259 | Thanks to Pavel Gulchouck for suggesion. 260 | 261 | 0.1.20-todo (2013-10-07) 262 | - socket close code fixed. Thanks to Martin J. Levy. 263 | - new flag -4, "force ipv4". Actually does a little more than allowing 264 | for pedantic checks. Thanks to Martin J. Levy. 265 | 266 | 0.1.19 (2013-05-09) 267 | - CLANG compilation issues fixed. 268 | - bgpq3.spec added. Thanks to Arnoud Vermeer. 269 | 270 | 0.1.18 (2013-01-08) 271 | - JSON output format. Thanks to Job Snijders (Atrato Networks). 272 | 273 | 0.1.17 (2012-10-25) 274 | - route-sets handling in command-line added. Thanks to Alexandr Turovsky 275 | for pointing out. 276 | - bug in aggregation documentation fixed. Thanks to Nikolay Shopik. 277 | 278 | 0.1.16 (2012-01-19) 279 | - new option -m : maximum length of accepted prefixes. 280 | Suggested by Eugene Demidov, used to discard 'too long prefixes' 281 | (like /30-/32) even if they are registered in IRR. By default 282 | limit is not set and all prefixes accepted. 283 | - documentation redesigned into text/markdown and text/html (manpage 284 | supported still). 285 | 286 | 0.1.15 (2011-07-15) 287 | - prefix-set's for Cisco IOS XR now supported too. 288 | 289 | 0.1.14 (2011-06-18) 290 | - Fixed bug in sx_maxsockbuf in rare cases of OS maxsockbuf >2M. 291 | Thanks to Andreas Lundin. 292 | 293 | 0.1.13 (2011-06-14) 294 | - never publically released. 295 | 296 | 0.1.12 (2010-10-08) 297 | - Fixed bug preventing AS262144 (that's AS4.0 in asdot) to expand. 298 | Thanks to Sergey Matveychuk 299 | 300 | 0.1.11 (2010-04-19) 301 | - Fixed another bug in aggregation (-A) mode, thanks to Dmitry Tejblum. 302 | 303 | 0.1.10 (2009-06-13) 304 | - Fixed bug in aggregation (-A) mode, thanks to Sergey Gonchar. 305 | 306 | 0.1.9 (2009-03-27) 307 | - RIPE changed ASN32 notation to asplain. And RADB does not support 308 | asplain indexing (yet?).... Fixed. Thanks to Pavel Gluchouk. 309 | 310 | 0.1.8 (2008-12-25) 311 | - new flag -D for Cisco asdot notation. Cisco behaviour is a bit 312 | strange for me, but, well, that's their decision: 313 | When the asdot format is enabled as the default, any regular expressions 314 | to match 4-byte autonomous system numbers must be written using the asdot 315 | format, or else the regular expression match will fail. 316 | (c) http://www.cisco.com/en/US/docs/ios/12_0s/release/ntes/120SNEWF.html 317 | #wp3521658 (note the URL wrap). 318 | 319 | 0.1.7 (2008-12-19): 320 | - man page. Finally :) 321 | - option -h now means not help, but now it can be used to point to 322 | alternate IRRD host, like in old bgpq. 323 | 324 | 0.1.6 (2008-08-08): 325 | - maxsockbuf call added, that can help with pipelining of really large 326 | as-sets. 327 | - new key -M for juniper route-filters, f.e.: 328 | bgpq3 -JEM "protocol bgp;\n community no-export" -l PolicyName/TermName 329 | will generate term with additional match conditions, like: 330 | 331 | policy-options { 332 | policy-statement PolicyName { 333 | term TermName { 334 | replace: 335 | from { 336 | protocol bgp; 337 | community no-export; 338 | route-filter 10.0.0.0/24 exact; 339 | } 340 | } 341 | } 342 | } 343 | 344 | 0.1.5 (2008-06-02): 345 | - route-set's expansion added. Fully functional for IPv4 prefixes, but 346 | not for IPv6 - only those prefixes explicitely marked as 'member-of: RS..' 347 | will be expanded. This is due to limitation in IRRd. 348 | - extended access-lists (Cisco) and route-filters (Juniper) generation 349 | is supported now with new -E key. For Cisco ipv6 access-lists is not 350 | yet supported. 351 | 352 | 0.1.4 (2008-05-30): 353 | - bugfix for juniper as-path group generation. Thanks to Alexander Shikoff. 354 | 355 | 0.1.3 (2008-05-20): 356 | - aggregation (-A) now supported for Cisco prefix-lists. 357 | - pipelining now can be enabled for RIPE-style queries too (ipv6). 358 | - more-specific routes (-R len) feature ported from bgpq 359 | - pipelining now set by default. -T flag now disables pipelining. 360 | - strlcpy.c imported into sources. Not found on Linux :) 361 | 362 | 0.1.2 (2008-05-19): 363 | - final support for asn32, now with correct syntax for Juniper. 364 | - experimental 'pipelining' mode (flag -T), much faster when 365 | working with big as-set's. 366 | - RIPE-style query (-i origin) now requests only route6 objects. 367 | 368 | 0.1.1 (2008-05-16): 369 | - initial support for asn32 added (flag -3). By default it's off, 370 | and when bgpq sees 32-bit asn in resolver queue, it either replaces 371 | it with AS23456 (in as-path generation mode) or queries radb for 372 | prefixes with that origin. 373 | Note: for now only JunOS 9.1 can handle asn32, not Cisco IOS.. 374 | 375 | -------------------------------------------------------------------------------- /COPYRIGHT: -------------------------------------------------------------------------------- 1 | /*- 2 | * Copyright (c) 2007-2019 Alexandre Snarskii 3 | * All rights reserved. 4 | * 5 | * Redistribution and use in source and binary forms, with or without 6 | * modification, are permitted provided that the following conditions 7 | * are met: 8 | * 1. Redistributions of source code must retain the above copyright 9 | * notice, this list of conditions and the following disclaimer. 10 | * 2. Redistributions in binary form must reproduce the above copyright 11 | * notice, this list of conditions and the following disclaimer in the 12 | * documentation and/or other materials provided with the distribution. 13 | * 14 | * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 15 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 17 | * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 18 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 20 | * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 21 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 22 | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 23 | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 24 | * SUCH DAMAGE. 25 | * 26 | */ 27 | -------------------------------------------------------------------------------- /IDEAS: -------------------------------------------------------------------------------- 1 | Ben Maddison taught me another aggregation trick: 2 | 3 | route-set: AS37271:RS-EXAMPLE 4 | mp-members: 192.0.2.0/27 5 | mp-members: 192.0.2.32/27 6 | mp-members: 192.0.2.64/27 7 | mp-members: 192.0.2.96/27 8 | mp-members: 192.0.2.128/26 9 | mp-members: 192.0.2.128/27 10 | mp-members: 192.0.2.160/27 11 | mp-members: 192.0.2.192/27 12 | mp-members: 192.0.2.224/27 13 | descr: Example route-set 14 | mnt-by: MAINT-AS37271 15 | changed: benm@workonline.africa 20210819 16 | source: RADB 17 | 18 | BGPQ4 produces the following: 19 | 20 | $ bgpq4 -A AS37271:RS-EXAMPLE 21 | no ip prefix-list NN 22 | ip prefix-list NN permit 192.0.2.0/25 ge 27 le 27 23 | ip prefix-list NN permit 192.0.2.128/26 le 27 24 | ip prefix-list NN permit 192.0.2.192/26 ge 27 le 27 25 | 26 | But the following aggregation also is valid, and shorter: 27 | 28 | ip prefix-list NN permit 192.0.2.0/24 ge 27 le 27 29 | ip prefix-list NN permit 192.0.2.128/26 30 | -------------------------------------------------------------------------------- /Makefile.am: -------------------------------------------------------------------------------- 1 | SUBDIRS = include 2 | ACLOCAL_AMFLAGS = -I m4 3 | 4 | AM_CPPFLAGS = -I$(top_srcdir)/include 5 | AM_CPPFLAGS += -I$(top_srcdir)/compat 6 | 7 | AUTOMAKE_OPTIONS=foreign subdir-objects 8 | 9 | bin_PROGRAMS=bgpq4 10 | dist_man8_MANS=bgpq4.8 11 | 12 | bgpq4_LDADD = $(PLATFORM_LDADD) $(PROG_LDADD) 13 | 14 | if !HAVE_STRLCPY 15 | SUBDIRS += compat 16 | bgpq4_LDADD += $(top_builddir)/compat/libcompat.la 17 | endif 18 | 19 | bgpq4_SOURCES=main.c extern.h printer.c expander.c \ 20 | sx_prefix.c sx_prefix.h \ 21 | sx_report.c sx_report.h \ 22 | sx_slentry.c 23 | 24 | 25 | EXTRA_DIST=bootstrap README.md CHANGES 26 | 27 | MAINTAINERCLEANFILES=configure aclocal.m4 compile \ 28 | install-sh missing Makefile.in depcomp \ 29 | stamp-h1 compat/Makefile.in \ 30 | config.guess config.sub include/Makefile.in \ 31 | ltmain.sh 32 | 33 | maintainer-clean-local: 34 | -rm -rf m4 autom4te.cache 35 | 36 | check: 37 | ./bgpq4 -v 38 | @echo 39 | -if [ -s /etc/resolv.conf ]; then \ 40 | ./bgpq4 -ddd -6 AS15562:AS-SNIJDERS ; \ 41 | else \ 42 | echo "No or empty /etc/resolv.conf, skipping online test"; \ 43 | fi 44 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | [![CI](https://github.com/bgp/bgpq4/actions/workflows/unit-tests.yml/badge.svg)](https://github.com/bgp/bgpq4/actions/workflows/unit-tests.yml) 2 | 3 | 4 | Packaging status 5 | 6 | 7 | # NAME 8 | 9 | **bgpq4** - bgp filtering automation tool 10 | 11 | # SYNOPSIS 12 | 13 | **bgpq4** 14 | \[**-h** *host\[:port]*] 15 | \[**-S** *sources*] 16 | \[**-EPz**] 17 | \[**-f** *asn* | 18 | **-F** *fmt* | 19 | **-G** *asn* 20 | **-H** *asn* 21 | **-t**] 22 | \[**-46ABbDdJjNnpsXU**] 23 | \[**-a** *asn*] 24 | \[**-r** *len*] 25 | \[**-R** *len*] 26 | \[**-m** *max*] 27 | \[**-W** *len*] 28 | *OBJECTS* 29 | \[...] 30 | \[EXCEPT OBJECTS] 31 | 32 | # DESCRIPTION 33 | 34 | The 35 | **bgpq4** 36 | utility is used to generate configurations (prefix-lists, extended 37 | access-lists, policy-statement terms and as-path lists) based on IRR data. 38 | 39 | It's options are as follows: 40 | 41 | **-4** 42 | 43 | > generate IPv4 prefix/access-lists (default). 44 | 45 | **-6** 46 | 47 | > generate IPv6 prefix/access-lists (IPv4 by default). 48 | 49 | **-A** 50 | 51 | > try to aggregate prefix-lists as much as possible (not all output 52 | > formats supported). 53 | 54 | **-a** *asn* 55 | 56 | > specify what asn shall be denied in case of empty prefix-list (OpenBGPD) 57 | 58 | **-B** 59 | 60 | > generate output in OpenBGPD format (default: Cisco) 61 | 62 | **-b** 63 | 64 | > generate output in BIRD format (default: Cisco). 65 | 66 | **-d** 67 | 68 | > enable some debugging output. 69 | 70 | **-e** 71 | 72 | > generate output in Arista EOS format (default: Cisco). 73 | 74 | **-E** 75 | 76 | > generate extended access-list (Cisco), policy-statement term using 77 | > route-filters (Juniper), \[ip|ipv6]-prefix-list (Nokia) or prefix-sets 78 | > (OpenBGPd). 79 | 80 | **-f** *number* 81 | 82 | > generate input as-path access-list. 83 | 84 | **-F** *fmt* 85 | 86 | > generate output in user-defined format. 87 | 88 | **-G** *number* 89 | 90 | > generate output as-path access-list. 91 | 92 | **-H** *number* 93 | 94 | > generate output as-list for JunOS 21.3R1+ `as-path-origin` filter (JunOS only) 95 | 96 | **-h** *host\[:port]* 97 | 98 | > host running IRRD database (default: rr.ntt.net). 99 | 100 | **-J** 101 | 102 | > generate config for Juniper (default: Cisco). 103 | 104 | **-j** 105 | 106 | > generate output in JSON format (default: Cisco). 107 | 108 | **-K** 109 | 110 | > generate config for Mikrotik ROSv6 (default: Cisco). 111 | 112 | **-K7** 113 | 114 | > generate config for Mikrotik ROSv7 (default: Cisco). 115 | 116 | **-l** *name* 117 | 118 | > name of generated entry. 119 | 120 | **-L** *limit* 121 | 122 | > limit recursion depth when expanding as-sets. 123 | 124 | **-m** *len* 125 | 126 | > maximum prefix-length of accepted prefixes (default: 32 for IPv4 and 127 | > 128 for IPv6). 128 | 129 | **-M** *match* 130 | 131 | > extra match conditions for Juniper route-filters. 132 | 133 | **-n** 134 | 135 | > generate config for Nokia SR OS MD-CLI (Cisco IOS by default) 136 | 137 | **-n2** 138 | 139 | > generate config for Nokia SR Linux (Cisco IOS by default) 140 | 141 | **-N** 142 | 143 | > generate config for Nokia SR OS classic CLI (Cisco IOS by default). 144 | 145 | **-p** 146 | 147 | > emit prefixes where the origin ASN is in the private ASN range 148 | > (disabled by default). 149 | 150 | **-r** *len* 151 | 152 | > allow more specific routes starting with specified masklen too. 153 | 154 | **-R** *len* 155 | 156 | > allow more specific routes up to specified masklen too. 157 | 158 | **-s** 159 | 160 | > generate sequence numbers in IOS-style prefix-lists. 161 | 162 | **-S** *sources* 163 | 164 | > use specified sources only (recommended: RPKI,AFRINIC,ARIN,APNIC,LACNIC,RIPE). 165 | 166 | **-t** 167 | 168 | > generate as-sets for OpenBGPd, BIRD and JSON formats. 169 | 170 | **-T** 171 | 172 | > disable pipelining (not recommended). 173 | 174 | **-W** *len* 175 | 176 | > generate as-path strings of no more than len items (use 0 for infinity). 177 | 178 | **-U** 179 | 180 | > generate config for Huawei devices (Cisco IOS by default) 181 | 182 | **-u** 183 | 184 | > generate output in Huawei XPL format. 185 | 186 | **-X** 187 | 188 | > generate config for Cisco IOS XR devices (plain IOS by default). 189 | 190 | **-z** 191 | 192 | > generate route-filter-lists (JunOS 16.2+). 193 | 194 | *OBJECTS* 195 | 196 | > means networks (in prefix format), autonomous systems, as-sets and route-sets. 197 | 198 | *EXCEPT OBJECTS* 199 | 200 | > those objects will be excluded from expansion. 201 | 202 | # EXAMPLES 203 | 204 | Generating named juniper prefix-filter for AS20597: 205 | 206 | $ bgpq4 -Jl eltel AS20597 207 | policy-options { 208 | replace: 209 | prefix-list eltel { 210 | 81.9.0.0/20; 211 | 81.9.32.0/20; 212 | 81.9.96.0/20; 213 | 81.222.128.0/20; 214 | 81.222.192.0/18; 215 | 85.249.8.0/21; 216 | 85.249.224.0/19; 217 | 89.112.0.0/19; 218 | 89.112.4.0/22; 219 | 89.112.32.0/19; 220 | 89.112.64.0/19; 221 | 217.170.64.0/20; 222 | 217.170.80.0/20; 223 | } 224 | } 225 | 226 | For Cisco we can use aggregation (-A) flag to make this prefix-filter 227 | more compact: 228 | 229 | $ bgpq4 -Al eltel AS20597 230 | no ip prefix-list eltel 231 | ip prefix-list eltel permit 81.9.0.0/20 232 | ip prefix-list eltel permit 81.9.32.0/20 233 | ip prefix-list eltel permit 81.9.96.0/20 234 | ip prefix-list eltel permit 81.222.128.0/20 235 | ip prefix-list eltel permit 81.222.192.0/18 236 | ip prefix-list eltel permit 85.249.8.0/21 237 | ip prefix-list eltel permit 85.249.224.0/19 238 | ip prefix-list eltel permit 89.112.0.0/18 ge 19 le 19 239 | ip prefix-list eltel permit 89.112.4.0/22 240 | ip prefix-list eltel permit 89.112.64.0/19 241 | ip prefix-list eltel permit 217.170.64.0/19 ge 20 le 20 242 | 243 | Prefixes 89.112.0.0/19 and 89.112.32.0/19 now aggregated 244 | into single entry 89.112.0.0/18 ge 19 le 19. 245 | 246 | Well, for Juniper we can generate even more interesting policy-options, 247 | using -M <extra match conditions>, -R <len> and hierarchical names: 248 | 249 | $ bgpq4 -AJEl eltel/specifics -r 29 -R 32 -M "community blackhole" AS20597 250 | policy-options { 251 | policy-statement eltel { 252 | term specifics { 253 | replace: 254 | from { 255 | community blackhole; 256 | route-filter 81.9.0.0/20 prefix-length-range /29-/32; 257 | route-filter 81.9.32.0/20 prefix-length-range /29-/32; 258 | route-filter 81.9.96.0/20 prefix-length-range /29-/32; 259 | route-filter 81.222.128.0/20 prefix-length-range /29-/32; 260 | route-filter 81.222.192.0/18 prefix-length-range /29-/32; 261 | route-filter 85.249.8.0/21 prefix-length-range /29-/32; 262 | route-filter 85.249.224.0/19 prefix-length-range /29-/32; 263 | route-filter 89.112.0.0/17 prefix-length-range /29-/32; 264 | route-filter 217.170.64.0/19 prefix-length-range /29-/32; 265 | } 266 | } 267 | } 268 | } 269 | 270 | generated policy-option term now allows all specifics with prefix-length 271 | between /29 and /32 for eltel networks if they match with special community 272 | blackhole (defined elsewhere in configuration). 273 | 274 | Of course, this version supports IPv6 (-6): 275 | 276 | $ bgpq4 -6l as-retn-6 AS-RETN6 277 | no ipv6 prefix-list as-retn-6 278 | ipv6 prefix-list as-retn-6 permit 2001:7fb:fe00::/48 279 | ipv6 prefix-list as-retn-6 permit 2001:7fb:fe01::/48 280 | [....] 281 | 282 | and assumes your device supports 32-bit ASNs 283 | 284 | $ bgpq4 -Jf 112 AS-SPACENET 285 | policy-options { 286 | replace: 287 | as-path-group NN { 288 | as-path a0 "^112(112)*$"; 289 | as-path a1 "^112(.)*(1898|5539|8495|8763|8878|12136|12931|15909)$"; 290 | as-path a2 "^112(.)*(21358|23456|23600|24151|25152|31529|34127|34906)$"; 291 | as-path a3 "^112(.)*(35052|41720|43628|44450|196611)$"; 292 | } 293 | } 294 | 295 | see \`AS196611\` in the end of the list ? That's a 32-bit ASN. 296 | 297 | # USER-DEFINED FORMAT 298 | 299 | If you want to generate configuration not for routers, but for some 300 | other programs/systems, you may use user-defined formatting, like in 301 | example below: 302 | 303 | $ bgpq4 -F "ipfw add pass all from %n/%l to any\n" as3254 304 | ipfw add pass all from 62.244.0.0/18 to any 305 | ipfw add pass all from 91.219.29.0/24 to any 306 | ipfw add pass all from 91.219.30.0/24 to any 307 | ipfw add pass all from 193.193.192.0/19 to any 308 | 309 | Recognized format sequences are: 310 | 311 | **%n** 312 | 313 | > network 314 | 315 | **%l** 316 | 317 | > mask length 318 | 319 | **%a** 320 | 321 | > aggregate low mask length 322 | 323 | **%A** 324 | 325 | > aggregate high mask length 326 | 327 | **%N** 328 | 329 | > object name 330 | 331 | **%m** 332 | 333 | > object mask 334 | 335 | **%i** 336 | 337 | > inversed mask 338 | 339 | **\n** 340 | 341 | > new line 342 | 343 | **\t** 344 | 345 | > tabulation 346 | 347 | Please note that no new lines inserted automatically after each sentence, 348 | you have to add them into format string manually, elsewhere output will 349 | be in one line (sometimes it makes sense): 350 | 351 | $ bgpq4 -6F "%n/%l; " as-eltel 352 | 2001:1b00::/32; 2620:4f:8000::/48; 2a04:bac0::/29; 2a05:3a80::/48; 353 | 354 | # NOTES ON SOURCES 355 | 356 | By default *bgpq4* trusts data from all databases mirrored into NTT's IRR service. 357 | Unfortunately, not all these databases are equal in how much can we trust their 358 | data. 359 | RIR maintained databases (AFRINIC, ARIN, APNIC, LACNIC and RIPE) 360 | shall be trusted more than the others because they have the knowledge about 361 | which address space is allocated to each ASN, other databases lack this 362 | knowledge and can (and actually do) contain some stale data: nobody but RIRs 363 | care to remove outdated route-objects when address space is revoked from one 364 | ASN and allocated to another. In order to keep their filters both compact and 365 | current, *bgpq4 users* are encouraged to use one of two method to limit 366 | database sources to only ones they trust. 367 | 368 | One option is to use the '-S' flag. This limits all queries to a specific data 369 | source. For example, the following command tells IIRd to only use data from 370 | the RIPE RIR DB to build the prefix list for the AS-SET: 371 | 372 | $./bgpq4 -S RIPE AS-VOSTRON 373 | no ip prefix-list NN 374 | ip prefix-list NN permit 89.21.224.0/19 375 | ip prefix-list NN permit 134.0.64.0/21 376 | 377 | Be aware though, than an AS-SET may contain members from other data sources. 378 | In this case IRRd won't respond to the bgpq4 query will all the prefixes in the 379 | AS-SET tree. Make sure to use the '-S' flag with all the data sources required 380 | for the AS-SET being expanded: 381 | 382 | $./bgpq4 -S RIPE,ARIN AS-VOSTRON 383 | no ip prefix-list NN 384 | ip prefix-list NN permit 89.21.224.0/19 385 | ip prefix-list NN permit 134.0.64.0/21 386 | ip prefix-list NN permit 208.86.232.0/24 387 | ip prefix-list NN permit 208.86.233.0/24 388 | ip prefix-list NN permit 208.86.234.0/24 389 | ip prefix-list NN permit 208.86.235.0/24 390 | 391 | The other option is to specify a source for an AS-SET or Route Set using the 392 | "::" notation. When bgpq4 detects this, it will look for "::" in the specified 393 | AS-SET or RS on the CLI, and in all members of the AS-SET/RS, and for each 394 | member with a data source specified in "::" format, it will set the IRRd data 395 | source to the given value, query the AS-SET/RS, then reset the data sources back 396 | to the default list for the next object in the tree. 397 | 398 | $./bgpq4 RIPE::AS-VOSTRON 399 | no ip prefix-list NN 400 | ip prefix-list NN permit 89.21.224.0/19 401 | ip prefix-list NN permit 134.0.64.0/21 402 | ip prefix-list NN permit 208.86.232.0/22 403 | ip prefix-list NN permit 208.86.232.0/24 404 | ip prefix-list NN permit 208.86.233.0/24 405 | ip prefix-list NN permit 208.86.234.0/24 406 | ip prefix-list NN permit 208.86.235.0/24 407 | 408 | In comparison to the '-S' flag, this method return all the prefixes under the 409 | AS-SET, but the root of the tree "AS-VOSTRON" was queries from RIPE only. None 410 | of the member objects used the "::" notation so they were queries from the 411 | default source list (which is all sources). 412 | 413 | 414 | General recommendations: 415 | 416 | Use minimal set of RIR databases (only those in which you and your 417 | customers have registered route-objects). 418 | 419 | Avoid using ARIN-NONAUTH and RIPE-NONAUTH as trusted sources: these records 420 | were created in database but for address space allocated to different RIR, 421 | so the NONAUTH databases have no chance to confirm validity of this route 422 | object. 423 | 424 | $ bgpq4 -S RIPE,RADB as-space 425 | no ip prefix-list NN 426 | ip prefix-list NN permit 195.190.32.0/19 427 | 428 | $ bgpq4 -S RADB,RIPE as-space 429 | no ip prefix-list NN 430 | ip prefix-list NN permit 45.4.4.0/22 431 | ip prefix-list NN permit 45.4.132.0/22 432 | ip prefix-list NN permit 45.6.128.0/22 433 | ip prefix-list NN permit 45.65.184.0/22 434 | [...] 435 | 436 | When known, use the "::" notation to speicy the authortative data source for 437 | an AS-SET or RS instead of the -S flag. 438 | 439 | # PERFORMANCE 440 | 441 | To improve \`bgpq4\` performance when expanding extra-large AS-SETs you 442 | shall tune OS settings to enlarge TCP send buffer. 443 | 444 | FreeBSD can be tuned in the following way: 445 | 446 | sysctl -w net.inet.tcp.sendbuf_max=2097152 447 | 448 | Linux can be tuned in the following way: 449 | 450 | sysctl -w net.ipv4.tcp_window_scaling=1 451 | 452 | sysctl -w net.core.rmem_max=2097152 453 | 454 | sysctl -w net.core.wmem_max=2097152 455 | 456 | sysctl -w net.ipv4.tcp_rmem="4096 87380 2097152" 457 | 458 | sysctl -w net.ipv4.tcp_wmem="4096 65536 2097152" 459 | 460 | # CONTAINER IMAGE 461 | 462 | A multi-arch (linux/amd64 and linux/arm64) container image is built automatically for all tagged releases and `main` branch. The image is based on Alpine Linux and is available on [GitHub Container Registry](https://github.com/bgp/bgpq4/pkgs/container/bgpq4). 463 | 464 | Using the image is as simple as: 465 | 466 | ``` 467 | docker run --rm ghcr.io/bgp/bgpq4:latest -Jl eltel AS20597 468 | policy-options { 469 | replace: 470 | prefix-list eltel { 471 | 81.9.0.0/20; 472 | 81.9.32.0/20; 473 | 81.9.96.0/20; 474 | 81.222.128.0/20; 475 | 81.222.160.0/20; 476 | 81.222.192.0/18; 477 | 85.249.8.0/21; 478 | 85.249.224.0/19; 479 | 89.112.0.0/17; 480 | 217.170.64.0/19; 481 | } 482 | } 483 | ``` 484 | 485 | # BUILDING 486 | 487 | This project uses autotools. If you are building from the repository, 488 | run the following command to prepare the build system: 489 | 490 | ./bootstrap 491 | 492 | In order to compile the software, run: 493 | 494 | ./configure 495 | 496 | make 497 | 498 | make install 499 | 500 | If you wish to remove the generated build system files from your 501 | working tree, run: 502 | 503 | make maintainer-clean 504 | 505 | In order to create a distribution archive, run: 506 | 507 | make dist 508 | 509 | # DIAGNOSTICS 510 | 511 | When everything is OK, 512 | **bgpq4** 513 | generates access-list to standard output and exits with status == 0. 514 | In case of errors they are printed to stderr and the program exits with 515 | non-zero status. 516 | 517 | # TESTS 518 | 519 | The [tests/](tests/) folder contains reference output data in [text files](tests/reference/). The [generate_outputs.sh](tests/generate_outputs.sh) script is used in the [Github workflow](.github/workflows/unit-tests.yml) to generate the same output data, using the latest commit, and compare the output data to the stored "known-good" reference data, and check there are no changes. 520 | 521 | To update the reference data (i.e. if the bgpq4 output is modified), simply run the script again (`./tests/generate_outputs.sh ./bgpq4 tests/reference`) and commit the changes. 522 | 523 | # AUTHORS 524 | 525 | Alexandre Snarskii, Christian David, Claudio Jeker, Job Snijders, 526 | Massimiliano Stucchi, Michail Litvak, Peter Schoenmaker, Roelf Wichertjes, 527 | and contributions from many others. 528 | 529 | # SEE ALSO 530 | 531 | **https://github.com/bgp/bgpq4** 532 | BGPQ4 on Github. 533 | 534 | **http://bgpfilterguide.nlnog.net/** 535 | NLNOG's BGP Filter Guide. 536 | 537 | **https://tcp0.com/cgi-bin/mailman/listinfo/bgpq4** 538 | Users and interested parties can subscribe to the BGPQ4 mailing list bgpq4@tcp0.com 539 | 540 | # PROJECT MAINTAINER 541 | 542 | Job Snijders <job@sobornost.net> 543 | -------------------------------------------------------------------------------- /VERSION: -------------------------------------------------------------------------------- 1 | 1.15 2 | -------------------------------------------------------------------------------- /bgpq4.8: -------------------------------------------------------------------------------- 1 | .\" Copyright (c) 2007-2019 Alexandre Snarskii 2 | .\" All rights reserved. 3 | .\" 4 | .\" Redistribution and use in source and binary forms, with or without 5 | .\" modification, are permitted provided that the following conditions 6 | .\" are met: 7 | .\" 1. Redistributions of source code must retain the above copyright 8 | .\" notice, this list of conditions and the following disclaimer. 9 | .\" 2. Redistributions in binary form must reproduce the above copyright 10 | .\" notice, this list of conditions and the following disclaimer in the 11 | .\" documentation and/or other materials provided with the distribution. 12 | .\" 13 | .\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 14 | .\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 15 | .\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 16 | .\" ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 17 | .\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 18 | .\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 19 | .\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 20 | .\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 21 | .\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 22 | .\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 23 | .\" SUCH DAMAGE. 24 | .\" 25 | .Dd December 23, 2020 26 | .Dt BGPQ4 8 27 | .Os 28 | .Sh NAME 29 | .Nm bgpq4 30 | .Nd "bgp filtering automation tool" 31 | .Sh SYNOPSIS 32 | .Nm 33 | .Op Fl h Ar host[:port] 34 | .Op Fl S Ar sources 35 | .Op Fl EPz 36 | .Oo 37 | .Fl f Ar asn | 38 | .Fl F Ar fmt | 39 | .Fl G Ar asn 40 | .Fl H Ar asn 41 | .Fl t 42 | .Oc 43 | .Op Fl 46ABbDdJjNnpsXU 44 | .Op Fl a Ar asn 45 | .Op Fl r Ar len 46 | .Op Fl R Ar len 47 | .Op Fl m Ar max 48 | .Op Fl W Ar len 49 | .Ar OBJECTS 50 | .Op "..." 51 | .Op EXCEPT OBJECTS 52 | .Sh DESCRIPTION 53 | The 54 | .Nm 55 | utility used to generate configurations (prefix-lists, extended 56 | access-lists, policy-statement terms and as-path lists) based on IRR data. 57 | .Pp 58 | The options are as follows: 59 | .Bl -tag -width Ds 60 | .It Fl 4 61 | generate IPv4 prefix/access-lists (default). 62 | .It Fl 6 63 | generate IPv6 prefix/access-lists (IPv4 by default). 64 | .It Fl A 65 | try to aggregate prefix-lists as much as possible (not all output 66 | formats supported). 67 | .It Fl a Ar asn 68 | specify what asn shall be denied in case of empty prefix-list (OpenBGPD) 69 | .It Fl B 70 | generate output in OpenBGPD format (default: Cisco) 71 | .It Fl b 72 | generate output in BIRD format (default: Cisco). 73 | .It Fl d 74 | enable some debugging output. 75 | .It Fl e 76 | generate output in Arista EOS format (default: Cisco). 77 | .It Fl E 78 | generate extended access-list (Cisco), policy-statement term using 79 | route-filters (Juniper), [ip|ipv6]-prefix-list (Nokia) or prefix-sets 80 | (OpenBGPd). 81 | .It Fl f Ar number 82 | generate input as-path access-list. 83 | .It Fl F Ar fmt 84 | generate output in user-defined format. 85 | .It Fl G Ar number 86 | generate output as-path access-list. 87 | .It Fl H Ar number 88 | generate output as-list for 89 | .Em as-path-origin 90 | filter (JunOS 21.3R1+) 91 | .It Fl h Ar host[:port] 92 | host running IRRD database (default: rr.ntt.net). 93 | .It Fl J 94 | generate config for Juniper (default: Cisco). 95 | .It Fl j 96 | generate output in JSON format (default: Cisco). 97 | .It Fl K 98 | generate config for Mikrotik ROSv6 (default: Cisco). 99 | .It Fl K7 100 | generate config for Mikrotik ROSv7 (default: Cisco). 101 | .It Fl l Ar name 102 | name of generated entry. 103 | .It Fl L Ar limit 104 | limit recursion depth when expanding as-sets. 105 | .It Fl m Ar len 106 | maximum prefix-length of accepted prefixes (default: 32 for IPv4 and 107 | 128 for IPv6). 108 | .It Fl M Ar match 109 | extra match conditions for Juniper route-filters. 110 | .It Fl n 111 | generate config for Nokia SR OS MD-CLI (Cisco IOS by default) 112 | .It Fl n2 113 | generate config for Nokia SR Linux (Cisco IOS by default) 114 | .It Fl N 115 | generate config for Nokia SR OS classic CLI (Cisco IOS by default). 116 | .It Fl p 117 | emit prefixes where the origin ASN is 23456 or in the private ASN range 118 | (disabled by default). 119 | .It Fl r Ar len 120 | allow more specific routes starting with specified masklen too. 121 | .It Fl R Ar len 122 | allow more specific routes up to specified masklen too. 123 | .It Fl s 124 | generate sequence numbers in IOS-style prefix-lists. 125 | .It Fl S Ar sources 126 | use specified sources only (recommended: RPKI,AFRINIC,APNIC,ARIN,LACNIC,RIPE). 127 | .It Fl t 128 | generate as-sets for OpenBGPd, BIRD and JSON formats. 129 | .It Fl T 130 | disable pipelining (not recommended). 131 | .It Fl U 132 | generate config for Huawei devices (Cisco IOS by default) 133 | .It Fl u 134 | generate config for Huawei devices in XPL format (Cisco IOS by default) 135 | .It Fl W Ar len 136 | generate as-path strings of no more than len items (use 0 for infinity). 137 | .It Fl X 138 | generate config for Cisco IOS XR devices (plain IOS by default). 139 | .It Fl z 140 | generate route-filter-lists (JunOS 16.2+). 141 | .It Ar OBJECTS 142 | means networks (in prefix format), autonomous systems, as-sets and route-sets. 143 | .It Ar EXCEPT OBJECTS 144 | those objects will be excluded from expansion. 145 | .El 146 | .Sh EXAMPLES 147 | Generating named juniper prefix-filter for AS20597: 148 | .nf 149 | .Bd -literal 150 | $ bgpq4 -Jl eltel AS20597 151 | policy-options { 152 | replace: 153 | prefix-list eltel { 154 | 81.9.0.0/20; 155 | 81.9.32.0/20; 156 | 81.9.96.0/20; 157 | 81.222.128.0/20; 158 | 81.222.192.0/18; 159 | 85.249.8.0/21; 160 | 85.249.224.0/19; 161 | 89.112.0.0/19; 162 | 89.112.4.0/22; 163 | 89.112.32.0/19; 164 | 89.112.64.0/19; 165 | 217.170.64.0/20; 166 | 217.170.80.0/20; 167 | } 168 | } 169 | .Ed 170 | .fi 171 | .Pp 172 | For Cisco we can use aggregation (-A) flag to make this prefix-filter 173 | more compact: 174 | .nf 175 | .Bd -literal 176 | $ bgpq4 -Al eltel AS20597 177 | no ip prefix-list eltel 178 | ip prefix-list eltel permit 81.9.0.0/20 179 | ip prefix-list eltel permit 81.9.32.0/20 180 | ip prefix-list eltel permit 81.9.96.0/20 181 | ip prefix-list eltel permit 81.222.128.0/20 182 | ip prefix-list eltel permit 81.222.192.0/18 183 | ip prefix-list eltel permit 85.249.8.0/21 184 | ip prefix-list eltel permit 85.249.224.0/19 185 | ip prefix-list eltel permit 89.112.0.0/18 ge 19 le 19 186 | ip prefix-list eltel permit 89.112.4.0/22 187 | ip prefix-list eltel permit 89.112.64.0/19 188 | ip prefix-list eltel permit 217.170.64.0/19 ge 20 le 20 189 | .Ed 190 | .fi 191 | .Pp 192 | Prefixes 89.112.0.0/19 and 89.112.32.0/19 now aggregated 193 | into single entry 89.112.0.0/18 ge 19 le 19. 194 | .Pp 195 | Well, for Juniper we can generate even more interesting policy-options, 196 | using -M , -R and hierarchical names: 197 | .nf 198 | .Bd -literal 199 | $ bgpq4 -AJEl eltel/specifics -r 29 -R 32 -M "community blackhole" AS20597 200 | policy-options { 201 | policy-statement eltel { 202 | term specifics { 203 | replace: 204 | from { 205 | community blackhole; 206 | route-filter 81.9.0.0/20 prefix-length-range /29-/32; 207 | route-filter 81.9.32.0/20 prefix-length-range /29-/32; 208 | route-filter 81.9.96.0/20 prefix-length-range /29-/32; 209 | route-filter 81.222.128.0/20 prefix-length-range /29-/32; 210 | route-filter 81.222.192.0/18 prefix-length-range /29-/32; 211 | route-filter 85.249.8.0/21 prefix-length-range /29-/32; 212 | route-filter 85.249.224.0/19 prefix-length-range /29-/32; 213 | route-filter 89.112.0.0/17 prefix-length-range /29-/32; 214 | route-filter 217.170.64.0/19 prefix-length-range /29-/32; 215 | } 216 | } 217 | } 218 | } 219 | .Ed 220 | .fi 221 | generated policy-option term now allows all specifics with prefix-length 222 | between /29 and /32 for eltel networks if they match with special community 223 | blackhole (defined elsewhere in configuration). 224 | .Pp 225 | Of course, this version supports IPv6 (-6): 226 | .nf 227 | .Bd -literal 228 | $ bgpq4 -6l as-retn-6 AS-RETN6 229 | no ipv6 prefix-list as-retn-6 230 | ipv6 prefix-list as-retn-6 permit 2001:7fb:fe00::/48 231 | ipv6 prefix-list as-retn-6 permit 2001:7fb:fe01::/48 232 | [....] 233 | .Ed 234 | .fi 235 | and assumes your device supports 32-bit ASNs 236 | .nf 237 | .Bd -literal 238 | $ bgpq4 -Jf 112 AS-SPACENET 239 | policy-options { 240 | replace: 241 | as-path-group NN { 242 | as-path a0 "^112(112)*$"; 243 | as-path a1 "^112(.)*(1898|5539|8495|8763|8878|12136|12931|15909)$"; 244 | as-path a2 "^112(.)*(21358|23456|23600|24151|25152|31529|34127|34906)$"; 245 | as-path a3 "^112(.)*(35052|41720|43628|44450|196611)$"; 246 | } 247 | } 248 | .Ed 249 | .fi 250 | see `AS196611` in the end of the list ? That's a 32-bit ASN. 251 | .Sh USER-DEFINED FORMAT 252 | If you want to generate configuration not for routers, but for some 253 | other programs/systems, you may use user-defined formatting, like in 254 | example below: 255 | .nf 256 | .Bd -literal 257 | $ bgpq4 -F "ipfw add pass all from %n/%l to any\\n" as3254 258 | ipfw add pass all from 62.244.0.0/18 to any 259 | ipfw add pass all from 91.219.29.0/24 to any 260 | ipfw add pass all from 91.219.30.0/24 to any 261 | ipfw add pass all from 193.193.192.0/19 to any 262 | .Ed 263 | .fi 264 | .Pp 265 | Recognized format sequences are: 266 | .Pp 267 | .Bl -tag -width Ds -offset indent -compact 268 | .It Cm %n 269 | network 270 | .It Cm %l 271 | mask length 272 | .It Cm %a 273 | aggregate low mask length 274 | .It Cm \&%A 275 | aggregate high mask length 276 | .It Cm \&%N 277 | object name 278 | .It Cm %m 279 | object mask 280 | .It Cm %i 281 | inversed mask 282 | .It Cm \en 283 | new line 284 | .It Cm \et 285 | tabulation 286 | .El 287 | .Pp 288 | Please note that no new lines are inserted automatically after each sentence. 289 | You have to add them into format string manually, otherwise the output will 290 | be in one single line (sometimes it makes sense): 291 | .nf 292 | .Bd -literal 293 | $ bgpq4 -6F "%n/%l; " as-eltel 294 | 2001:1b00::/32; 2620:4f:8000::/48; 2a04:bac0::/29; 2a05:3a80::/48; 295 | .Ed 296 | .fi 297 | .Sh NOTES ON SOURCES 298 | By default 299 | .Em bgpq4 300 | trusts data from all the databases mirrored into NTT's IRR service. 301 | Unfortunately, not all these databases are equal in how much we can 302 | trust their data. 303 | RIR maintained databases (AFRINIC, ARIN, APNIC, LACNIC and RIPE) 304 | shall be trusted more than the others because they have the 305 | knowledge about who the rightful holders of resources are, while 306 | other databases lack this knowledge and can (and, actually do) contain 307 | stale data: no one but the RIRs care to remove outdated route-objects 308 | when address space is de-allocated or transferred. 309 | In order to keep their filters both compact and actual, 310 | .Em bgpq4 users 311 | are encouraged to use '-S' flag to limit database sources to only 312 | the ones they trust. 313 | .Pp 314 | General recommendations: 315 | .Pp 316 | Use a minimal set of RIR databases (only those in which you and your 317 | customers have registered route-objects). 318 | .Pp 319 | Avoid using ARIN-NONAUTH and RIPE-NONAUTH as trusted sources: these records 320 | were created in the database, but for address space allocated to different RIRs, 321 | so the NONAUTH databases have no chance to confirm validity of the route 322 | objects they contain. 323 | .Bd -literal 324 | $ bgpq4 -S RIPE,RADB as-space 325 | no ip prefix-list NN 326 | ip prefix-list NN permit 195.190.32.0/19 327 | 328 | $ bgpq4 -S RADB,RIPE as-space 329 | no ip prefix-list NN 330 | ip prefix-list NN permit 45.4.4.0/22 331 | ip prefix-list NN permit 45.4.132.0/22 332 | ip prefix-list NN permit 45.6.128.0/22 333 | ip prefix-list NN permit 45.65.184.0/22 334 | [...] 335 | .Ed 336 | .Sh PERFORMANCE 337 | To improve `bgpq4` performance when expanding extra-large AS-SETs you 338 | shall tune OS settings to enlarge TCP send buffer. 339 | .Pp 340 | FreeBSD can be tuned in the following way: 341 | .Pp 342 | .Dl sysctl -w net.inet.tcp.sendbuf_max=2097152 343 | .Pp 344 | Linux can be tuned in the following way: 345 | .Pp 346 | .Dl sysctl -w net.ipv4.tcp_window_scaling=1 347 | .Dl sysctl -w net.core.rmem_max=2097152 348 | .Dl sysctl -w net.core.wmem_max=2097152 349 | .Dl sysctl -w net.ipv4.tcp_rmem="4096 87380 2097152" 350 | .Dl sysctl -w net.ipv4.tcp_wmem="4096 65536 2097152" 351 | .Sh BUILDING 352 | This project uses autotools. If you are building from the repository, 353 | run the following command to prepare the build system: 354 | .Pp 355 | .Dl ./bootstrap 356 | .Pp 357 | In order to compile the software, run: 358 | .Pp 359 | .Dl ./configure 360 | .Dl make 361 | .Dl make install 362 | .Pp 363 | If you wish to remove the generated build system files from your 364 | working tree, run: 365 | .Pp 366 | .Dl make maintainer-clean 367 | .Pp 368 | In order to create a distribution archive, run: 369 | .Pp 370 | .Dl make dist 371 | .Sh DIAGNOSTICS 372 | When everything is OK, 373 | .Nm 374 | generates access-list to standard output and exits with status == 0. 375 | In case of errors they are printed to stderr and program exits with 376 | non-zero status. 377 | .Sh AUTHORS 378 | Alexandre Snarskii, Christian David, Claudio Jeker, Job Snijders, 379 | Massimiliano Stucchi, Michail Litvak, Peter Schoenmaker, Roelf Wichertjes, 380 | and contributions from many others. 381 | .Sh SEE ALSO 382 | .Sy https://github.com/bgp/bgpq4 383 | BGPQ4 on Github. 384 | .Pp 385 | .Sy http://bgpfilterguide.nlnog.net/ 386 | NLNOG's BGP Filter Guide. 387 | .Pp 388 | .Sy https://tcp0.com/cgi-bin/mailman/listinfo/bgpq4 389 | Users and interested parties can subscribe to the BGPQ4 mailing list bgpq4@tcp0.com 390 | .Sh PROJECT MAINTAINER 391 | .An Job Snijders Aq job@sobornost.net 392 | -------------------------------------------------------------------------------- /bootstrap: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # 3 | # Script to help bootstrap the build system when checked out from git 4 | # 5 | 6 | bsd_environment() { 7 | # Based on https://github.com/rvm/rvm/blob/59fe3b39f0fb5ae01ed5b9aa187201080815ac16/scripts/functions/build_config_system#L123 8 | if [ -z "${AUTOCONF_VERSION}" ] 9 | then 10 | export AUTOCONF_VERSION 11 | AUTOCONF_VERSION="$( 12 | ls -1 /usr/local/bin/autoreconf-* | 13 | awk -F- '{print $NF}' | 14 | sort | 15 | tail -n 1 16 | )" 17 | echo "Using autoconf version: $AUTOCONF_VERSION" 18 | fi 19 | 20 | if [ -z "${AUTOMAKE_VERSION}" ] 21 | then 22 | export AUTOMAKE_VERSION 23 | # FreeBSD might have automake-wrapper 24 | AUTOMAKE_VERSION="$( 25 | ls -1 /usr/local/bin/automake-1* | 26 | awk -F- '{print $NF}' | 27 | sort | 28 | tail -n 1 29 | )" 30 | echo "Using automake version: $AUTOMAKE_VERSION" 31 | fi 32 | } 33 | 34 | # Use the uname string to figure out if this is a BSD 35 | case "$(uname)" in 36 | *BSD*) bsd_environment ;; 37 | esac 38 | 39 | test -n "$srcdir" || srcdir="$(dirname "$0")" 40 | test -n "$srcdir" || srcdir=. 41 | 42 | autoreconf --force --install --verbose "$srcdir" 43 | -------------------------------------------------------------------------------- /compat/Makefile.am: -------------------------------------------------------------------------------- 1 | AM_CPPFLAGS = -I$(top_srcdir)/include 2 | 3 | noinst_LTLIBRARIES = libcompat.la 4 | 5 | libcompat_la_LIBADD = $(PLATFORM_LDADD) 6 | 7 | libcompat_la_SOURCES = strlcpy.c 8 | -------------------------------------------------------------------------------- /compat/strlcpy.c: -------------------------------------------------------------------------------- 1 | /* $OpenBSD: strlcpy.c,v 1.16 2019/01/25 00:19:25 millert Exp $ */ 2 | 3 | /* 4 | * Copyright (c) 1998, 2015 Todd C. Miller 5 | * 6 | * Permission to use, copy, modify, and distribute this software for any 7 | * purpose with or without fee is hereby granted, provided that the above 8 | * copyright notice and this permission notice appear in all copies. 9 | * 10 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 11 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 12 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 13 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 14 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 15 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 16 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 17 | */ 18 | 19 | #include 20 | #include 21 | 22 | /* 23 | * Copy string src to buffer dst of size dsize. At most dsize-1 24 | * chars will be copied. Always NUL terminates (unless dsize == 0). 25 | * Returns strlen(src); if retval >= dsize, truncation occurred. 26 | */ 27 | size_t 28 | strlcpy(char *dst, const char *src, size_t dsize) 29 | { 30 | const char *osrc = src; 31 | size_t nleft = dsize; 32 | 33 | /* Copy as many bytes as will fit. */ 34 | if (nleft != 0) { 35 | while (--nleft != 0) { 36 | if ((*dst++ = *src++) == '\0') 37 | break; 38 | } 39 | } 40 | 41 | /* Not enough room in dst, add NUL and traverse rest of src. */ 42 | if (nleft == 0) { 43 | if (dsize != 0) 44 | *dst = '\0'; /* NUL-terminate dst */ 45 | while (*src++) 46 | ; 47 | } 48 | 49 | return(src - osrc - 1); /* count does not include NUL */ 50 | } 51 | -------------------------------------------------------------------------------- /configure.ac: -------------------------------------------------------------------------------- 1 | # 2 | # Copyright (c) 2019 Brent Cook 3 | # 4 | # Permission to use, copy, modify, and distribute this software for any 5 | # purpose with or without fee is hereby granted, provided that the above 6 | # copyright notice and this permission notice appear in all copies. 7 | # 8 | # THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 9 | # WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 10 | # MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 11 | # ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 12 | # WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 13 | # ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 14 | # OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 15 | 16 | AC_INIT([bgpq4], m4_esyscmd([tr -d '\n' < VERSION]), [job@sobornost.net]) 17 | 18 | AC_CANONICAL_HOST 19 | AM_INIT_AUTOMAKE([subdir-objects foreign]) 20 | AC_CONFIG_MACRO_DIR([m4]) 21 | m4_ifdef([AM_SILENT_RULES], [AM_SILENT_RULES([yes])]) 22 | AC_PROG_CC([cc gcc]) 23 | 24 | case $host_os in 25 | *darwin*) 26 | HOST_OS=darwin 27 | AC_DEFINE([SPT_TYPE], [SPT_REUSEARGV]) 28 | ;; 29 | *freebsd*) 30 | HOST_OS=freebsd 31 | ;; 32 | *linux*) 33 | HOST_OS=linux 34 | CFLAGS="$CFLAGS -D_DEFAULT_SOURCE -D_BSD_SOURCE -D_GNU_SOURCE" 35 | AC_DEFINE([SPT_TYPE], [SPT_REUSEARGV]) 36 | ;; 37 | *netbsd*) 38 | HOST_OS=netbsd 39 | ;; 40 | *openbsd*) 41 | HOST_OS=openbsd 42 | AC_DEFINE([HAVE_ATTRIBUTE__BOUNDED__], [1], [OpenBSD has __bounded__]) 43 | AC_DEFINE([HAVE_ATTRIBUTE__DEAD], [1], [OpenBSD has __dead]) 44 | ;; 45 | *solaris*) 46 | HOST_OS=solaris 47 | CFLAGS="$CFLAGS -D__EXTENSIONS__ -D_XOPEN_SOURCE=600 -DBSD_COMP" 48 | ;; 49 | *) ;; 50 | esac 51 | 52 | AM_CONDITIONAL([HOST_DARWIN], [test x$HOST_OS = xdarwin]) 53 | AM_CONDITIONAL([HOST_FREEBSD], [test x$HOST_OS = xfreebsd]) 54 | AM_CONDITIONAL([HOST_LINUX], [test x$HOST_OS = xlinux]) 55 | AM_CONDITIONAL([HOST_NETBSD], [test x$HOST_OS = xnetbsd]) 56 | AM_CONDITIONAL([HOST_SOLARIS], [test x$HOST_OS = xsolaris]) 57 | 58 | AC_PROG_CC 59 | AM_PROG_CC_C_O 60 | LT_INIT 61 | AC_PROG_INSTALL 62 | 63 | AC_ARG_ENABLE(warnings, 64 | AS_HELP_STRING([--disable-warnings], 65 | [ enable compiler warnings [default=enabled]]), 66 | [case $enableval in 67 | yes) enable_warnings=yes;; 68 | no) enable_warnings=no;; 69 | *) enable_warnings=yes;; esac], 70 | enable_warnings=yes) 71 | 72 | if test "$enable_warnings" = yes; then 73 | AM_CFLAGS="$AM_CFLAGS -Wall -Wstrict-prototypes -Wmissing-prototypes -Wmissing-declarations -Wshadow -Wpointer-arith -Wsign-compare -Werror-implicit-function-declaration" 74 | save_cflags="$CFLAGS" 75 | CFLAGS=-Wno-pointer-sign 76 | AC_MSG_CHECKING([whether CC supports -Wno-pointer-sign]) 77 | AC_COMPILE_IFELSE([AC_LANG_PROGRAM([])], 78 | [AC_MSG_RESULT([yes])] 79 | [WARN_CFLAGS=-Wno-pointer-sign], 80 | [AC_MSG_RESULT([no])] 81 | ) 82 | AM_CFLAGS="$AM_CFLAGS $WARN_CFLAGS" 83 | CFLAGS="$save_cflags" 84 | fi 85 | 86 | AC_MSG_CHECKING([if compiling with clang]) 87 | AC_COMPILE_IFELSE([AC_LANG_PROGRAM([], [[ 88 | #ifndef __clang__ 89 | not clang 90 | #endif 91 | ]])], 92 | [AC_MSG_RESULT([yes])] 93 | [CLANG_FLAGS=-Qunused-arguments], 94 | [AC_MSG_RESULT([no])] 95 | ) 96 | AM_CFLAGS="$AM_CFLAGS $CLANG_FLAGS" 97 | AM_LDFLAGS="$LDFLAGS $CLANG_FLAGS" 98 | AC_SUBST(AM_CFLAGS) 99 | AC_SUBST(AM_LDFLAGS) 100 | 101 | AC_CHECK_FUNCS(strlcpy) 102 | AC_CHECK_FUNCS(pledge) 103 | 104 | AC_CHECK_LIB(socket,socket) 105 | AC_CHECK_LIB(nsl,getaddrinfo) 106 | 107 | AC_CHECK_HEADERS([sys/cdefs.h sys/queue.h sys/tree.h sys/select.h]) 108 | 109 | AM_CONDITIONAL([HAVE_PLEDGE], [test "x$ac_cv_func_pledge" = xyes]) 110 | AM_CONDITIONAL([HAVE_STRLCPY], [test "x$ac_cv_func_strlcpy" = xyes]) 111 | 112 | AC_CONFIG_FILES([ 113 | Makefile 114 | include/Makefile 115 | compat/Makefile 116 | ]) 117 | 118 | AC_OUTPUT 119 | -------------------------------------------------------------------------------- /extern.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2007-2019 Alexandre Snarskii 3 | * All rights reserved. 4 | * 5 | * Redistribution and use in source and binary forms, with or without 6 | * modification, are permitted provided that the following conditions 7 | * are met: 8 | * 1. Redistributions of source code must retain the above copyright 9 | * notice, this list of conditions and the following disclaimer. 10 | * 2. Redistributions in binary form must reproduce the above copyright 11 | * notice, this list of conditions and the following disclaimer in the 12 | * documentation and/or other materials provided with the distribution. 13 | * 14 | * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 15 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 17 | * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 18 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 20 | * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 21 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 22 | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 23 | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 24 | * SUCH DAMAGE. 25 | */ 26 | 27 | #include 28 | #include 29 | 30 | #include "sx_prefix.h" 31 | 32 | struct slentry { 33 | STAILQ_ENTRY(slentry) entry; 34 | char *text; 35 | }; 36 | 37 | struct slentry *sx_slentry_new(char *text); 38 | 39 | struct sx_tentry { 40 | RB_ENTRY(sx_tentry) entry; 41 | char *text; 42 | }; 43 | 44 | struct sx_tentry *sx_tentry_new(char *text); 45 | 46 | struct asn_entry { 47 | RB_ENTRY(asn_entry) entry; 48 | uint32_t asn; 49 | }; 50 | 51 | typedef enum { 52 | V_CISCO = 0, 53 | V_JUNIPER, 54 | V_CISCO_XR, 55 | V_JSON, 56 | V_BIRD, 57 | V_OPENBGPD, 58 | V_FORMAT, 59 | V_NOKIA, 60 | V_HUAWEI, 61 | V_HUAWEI_XPL, 62 | V_MIKROTIK6, 63 | V_MIKROTIK7, 64 | V_NOKIA_MD, 65 | V_ARISTA, 66 | V_NOKIA_SRL, 67 | } bgpq_vendor_t; 68 | 69 | typedef enum { 70 | T_NONE = 0, 71 | T_ASPATH, 72 | T_OASPATH, 73 | T_ASLIST, 74 | T_ASSET, 75 | T_PREFIXLIST, 76 | T_EACL, 77 | T_ROUTE_FILTER_LIST 78 | } bgpq_gen_t; 79 | 80 | struct bgpq_expander; 81 | 82 | struct request { 83 | STAILQ_ENTRY(request) next; 84 | char *request; 85 | int size, offset; 86 | void *udata; 87 | unsigned int depth; 88 | int (*callback)(char *, struct bgpq_expander *, 89 | struct request *); 90 | }; 91 | 92 | struct bgpq_expander { 93 | struct sx_radix_tree *tree; 94 | int family; 95 | char *sources; 96 | char *defaultsources; 97 | unsigned int usesource; 98 | uint32_t asnumber; 99 | int aswidth; 100 | char *name; 101 | bgpq_vendor_t vendor; 102 | bgpq_gen_t generation; 103 | int identify; 104 | int sequence; 105 | unsigned int maxdepth; 106 | unsigned int cdepth; 107 | int validate_asns; 108 | struct bgpq_prequest *firstpipe, *lastpipe; 109 | int piped; 110 | char *match; 111 | char *server; 112 | char *port; 113 | char *format; 114 | unsigned int maxlen; 115 | int fd; 116 | RB_HEAD(asn_tree, asn_entry) asnlist; 117 | STAILQ_HEAD(requests, request) wq, rq; 118 | STAILQ_HEAD(slentries, slentry) macroses, rsets; 119 | RB_HEAD(tentree, sx_tentry) already, stoplist; 120 | }; 121 | 122 | int asn_cmp(struct asn_entry *, struct asn_entry *); 123 | RB_PROTOTYPE(asn_tree, asn_entry, entry, asn_cmp); 124 | 125 | int bgpq_expander_init(struct bgpq_expander *b, int af); 126 | int bgpq_expander_add_asset(struct bgpq_expander *b, char *set); 127 | int bgpq_expander_add_rset(struct bgpq_expander *b, char *set); 128 | int bgpq_expander_add_as(struct bgpq_expander *b, char *as); 129 | int bgpq_expander_add_prefix(struct bgpq_expander *b, char *prefix); 130 | int bgpq_expander_add_prefix_range(struct bgpq_expander *b, char *prefix); 131 | int bgpq_expander_add_stop(struct bgpq_expander *b, char *object); 132 | 133 | char* bgpq_get_asset(char *object); 134 | char* bgpq_get_rset(char *object); 135 | char* bgpq_get_source(char *object); 136 | 137 | int bgpq_expand(struct bgpq_expander *b); 138 | 139 | void bgpq4_print_prefixlist(FILE *f, struct bgpq_expander *b); 140 | void bgpq4_print_eacl(FILE *f, struct bgpq_expander *b); 141 | void bgpq4_print_aspath(FILE *f, struct bgpq_expander *b); 142 | void bgpq4_print_asset(FILE *f, struct bgpq_expander *b); 143 | void bgpq4_print_oaspath(FILE *f, struct bgpq_expander *b); 144 | void bgpq4_print_aslist(FILE *f, struct bgpq_expander *b); 145 | void bgpq4_print_route_filter_list(FILE *f, struct bgpq_expander *b); 146 | 147 | void sx_radix_node_freeall(struct sx_radix_node *n); 148 | void sx_radix_tree_freeall(struct sx_radix_tree *t); 149 | void bgpq_prequest_freeall(struct bgpq_prequest *bpr); 150 | void expander_freeall(struct bgpq_expander *expander); 151 | 152 | #ifndef HAVE_STRLCPY 153 | size_t strlcpy(char *dst, const char *src, size_t size); 154 | #endif 155 | -------------------------------------------------------------------------------- /include/Makefile.am: -------------------------------------------------------------------------------- 1 | noinst_HEADERS = 2 | noinst_HEADERS += sys/_null.h 3 | noinst_HEADERS += sys/queue.h 4 | noinst_HEADERS += sys/tree.h 5 | noinst_HEADERS += sys/types.h 6 | noinst_HEADERS += string.h 7 | -------------------------------------------------------------------------------- /include/string.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Public domain 3 | * string.h compatibility shim 4 | */ 5 | 6 | #include_next 7 | 8 | #ifndef LIBCOMPAT_STRING_H 9 | #define LIBCOMPAT_STRING_H 10 | 11 | #include 12 | 13 | #if defined(__sun) || defined(__hpux) 14 | /* Some functions historically defined in string.h were placed in strings.h by 15 | * SUS. Use the same hack as OS X and FreeBSD use to work around on Solaris and HPUX. 16 | */ 17 | #include 18 | #endif 19 | 20 | #ifndef HAVE_STRLCPY 21 | size_t strlcpy(char *dst, const char *src, size_t siz); 22 | #endif 23 | 24 | #endif 25 | -------------------------------------------------------------------------------- /include/sys/_null.h: -------------------------------------------------------------------------------- 1 | /* $OpenBSD: _null.h,v 1.2 2016/09/09 22:07:58 millert Exp $ */ 2 | 3 | /* 4 | * Written by Todd C. Miller, September 9, 2016 5 | * Public domain. 6 | */ 7 | 8 | #ifndef NULL 9 | #if !defined(__cplusplus) 10 | #define NULL ((void *)0) 11 | #elif __cplusplus >= 201103L 12 | #define NULL nullptr 13 | #elif defined(__GNUG__) 14 | #define NULL __null 15 | #else 16 | #define NULL 0L 17 | #endif 18 | #endif 19 | -------------------------------------------------------------------------------- /include/sys/queue.h: -------------------------------------------------------------------------------- 1 | /* $OpenBSD: queue.h,v 1.46 2020/12/30 13:33:12 millert Exp $ */ 2 | /* $NetBSD: queue.h,v 1.11 1996/05/16 05:17:14 mycroft Exp $ */ 3 | 4 | /* 5 | * Copyright (c) 1991, 1993 6 | * The Regents of the University of California. All rights reserved. 7 | * 8 | * Redistribution and use in source and binary forms, with or without 9 | * modification, are permitted provided that the following conditions 10 | * are met: 11 | * 1. Redistributions of source code must retain the above copyright 12 | * notice, this list of conditions and the following disclaimer. 13 | * 2. Redistributions in binary form must reproduce the above copyright 14 | * notice, this list of conditions and the following disclaimer in the 15 | * documentation and/or other materials provided with the distribution. 16 | * 3. Neither the name of the University nor the names of its contributors 17 | * may be used to endorse or promote products derived from this software 18 | * without specific prior written permission. 19 | * 20 | * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 21 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 22 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 23 | * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 24 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 25 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 26 | * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 27 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 28 | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 29 | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 30 | * SUCH DAMAGE. 31 | * 32 | * @(#)queue.h 8.5 (Berkeley) 8/20/94 33 | */ 34 | 35 | #ifndef _SYS_QUEUE_H_ 36 | #define _SYS_QUEUE_H_ 37 | 38 | #include 39 | 40 | /* 41 | * This file defines five types of data structures: singly-linked lists, 42 | * lists, simple queues, tail queues and XOR simple queues. 43 | * 44 | * 45 | * A singly-linked list is headed by a single forward pointer. The elements 46 | * are singly linked for minimum space and pointer manipulation overhead at 47 | * the expense of O(n) removal for arbitrary elements. New elements can be 48 | * added to the list after an existing element or at the head of the list. 49 | * Elements being removed from the head of the list should use the explicit 50 | * macro for this purpose for optimum efficiency. A singly-linked list may 51 | * only be traversed in the forward direction. Singly-linked lists are ideal 52 | * for applications with large datasets and few or no removals or for 53 | * implementing a LIFO queue. 54 | * 55 | * A list is headed by a single forward pointer (or an array of forward 56 | * pointers for a hash table header). The elements are doubly linked 57 | * so that an arbitrary element can be removed without a need to 58 | * traverse the list. New elements can be added to the list before 59 | * or after an existing element or at the head of the list. A list 60 | * may only be traversed in the forward direction. 61 | * 62 | * A simple queue is headed by a pair of pointers, one to the head of the 63 | * list and the other to the tail of the list. The elements are singly 64 | * linked to save space, so elements can only be removed from the 65 | * head of the list. New elements can be added to the list before or after 66 | * an existing element, at the head of the list, or at the end of the 67 | * list. A simple queue may only be traversed in the forward direction. 68 | * 69 | * A tail queue is headed by a pair of pointers, one to the head of the 70 | * list and the other to the tail of the list. The elements are doubly 71 | * linked so that an arbitrary element can be removed without a need to 72 | * traverse the list. New elements can be added to the list before or 73 | * after an existing element, at the head of the list, or at the end of 74 | * the list. A tail queue may be traversed in either direction. 75 | * 76 | * An XOR simple queue is used in the same way as a regular simple queue. 77 | * The difference is that the head structure also includes a "cookie" that 78 | * is XOR'd with the queue pointer (first, last or next) to generate the 79 | * real pointer value. 80 | * 81 | * For details on the use of these macros, see the queue(3) manual page. 82 | */ 83 | 84 | #if defined(QUEUE_MACRO_DEBUG) || (defined(_KERNEL) && defined(DIAGNOSTIC)) 85 | #define _Q_INVALID ((void *)-1) 86 | #define _Q_INVALIDATE(a) (a) = _Q_INVALID 87 | #else 88 | #define _Q_INVALIDATE(a) 89 | #endif 90 | 91 | /* 92 | * Singly-linked List definitions. 93 | */ 94 | #define SLIST_HEAD(name, type) \ 95 | struct name { \ 96 | struct type *slh_first; /* first element */ \ 97 | } 98 | 99 | #define SLIST_HEAD_INITIALIZER(head) \ 100 | { NULL } 101 | 102 | #define SLIST_ENTRY(type) \ 103 | struct { \ 104 | struct type *sle_next; /* next element */ \ 105 | } 106 | 107 | /* 108 | * Singly-linked List access methods. 109 | */ 110 | #define SLIST_FIRST(head) ((head)->slh_first) 111 | #define SLIST_END(head) NULL 112 | #define SLIST_EMPTY(head) (SLIST_FIRST(head) == SLIST_END(head)) 113 | #define SLIST_NEXT(elm, field) ((elm)->field.sle_next) 114 | 115 | #define SLIST_FOREACH(var, head, field) \ 116 | for((var) = SLIST_FIRST(head); \ 117 | (var) != SLIST_END(head); \ 118 | (var) = SLIST_NEXT(var, field)) 119 | 120 | #define SLIST_FOREACH_SAFE(var, head, field, tvar) \ 121 | for ((var) = SLIST_FIRST(head); \ 122 | (var) && ((tvar) = SLIST_NEXT(var, field), 1); \ 123 | (var) = (tvar)) 124 | 125 | /* 126 | * Singly-linked List functions. 127 | */ 128 | #define SLIST_INIT(head) { \ 129 | SLIST_FIRST(head) = SLIST_END(head); \ 130 | } 131 | 132 | #define SLIST_INSERT_AFTER(slistelm, elm, field) do { \ 133 | (elm)->field.sle_next = (slistelm)->field.sle_next; \ 134 | (slistelm)->field.sle_next = (elm); \ 135 | } while (0) 136 | 137 | #define SLIST_INSERT_HEAD(head, elm, field) do { \ 138 | (elm)->field.sle_next = (head)->slh_first; \ 139 | (head)->slh_first = (elm); \ 140 | } while (0) 141 | 142 | #define SLIST_REMOVE_AFTER(elm, field) do { \ 143 | (elm)->field.sle_next = (elm)->field.sle_next->field.sle_next; \ 144 | } while (0) 145 | 146 | #define SLIST_REMOVE_HEAD(head, field) do { \ 147 | (head)->slh_first = (head)->slh_first->field.sle_next; \ 148 | } while (0) 149 | 150 | #define SLIST_REMOVE(head, elm, type, field) do { \ 151 | if ((head)->slh_first == (elm)) { \ 152 | SLIST_REMOVE_HEAD((head), field); \ 153 | } else { \ 154 | struct type *curelm = (head)->slh_first; \ 155 | \ 156 | while (curelm->field.sle_next != (elm)) \ 157 | curelm = curelm->field.sle_next; \ 158 | curelm->field.sle_next = \ 159 | curelm->field.sle_next->field.sle_next; \ 160 | } \ 161 | _Q_INVALIDATE((elm)->field.sle_next); \ 162 | } while (0) 163 | 164 | /* 165 | * List definitions. 166 | */ 167 | #define LIST_HEAD(name, type) \ 168 | struct name { \ 169 | struct type *lh_first; /* first element */ \ 170 | } 171 | 172 | #define LIST_HEAD_INITIALIZER(head) \ 173 | { NULL } 174 | 175 | #define LIST_ENTRY(type) \ 176 | struct { \ 177 | struct type *le_next; /* next element */ \ 178 | struct type **le_prev; /* address of previous next element */ \ 179 | } 180 | 181 | /* 182 | * List access methods. 183 | */ 184 | #define LIST_FIRST(head) ((head)->lh_first) 185 | #define LIST_END(head) NULL 186 | #define LIST_EMPTY(head) (LIST_FIRST(head) == LIST_END(head)) 187 | #define LIST_NEXT(elm, field) ((elm)->field.le_next) 188 | 189 | #define LIST_FOREACH(var, head, field) \ 190 | for((var) = LIST_FIRST(head); \ 191 | (var)!= LIST_END(head); \ 192 | (var) = LIST_NEXT(var, field)) 193 | 194 | #define LIST_FOREACH_SAFE(var, head, field, tvar) \ 195 | for ((var) = LIST_FIRST(head); \ 196 | (var) && ((tvar) = LIST_NEXT(var, field), 1); \ 197 | (var) = (tvar)) 198 | 199 | /* 200 | * List functions. 201 | */ 202 | #define LIST_INIT(head) do { \ 203 | LIST_FIRST(head) = LIST_END(head); \ 204 | } while (0) 205 | 206 | #define LIST_INSERT_AFTER(listelm, elm, field) do { \ 207 | if (((elm)->field.le_next = (listelm)->field.le_next) != NULL) \ 208 | (listelm)->field.le_next->field.le_prev = \ 209 | &(elm)->field.le_next; \ 210 | (listelm)->field.le_next = (elm); \ 211 | (elm)->field.le_prev = &(listelm)->field.le_next; \ 212 | } while (0) 213 | 214 | #define LIST_INSERT_BEFORE(listelm, elm, field) do { \ 215 | (elm)->field.le_prev = (listelm)->field.le_prev; \ 216 | (elm)->field.le_next = (listelm); \ 217 | *(listelm)->field.le_prev = (elm); \ 218 | (listelm)->field.le_prev = &(elm)->field.le_next; \ 219 | } while (0) 220 | 221 | #define LIST_INSERT_HEAD(head, elm, field) do { \ 222 | if (((elm)->field.le_next = (head)->lh_first) != NULL) \ 223 | (head)->lh_first->field.le_prev = &(elm)->field.le_next;\ 224 | (head)->lh_first = (elm); \ 225 | (elm)->field.le_prev = &(head)->lh_first; \ 226 | } while (0) 227 | 228 | #define LIST_REMOVE(elm, field) do { \ 229 | if ((elm)->field.le_next != NULL) \ 230 | (elm)->field.le_next->field.le_prev = \ 231 | (elm)->field.le_prev; \ 232 | *(elm)->field.le_prev = (elm)->field.le_next; \ 233 | _Q_INVALIDATE((elm)->field.le_prev); \ 234 | _Q_INVALIDATE((elm)->field.le_next); \ 235 | } while (0) 236 | 237 | #define LIST_REPLACE(elm, elm2, field) do { \ 238 | if (((elm2)->field.le_next = (elm)->field.le_next) != NULL) \ 239 | (elm2)->field.le_next->field.le_prev = \ 240 | &(elm2)->field.le_next; \ 241 | (elm2)->field.le_prev = (elm)->field.le_prev; \ 242 | *(elm2)->field.le_prev = (elm2); \ 243 | _Q_INVALIDATE((elm)->field.le_prev); \ 244 | _Q_INVALIDATE((elm)->field.le_next); \ 245 | } while (0) 246 | 247 | /* 248 | * Simple queue definitions. 249 | */ 250 | #define SIMPLEQ_HEAD(name, type) \ 251 | struct name { \ 252 | struct type *sqh_first; /* first element */ \ 253 | struct type **sqh_last; /* addr of last next element */ \ 254 | } 255 | 256 | #define SIMPLEQ_HEAD_INITIALIZER(head) \ 257 | { NULL, &(head).sqh_first } 258 | 259 | #define SIMPLEQ_ENTRY(type) \ 260 | struct { \ 261 | struct type *sqe_next; /* next element */ \ 262 | } 263 | 264 | /* 265 | * Simple queue access methods. 266 | */ 267 | #define SIMPLEQ_FIRST(head) ((head)->sqh_first) 268 | #define SIMPLEQ_END(head) NULL 269 | #define SIMPLEQ_EMPTY(head) (SIMPLEQ_FIRST(head) == SIMPLEQ_END(head)) 270 | #define SIMPLEQ_NEXT(elm, field) ((elm)->field.sqe_next) 271 | 272 | #define SIMPLEQ_FOREACH(var, head, field) \ 273 | for((var) = SIMPLEQ_FIRST(head); \ 274 | (var) != SIMPLEQ_END(head); \ 275 | (var) = SIMPLEQ_NEXT(var, field)) 276 | 277 | #define SIMPLEQ_FOREACH_SAFE(var, head, field, tvar) \ 278 | for ((var) = SIMPLEQ_FIRST(head); \ 279 | (var) && ((tvar) = SIMPLEQ_NEXT(var, field), 1); \ 280 | (var) = (tvar)) 281 | 282 | /* 283 | * Simple queue functions. 284 | */ 285 | #define SIMPLEQ_INIT(head) do { \ 286 | (head)->sqh_first = NULL; \ 287 | (head)->sqh_last = &(head)->sqh_first; \ 288 | } while (0) 289 | 290 | #define SIMPLEQ_INSERT_HEAD(head, elm, field) do { \ 291 | if (((elm)->field.sqe_next = (head)->sqh_first) == NULL) \ 292 | (head)->sqh_last = &(elm)->field.sqe_next; \ 293 | (head)->sqh_first = (elm); \ 294 | } while (0) 295 | 296 | #define SIMPLEQ_INSERT_TAIL(head, elm, field) do { \ 297 | (elm)->field.sqe_next = NULL; \ 298 | *(head)->sqh_last = (elm); \ 299 | (head)->sqh_last = &(elm)->field.sqe_next; \ 300 | } while (0) 301 | 302 | #define SIMPLEQ_INSERT_AFTER(head, listelm, elm, field) do { \ 303 | if (((elm)->field.sqe_next = (listelm)->field.sqe_next) == NULL)\ 304 | (head)->sqh_last = &(elm)->field.sqe_next; \ 305 | (listelm)->field.sqe_next = (elm); \ 306 | } while (0) 307 | 308 | #define SIMPLEQ_REMOVE_HEAD(head, field) do { \ 309 | if (((head)->sqh_first = (head)->sqh_first->field.sqe_next) == NULL) \ 310 | (head)->sqh_last = &(head)->sqh_first; \ 311 | } while (0) 312 | 313 | #define SIMPLEQ_REMOVE_AFTER(head, elm, field) do { \ 314 | if (((elm)->field.sqe_next = (elm)->field.sqe_next->field.sqe_next) \ 315 | == NULL) \ 316 | (head)->sqh_last = &(elm)->field.sqe_next; \ 317 | } while (0) 318 | 319 | #define SIMPLEQ_CONCAT(head1, head2) do { \ 320 | if (!SIMPLEQ_EMPTY((head2))) { \ 321 | *(head1)->sqh_last = (head2)->sqh_first; \ 322 | (head1)->sqh_last = (head2)->sqh_last; \ 323 | SIMPLEQ_INIT((head2)); \ 324 | } \ 325 | } while (0) 326 | 327 | /* 328 | * XOR Simple queue definitions. 329 | */ 330 | #define XSIMPLEQ_HEAD(name, type) \ 331 | struct name { \ 332 | struct type *sqx_first; /* first element */ \ 333 | struct type **sqx_last; /* addr of last next element */ \ 334 | unsigned long sqx_cookie; \ 335 | } 336 | 337 | #define XSIMPLEQ_ENTRY(type) \ 338 | struct { \ 339 | struct type *sqx_next; /* next element */ \ 340 | } 341 | 342 | /* 343 | * XOR Simple queue access methods. 344 | */ 345 | #define XSIMPLEQ_XOR(head, ptr) ((__typeof(ptr))((head)->sqx_cookie ^ \ 346 | (unsigned long)(ptr))) 347 | #define XSIMPLEQ_FIRST(head) XSIMPLEQ_XOR(head, ((head)->sqx_first)) 348 | #define XSIMPLEQ_END(head) NULL 349 | #define XSIMPLEQ_EMPTY(head) (XSIMPLEQ_FIRST(head) == XSIMPLEQ_END(head)) 350 | #define XSIMPLEQ_NEXT(head, elm, field) XSIMPLEQ_XOR(head, ((elm)->field.sqx_next)) 351 | 352 | 353 | #define XSIMPLEQ_FOREACH(var, head, field) \ 354 | for ((var) = XSIMPLEQ_FIRST(head); \ 355 | (var) != XSIMPLEQ_END(head); \ 356 | (var) = XSIMPLEQ_NEXT(head, var, field)) 357 | 358 | #define XSIMPLEQ_FOREACH_SAFE(var, head, field, tvar) \ 359 | for ((var) = XSIMPLEQ_FIRST(head); \ 360 | (var) && ((tvar) = XSIMPLEQ_NEXT(head, var, field), 1); \ 361 | (var) = (tvar)) 362 | 363 | /* 364 | * XOR Simple queue functions. 365 | */ 366 | #define XSIMPLEQ_INIT(head) do { \ 367 | arc4random_buf(&(head)->sqx_cookie, sizeof((head)->sqx_cookie)); \ 368 | (head)->sqx_first = XSIMPLEQ_XOR(head, NULL); \ 369 | (head)->sqx_last = XSIMPLEQ_XOR(head, &(head)->sqx_first); \ 370 | } while (0) 371 | 372 | #define XSIMPLEQ_INSERT_HEAD(head, elm, field) do { \ 373 | if (((elm)->field.sqx_next = (head)->sqx_first) == \ 374 | XSIMPLEQ_XOR(head, NULL)) \ 375 | (head)->sqx_last = XSIMPLEQ_XOR(head, &(elm)->field.sqx_next); \ 376 | (head)->sqx_first = XSIMPLEQ_XOR(head, (elm)); \ 377 | } while (0) 378 | 379 | #define XSIMPLEQ_INSERT_TAIL(head, elm, field) do { \ 380 | (elm)->field.sqx_next = XSIMPLEQ_XOR(head, NULL); \ 381 | *(XSIMPLEQ_XOR(head, (head)->sqx_last)) = XSIMPLEQ_XOR(head, (elm)); \ 382 | (head)->sqx_last = XSIMPLEQ_XOR(head, &(elm)->field.sqx_next); \ 383 | } while (0) 384 | 385 | #define XSIMPLEQ_INSERT_AFTER(head, listelm, elm, field) do { \ 386 | if (((elm)->field.sqx_next = (listelm)->field.sqx_next) == \ 387 | XSIMPLEQ_XOR(head, NULL)) \ 388 | (head)->sqx_last = XSIMPLEQ_XOR(head, &(elm)->field.sqx_next); \ 389 | (listelm)->field.sqx_next = XSIMPLEQ_XOR(head, (elm)); \ 390 | } while (0) 391 | 392 | #define XSIMPLEQ_REMOVE_HEAD(head, field) do { \ 393 | if (((head)->sqx_first = XSIMPLEQ_XOR(head, \ 394 | (head)->sqx_first)->field.sqx_next) == XSIMPLEQ_XOR(head, NULL)) \ 395 | (head)->sqx_last = XSIMPLEQ_XOR(head, &(head)->sqx_first); \ 396 | } while (0) 397 | 398 | #define XSIMPLEQ_REMOVE_AFTER(head, elm, field) do { \ 399 | if (((elm)->field.sqx_next = XSIMPLEQ_XOR(head, \ 400 | (elm)->field.sqx_next)->field.sqx_next) \ 401 | == XSIMPLEQ_XOR(head, NULL)) \ 402 | (head)->sqx_last = \ 403 | XSIMPLEQ_XOR(head, &(elm)->field.sqx_next); \ 404 | } while (0) 405 | 406 | 407 | /* 408 | * Tail queue definitions. 409 | */ 410 | #define TAILQ_HEAD(name, type) \ 411 | struct name { \ 412 | struct type *tqh_first; /* first element */ \ 413 | struct type **tqh_last; /* addr of last next element */ \ 414 | } 415 | 416 | #define TAILQ_HEAD_INITIALIZER(head) \ 417 | { NULL, &(head).tqh_first } 418 | 419 | #define TAILQ_ENTRY(type) \ 420 | struct { \ 421 | struct type *tqe_next; /* next element */ \ 422 | struct type **tqe_prev; /* address of previous next element */ \ 423 | } 424 | 425 | /* 426 | * Tail queue access methods. 427 | */ 428 | #define TAILQ_FIRST(head) ((head)->tqh_first) 429 | #define TAILQ_END(head) NULL 430 | #define TAILQ_NEXT(elm, field) ((elm)->field.tqe_next) 431 | #define TAILQ_LAST(head, headname) \ 432 | (*(((struct headname *)((head)->tqh_last))->tqh_last)) 433 | /* XXX */ 434 | #define TAILQ_PREV(elm, headname, field) \ 435 | (*(((struct headname *)((elm)->field.tqe_prev))->tqh_last)) 436 | #define TAILQ_EMPTY(head) \ 437 | (TAILQ_FIRST(head) == TAILQ_END(head)) 438 | 439 | #define TAILQ_FOREACH(var, head, field) \ 440 | for((var) = TAILQ_FIRST(head); \ 441 | (var) != TAILQ_END(head); \ 442 | (var) = TAILQ_NEXT(var, field)) 443 | 444 | #define TAILQ_FOREACH_SAFE(var, head, field, tvar) \ 445 | for ((var) = TAILQ_FIRST(head); \ 446 | (var) != TAILQ_END(head) && \ 447 | ((tvar) = TAILQ_NEXT(var, field), 1); \ 448 | (var) = (tvar)) 449 | 450 | 451 | #define TAILQ_FOREACH_REVERSE(var, head, headname, field) \ 452 | for((var) = TAILQ_LAST(head, headname); \ 453 | (var) != TAILQ_END(head); \ 454 | (var) = TAILQ_PREV(var, headname, field)) 455 | 456 | #define TAILQ_FOREACH_REVERSE_SAFE(var, head, headname, field, tvar) \ 457 | for ((var) = TAILQ_LAST(head, headname); \ 458 | (var) != TAILQ_END(head) && \ 459 | ((tvar) = TAILQ_PREV(var, headname, field), 1); \ 460 | (var) = (tvar)) 461 | 462 | /* 463 | * Tail queue functions. 464 | */ 465 | #define TAILQ_INIT(head) do { \ 466 | (head)->tqh_first = NULL; \ 467 | (head)->tqh_last = &(head)->tqh_first; \ 468 | } while (0) 469 | 470 | #define TAILQ_INSERT_HEAD(head, elm, field) do { \ 471 | if (((elm)->field.tqe_next = (head)->tqh_first) != NULL) \ 472 | (head)->tqh_first->field.tqe_prev = \ 473 | &(elm)->field.tqe_next; \ 474 | else \ 475 | (head)->tqh_last = &(elm)->field.tqe_next; \ 476 | (head)->tqh_first = (elm); \ 477 | (elm)->field.tqe_prev = &(head)->tqh_first; \ 478 | } while (0) 479 | 480 | #define TAILQ_INSERT_TAIL(head, elm, field) do { \ 481 | (elm)->field.tqe_next = NULL; \ 482 | (elm)->field.tqe_prev = (head)->tqh_last; \ 483 | *(head)->tqh_last = (elm); \ 484 | (head)->tqh_last = &(elm)->field.tqe_next; \ 485 | } while (0) 486 | 487 | #define TAILQ_INSERT_AFTER(head, listelm, elm, field) do { \ 488 | if (((elm)->field.tqe_next = (listelm)->field.tqe_next) != NULL)\ 489 | (elm)->field.tqe_next->field.tqe_prev = \ 490 | &(elm)->field.tqe_next; \ 491 | else \ 492 | (head)->tqh_last = &(elm)->field.tqe_next; \ 493 | (listelm)->field.tqe_next = (elm); \ 494 | (elm)->field.tqe_prev = &(listelm)->field.tqe_next; \ 495 | } while (0) 496 | 497 | #define TAILQ_INSERT_BEFORE(listelm, elm, field) do { \ 498 | (elm)->field.tqe_prev = (listelm)->field.tqe_prev; \ 499 | (elm)->field.tqe_next = (listelm); \ 500 | *(listelm)->field.tqe_prev = (elm); \ 501 | (listelm)->field.tqe_prev = &(elm)->field.tqe_next; \ 502 | } while (0) 503 | 504 | #define TAILQ_REMOVE(head, elm, field) do { \ 505 | if (((elm)->field.tqe_next) != NULL) \ 506 | (elm)->field.tqe_next->field.tqe_prev = \ 507 | (elm)->field.tqe_prev; \ 508 | else \ 509 | (head)->tqh_last = (elm)->field.tqe_prev; \ 510 | *(elm)->field.tqe_prev = (elm)->field.tqe_next; \ 511 | _Q_INVALIDATE((elm)->field.tqe_prev); \ 512 | _Q_INVALIDATE((elm)->field.tqe_next); \ 513 | } while (0) 514 | 515 | #define TAILQ_REPLACE(head, elm, elm2, field) do { \ 516 | if (((elm2)->field.tqe_next = (elm)->field.tqe_next) != NULL) \ 517 | (elm2)->field.tqe_next->field.tqe_prev = \ 518 | &(elm2)->field.tqe_next; \ 519 | else \ 520 | (head)->tqh_last = &(elm2)->field.tqe_next; \ 521 | (elm2)->field.tqe_prev = (elm)->field.tqe_prev; \ 522 | *(elm2)->field.tqe_prev = (elm2); \ 523 | _Q_INVALIDATE((elm)->field.tqe_prev); \ 524 | _Q_INVALIDATE((elm)->field.tqe_next); \ 525 | } while (0) 526 | 527 | #define TAILQ_CONCAT(head1, head2, field) do { \ 528 | if (!TAILQ_EMPTY(head2)) { \ 529 | *(head1)->tqh_last = (head2)->tqh_first; \ 530 | (head2)->tqh_first->field.tqe_prev = (head1)->tqh_last; \ 531 | (head1)->tqh_last = (head2)->tqh_last; \ 532 | TAILQ_INIT((head2)); \ 533 | } \ 534 | } while (0) 535 | 536 | /* 537 | * Singly-linked Tail queue declarations. 538 | */ 539 | #define STAILQ_HEAD(name, type) \ 540 | struct name { \ 541 | struct type *stqh_first; /* first element */ \ 542 | struct type **stqh_last; /* addr of last next element */ \ 543 | } 544 | 545 | #define STAILQ_HEAD_INITIALIZER(head) \ 546 | { NULL, &(head).stqh_first } 547 | 548 | #define STAILQ_ENTRY(type) \ 549 | struct { \ 550 | struct type *stqe_next; /* next element */ \ 551 | } 552 | 553 | /* 554 | * Singly-linked Tail queue access methods. 555 | */ 556 | #define STAILQ_FIRST(head) ((head)->stqh_first) 557 | #define STAILQ_END(head) NULL 558 | #define STAILQ_EMPTY(head) (STAILQ_FIRST(head) == STAILQ_END(head)) 559 | #define STAILQ_NEXT(elm, field) ((elm)->field.stqe_next) 560 | 561 | #define STAILQ_FOREACH(var, head, field) \ 562 | for ((var) = STAILQ_FIRST(head); \ 563 | (var) != STAILQ_END(head); \ 564 | (var) = STAILQ_NEXT(var, field)) 565 | 566 | #define STAILQ_FOREACH_SAFE(var, head, field, tvar) \ 567 | for ((var) = STAILQ_FIRST(head); \ 568 | (var) && ((tvar) = STAILQ_NEXT(var, field), 1); \ 569 | (var) = (tvar)) 570 | 571 | /* 572 | * Singly-linked Tail queue functions. 573 | */ 574 | #define STAILQ_INIT(head) do { \ 575 | STAILQ_FIRST((head)) = NULL; \ 576 | (head)->stqh_last = &STAILQ_FIRST((head)); \ 577 | } while (0) 578 | 579 | #define STAILQ_INSERT_HEAD(head, elm, field) do { \ 580 | if ((STAILQ_NEXT((elm), field) = STAILQ_FIRST((head))) == NULL) \ 581 | (head)->stqh_last = &STAILQ_NEXT((elm), field); \ 582 | STAILQ_FIRST((head)) = (elm); \ 583 | } while (0) 584 | 585 | #define STAILQ_INSERT_TAIL(head, elm, field) do { \ 586 | STAILQ_NEXT((elm), field) = NULL; \ 587 | *(head)->stqh_last = (elm); \ 588 | (head)->stqh_last = &STAILQ_NEXT((elm), field); \ 589 | } while (0) 590 | 591 | #define STAILQ_INSERT_AFTER(head, listelm, elm, field) do { \ 592 | if ((STAILQ_NEXT((elm), field) = STAILQ_NEXT((elm), field)) == NULL)\ 593 | (head)->stqh_last = &STAILQ_NEXT((elm), field); \ 594 | STAILQ_NEXT((elm), field) = (elm); \ 595 | } while (0) 596 | 597 | #define STAILQ_REMOVE_HEAD(head, field) do { \ 598 | if ((STAILQ_FIRST((head)) = \ 599 | STAILQ_NEXT(STAILQ_FIRST((head)), field)) == NULL) \ 600 | (head)->stqh_last = &STAILQ_FIRST((head)); \ 601 | } while (0) 602 | 603 | #define STAILQ_REMOVE_AFTER(head, elm, field) do { \ 604 | if ((STAILQ_NEXT(elm, field) = \ 605 | STAILQ_NEXT(STAILQ_NEXT(elm, field), field)) == NULL) \ 606 | (head)->stqh_last = &STAILQ_NEXT((elm), field); \ 607 | } while (0) 608 | 609 | #define STAILQ_REMOVE(head, elm, type, field) do { \ 610 | if (STAILQ_FIRST((head)) == (elm)) { \ 611 | STAILQ_REMOVE_HEAD((head), field); \ 612 | } else { \ 613 | struct type *curelm = (head)->stqh_first; \ 614 | while (STAILQ_NEXT(curelm, field) != (elm)) \ 615 | curelm = STAILQ_NEXT(curelm, field); \ 616 | STAILQ_REMOVE_AFTER(head, curelm, field); \ 617 | } \ 618 | } while (0) 619 | 620 | #define STAILQ_CONCAT(head1, head2) do { \ 621 | if (!STAILQ_EMPTY((head2))) { \ 622 | *(head1)->stqh_last = (head2)->stqh_first; \ 623 | (head1)->stqh_last = (head2)->stqh_last; \ 624 | STAILQ_INIT((head2)); \ 625 | } \ 626 | } while (0) 627 | 628 | #define STAILQ_LAST(head, type, field) \ 629 | (STAILQ_EMPTY((head)) ? NULL : \ 630 | ((struct type *)(void *) \ 631 | ((char *)((head)->stqh_last) - offsetof(struct type, field)))) 632 | 633 | #endif /* !_SYS_QUEUE_H_ */ 634 | -------------------------------------------------------------------------------- /include/sys/tree.h: -------------------------------------------------------------------------------- 1 | /* $OpenBSD: tree.h,v 1.30 2020/10/10 18:03:41 otto Exp $ */ 2 | /* 3 | * Copyright 2002 Niels Provos 4 | * All rights reserved. 5 | * 6 | * Redistribution and use in source and binary forms, with or without 7 | * modification, are permitted provided that the following conditions 8 | * are met: 9 | * 1. Redistributions of source code must retain the above copyright 10 | * notice, this list of conditions and the following disclaimer. 11 | * 2. Redistributions in binary form must reproduce the above copyright 12 | * notice, this list of conditions and the following disclaimer in the 13 | * documentation and/or other materials provided with the distribution. 14 | * 15 | * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 16 | * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 17 | * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 18 | * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 19 | * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 20 | * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 21 | * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 22 | * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 23 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 24 | * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 25 | */ 26 | 27 | #ifndef _SYS_TREE_H_ 28 | #define _SYS_TREE_H_ 29 | 30 | #include 31 | 32 | /* 33 | * This file defines data structures for different types of trees: 34 | * splay trees and red-black trees. 35 | * 36 | * A splay tree is a self-organizing data structure. Every operation 37 | * on the tree causes a splay to happen. The splay moves the requested 38 | * node to the root of the tree and partly rebalances it. 39 | * 40 | * This has the benefit that request locality causes faster lookups as 41 | * the requested nodes move to the top of the tree. On the other hand, 42 | * every lookup causes memory writes. 43 | * 44 | * The Balance Theorem bounds the total access time for m operations 45 | * and n inserts on an initially empty tree as O((m + n)lg n). The 46 | * amortized cost for a sequence of m accesses to a splay tree is O(lg n); 47 | * 48 | * A red-black tree is a binary search tree with the node color as an 49 | * extra attribute. It fulfills a set of conditions: 50 | * - every search path from the root to a leaf consists of the 51 | * same number of black nodes, 52 | * - each red node (except for the root) has a black parent, 53 | * - each leaf node is black. 54 | * 55 | * Every operation on a red-black tree is bounded as O(lg n). 56 | * The maximum height of a red-black tree is 2lg (n+1). 57 | */ 58 | 59 | #define SPLAY_HEAD(name, type) \ 60 | struct name { \ 61 | struct type *sph_root; /* root of the tree */ \ 62 | } 63 | 64 | #define SPLAY_INITIALIZER(root) \ 65 | { NULL } 66 | 67 | #define SPLAY_INIT(root) do { \ 68 | (root)->sph_root = NULL; \ 69 | } while (0) 70 | 71 | #define SPLAY_ENTRY(type) \ 72 | struct { \ 73 | struct type *spe_left; /* left element */ \ 74 | struct type *spe_right; /* right element */ \ 75 | } 76 | 77 | #define SPLAY_LEFT(elm, field) (elm)->field.spe_left 78 | #define SPLAY_RIGHT(elm, field) (elm)->field.spe_right 79 | #define SPLAY_ROOT(head) (head)->sph_root 80 | #define SPLAY_EMPTY(head) (SPLAY_ROOT(head) == NULL) 81 | 82 | /* SPLAY_ROTATE_{LEFT,RIGHT} expect that tmp hold SPLAY_{RIGHT,LEFT} */ 83 | #define SPLAY_ROTATE_RIGHT(head, tmp, field) do { \ 84 | SPLAY_LEFT((head)->sph_root, field) = SPLAY_RIGHT(tmp, field); \ 85 | SPLAY_RIGHT(tmp, field) = (head)->sph_root; \ 86 | (head)->sph_root = tmp; \ 87 | } while (0) 88 | 89 | #define SPLAY_ROTATE_LEFT(head, tmp, field) do { \ 90 | SPLAY_RIGHT((head)->sph_root, field) = SPLAY_LEFT(tmp, field); \ 91 | SPLAY_LEFT(tmp, field) = (head)->sph_root; \ 92 | (head)->sph_root = tmp; \ 93 | } while (0) 94 | 95 | #define SPLAY_LINKLEFT(head, tmp, field) do { \ 96 | SPLAY_LEFT(tmp, field) = (head)->sph_root; \ 97 | tmp = (head)->sph_root; \ 98 | (head)->sph_root = SPLAY_LEFT((head)->sph_root, field); \ 99 | } while (0) 100 | 101 | #define SPLAY_LINKRIGHT(head, tmp, field) do { \ 102 | SPLAY_RIGHT(tmp, field) = (head)->sph_root; \ 103 | tmp = (head)->sph_root; \ 104 | (head)->sph_root = SPLAY_RIGHT((head)->sph_root, field); \ 105 | } while (0) 106 | 107 | #define SPLAY_ASSEMBLE(head, node, left, right, field) do { \ 108 | SPLAY_RIGHT(left, field) = SPLAY_LEFT((head)->sph_root, field); \ 109 | SPLAY_LEFT(right, field) = SPLAY_RIGHT((head)->sph_root, field);\ 110 | SPLAY_LEFT((head)->sph_root, field) = SPLAY_RIGHT(node, field); \ 111 | SPLAY_RIGHT((head)->sph_root, field) = SPLAY_LEFT(node, field); \ 112 | } while (0) 113 | 114 | /* Generates prototypes and inline functions */ 115 | 116 | #define SPLAY_PROTOTYPE(name, type, field, cmp) \ 117 | void name##_SPLAY(struct name *, struct type *); \ 118 | void name##_SPLAY_MINMAX(struct name *, int); \ 119 | struct type *name##_SPLAY_INSERT(struct name *, struct type *); \ 120 | struct type *name##_SPLAY_REMOVE(struct name *, struct type *); \ 121 | \ 122 | /* Finds the node with the same key as elm */ \ 123 | static __unused __inline struct type * \ 124 | name##_SPLAY_FIND(struct name *head, struct type *elm) \ 125 | { \ 126 | if (SPLAY_EMPTY(head)) \ 127 | return(NULL); \ 128 | name##_SPLAY(head, elm); \ 129 | if ((cmp)(elm, (head)->sph_root) == 0) \ 130 | return (head->sph_root); \ 131 | return (NULL); \ 132 | } \ 133 | \ 134 | static __unused __inline struct type * \ 135 | name##_SPLAY_NEXT(struct name *head, struct type *elm) \ 136 | { \ 137 | name##_SPLAY(head, elm); \ 138 | if (SPLAY_RIGHT(elm, field) != NULL) { \ 139 | elm = SPLAY_RIGHT(elm, field); \ 140 | while (SPLAY_LEFT(elm, field) != NULL) { \ 141 | elm = SPLAY_LEFT(elm, field); \ 142 | } \ 143 | } else \ 144 | elm = NULL; \ 145 | return (elm); \ 146 | } \ 147 | \ 148 | static __unused __inline struct type * \ 149 | name##_SPLAY_MIN_MAX(struct name *head, int val) \ 150 | { \ 151 | name##_SPLAY_MINMAX(head, val); \ 152 | return (SPLAY_ROOT(head)); \ 153 | } 154 | 155 | /* Main splay operation. 156 | * Moves node close to the key of elm to top 157 | */ 158 | #define SPLAY_GENERATE(name, type, field, cmp) \ 159 | struct type * \ 160 | name##_SPLAY_INSERT(struct name *head, struct type *elm) \ 161 | { \ 162 | if (SPLAY_EMPTY(head)) { \ 163 | SPLAY_LEFT(elm, field) = SPLAY_RIGHT(elm, field) = NULL; \ 164 | } else { \ 165 | int __comp; \ 166 | name##_SPLAY(head, elm); \ 167 | __comp = (cmp)(elm, (head)->sph_root); \ 168 | if(__comp < 0) { \ 169 | SPLAY_LEFT(elm, field) = SPLAY_LEFT((head)->sph_root, field);\ 170 | SPLAY_RIGHT(elm, field) = (head)->sph_root; \ 171 | SPLAY_LEFT((head)->sph_root, field) = NULL; \ 172 | } else if (__comp > 0) { \ 173 | SPLAY_RIGHT(elm, field) = SPLAY_RIGHT((head)->sph_root, field);\ 174 | SPLAY_LEFT(elm, field) = (head)->sph_root; \ 175 | SPLAY_RIGHT((head)->sph_root, field) = NULL; \ 176 | } else \ 177 | return ((head)->sph_root); \ 178 | } \ 179 | (head)->sph_root = (elm); \ 180 | return (NULL); \ 181 | } \ 182 | \ 183 | struct type * \ 184 | name##_SPLAY_REMOVE(struct name *head, struct type *elm) \ 185 | { \ 186 | struct type *__tmp; \ 187 | if (SPLAY_EMPTY(head)) \ 188 | return (NULL); \ 189 | name##_SPLAY(head, elm); \ 190 | if ((cmp)(elm, (head)->sph_root) == 0) { \ 191 | if (SPLAY_LEFT((head)->sph_root, field) == NULL) { \ 192 | (head)->sph_root = SPLAY_RIGHT((head)->sph_root, field);\ 193 | } else { \ 194 | __tmp = SPLAY_RIGHT((head)->sph_root, field); \ 195 | (head)->sph_root = SPLAY_LEFT((head)->sph_root, field);\ 196 | name##_SPLAY(head, elm); \ 197 | SPLAY_RIGHT((head)->sph_root, field) = __tmp; \ 198 | } \ 199 | return (elm); \ 200 | } \ 201 | return (NULL); \ 202 | } \ 203 | \ 204 | void \ 205 | name##_SPLAY(struct name *head, struct type *elm) \ 206 | { \ 207 | struct type __node, *__left, *__right, *__tmp; \ 208 | int __comp; \ 209 | \ 210 | SPLAY_LEFT(&__node, field) = SPLAY_RIGHT(&__node, field) = NULL;\ 211 | __left = __right = &__node; \ 212 | \ 213 | while ((__comp = (cmp)(elm, (head)->sph_root))) { \ 214 | if (__comp < 0) { \ 215 | __tmp = SPLAY_LEFT((head)->sph_root, field); \ 216 | if (__tmp == NULL) \ 217 | break; \ 218 | if ((cmp)(elm, __tmp) < 0){ \ 219 | SPLAY_ROTATE_RIGHT(head, __tmp, field); \ 220 | if (SPLAY_LEFT((head)->sph_root, field) == NULL)\ 221 | break; \ 222 | } \ 223 | SPLAY_LINKLEFT(head, __right, field); \ 224 | } else if (__comp > 0) { \ 225 | __tmp = SPLAY_RIGHT((head)->sph_root, field); \ 226 | if (__tmp == NULL) \ 227 | break; \ 228 | if ((cmp)(elm, __tmp) > 0){ \ 229 | SPLAY_ROTATE_LEFT(head, __tmp, field); \ 230 | if (SPLAY_RIGHT((head)->sph_root, field) == NULL)\ 231 | break; \ 232 | } \ 233 | SPLAY_LINKRIGHT(head, __left, field); \ 234 | } \ 235 | } \ 236 | SPLAY_ASSEMBLE(head, &__node, __left, __right, field); \ 237 | } \ 238 | \ 239 | /* Splay with either the minimum or the maximum element \ 240 | * Used to find minimum or maximum element in tree. \ 241 | */ \ 242 | void name##_SPLAY_MINMAX(struct name *head, int __comp) \ 243 | { \ 244 | struct type __node, *__left, *__right, *__tmp; \ 245 | \ 246 | SPLAY_LEFT(&__node, field) = SPLAY_RIGHT(&__node, field) = NULL;\ 247 | __left = __right = &__node; \ 248 | \ 249 | while (1) { \ 250 | if (__comp < 0) { \ 251 | __tmp = SPLAY_LEFT((head)->sph_root, field); \ 252 | if (__tmp == NULL) \ 253 | break; \ 254 | if (__comp < 0){ \ 255 | SPLAY_ROTATE_RIGHT(head, __tmp, field); \ 256 | if (SPLAY_LEFT((head)->sph_root, field) == NULL)\ 257 | break; \ 258 | } \ 259 | SPLAY_LINKLEFT(head, __right, field); \ 260 | } else if (__comp > 0) { \ 261 | __tmp = SPLAY_RIGHT((head)->sph_root, field); \ 262 | if (__tmp == NULL) \ 263 | break; \ 264 | if (__comp > 0) { \ 265 | SPLAY_ROTATE_LEFT(head, __tmp, field); \ 266 | if (SPLAY_RIGHT((head)->sph_root, field) == NULL)\ 267 | break; \ 268 | } \ 269 | SPLAY_LINKRIGHT(head, __left, field); \ 270 | } \ 271 | } \ 272 | SPLAY_ASSEMBLE(head, &__node, __left, __right, field); \ 273 | } 274 | 275 | #define SPLAY_NEGINF -1 276 | #define SPLAY_INF 1 277 | 278 | #define SPLAY_INSERT(name, x, y) name##_SPLAY_INSERT(x, y) 279 | #define SPLAY_REMOVE(name, x, y) name##_SPLAY_REMOVE(x, y) 280 | #define SPLAY_FIND(name, x, y) name##_SPLAY_FIND(x, y) 281 | #define SPLAY_NEXT(name, x, y) name##_SPLAY_NEXT(x, y) 282 | #define SPLAY_MIN(name, x) (SPLAY_EMPTY(x) ? NULL \ 283 | : name##_SPLAY_MIN_MAX(x, SPLAY_NEGINF)) 284 | #define SPLAY_MAX(name, x) (SPLAY_EMPTY(x) ? NULL \ 285 | : name##_SPLAY_MIN_MAX(x, SPLAY_INF)) 286 | 287 | #define SPLAY_FOREACH(x, name, head) \ 288 | for ((x) = SPLAY_MIN(name, head); \ 289 | (x) != NULL; \ 290 | (x) = SPLAY_NEXT(name, head, x)) 291 | 292 | /* Macros that define a red-black tree */ 293 | #define RB_HEAD(name, type) \ 294 | struct name { \ 295 | struct type *rbh_root; /* root of the tree */ \ 296 | } 297 | 298 | #define RB_INITIALIZER(root) \ 299 | { NULL } 300 | 301 | #define RB_INIT(root) do { \ 302 | (root)->rbh_root = NULL; \ 303 | } while (0) 304 | 305 | #define RB_BLACK 0 306 | #define RB_RED 1 307 | #define RB_ENTRY(type) \ 308 | struct { \ 309 | struct type *rbe_left; /* left element */ \ 310 | struct type *rbe_right; /* right element */ \ 311 | struct type *rbe_parent; /* parent element */ \ 312 | int rbe_color; /* node color */ \ 313 | } 314 | 315 | #define RB_LEFT(elm, field) (elm)->field.rbe_left 316 | #define RB_RIGHT(elm, field) (elm)->field.rbe_right 317 | #define RB_PARENT(elm, field) (elm)->field.rbe_parent 318 | #define RB_COLOR(elm, field) (elm)->field.rbe_color 319 | #define RB_ROOT(head) (head)->rbh_root 320 | #define RB_EMPTY(head) (RB_ROOT(head) == NULL) 321 | 322 | #define RB_SET(elm, parent, field) do { \ 323 | RB_PARENT(elm, field) = parent; \ 324 | RB_LEFT(elm, field) = RB_RIGHT(elm, field) = NULL; \ 325 | RB_COLOR(elm, field) = RB_RED; \ 326 | } while (0) 327 | 328 | #define RB_SET_BLACKRED(black, red, field) do { \ 329 | RB_COLOR(black, field) = RB_BLACK; \ 330 | RB_COLOR(red, field) = RB_RED; \ 331 | } while (0) 332 | 333 | #ifndef RB_AUGMENT 334 | #define RB_AUGMENT(x) do {} while (0) 335 | #endif 336 | 337 | #define RB_ROTATE_LEFT(head, elm, tmp, field) do { \ 338 | (tmp) = RB_RIGHT(elm, field); \ 339 | if ((RB_RIGHT(elm, field) = RB_LEFT(tmp, field))) { \ 340 | RB_PARENT(RB_LEFT(tmp, field), field) = (elm); \ 341 | } \ 342 | RB_AUGMENT(elm); \ 343 | if ((RB_PARENT(tmp, field) = RB_PARENT(elm, field))) { \ 344 | if ((elm) == RB_LEFT(RB_PARENT(elm, field), field)) \ 345 | RB_LEFT(RB_PARENT(elm, field), field) = (tmp); \ 346 | else \ 347 | RB_RIGHT(RB_PARENT(elm, field), field) = (tmp); \ 348 | } else \ 349 | (head)->rbh_root = (tmp); \ 350 | RB_LEFT(tmp, field) = (elm); \ 351 | RB_PARENT(elm, field) = (tmp); \ 352 | RB_AUGMENT(tmp); \ 353 | if ((RB_PARENT(tmp, field))) \ 354 | RB_AUGMENT(RB_PARENT(tmp, field)); \ 355 | } while (0) 356 | 357 | #define RB_ROTATE_RIGHT(head, elm, tmp, field) do { \ 358 | (tmp) = RB_LEFT(elm, field); \ 359 | if ((RB_LEFT(elm, field) = RB_RIGHT(tmp, field))) { \ 360 | RB_PARENT(RB_RIGHT(tmp, field), field) = (elm); \ 361 | } \ 362 | RB_AUGMENT(elm); \ 363 | if ((RB_PARENT(tmp, field) = RB_PARENT(elm, field))) { \ 364 | if ((elm) == RB_LEFT(RB_PARENT(elm, field), field)) \ 365 | RB_LEFT(RB_PARENT(elm, field), field) = (tmp); \ 366 | else \ 367 | RB_RIGHT(RB_PARENT(elm, field), field) = (tmp); \ 368 | } else \ 369 | (head)->rbh_root = (tmp); \ 370 | RB_RIGHT(tmp, field) = (elm); \ 371 | RB_PARENT(elm, field) = (tmp); \ 372 | RB_AUGMENT(tmp); \ 373 | if ((RB_PARENT(tmp, field))) \ 374 | RB_AUGMENT(RB_PARENT(tmp, field)); \ 375 | } while (0) 376 | 377 | /* Generates prototypes and inline functions */ 378 | #define RB_PROTOTYPE(name, type, field, cmp) \ 379 | RB_PROTOTYPE_INTERNAL(name, type, field, cmp,) 380 | #define RB_PROTOTYPE_STATIC(name, type, field, cmp) \ 381 | RB_PROTOTYPE_INTERNAL(name, type, field, cmp, __attribute__((__unused__)) static) 382 | #define RB_PROTOTYPE_INTERNAL(name, type, field, cmp, attr) \ 383 | attr void name##_RB_INSERT_COLOR(struct name *, struct type *); \ 384 | attr void name##_RB_REMOVE_COLOR(struct name *, struct type *, struct type *);\ 385 | attr struct type *name##_RB_REMOVE(struct name *, struct type *); \ 386 | attr struct type *name##_RB_INSERT(struct name *, struct type *); \ 387 | attr struct type *name##_RB_FIND(struct name *, struct type *); \ 388 | attr struct type *name##_RB_NFIND(struct name *, struct type *); \ 389 | attr struct type *name##_RB_NEXT(struct type *); \ 390 | attr struct type *name##_RB_PREV(struct type *); \ 391 | attr struct type *name##_RB_MINMAX(struct name *, int); \ 392 | \ 393 | 394 | /* Main rb operation. 395 | * Moves node close to the key of elm to top 396 | */ 397 | #define RB_GENERATE(name, type, field, cmp) \ 398 | RB_GENERATE_INTERNAL(name, type, field, cmp,) 399 | #define RB_GENERATE_STATIC(name, type, field, cmp) \ 400 | RB_GENERATE_INTERNAL(name, type, field, cmp, __attribute__((__unused__)) static) 401 | #define RB_GENERATE_INTERNAL(name, type, field, cmp, attr) \ 402 | attr void \ 403 | name##_RB_INSERT_COLOR(struct name *head, struct type *elm) \ 404 | { \ 405 | struct type *parent, *gparent, *tmp; \ 406 | while ((parent = RB_PARENT(elm, field)) && \ 407 | RB_COLOR(parent, field) == RB_RED) { \ 408 | gparent = RB_PARENT(parent, field); \ 409 | if (parent == RB_LEFT(gparent, field)) { \ 410 | tmp = RB_RIGHT(gparent, field); \ 411 | if (tmp && RB_COLOR(tmp, field) == RB_RED) { \ 412 | RB_COLOR(tmp, field) = RB_BLACK; \ 413 | RB_SET_BLACKRED(parent, gparent, field);\ 414 | elm = gparent; \ 415 | continue; \ 416 | } \ 417 | if (RB_RIGHT(parent, field) == elm) { \ 418 | RB_ROTATE_LEFT(head, parent, tmp, field);\ 419 | tmp = parent; \ 420 | parent = elm; \ 421 | elm = tmp; \ 422 | } \ 423 | RB_SET_BLACKRED(parent, gparent, field); \ 424 | RB_ROTATE_RIGHT(head, gparent, tmp, field); \ 425 | } else { \ 426 | tmp = RB_LEFT(gparent, field); \ 427 | if (tmp && RB_COLOR(tmp, field) == RB_RED) { \ 428 | RB_COLOR(tmp, field) = RB_BLACK; \ 429 | RB_SET_BLACKRED(parent, gparent, field);\ 430 | elm = gparent; \ 431 | continue; \ 432 | } \ 433 | if (RB_LEFT(parent, field) == elm) { \ 434 | RB_ROTATE_RIGHT(head, parent, tmp, field);\ 435 | tmp = parent; \ 436 | parent = elm; \ 437 | elm = tmp; \ 438 | } \ 439 | RB_SET_BLACKRED(parent, gparent, field); \ 440 | RB_ROTATE_LEFT(head, gparent, tmp, field); \ 441 | } \ 442 | } \ 443 | RB_COLOR(head->rbh_root, field) = RB_BLACK; \ 444 | } \ 445 | \ 446 | attr void \ 447 | name##_RB_REMOVE_COLOR(struct name *head, struct type *parent, struct type *elm) \ 448 | { \ 449 | struct type *tmp; \ 450 | while ((elm == NULL || RB_COLOR(elm, field) == RB_BLACK) && \ 451 | elm != RB_ROOT(head)) { \ 452 | if (RB_LEFT(parent, field) == elm) { \ 453 | tmp = RB_RIGHT(parent, field); \ 454 | if (RB_COLOR(tmp, field) == RB_RED) { \ 455 | RB_SET_BLACKRED(tmp, parent, field); \ 456 | RB_ROTATE_LEFT(head, parent, tmp, field);\ 457 | tmp = RB_RIGHT(parent, field); \ 458 | } \ 459 | if ((RB_LEFT(tmp, field) == NULL || \ 460 | RB_COLOR(RB_LEFT(tmp, field), field) == RB_BLACK) &&\ 461 | (RB_RIGHT(tmp, field) == NULL || \ 462 | RB_COLOR(RB_RIGHT(tmp, field), field) == RB_BLACK)) {\ 463 | RB_COLOR(tmp, field) = RB_RED; \ 464 | elm = parent; \ 465 | parent = RB_PARENT(elm, field); \ 466 | } else { \ 467 | if (RB_RIGHT(tmp, field) == NULL || \ 468 | RB_COLOR(RB_RIGHT(tmp, field), field) == RB_BLACK) {\ 469 | struct type *oleft; \ 470 | if ((oleft = RB_LEFT(tmp, field)))\ 471 | RB_COLOR(oleft, field) = RB_BLACK;\ 472 | RB_COLOR(tmp, field) = RB_RED; \ 473 | RB_ROTATE_RIGHT(head, tmp, oleft, field);\ 474 | tmp = RB_RIGHT(parent, field); \ 475 | } \ 476 | RB_COLOR(tmp, field) = RB_COLOR(parent, field);\ 477 | RB_COLOR(parent, field) = RB_BLACK; \ 478 | if (RB_RIGHT(tmp, field)) \ 479 | RB_COLOR(RB_RIGHT(tmp, field), field) = RB_BLACK;\ 480 | RB_ROTATE_LEFT(head, parent, tmp, field);\ 481 | elm = RB_ROOT(head); \ 482 | break; \ 483 | } \ 484 | } else { \ 485 | tmp = RB_LEFT(parent, field); \ 486 | if (RB_COLOR(tmp, field) == RB_RED) { \ 487 | RB_SET_BLACKRED(tmp, parent, field); \ 488 | RB_ROTATE_RIGHT(head, parent, tmp, field);\ 489 | tmp = RB_LEFT(parent, field); \ 490 | } \ 491 | if ((RB_LEFT(tmp, field) == NULL || \ 492 | RB_COLOR(RB_LEFT(tmp, field), field) == RB_BLACK) &&\ 493 | (RB_RIGHT(tmp, field) == NULL || \ 494 | RB_COLOR(RB_RIGHT(tmp, field), field) == RB_BLACK)) {\ 495 | RB_COLOR(tmp, field) = RB_RED; \ 496 | elm = parent; \ 497 | parent = RB_PARENT(elm, field); \ 498 | } else { \ 499 | if (RB_LEFT(tmp, field) == NULL || \ 500 | RB_COLOR(RB_LEFT(tmp, field), field) == RB_BLACK) {\ 501 | struct type *oright; \ 502 | if ((oright = RB_RIGHT(tmp, field)))\ 503 | RB_COLOR(oright, field) = RB_BLACK;\ 504 | RB_COLOR(tmp, field) = RB_RED; \ 505 | RB_ROTATE_LEFT(head, tmp, oright, field);\ 506 | tmp = RB_LEFT(parent, field); \ 507 | } \ 508 | RB_COLOR(tmp, field) = RB_COLOR(parent, field);\ 509 | RB_COLOR(parent, field) = RB_BLACK; \ 510 | if (RB_LEFT(tmp, field)) \ 511 | RB_COLOR(RB_LEFT(tmp, field), field) = RB_BLACK;\ 512 | RB_ROTATE_RIGHT(head, parent, tmp, field);\ 513 | elm = RB_ROOT(head); \ 514 | break; \ 515 | } \ 516 | } \ 517 | } \ 518 | if (elm) \ 519 | RB_COLOR(elm, field) = RB_BLACK; \ 520 | } \ 521 | \ 522 | attr struct type * \ 523 | name##_RB_REMOVE(struct name *head, struct type *elm) \ 524 | { \ 525 | struct type *child, *parent, *old = elm; \ 526 | int color; \ 527 | if (RB_LEFT(elm, field) == NULL) \ 528 | child = RB_RIGHT(elm, field); \ 529 | else if (RB_RIGHT(elm, field) == NULL) \ 530 | child = RB_LEFT(elm, field); \ 531 | else { \ 532 | struct type *left; \ 533 | elm = RB_RIGHT(elm, field); \ 534 | while ((left = RB_LEFT(elm, field))) \ 535 | elm = left; \ 536 | child = RB_RIGHT(elm, field); \ 537 | parent = RB_PARENT(elm, field); \ 538 | color = RB_COLOR(elm, field); \ 539 | if (child) \ 540 | RB_PARENT(child, field) = parent; \ 541 | if (parent) { \ 542 | if (RB_LEFT(parent, field) == elm) \ 543 | RB_LEFT(parent, field) = child; \ 544 | else \ 545 | RB_RIGHT(parent, field) = child; \ 546 | RB_AUGMENT(parent); \ 547 | } else \ 548 | RB_ROOT(head) = child; \ 549 | if (RB_PARENT(elm, field) == old) \ 550 | parent = elm; \ 551 | (elm)->field = (old)->field; \ 552 | if (RB_PARENT(old, field)) { \ 553 | if (RB_LEFT(RB_PARENT(old, field), field) == old)\ 554 | RB_LEFT(RB_PARENT(old, field), field) = elm;\ 555 | else \ 556 | RB_RIGHT(RB_PARENT(old, field), field) = elm;\ 557 | RB_AUGMENT(RB_PARENT(old, field)); \ 558 | } else \ 559 | RB_ROOT(head) = elm; \ 560 | RB_PARENT(RB_LEFT(old, field), field) = elm; \ 561 | if (RB_RIGHT(old, field)) \ 562 | RB_PARENT(RB_RIGHT(old, field), field) = elm; \ 563 | if (parent) { \ 564 | left = parent; \ 565 | do { \ 566 | RB_AUGMENT(left); \ 567 | } while ((left = RB_PARENT(left, field))); \ 568 | } \ 569 | goto color; \ 570 | } \ 571 | parent = RB_PARENT(elm, field); \ 572 | color = RB_COLOR(elm, field); \ 573 | if (child) \ 574 | RB_PARENT(child, field) = parent; \ 575 | if (parent) { \ 576 | if (RB_LEFT(parent, field) == elm) \ 577 | RB_LEFT(parent, field) = child; \ 578 | else \ 579 | RB_RIGHT(parent, field) = child; \ 580 | RB_AUGMENT(parent); \ 581 | } else \ 582 | RB_ROOT(head) = child; \ 583 | color: \ 584 | if (color == RB_BLACK) \ 585 | name##_RB_REMOVE_COLOR(head, parent, child); \ 586 | return (old); \ 587 | } \ 588 | \ 589 | /* Inserts a node into the RB tree */ \ 590 | attr struct type * \ 591 | name##_RB_INSERT(struct name *head, struct type *elm) \ 592 | { \ 593 | struct type *tmp; \ 594 | struct type *parent = NULL; \ 595 | int comp = 0; \ 596 | tmp = RB_ROOT(head); \ 597 | while (tmp) { \ 598 | parent = tmp; \ 599 | comp = (cmp)(elm, parent); \ 600 | if (comp < 0) \ 601 | tmp = RB_LEFT(tmp, field); \ 602 | else if (comp > 0) \ 603 | tmp = RB_RIGHT(tmp, field); \ 604 | else \ 605 | return (tmp); \ 606 | } \ 607 | RB_SET(elm, parent, field); \ 608 | if (parent != NULL) { \ 609 | if (comp < 0) \ 610 | RB_LEFT(parent, field) = elm; \ 611 | else \ 612 | RB_RIGHT(parent, field) = elm; \ 613 | RB_AUGMENT(parent); \ 614 | } else \ 615 | RB_ROOT(head) = elm; \ 616 | name##_RB_INSERT_COLOR(head, elm); \ 617 | return (NULL); \ 618 | } \ 619 | \ 620 | /* Finds the node with the same key as elm */ \ 621 | attr struct type * \ 622 | name##_RB_FIND(struct name *head, struct type *elm) \ 623 | { \ 624 | struct type *tmp = RB_ROOT(head); \ 625 | int comp; \ 626 | while (tmp) { \ 627 | comp = cmp(elm, tmp); \ 628 | if (comp < 0) \ 629 | tmp = RB_LEFT(tmp, field); \ 630 | else if (comp > 0) \ 631 | tmp = RB_RIGHT(tmp, field); \ 632 | else \ 633 | return (tmp); \ 634 | } \ 635 | return (NULL); \ 636 | } \ 637 | \ 638 | /* Finds the first node greater than or equal to the search key */ \ 639 | attr struct type * \ 640 | name##_RB_NFIND(struct name *head, struct type *elm) \ 641 | { \ 642 | struct type *tmp = RB_ROOT(head); \ 643 | struct type *res = NULL; \ 644 | int comp; \ 645 | while (tmp) { \ 646 | comp = cmp(elm, tmp); \ 647 | if (comp < 0) { \ 648 | res = tmp; \ 649 | tmp = RB_LEFT(tmp, field); \ 650 | } \ 651 | else if (comp > 0) \ 652 | tmp = RB_RIGHT(tmp, field); \ 653 | else \ 654 | return (tmp); \ 655 | } \ 656 | return (res); \ 657 | } \ 658 | \ 659 | /* ARGSUSED */ \ 660 | attr struct type * \ 661 | name##_RB_NEXT(struct type *elm) \ 662 | { \ 663 | if (RB_RIGHT(elm, field)) { \ 664 | elm = RB_RIGHT(elm, field); \ 665 | while (RB_LEFT(elm, field)) \ 666 | elm = RB_LEFT(elm, field); \ 667 | } else { \ 668 | if (RB_PARENT(elm, field) && \ 669 | (elm == RB_LEFT(RB_PARENT(elm, field), field))) \ 670 | elm = RB_PARENT(elm, field); \ 671 | else { \ 672 | while (RB_PARENT(elm, field) && \ 673 | (elm == RB_RIGHT(RB_PARENT(elm, field), field)))\ 674 | elm = RB_PARENT(elm, field); \ 675 | elm = RB_PARENT(elm, field); \ 676 | } \ 677 | } \ 678 | return (elm); \ 679 | } \ 680 | \ 681 | /* ARGSUSED */ \ 682 | attr struct type * \ 683 | name##_RB_PREV(struct type *elm) \ 684 | { \ 685 | if (RB_LEFT(elm, field)) { \ 686 | elm = RB_LEFT(elm, field); \ 687 | while (RB_RIGHT(elm, field)) \ 688 | elm = RB_RIGHT(elm, field); \ 689 | } else { \ 690 | if (RB_PARENT(elm, field) && \ 691 | (elm == RB_RIGHT(RB_PARENT(elm, field), field))) \ 692 | elm = RB_PARENT(elm, field); \ 693 | else { \ 694 | while (RB_PARENT(elm, field) && \ 695 | (elm == RB_LEFT(RB_PARENT(elm, field), field)))\ 696 | elm = RB_PARENT(elm, field); \ 697 | elm = RB_PARENT(elm, field); \ 698 | } \ 699 | } \ 700 | return (elm); \ 701 | } \ 702 | \ 703 | attr struct type * \ 704 | name##_RB_MINMAX(struct name *head, int val) \ 705 | { \ 706 | struct type *tmp = RB_ROOT(head); \ 707 | struct type *parent = NULL; \ 708 | while (tmp) { \ 709 | parent = tmp; \ 710 | if (val < 0) \ 711 | tmp = RB_LEFT(tmp, field); \ 712 | else \ 713 | tmp = RB_RIGHT(tmp, field); \ 714 | } \ 715 | return (parent); \ 716 | } 717 | 718 | #define RB_NEGINF -1 719 | #define RB_INF 1 720 | 721 | #define RB_INSERT(name, x, y) name##_RB_INSERT(x, y) 722 | #define RB_REMOVE(name, x, y) name##_RB_REMOVE(x, y) 723 | #define RB_FIND(name, x, y) name##_RB_FIND(x, y) 724 | #define RB_NFIND(name, x, y) name##_RB_NFIND(x, y) 725 | #define RB_NEXT(name, x, y) name##_RB_NEXT(y) 726 | #define RB_PREV(name, x, y) name##_RB_PREV(y) 727 | #define RB_MIN(name, x) name##_RB_MINMAX(x, RB_NEGINF) 728 | #define RB_MAX(name, x) name##_RB_MINMAX(x, RB_INF) 729 | 730 | #define RB_FOREACH(x, name, head) \ 731 | for ((x) = RB_MIN(name, head); \ 732 | (x) != NULL; \ 733 | (x) = name##_RB_NEXT(x)) 734 | 735 | #define RB_FOREACH_SAFE(x, name, head, y) \ 736 | for ((x) = RB_MIN(name, head); \ 737 | ((x) != NULL) && ((y) = name##_RB_NEXT(x), 1); \ 738 | (x) = (y)) 739 | 740 | #define RB_FOREACH_REVERSE(x, name, head) \ 741 | for ((x) = RB_MAX(name, head); \ 742 | (x) != NULL; \ 743 | (x) = name##_RB_PREV(x)) 744 | 745 | #define RB_FOREACH_REVERSE_SAFE(x, name, head, y) \ 746 | for ((x) = RB_MAX(name, head); \ 747 | ((x) != NULL) && ((y) = name##_RB_PREV(x), 1); \ 748 | (x) = (y)) 749 | 750 | 751 | /* 752 | * Copyright (c) 2016 David Gwynne 753 | * 754 | * Permission to use, copy, modify, and distribute this software for any 755 | * purpose with or without fee is hereby granted, provided that the above 756 | * copyright notice and this permission notice appear in all copies. 757 | * 758 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 759 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 760 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 761 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 762 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 763 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 764 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 765 | */ 766 | 767 | struct rb_type { 768 | int (*t_compare)(const void *, const void *); 769 | void (*t_augment)(void *); 770 | unsigned int t_offset; /* offset of rb_entry in type */ 771 | }; 772 | 773 | struct rb_tree { 774 | struct rb_entry *rbt_root; 775 | }; 776 | 777 | struct rb_entry { 778 | struct rb_entry *rbt_parent; 779 | struct rb_entry *rbt_left; 780 | struct rb_entry *rbt_right; 781 | unsigned int rbt_color; 782 | }; 783 | 784 | #define RBT_HEAD(_name, _type) \ 785 | struct _name { \ 786 | struct rb_tree rbh_root; \ 787 | } 788 | 789 | #define RBT_ENTRY(_type) struct rb_entry 790 | 791 | static inline void 792 | _rb_init(struct rb_tree *rbt) 793 | { 794 | rbt->rbt_root = NULL; 795 | } 796 | 797 | static inline int 798 | _rb_empty(struct rb_tree *rbt) 799 | { 800 | return (rbt->rbt_root == NULL); 801 | } 802 | 803 | void *_rb_insert(const struct rb_type *, struct rb_tree *, void *); 804 | void *_rb_remove(const struct rb_type *, struct rb_tree *, void *); 805 | void *_rb_find(const struct rb_type *, struct rb_tree *, const void *); 806 | void *_rb_nfind(const struct rb_type *, struct rb_tree *, const void *); 807 | void *_rb_root(const struct rb_type *, struct rb_tree *); 808 | void *_rb_min(const struct rb_type *, struct rb_tree *); 809 | void *_rb_max(const struct rb_type *, struct rb_tree *); 810 | void *_rb_next(const struct rb_type *, void *); 811 | void *_rb_prev(const struct rb_type *, void *); 812 | void *_rb_left(const struct rb_type *, void *); 813 | void *_rb_right(const struct rb_type *, void *); 814 | void *_rb_parent(const struct rb_type *, void *); 815 | void _rb_set_left(const struct rb_type *, void *, void *); 816 | void _rb_set_right(const struct rb_type *, void *, void *); 817 | void _rb_set_parent(const struct rb_type *, void *, void *); 818 | void _rb_poison(const struct rb_type *, void *, unsigned long); 819 | int _rb_check(const struct rb_type *, void *, unsigned long); 820 | 821 | #define RBT_INITIALIZER(_head) { { NULL } } 822 | 823 | #define RBT_PROTOTYPE(_name, _type, _field, _cmp) \ 824 | extern const struct rb_type *const _name##_RBT_TYPE; \ 825 | \ 826 | __unused static inline void \ 827 | _name##_RBT_INIT(struct _name *head) \ 828 | { \ 829 | _rb_init(&head->rbh_root); \ 830 | } \ 831 | \ 832 | __unused static inline struct _type * \ 833 | _name##_RBT_INSERT(struct _name *head, struct _type *elm) \ 834 | { \ 835 | return _rb_insert(_name##_RBT_TYPE, &head->rbh_root, elm); \ 836 | } \ 837 | \ 838 | __unused static inline struct _type * \ 839 | _name##_RBT_REMOVE(struct _name *head, struct _type *elm) \ 840 | { \ 841 | return _rb_remove(_name##_RBT_TYPE, &head->rbh_root, elm); \ 842 | } \ 843 | \ 844 | __unused static inline struct _type * \ 845 | _name##_RBT_FIND(struct _name *head, const struct _type *key) \ 846 | { \ 847 | return _rb_find(_name##_RBT_TYPE, &head->rbh_root, key); \ 848 | } \ 849 | \ 850 | __unused static inline struct _type * \ 851 | _name##_RBT_NFIND(struct _name *head, const struct _type *key) \ 852 | { \ 853 | return _rb_nfind(_name##_RBT_TYPE, &head->rbh_root, key); \ 854 | } \ 855 | \ 856 | __unused static inline struct _type * \ 857 | _name##_RBT_ROOT(struct _name *head) \ 858 | { \ 859 | return _rb_root(_name##_RBT_TYPE, &head->rbh_root); \ 860 | } \ 861 | \ 862 | __unused static inline int \ 863 | _name##_RBT_EMPTY(struct _name *head) \ 864 | { \ 865 | return _rb_empty(&head->rbh_root); \ 866 | } \ 867 | \ 868 | __unused static inline struct _type * \ 869 | _name##_RBT_MIN(struct _name *head) \ 870 | { \ 871 | return _rb_min(_name##_RBT_TYPE, &head->rbh_root); \ 872 | } \ 873 | \ 874 | __unused static inline struct _type * \ 875 | _name##_RBT_MAX(struct _name *head) \ 876 | { \ 877 | return _rb_max(_name##_RBT_TYPE, &head->rbh_root); \ 878 | } \ 879 | \ 880 | __unused static inline struct _type * \ 881 | _name##_RBT_NEXT(struct _type *elm) \ 882 | { \ 883 | return _rb_next(_name##_RBT_TYPE, elm); \ 884 | } \ 885 | \ 886 | __unused static inline struct _type * \ 887 | _name##_RBT_PREV(struct _type *elm) \ 888 | { \ 889 | return _rb_prev(_name##_RBT_TYPE, elm); \ 890 | } \ 891 | \ 892 | __unused static inline struct _type * \ 893 | _name##_RBT_LEFT(struct _type *elm) \ 894 | { \ 895 | return _rb_left(_name##_RBT_TYPE, elm); \ 896 | } \ 897 | \ 898 | __unused static inline struct _type * \ 899 | _name##_RBT_RIGHT(struct _type *elm) \ 900 | { \ 901 | return _rb_right(_name##_RBT_TYPE, elm); \ 902 | } \ 903 | \ 904 | __unused static inline struct _type * \ 905 | _name##_RBT_PARENT(struct _type *elm) \ 906 | { \ 907 | return _rb_parent(_name##_RBT_TYPE, elm); \ 908 | } \ 909 | \ 910 | __unused static inline void \ 911 | _name##_RBT_SET_LEFT(struct _type *elm, struct _type *left) \ 912 | { \ 913 | _rb_set_left(_name##_RBT_TYPE, elm, left); \ 914 | } \ 915 | \ 916 | __unused static inline void \ 917 | _name##_RBT_SET_RIGHT(struct _type *elm, struct _type *right) \ 918 | { \ 919 | _rb_set_right(_name##_RBT_TYPE, elm, right); \ 920 | } \ 921 | \ 922 | __unused static inline void \ 923 | _name##_RBT_SET_PARENT(struct _type *elm, struct _type *parent) \ 924 | { \ 925 | _rb_set_parent(_name##_RBT_TYPE, elm, parent); \ 926 | } \ 927 | \ 928 | __unused static inline void \ 929 | _name##_RBT_POISON(struct _type *elm, unsigned long poison) \ 930 | { \ 931 | _rb_poison(_name##_RBT_TYPE, elm, poison); \ 932 | } \ 933 | \ 934 | __unused static inline int \ 935 | _name##_RBT_CHECK(struct _type *elm, unsigned long poison) \ 936 | { \ 937 | return _rb_check(_name##_RBT_TYPE, elm, poison); \ 938 | } 939 | 940 | #define RBT_GENERATE_INTERNAL(_name, _type, _field, _cmp, _aug) \ 941 | static int \ 942 | _name##_RBT_COMPARE(const void *lptr, const void *rptr) \ 943 | { \ 944 | const struct _type *l = lptr, *r = rptr; \ 945 | return _cmp(l, r); \ 946 | } \ 947 | static const struct rb_type _name##_RBT_INFO = { \ 948 | _name##_RBT_COMPARE, \ 949 | _aug, \ 950 | offsetof(struct _type, _field), \ 951 | }; \ 952 | const struct rb_type *const _name##_RBT_TYPE = &_name##_RBT_INFO 953 | 954 | #define RBT_GENERATE_AUGMENT(_name, _type, _field, _cmp, _aug) \ 955 | static void \ 956 | _name##_RBT_AUGMENT(void *ptr) \ 957 | { \ 958 | struct _type *p = ptr; \ 959 | return _aug(p); \ 960 | } \ 961 | RBT_GENERATE_INTERNAL(_name, _type, _field, _cmp, _name##_RBT_AUGMENT) 962 | 963 | #define RBT_GENERATE(_name, _type, _field, _cmp) \ 964 | RBT_GENERATE_INTERNAL(_name, _type, _field, _cmp, NULL) 965 | 966 | #define RBT_INIT(_name, _head) _name##_RBT_INIT(_head) 967 | #define RBT_INSERT(_name, _head, _elm) _name##_RBT_INSERT(_head, _elm) 968 | #define RBT_REMOVE(_name, _head, _elm) _name##_RBT_REMOVE(_head, _elm) 969 | #define RBT_FIND(_name, _head, _key) _name##_RBT_FIND(_head, _key) 970 | #define RBT_NFIND(_name, _head, _key) _name##_RBT_NFIND(_head, _key) 971 | #define RBT_ROOT(_name, _head) _name##_RBT_ROOT(_head) 972 | #define RBT_EMPTY(_name, _head) _name##_RBT_EMPTY(_head) 973 | #define RBT_MIN(_name, _head) _name##_RBT_MIN(_head) 974 | #define RBT_MAX(_name, _head) _name##_RBT_MAX(_head) 975 | #define RBT_NEXT(_name, _elm) _name##_RBT_NEXT(_elm) 976 | #define RBT_PREV(_name, _elm) _name##_RBT_PREV(_elm) 977 | #define RBT_LEFT(_name, _elm) _name##_RBT_LEFT(_elm) 978 | #define RBT_RIGHT(_name, _elm) _name##_RBT_RIGHT(_elm) 979 | #define RBT_PARENT(_name, _elm) _name##_RBT_PARENT(_elm) 980 | #define RBT_SET_LEFT(_name, _elm, _l) _name##_RBT_SET_LEFT(_elm, _l) 981 | #define RBT_SET_RIGHT(_name, _elm, _r) _name##_RBT_SET_RIGHT(_elm, _r) 982 | #define RBT_SET_PARENT(_name, _elm, _p) _name##_RBT_SET_PARENT(_elm, _p) 983 | #define RBT_POISON(_name, _elm, _p) _name##_RBT_POISON(_elm, _p) 984 | #define RBT_CHECK(_name, _elm, _p) _name##_RBT_CHECK(_elm, _p) 985 | 986 | #define RBT_FOREACH(_e, _name, _head) \ 987 | for ((_e) = RBT_MIN(_name, (_head)); \ 988 | (_e) != NULL; \ 989 | (_e) = RBT_NEXT(_name, (_e))) 990 | 991 | #define RBT_FOREACH_SAFE(_e, _name, _head, _n) \ 992 | for ((_e) = RBT_MIN(_name, (_head)); \ 993 | (_e) != NULL && ((_n) = RBT_NEXT(_name, (_e)), 1); \ 994 | (_e) = (_n)) 995 | 996 | #define RBT_FOREACH_REVERSE(_e, _name, _head) \ 997 | for ((_e) = RBT_MAX(_name, (_head)); \ 998 | (_e) != NULL; \ 999 | (_e) = RBT_PREV(_name, (_e))) 1000 | 1001 | #define RBT_FOREACH_REVERSE_SAFE(_e, _name, _head, _n) \ 1002 | for ((_e) = RBT_MAX(_name, (_head)); \ 1003 | (_e) != NULL && ((_n) = RBT_PREV(_name, (_e)), 1); \ 1004 | (_e) = (_n)) 1005 | 1006 | #endif /* _SYS_TREE_H_ */ 1007 | -------------------------------------------------------------------------------- /include/sys/types.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Public domain 3 | * sys/types.h compatibility shim 4 | */ 5 | 6 | #include_next 7 | 8 | #ifndef LIBCOMPAT_SYS_TYPES_H 9 | #define LIBCOMPAT_SYS_TYPES_H 10 | 11 | #include 12 | 13 | #ifdef __MINGW32__ 14 | #include <_bsd_types.h> 15 | #endif 16 | 17 | #if !defined(HAVE_ATTRIBUTE__DEAD) && !defined(__dead) 18 | #define __dead __attribute__((__noreturn__)) 19 | #endif 20 | 21 | #if !defined(HAVE_ATTRIBUTE__BOUNDED__) && !defined(__bounded__) 22 | # define __bounded__(x, y, z) 23 | #endif 24 | 25 | #endif 26 | -------------------------------------------------------------------------------- /main.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2019-2021 Job Snijders 3 | * Copyright (c) 2007-2019 Alexandre Snarskii 4 | * All rights reserved. 5 | * 6 | * Redistribution and use in source and binary forms, with or without 7 | * modification, are permitted provided that the following conditions 8 | * are met: 9 | * 1. Redistributions of source code must retain the above copyright 10 | * notice, this list of conditions and the following disclaimer. 11 | * 2. Redistributions in binary form must reproduce the above copyright 12 | * notice, this list of conditions and the following disclaimer in the 13 | * documentation and/or other materials provided with the distribution. 14 | * 15 | * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 16 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 17 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 18 | * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 19 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 20 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 21 | * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 22 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 23 | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 24 | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 25 | * SUCH DAMAGE. 26 | */ 27 | 28 | #if HAVE_CONFIG_H 29 | #include 30 | #endif 31 | 32 | #include 33 | #include 34 | 35 | #include 36 | #include 37 | #include 38 | #include 39 | #include 40 | #include 41 | 42 | #include "extern.h" 43 | #include "sx_report.h" 44 | 45 | extern int debug_expander; 46 | extern int debug_aggregation; 47 | extern int pipelining; 48 | extern int expand_special_asn; 49 | 50 | static int 51 | usage(int ecode) 52 | { 53 | printf("\nUsage: bgpq4 [-h host[:port]] [-S sources] [-E|G|H " 54 | "|f |t] [-46ABbdJjKNnpwXz] [-R len] ... " 55 | "[EXCEPT ...]\n"); 56 | printf("\nVendor targets:\n"); 57 | printf(" no option : Cisco IOS Classic (default)\n"); 58 | printf(" -X : Cisco IOS XR\n"); 59 | printf(" -U : Huawei\n"); 60 | printf(" -u : Huawei XPL\n"); 61 | printf(" -j : JSON\n"); 62 | printf(" -J : Juniper Junos\n"); 63 | printf(" -K : MikroTik RouterOSv6\n"); 64 | printf(" -K7 : MikroTik RouterOSv7\n"); 65 | printf(" -b : NIC.CZ BIRD\n"); 66 | printf(" -N : Nokia SR OS (Classic CLI)\n"); 67 | printf(" -n : Nokia SR OS (MD-CLI)\n"); 68 | printf(" -n2 : Nokia SR Linux\n"); 69 | printf(" -B : OpenBSD OpenBGPD\n"); 70 | printf(" -e : Arista EOS\n"); 71 | printf(" -F fmt : User defined format (example: '-F %%n/%%l')\n"); 72 | 73 | printf("\nInput filters:\n"); 74 | printf(" -4 : generate IPv4 prefix-lists (default)\n"); 75 | printf(" -6 : generate IPv6 prefix-lists\n"); 76 | printf(" -m len : maximum prefix length (default: 32 for IPv4, " 77 | "128 for IPv6)\n"); 78 | printf(" -L depth : limit recursion depth (default: unlimited)\n"), 79 | printf(" -S sources: only use specified IRR sources, in the specified " 80 | "order (comma separated)\n"); 81 | printf(" -w : 'validate' AS numbers: accept only ones with " 82 | "registered routes\n"); 83 | 84 | printf("\nOutput modifiers:\n"); 85 | printf(" -3 : assume that your device is asn32-safe (default)\n"); 86 | printf(" -A : try to aggregate prefix-lists/route-filters\n"); 87 | printf(" -E : generate extended access-list (Cisco), " 88 | "route-filter (Juniper)\n" 89 | " [ip|ipv6]-prefix-list (Nokia) or prefix-set " 90 | "(OpenBGPD)\n"); 91 | printf(" -f number : generate input as-path access-list\n"); 92 | printf(" -G number : generate output as-path access-list\n"); 93 | printf(" -H number : generate origin as-lists (JunOS only)\n"); 94 | printf(" -M match : extra match conditions for JunOS route-filters\n"); 95 | printf(" -l name : use specified name for generated access/prefix/.." 96 | " list\n"); 97 | printf(" -p : allow special ASNs like 23456 or in the private range\n"); 98 | printf(" -R len : allow more specific routes up to specified masklen\n"); 99 | printf(" -r len : allow more specific routes from masklen specified\n"); 100 | printf(" -s : generate sequence numbers in prefix-lists (IOS only)\n"); 101 | printf(" -t : generate as-sets for OpenBGPD (OpenBGPD 6.4+), BIRD " 102 | "and JSON formats\n"); 103 | printf(" -z : generate route-filter-list (Junos only)\n"); 104 | printf(" -W len : specify max-entries on as-path/as-list line (use 0 for " 105 | "infinity)\n"); 106 | 107 | printf("\nUtility operations:\n"); 108 | printf(" -d : generate some debugging output\n"); 109 | printf(" -h host : host running IRRD software (default: rr.ntt.net)\n" 110 | " use 'host:port' to specify alternate port\n"); 111 | printf(" -T : disable pipelining (not recommended)\n"); 112 | printf(" -v : print version and exit\n"); 113 | printf("\n" PACKAGE_NAME " version: " PACKAGE_VERSION " " 114 | "(https://github.com/bgp/bgpq4)\n"); 115 | exit(ecode); 116 | } 117 | 118 | static void 119 | version(void) 120 | { 121 | printf(PACKAGE_NAME " - a versatile utility to generate BGP filters\n" 122 | "version: " PACKAGE_VERSION "\n" 123 | "website: https://github.com/bgp/bgpq4\n" 124 | "maintainer: Job Snijders \n"); 125 | exit(0); 126 | } 127 | 128 | static void 129 | exclusive(void) 130 | { 131 | fprintf(stderr,"-E, -F, -K , -f , -G , and -t are mutually" 132 | " exclusive\n"); 133 | exit(1); 134 | } 135 | 136 | static void 137 | vendor_exclusive(void) 138 | { 139 | fprintf(stderr, "-b (BIRD), -B (OpenBGPD), -F (formatted), -J (Junos)," 140 | " -j (JSON), -K[7] (Microtik ROS), -N (Nokia SR OS Classic)," 141 | " -n (Nokia SR OS MD-CLI), -U (Huawei), -u (Huawei XPL)," 142 | "-e (Arista) and -X (IOS XR) options are mutually exclusive\n"); 143 | exit(1); 144 | } 145 | 146 | static int 147 | parseasnumber(struct bgpq_expander *expander, char *asnstr) 148 | { 149 | char *eon = NULL; 150 | 151 | expander->asnumber = strtoul(asnstr, &eon, 10); 152 | if (expander->asnumber < 1 || expander->asnumber > (65535ul * 65535)) { 153 | sx_report(SX_FATAL, "Invalid AS number: %s\n", asnstr); 154 | exit(1); 155 | } 156 | if (eon && *eon == '.') { 157 | /* -f 3.3, for example */ 158 | uint32_t loas = strtoul(eon + 1, &eon, 10); 159 | if (expander->asnumber > 65535) { 160 | /* should prevent incorrect numbers like 65537.1 */ 161 | sx_report(SX_FATAL,"Invalid AS number: %s\n", asnstr); 162 | exit(1); 163 | } 164 | if (loas < 1 || loas > 65535) { 165 | sx_report(SX_FATAL,"Invalid AS number: %s\n", asnstr); 166 | exit(1); 167 | } 168 | if (eon && *eon) { 169 | sx_report(SX_FATAL,"Invalid symbol in AS number: " 170 | "%c (%s)\n", *eon, asnstr); 171 | exit(1); 172 | } 173 | expander->asnumber=(expander->asnumber << 16) + loas; 174 | } else if (eon && *eon) { 175 | sx_report(SX_FATAL,"Invalid symbol in AS number: %c (%s)\n", 176 | *eon, asnstr); 177 | exit(1); 178 | } 179 | return 0; 180 | } 181 | 182 | int 183 | main(int argc, char* argv[]) 184 | { 185 | int c; 186 | struct bgpq_expander expander; 187 | int af = AF_INET, selectedipv4 = 0, exceptmode = 0; 188 | int widthSet = 0, aggregate = 0, refine = 0, refineLow = 0; 189 | unsigned long maxlen = 0; 190 | 191 | #ifdef HAVE_PLEDGE 192 | if (pledge("stdio inet dns", NULL) == -1) { 193 | sx_report(SX_ERROR, "pledge() failed"); 194 | exit(1); 195 | } 196 | #endif 197 | 198 | bgpq_expander_init(&expander, af); 199 | 200 | if (getenv("IRRD_SOURCES")) 201 | expander.sources=getenv("IRRD_SOURCES"); 202 | 203 | while ((c = getopt(argc, argv, 204 | "23467a:AbBdDEeF:S:jJKf:l:L:m:M:NnpW:r:R:G:H:tTh:UuwXsvz")) != EOF) { 205 | switch (c) { 206 | case '2': 207 | if (expander.vendor != V_NOKIA_MD) { 208 | sx_report(SX_FATAL, "'2' can only be used after -n\n"); 209 | exit(1); 210 | } 211 | expander.vendor = V_NOKIA_SRL; 212 | break; 213 | case '3': 214 | /* do nothing, 32-bit ASN support is assumed */ 215 | break; 216 | case '4': 217 | /* do nothing, expander already configured for IPv4 */ 218 | if (expander.family == AF_INET6) { 219 | sx_report(SX_FATAL, "-4 and -6 are mutually " 220 | "exclusive\n"); 221 | exit(1); 222 | } 223 | selectedipv4 = 1; 224 | break; 225 | case '6': 226 | if (selectedipv4) { 227 | sx_report(SX_FATAL, "-4 and -6 are mutually " 228 | "exclusive\n"); 229 | exit(1); 230 | } 231 | af = AF_INET6; 232 | expander.family = AF_INET6; 233 | expander.tree->family = AF_INET6; 234 | break; 235 | case '7': 236 | if (expander.vendor != V_MIKROTIK6) { 237 | sx_report(SX_FATAL, "'7' can only be used after -K\n"); 238 | exit(1); 239 | } 240 | expander.vendor = V_MIKROTIK7; 241 | break; 242 | case 'a': 243 | parseasnumber(&expander, optarg); 244 | break; 245 | case 'A': 246 | if (aggregate) 247 | debug_aggregation++; 248 | aggregate = 1; 249 | break; 250 | case 'b': 251 | if (expander.vendor) 252 | vendor_exclusive(); 253 | expander.vendor = V_BIRD; 254 | break; 255 | case 'B': 256 | if (expander.vendor) 257 | vendor_exclusive(); 258 | expander.vendor = V_OPENBGPD; 259 | break; 260 | case 'd': 261 | debug_expander++; 262 | break; 263 | case 'E': 264 | if (expander.generation) 265 | exclusive(); 266 | expander.generation = T_EACL; 267 | break; 268 | case 'e': 269 | if (expander.vendor) 270 | vendor_exclusive(); 271 | expander.vendor = V_ARISTA; 272 | expander.sequence = 1; 273 | break; 274 | case 'F': 275 | if (expander.vendor) 276 | exclusive(); 277 | expander.vendor = V_FORMAT; 278 | expander.format = optarg; 279 | break; 280 | case 'f': 281 | if (expander.generation) 282 | exclusive(); 283 | expander.generation = T_ASPATH; 284 | parseasnumber(&expander, optarg); 285 | break; 286 | case 'G': 287 | if (expander.generation) 288 | exclusive(); 289 | expander.generation = T_OASPATH; 290 | parseasnumber(&expander, optarg); 291 | break; 292 | case 'H': 293 | if (expander.generation) 294 | exclusive(); 295 | expander.generation = T_ASLIST; 296 | parseasnumber(&expander, optarg); 297 | break; 298 | case 'h': 299 | { 300 | char *d = strchr(optarg, ':'); 301 | expander.server = optarg; 302 | if (d) { 303 | *d = 0; 304 | expander.port = d + 1; 305 | } 306 | } 307 | break; 308 | case 'J': 309 | if (expander.vendor) 310 | vendor_exclusive(); 311 | expander.vendor = V_JUNIPER; 312 | break; 313 | case 'j': 314 | if (expander.vendor) 315 | vendor_exclusive(); 316 | expander.vendor = V_JSON; 317 | break; 318 | case 'K': 319 | if (expander.vendor) 320 | vendor_exclusive(); 321 | expander.vendor = V_MIKROTIK6; 322 | break; 323 | case 'r': 324 | refineLow = strtoul(optarg, NULL, 10); 325 | if (!refineLow) { 326 | sx_report(SX_FATAL, "Invalid refineLow value:" 327 | " %s\n", optarg); 328 | exit(1); 329 | } 330 | break; 331 | case 'R': 332 | refine = strtoul(optarg, NULL, 10); 333 | if (!refine) { 334 | sx_report(SX_FATAL,"Invalid refine length:" 335 | " %s\n", optarg); 336 | exit(1); 337 | } 338 | break; 339 | case 'l': 340 | expander.name = optarg; 341 | break; 342 | case 'L': 343 | expander.maxdepth = strtol(optarg, NULL, 10); 344 | if (expander.maxdepth < 1) { 345 | sx_report(SX_FATAL, "Invalid maximum recursion" 346 | " (-L): %s\n", optarg); 347 | exit(1); 348 | } 349 | break; 350 | case 'm': 351 | maxlen=strtoul(optarg, NULL, 10); 352 | if (!maxlen) { 353 | sx_report(SX_FATAL, "Invalid maxlen (-m): %s\n", 354 | optarg); 355 | exit(1); 356 | } 357 | break; 358 | case 'M': 359 | { 360 | char *mc, *md; 361 | expander.match = strdup(optarg); 362 | mc = md = expander.match; 363 | while (*mc) { 364 | if (*mc == '\\') { 365 | if (*(mc + 1) == '\n') { 366 | *md = '\n'; 367 | md++; 368 | mc += 2; 369 | } else if (*(mc + 1) == 'r') { 370 | *md = '\r'; 371 | md++; 372 | mc += 2; 373 | } else if (*(mc + 1) == 't') { 374 | *md = '\t'; 375 | md++; 376 | mc += 2; 377 | } else if (*(mc + 1) == '\\') { 378 | *md = '\\'; 379 | md++; 380 | mc += 2; 381 | } else { 382 | sx_report(SX_FATAL, "Unsupported" 383 | " escape \%c (0x%2.2x) in " 384 | "'%s'\n", 385 | isprint(*mc) ? *mc : 20, 386 | *mc, optarg); 387 | exit(1); 388 | } 389 | } else { 390 | if (mc != md) { 391 | *md = *mc; 392 | } 393 | md++; 394 | mc++; 395 | } 396 | } 397 | *md = 0; 398 | } 399 | break; 400 | case 'N': 401 | if (expander.vendor) 402 | vendor_exclusive(); 403 | expander.vendor = V_NOKIA; 404 | break; 405 | case 'n': 406 | if (expander.vendor) 407 | vendor_exclusive(); 408 | expander.vendor = V_NOKIA_MD; 409 | break; 410 | case 'p': 411 | expand_special_asn = 1; 412 | break; 413 | case 't': 414 | if (expander.generation) 415 | exclusive(); 416 | expander.generation = T_ASSET; 417 | break; 418 | case 'T': 419 | pipelining = 0; 420 | break; 421 | case 's': 422 | expander.sequence = 1; 423 | break; 424 | case 'S': 425 | expander.sources = optarg; 426 | break; 427 | case 'U': 428 | if (expander.vendor) 429 | exclusive(); 430 | expander.vendor = V_HUAWEI; 431 | break; 432 | case 'u': 433 | if (expander.vendor) 434 | exclusive(); 435 | expander.vendor = V_HUAWEI_XPL; 436 | break; 437 | case 'W': 438 | expander.aswidth = atoi(optarg); 439 | if (expander.aswidth < 0) { 440 | sx_report(SX_FATAL,"Invalid as-width: %s\n", optarg); 441 | exit(1); 442 | } 443 | widthSet = 1; 444 | break; 445 | case 'w': 446 | expander.validate_asns = 1; 447 | break; 448 | case 'X': 449 | if (expander.vendor) 450 | vendor_exclusive(); 451 | expander.vendor = V_CISCO_XR; 452 | break; 453 | case 'v': 454 | version(); 455 | break; 456 | case 'z': 457 | if (expander.generation) 458 | exclusive(); 459 | expander.generation = T_ROUTE_FILTER_LIST; 460 | break; 461 | default: 462 | usage(1); 463 | } 464 | } 465 | 466 | argc -= optind; 467 | argv += optind; 468 | 469 | if (!widthSet) { 470 | if (expander.generation == T_ASPATH) { 471 | int vendor = expander.vendor; 472 | switch (vendor) { 473 | case V_ARISTA: 474 | case V_CISCO: 475 | case V_MIKROTIK6: 476 | case V_MIKROTIK7: 477 | expander.aswidth = 4; 478 | break; 479 | case V_CISCO_XR: 480 | expander.aswidth = 6; 481 | break; 482 | case V_JUNIPER: 483 | case V_NOKIA: 484 | case V_NOKIA_MD: 485 | case V_NOKIA_SRL: 486 | expander.aswidth = 8; 487 | break; 488 | case V_BIRD: 489 | expander.aswidth = 10; 490 | break; 491 | } 492 | } else if (expander.generation == T_OASPATH) { 493 | int vendor = expander.vendor; 494 | switch (vendor) { 495 | case V_ARISTA: 496 | case V_CISCO: 497 | expander.aswidth = 5; 498 | break; 499 | case V_CISCO_XR: 500 | expander.aswidth = 7; 501 | break; 502 | case V_JUNIPER: 503 | case V_NOKIA: 504 | case V_NOKIA_MD: 505 | case V_NOKIA_SRL: 506 | expander.aswidth = 8; 507 | break; 508 | } 509 | } else if (expander.generation == T_ASLIST) { 510 | int vendor = expander.vendor; 511 | switch (vendor) { 512 | case V_JUNIPER: 513 | expander.aswidth = 8; 514 | break; 515 | } 516 | } 517 | } 518 | 519 | if (!expander.generation) 520 | expander.generation = T_PREFIXLIST; 521 | 522 | if (expander.vendor == V_CISCO_XR 523 | && expander.generation != T_PREFIXLIST 524 | && expander.generation != T_ASPATH 525 | && expander.generation != T_OASPATH) { 526 | sx_report(SX_FATAL, "Sorry, only prefix-sets and as-paths " 527 | "supported for IOS XR\n"); 528 | } 529 | if (expander.vendor == V_BIRD 530 | && expander.generation != T_PREFIXLIST 531 | && expander.generation != T_ASPATH 532 | && expander.generation != T_ASSET) { 533 | sx_report(SX_FATAL, "Sorry, only prefix-lists and as-paths/as-sets " 534 | "supported for BIRD output\n"); 535 | } 536 | if (expander.vendor == V_JSON 537 | && expander.generation != T_PREFIXLIST 538 | && expander.generation != T_ASPATH 539 | && expander.generation != T_ASSET) { 540 | sx_report(SX_FATAL, "Sorry, only prefix-lists and as-paths/as-sets " 541 | "supported for JSON output\n"); 542 | } 543 | 544 | if (expander.vendor == V_FORMAT 545 | && expander.generation != T_PREFIXLIST) 546 | sx_report(SX_FATAL, "Sorry, only prefix-lists supported in formatted " 547 | "output\n"); 548 | 549 | if (expander.vendor == V_HUAWEI 550 | && expander.generation != T_ASPATH 551 | && expander.generation != T_OASPATH 552 | && expander.generation != T_PREFIXLIST) 553 | sx_report(SX_FATAL, "Sorry, only as-paths and prefix-lists supported " 554 | "for Huawei output\n"); 555 | 556 | if (expander.generation == T_ROUTE_FILTER_LIST 557 | && expander.vendor != V_JUNIPER) 558 | sx_report(SX_FATAL, "Route-filter-lists (-z) supported for Juniper (-J)" 559 | " output only\n"); 560 | 561 | if (expander.generation == T_ASSET 562 | && expander.vendor != V_JSON 563 | && expander.vendor != V_OPENBGPD 564 | && expander.vendor != V_BIRD) 565 | sx_report(SX_FATAL, "As-Sets (-t) supported for JSON (-j), OpenBGPD " 566 | "(-B) and BIRD (-b) output only\n"); 567 | 568 | if (aggregate 569 | && expander.vendor == V_JUNIPER 570 | && expander.generation == T_PREFIXLIST) { 571 | sx_report(SX_FATAL, "Sorry, aggregation (-A) does not work in" 572 | " Juniper prefix-lists\nYou can try route-filters (-E) " 573 | "or route-filter-lists (-z) instead of prefix-lists\n."); 574 | exit(1); 575 | } 576 | 577 | if (aggregate 578 | && (expander.vendor == V_NOKIA_MD || expander.vendor == V_NOKIA || expander.vendor == V_NOKIA_SRL) 579 | && expander.generation != T_PREFIXLIST) { 580 | sx_report(SX_FATAL, "Sorry, aggregation (-A) is not supported with " 581 | "ip-prefix-lists (-E) on Nokia.\n"); 582 | exit(1); 583 | } 584 | 585 | if (refine 586 | && (expander.vendor == V_NOKIA_MD || expander.vendor == V_NOKIA || expander.vendor == V_NOKIA_SRL) 587 | && expander.generation != T_PREFIXLIST) { 588 | sx_report(SX_FATAL, "Sorry, more-specifics (-R) is not supported with " 589 | "ip-prefix-lists (-E) on Nokia.\n"); 590 | exit(1); 591 | } 592 | 593 | if (refineLow 594 | && (expander.vendor == V_NOKIA_MD || expander.vendor == V_NOKIA || expander.vendor == V_NOKIA_SRL) 595 | && expander.generation != T_PREFIXLIST) { 596 | sx_report(SX_FATAL, "Sorry, more-specifics (-r) is not supported with " 597 | "ip-prefix-lists (-E) on Nokia.\n"); 598 | exit(1); 599 | } 600 | 601 | if (aggregate && expander.generation < T_PREFIXLIST) { 602 | sx_report(SX_FATAL, "Sorry, aggregation (-A) used only for prefix-" 603 | "lists, extended access-lists and route-filters\n"); 604 | exit(1); 605 | } 606 | 607 | if (expander.vendor == V_ARISTA 608 | && expander.generation == T_EACL 609 | && expander.family == AF_INET6) { 610 | sx_report(SX_FATAL, "Sorry, extended access-lists is not compatible " 611 | "with Arista EOS and IPv6\n"); 612 | exit(1); 613 | } 614 | 615 | if (expander.sequence 616 | && (expander.vendor != V_CISCO && expander.vendor != V_ARISTA)) { 617 | sx_report(SX_FATAL, "Sorry, prefix-lists sequencing (-s) supported" 618 | " only for IOS and EOS\n"); 619 | exit(1); 620 | } 621 | 622 | if (expander.sequence && expander.generation < T_PREFIXLIST) { 623 | sx_report(SX_FATAL, "Sorry, prefix-lists sequencing (-s) can't be " 624 | " used for non prefix-list\n"); 625 | exit(1); 626 | } 627 | 628 | if (refineLow && !refine) { 629 | if (expander.family == AF_INET) 630 | refine = 32; 631 | else 632 | refine = 128; 633 | } 634 | 635 | if (refineLow && refineLow > refine) 636 | sx_report(SX_FATAL, "Incompatible values for -r %u and -R %u\n", 637 | refineLow, refine); 638 | 639 | if (refine || refineLow) { 640 | if (expander.family == AF_INET6 && refine > 128) { 641 | sx_report(SX_FATAL, "Invalid value for refine(-R): %u (1-128 for" 642 | " IPv6)\n", refine); 643 | } else if (expander.family == AF_INET6 && refineLow > 128) { 644 | sx_report(SX_FATAL, "Invalid value for refineLow(-r): %u (1-128 for" 645 | " IPv6)\n", refineLow); 646 | } else if (expander.family == AF_INET && refine > 32) { 647 | sx_report(SX_FATAL, "Invalid value for refine(-R): %u (1-32 for" 648 | " IPv4)\n", refine); 649 | } else if (expander.family == AF_INET && refineLow > 32) { 650 | sx_report(SX_FATAL, "Invalid value for refineLow(-r): %u (1-32 for" 651 | " IPv4)\n", refineLow); 652 | } 653 | 654 | if (expander.vendor == V_JUNIPER && expander.generation == T_PREFIXLIST) { 655 | if (refine) { 656 | sx_report(SX_FATAL, "Sorry, more-specific filters (-R %u) " 657 | "is not supported for Juniper prefix-lists.\n" 658 | "Use router-filters (-E) or route-filter-lists (-z) " 659 | "instead\n", refine); 660 | } else { 661 | sx_report(SX_FATAL, "Sorry, more-specific filters (-r %u) " 662 | "is not supported for Juniper prefix-lists.\n" 663 | "Use route-filters (-E) or route-filter-lists (-z) " 664 | "instead\n", refineLow); 665 | } 666 | } 667 | 668 | if (expander.generation < T_PREFIXLIST) { 669 | if (refine) 670 | sx_report(SX_FATAL, "Sorry, more-specific filter (-R %u) " 671 | "supported only with prefix-list generation\n", refine); 672 | else 673 | sx_report(SX_FATAL, "Sorry, more-specific filter (-r %u) " 674 | "supported only with prefix-list generation\n", refineLow); 675 | } 676 | } 677 | 678 | if (maxlen) { 679 | if ((expander.family == AF_INET6 && maxlen > 128) 680 | || (expander.family == AF_INET && maxlen > 32)) { 681 | sx_report(SX_FATAL, "Invalid value for max-prefixlen: %lu (1-128 " 682 | "for IPv6, 1-32 for IPv4)\n", maxlen); 683 | exit(1); 684 | } else if ((expander.family == AF_INET6 && maxlen < 128) 685 | || (expander.family == AF_INET && maxlen < 32)) { 686 | /* 687 | * inet6/128 and inet4/32 does not make sense - all 688 | * routes will be accepted, so save some CPU cycles :) 689 | */ 690 | expander.maxlen = maxlen; 691 | } 692 | } else if (expander.family == AF_INET) 693 | expander.maxlen = 32; 694 | else if (expander.family == AF_INET6) 695 | expander.maxlen = 128; 696 | 697 | if (expander.generation == T_EACL && expander.vendor == V_CISCO 698 | && expander.family == AF_INET6) { 699 | sx_report(SX_FATAL,"Sorry, ipv6 access-lists not supported " 700 | "for Cisco yet.\n"); 701 | } 702 | 703 | if (expander.match != NULL 704 | && (expander.vendor != V_JUNIPER || expander.generation != T_EACL)) { 705 | sx_report(SX_FATAL, "Sorry, extra match conditions (-M) can be used " 706 | "only with Juniper route-filters\n"); 707 | } 708 | 709 | if ((expander.generation == T_ASPATH 710 | || expander.generation == T_OASPATH 711 | || expander.generation == T_ASLIST) 712 | && af != AF_INET && !expander.validate_asns) { 713 | sx_report(SX_FATAL, "Sorry, -6 makes no sense with as-path (-f/-G) or as-list (-H) " 714 | "generation\n"); 715 | } 716 | 717 | if (expander.validate_asns 718 | && expander.generation != T_ASPATH 719 | && expander.generation != T_OASPATH 720 | && expander.generation != T_ASLIST) { 721 | sx_report(SX_FATAL, "Sorry, -w makes sense only for as-path " 722 | "(-f/-G) generation\n"); 723 | } 724 | 725 | if (!argv[0]) 726 | usage(1); 727 | 728 | while (argv[0]) { 729 | char *obj = argv[0]; 730 | char *delim = strstr(argv[0], "::"); 731 | if (delim) { 732 | expander.usesource = 1; 733 | obj = delim + 2; 734 | } 735 | if (!strcmp(argv[0], "EXCEPT")) { 736 | exceptmode = 1; 737 | } else if (exceptmode) { 738 | bgpq_expander_add_stop(&expander, argv[0]); 739 | } else if (!strncasecmp(obj, "AS-", 3)) { 740 | bgpq_expander_add_asset(&expander, argv[0]); 741 | } else if (!strncasecmp(obj, "RS-", 3)) { 742 | bgpq_expander_add_rset(&expander, argv[0]); 743 | } else if (!strncasecmp(obj, "AS", 2)) { 744 | char *ec; 745 | if ((ec = strchr(obj, ':'))) { 746 | if (!strncasecmp(ec + 1, "AS-", 3)) { 747 | bgpq_expander_add_asset(&expander, argv[0]); 748 | } else if (!strncasecmp(ec + 1, "RS-", 3)) { 749 | bgpq_expander_add_rset(&expander, argv[0]); 750 | } else { 751 | SX_DEBUG(debug_expander,"Unknown sub-as" 752 | " object %s\n", argv[0]); 753 | } 754 | } else { 755 | bgpq_expander_add_as(&expander, argv[0]); 756 | } 757 | } else { 758 | char *ec = strchr(argv[0], '^'); 759 | if (!ec && !bgpq_expander_add_prefix(&expander, argv[0])) { 760 | sx_report(SX_ERROR, "Unable to add prefix %s " 761 | "(bad prefix or address-family)\n", argv[0]); 762 | exit(1); 763 | } else if (ec && !bgpq_expander_add_prefix_range(&expander, 764 | argv[0])) { 765 | sx_report(SX_ERROR, "Unable to add prefix-range " 766 | "%s (bad range or address-family)\n", 767 | argv[0]); 768 | exit(1); 769 | } 770 | } 771 | argv++; 772 | argc--; 773 | } 774 | 775 | if (!bgpq_expand(&expander)) 776 | exit(1); 777 | 778 | if (refine) 779 | sx_radix_tree_refine(expander.tree, refine); 780 | 781 | if (refineLow) 782 | sx_radix_tree_refineLow(expander.tree, refineLow); 783 | 784 | if (aggregate) 785 | sx_radix_tree_aggregate(expander.tree); 786 | 787 | switch (expander.generation) { 788 | case T_NONE: 789 | sx_report(SX_FATAL,"Unreachable point"); 790 | exit(1); 791 | case T_ASPATH: 792 | bgpq4_print_aspath(stdout, &expander); 793 | break; 794 | case T_OASPATH: 795 | bgpq4_print_oaspath(stdout, &expander); 796 | break; 797 | case T_ASLIST: 798 | bgpq4_print_aslist(stdout, &expander); 799 | break; 800 | case T_ASSET: 801 | bgpq4_print_asset(stdout, &expander); 802 | break; 803 | case T_PREFIXLIST: 804 | bgpq4_print_prefixlist(stdout, &expander); 805 | break; 806 | case T_EACL: 807 | bgpq4_print_eacl(stdout, &expander); 808 | break; 809 | case T_ROUTE_FILTER_LIST: 810 | bgpq4_print_route_filter_list(stdout, &expander); 811 | break; 812 | } 813 | 814 | expander_freeall(&expander); 815 | 816 | return 0; 817 | } 818 | -------------------------------------------------------------------------------- /sx_prefix.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2007-2019 Alexandre Snarskii 3 | * All rights reserved. 4 | * 5 | * Redistribution and use in source and binary forms, with or without 6 | * modification, are permitted provided that the following conditions 7 | * are met: 8 | * 1. Redistributions of source code must retain the above copyright 9 | * notice, this list of conditions and the following disclaimer. 10 | * 2. Redistributions in binary form must reproduce the above copyright 11 | * notice, this list of conditions and the following disclaimer in the 12 | * documentation and/or other materials provided with the distribution. 13 | * 14 | * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 15 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 17 | * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 18 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 20 | * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 21 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 22 | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 23 | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 24 | * SUCH DAMAGE. 25 | */ 26 | 27 | #ifndef _SX_PREFIX_H_ 28 | #define _SX_PREFIX_H_ 29 | 30 | #include 31 | #include 32 | #include 33 | #include 34 | 35 | typedef struct sx_prefix { 36 | int family; 37 | unsigned int masklen; 38 | union { 39 | struct in_addr addr; 40 | struct in6_addr addr6; 41 | unsigned char addrs[sizeof(struct in6_addr)]; 42 | } addr; 43 | } sx_prefix_t; 44 | 45 | typedef struct sx_radix_node { 46 | struct sx_radix_node *parent, *l, *r, *son; 47 | void *payload; 48 | unsigned int isGlue:1; 49 | unsigned int isAggregated:1; 50 | unsigned int isAggregate:1; 51 | unsigned int aggregateLow; 52 | unsigned int aggregateHi; 53 | struct sx_prefix *prefix; 54 | } sx_radix_node_t; 55 | 56 | typedef struct sx_radix_tree { 57 | int family; 58 | struct sx_radix_node *head; 59 | } sx_radix_tree_t; 60 | 61 | /* most common operations with the tree is to: lookup/insert/unlink */ 62 | struct sx_radix_node *sx_radix_tree_lookup(struct sx_radix_tree *tree, 63 | struct sx_prefix *prefix); 64 | struct sx_radix_node *sx_radix_tree_insert(struct sx_radix_tree *tree, 65 | struct sx_prefix *prefix); 66 | void sx_radix_tree_unlink(struct sx_radix_tree *t, struct sx_radix_node *n); 67 | struct sx_radix_node *sx_radix_tree_lookup_exact(struct sx_radix_tree *tree, 68 | struct sx_prefix *prefix); 69 | 70 | struct sx_prefix *sx_prefix_alloc(struct sx_prefix *p); 71 | void sx_prefix_free(struct sx_prefix *p); 72 | void sx_radix_node_destroy(struct sx_radix_node *p); 73 | void sx_prefix_adjust_masklen(struct sx_prefix *p); 74 | struct sx_prefix *sx_prefix_new(int af, char *text); 75 | int sx_prefix_parse(struct sx_prefix *p, int af, char *text); 76 | int sx_prefix_range_parse(struct sx_radix_tree *t, int af, unsigned int ml, char *text); 77 | int sx_prefix_fprint(FILE *f, struct sx_prefix *p); 78 | int sx_prefix_snprintf(struct sx_prefix *p, char *rbuffer, int srb); 79 | int sx_prefix_snprintf_sep(struct sx_prefix *p, char *rbuffer, int srb, char *); 80 | void sx_prefix_snprintf_fmt(struct sx_prefix *p, FILE *f, 81 | const char *name, const char *fmt, unsigned int aggregateLow, 82 | unsigned int aggregateHi); 83 | int sx_prefix_jsnprintf(struct sx_prefix *p, char *rbuffer, int srb); 84 | struct sx_radix_tree *sx_radix_tree_new(int af); 85 | struct sx_radix_node *sx_radix_node_new(struct sx_prefix *prefix); 86 | struct sx_prefix *sx_prefix_overlay(struct sx_prefix *p, int n); 87 | int sx_radix_tree_empty(struct sx_radix_tree *t); 88 | void sx_radix_node_fprintf(struct sx_radix_node *node, void *udata); 89 | int sx_radix_node_foreach(struct sx_radix_node *node, 90 | void (*func)(struct sx_radix_node *, void *), void *udata); 91 | int sx_radix_tree_foreach(struct sx_radix_tree *tree, 92 | void (*func)(struct sx_radix_node *, void *), void *udata); 93 | int sx_radix_tree_aggregate(struct sx_radix_tree *tree); 94 | int sx_radix_tree_refine(struct sx_radix_tree *tree, unsigned refine); 95 | int sx_radix_tree_refineLow(struct sx_radix_tree *tree, unsigned refineLow); 96 | 97 | #ifndef HAVE_STRLCPY 98 | size_t strlcpy(char *dst, const char *src, size_t size); 99 | #endif 100 | 101 | #endif 102 | -------------------------------------------------------------------------------- /sx_report.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2007-2019 Alexandre Snarskii 3 | * All rights reserved. 4 | * 5 | * Redistribution and use in source and binary forms, with or without 6 | * modification, are permitted provided that the following conditions 7 | * are met: 8 | * 1. Redistributions of source code must retain the above copyright 9 | * notice, this list of conditions and the following disclaimer. 10 | * 2. Redistributions in binary form must reproduce the above copyright 11 | * notice, this list of conditions and the following disclaimer in the 12 | * documentation and/or other materials provided with the distribution. 13 | * 14 | * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 15 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 17 | * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 18 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 20 | * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 21 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 22 | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 23 | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 24 | * SUCH DAMAGE. 25 | */ 26 | 27 | #include 28 | #include 29 | #include 30 | #include 31 | #include 32 | #include 33 | #include 34 | #include 35 | 36 | #include "sx_report.h" 37 | 38 | static int reportStderr=1; 39 | 40 | static char const* 41 | sx_report_name(sx_report_t t) 42 | { 43 | switch (t) { 44 | case SX_MISFEATURE: 45 | return "MISSING FEATURE:"; 46 | case SX_FATAL: 47 | return "FATAL ERROR:"; 48 | case SX_ERROR: 49 | return "ERROR:"; 50 | case SX_NOTICE: 51 | return "Notice:"; 52 | case SX_DEBUG: 53 | return "Debug:"; 54 | } 55 | 56 | return "...... HMMMMM.... ERROR... \n"; 57 | } 58 | 59 | int 60 | sx_report(sx_report_t t, char* fmt, ...) 61 | { 62 | char buffer[65536]; 63 | va_list ap; 64 | va_start(ap, fmt); 65 | 66 | vsnprintf(buffer, sizeof(buffer), fmt, ap); 67 | va_end(ap); 68 | 69 | if (reportStderr) { 70 | fputs(sx_report_name(t), stderr); 71 | fputs(buffer, stderr); 72 | } else { 73 | switch(t) { 74 | case SX_FATAL: 75 | syslog(LOG_ERR,"FATAL ERROR: %s", buffer); 76 | break; 77 | case SX_MISFEATURE: 78 | case SX_ERROR: 79 | syslog(LOG_ERR,"ERROR: %s", buffer); 80 | break; 81 | case SX_NOTICE: 82 | syslog(LOG_WARNING,"Notice: %s", buffer); 83 | break; 84 | case SX_DEBUG: 85 | syslog(LOG_DEBUG,"Debug: %s", buffer); 86 | break; 87 | } 88 | } 89 | 90 | if (t == SX_FATAL) 91 | exit(-1); 92 | 93 | return 0; 94 | } 95 | 96 | int 97 | sx_debug(char const* const file, char const* const func, int const line, 98 | char* fmt, ...) 99 | { 100 | char buffer[65536]; 101 | char bline[65536]; 102 | 103 | va_list ap; 104 | va_start(ap, fmt); 105 | 106 | vsnprintf(buffer, sizeof(buffer), fmt, ap); 107 | va_end(ap); 108 | 109 | snprintf(bline, sizeof(bline), "DEBUG: %s:%i %s ", file, line, func); 110 | if (reportStderr) { 111 | fputs(bline, stderr); 112 | fputs(buffer, stderr); 113 | } else { 114 | syslog(LOG_DEBUG,"%s %s", bline, buffer); 115 | } 116 | 117 | return 0; 118 | } 119 | 120 | void 121 | sx_openlog(char* progname) 122 | { 123 | openlog(progname ? progname : "", LOG_PID, LOG_DAEMON); 124 | reportStderr = 0; 125 | } 126 | 127 | -------------------------------------------------------------------------------- /sx_report.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2007-2019 Alexandre Snarskii 3 | * All rights reserved. 4 | * 5 | * Redistribution and use in source and binary forms, with or without 6 | * modification, are permitted provided that the following conditions 7 | * are met: 8 | * 1. Redistributions of source code must retain the above copyright 9 | * notice, this list of conditions and the following disclaimer. 10 | * 2. Redistributions in binary form must reproduce the above copyright 11 | * notice, this list of conditions and the following disclaimer in the 12 | * documentation and/or other materials provided with the distribution. 13 | * 14 | * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 15 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 17 | * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 18 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 20 | * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 21 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 22 | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 23 | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 24 | * SUCH DAMAGE. 25 | */ 26 | 27 | #ifndef SX_REPORT_H_ 28 | #define SX_REPORT_H_ 29 | 30 | typedef enum { 31 | SX_DEBUG = 0, 32 | SX_NOTICE, 33 | SX_ERROR, 34 | SX_MISFEATURE, 35 | SX_FATAL 36 | } sx_report_t; 37 | 38 | /* opens syslog and disables logging to stderr */ 39 | void sx_openlog(char* progname); 40 | 41 | int sx_report(sx_report_t, char* fmt, ...) 42 | __attribute__ ((format (printf, 2, 3))); 43 | 44 | int sx_debug(char const* const, char const* const, int const, char* fmt, ...) 45 | __attribute__ ((format (printf, 4, 5))); 46 | 47 | #define SX_DEBUG(a,b,c...) if(a) sx_debug(__FILE__,__FUNCTION__,__LINE__,\ 48 | b, ## c); 49 | 50 | #endif 51 | -------------------------------------------------------------------------------- /sx_slentry.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2007-2019 Alexandre Snarskii 3 | * All rights reserved. 4 | * 5 | * Redistribution and use in source and binary forms, with or without 6 | * modification, are permitted provided that the following conditions 7 | * are met: 8 | * 1. Redistributions of source code must retain the above copyright 9 | * notice, this list of conditions and the following disclaimer. 10 | * 2. Redistributions in binary form must reproduce the above copyright 11 | * notice, this list of conditions and the following disclaimer in the 12 | * documentation and/or other materials provided with the distribution. 13 | * 14 | * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 15 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 17 | * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 18 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 20 | * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 21 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 22 | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 23 | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 24 | * SUCH DAMAGE. 25 | */ 26 | 27 | #include 28 | #include 29 | #include 30 | #include 31 | 32 | #include "extern.h" 33 | 34 | struct slentry * 35 | sx_slentry_new(char *t) 36 | { 37 | struct slentry *e = malloc(sizeof(struct slentry)); 38 | 39 | if (!e) 40 | return NULL; 41 | 42 | memset(e, 0, sizeof(struct slentry)); 43 | 44 | e->text = strdup(t); 45 | 46 | return e; 47 | } 48 | 49 | struct sx_tentry * 50 | sx_tentry_new(char *t) 51 | { 52 | struct sx_tentry *te = malloc(sizeof(struct sx_tentry)); 53 | 54 | if (!te) 55 | return NULL; 56 | 57 | memset(te, 0, sizeof(struct sx_tentry)); 58 | 59 | te->text = strdup(t); 60 | 61 | return te; 62 | } 63 | -------------------------------------------------------------------------------- /tests/generate_outputs.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | set -e 4 | 5 | if [ $# -ne 2 ] 6 | then 7 | echo "Usage: pass the following arguments in order:" 8 | echo "" 9 | echo "path to bgpq4 binary" 10 | echo "output directory path" 11 | echo "" 12 | echo "${0} ./bgpq4 /tmp" 13 | exit 1 14 | fi 15 | 16 | BGPQ4_PATH="${1}" 17 | TEST_ASN="112" 18 | TEST_AS_SET="AS-AS112" 19 | OUT_DIR="${2}" 20 | 21 | if [ ! -f "${BGPQ4_PATH}" ] 22 | then 23 | echo "File ${BGPQ4_PATH} does't exist" 24 | exit 1 25 | fi 26 | 27 | if [ ! -e "${OUT_DIR}" ] 28 | then 29 | echo "Output directory ${OUT_DIR} does't exist" 30 | exit 1 31 | fi 32 | 33 | # Test the IPv4 output formatting for each supported NOS: 34 | "${BGPQ4_PATH}" -4 -b "AS${TEST_ASN}" > "${OUT_DIR}/bird--4.txt" 35 | "${BGPQ4_PATH}" -4 -e "AS${TEST_ASN}" > "${OUT_DIR}/eos--4.txt" 36 | "${BGPQ4_PATH}" -4 -F '%n/%l ' "AS${TEST_ASN}" > "${OUT_DIR}/formated--4.txt" 37 | "${BGPQ4_PATH}" -4 -U "AS${TEST_ASN}" > "${OUT_DIR}/huawei--4.txt" 38 | "${BGPQ4_PATH}" -4 -u "AS${TEST_ASN}" > "${OUT_DIR}/huawei-xpl--4.txt" 39 | "${BGPQ4_PATH}" -4 "AS${TEST_ASN}" > "${OUT_DIR}/ios--4.txt" 40 | "${BGPQ4_PATH}" -4 -X "AS${TEST_ASN}" > "${OUT_DIR}/ios-xr--4.txt" 41 | "${BGPQ4_PATH}" -4 -j "AS${TEST_ASN}" > "${OUT_DIR}/json--4.txt" 42 | "${BGPQ4_PATH}" -4 -J "AS${TEST_ASN}" > "${OUT_DIR}/junos--4.txt" 43 | "${BGPQ4_PATH}" -4 -B "AS${TEST_ASN}" > "${OUT_DIR}/openbgpd--4.txt" 44 | "${BGPQ4_PATH}" -4 -K "AS${TEST_ASN}" > "${OUT_DIR}/routeros6--4.txt" 45 | "${BGPQ4_PATH}" -4 -K7 "AS${TEST_ASN}" > "${OUT_DIR}/routeros7--4.txt" 46 | "${BGPQ4_PATH}" -4 -N "AS${TEST_ASN}" > "${OUT_DIR}/sros--4.txt" 47 | "${BGPQ4_PATH}" -4 -n "AS${TEST_ASN}" > "${OUT_DIR}/sros-mdcli--4.txt" 48 | "${BGPQ4_PATH}" -4 -n2 "AS${TEST_ASN}" > "${OUT_DIR}/srlinux--4.txt" 49 | 50 | # Test the IPv6 prefix-list output formatting for each supported NOS: 51 | "${BGPQ4_PATH}" -6 -b "AS${TEST_ASN}" > "${OUT_DIR}/bird--6.txt" 52 | "${BGPQ4_PATH}" -6 -e "AS${TEST_ASN}" > "${OUT_DIR}/eos--6.txt" 53 | "${BGPQ4_PATH}" -6 -F '%n/%l ' "AS${TEST_ASN}" > "${OUT_DIR}/formated--6.txt" 54 | "${BGPQ4_PATH}" -6 -U "AS${TEST_ASN}" > "${OUT_DIR}/huawei--6.txt" 55 | "${BGPQ4_PATH}" -6 -u "AS${TEST_ASN}" > "${OUT_DIR}/huawei-xpl--6.txt" 56 | "${BGPQ4_PATH}" -6 "AS${TEST_ASN}" > "${OUT_DIR}/ios--6.txt" 57 | "${BGPQ4_PATH}" -6 -X "AS${TEST_ASN}" > "${OUT_DIR}/ios-xr--6.txt" 58 | "${BGPQ4_PATH}" -6 -j "AS${TEST_ASN}" > "${OUT_DIR}/json--6.txt" 59 | "${BGPQ4_PATH}" -6 -J "AS${TEST_ASN}" > "${OUT_DIR}/junos--6.txt" 60 | "${BGPQ4_PATH}" -6 -B "AS${TEST_ASN}" > "${OUT_DIR}/openbgpd--6.txt" 61 | "${BGPQ4_PATH}" -6 -K "AS${TEST_ASN}" > "${OUT_DIR}/routeros6--6.txt" 62 | "${BGPQ4_PATH}" -6 -K7 "AS${TEST_ASN}" > "${OUT_DIR}/routeros7--6.txt" 63 | "${BGPQ4_PATH}" -6 -N "AS${TEST_ASN}" > "${OUT_DIR}/sros--6.txt" 64 | "${BGPQ4_PATH}" -6 -n "AS${TEST_ASN}" > "${OUT_DIR}/sros-mdcli--6.txt" 65 | "${BGPQ4_PATH}" -6 -n2 "AS${TEST_ASN}" > "${OUT_DIR}/srlinux--6.txt" 66 | 67 | # Test the AS path list output formatting for each supported NOS: 68 | "${BGPQ4_PATH}" "${TEST_AS_SET}" -f "${TEST_ASN}" -b > "${OUT_DIR}/bird--asp.txt" 69 | # "${BGPQ4_PATH}" "${TEST_AS_SET}" -f "${TEST_ASN}" -e > "${OUT_DIR}/eos--asp.txt" # Not supported 70 | "${BGPQ4_PATH}" "${TEST_AS_SET}" -f "${TEST_ASN}" -U > "${OUT_DIR}/huawei--asp.txt" 71 | "${BGPQ4_PATH}" "${TEST_AS_SET}" -f "${TEST_ASN}" -u > "${OUT_DIR}/huawei-xpl--asp.txt" 72 | "${BGPQ4_PATH}" "${TEST_AS_SET}" -f "${TEST_ASN}" > "${OUT_DIR}/ios--asp.txt" 73 | "${BGPQ4_PATH}" "${TEST_AS_SET}" -f "${TEST_ASN}" -X > "${OUT_DIR}/ios-xr--asp.txt" 74 | "${BGPQ4_PATH}" "${TEST_AS_SET}" -f "${TEST_ASN}" -j > "${OUT_DIR}/json--asp.txt" 75 | "${BGPQ4_PATH}" "${TEST_AS_SET}" -f "${TEST_ASN}" -J > "${OUT_DIR}/junos--asp.txt" 76 | "${BGPQ4_PATH}" "${TEST_AS_SET}" -f "${TEST_ASN}" -B > "${OUT_DIR}/openbgpd--asp.txt" 77 | # "${BGPQ4_PATH}" "${TEST_AS_SET}" -f "${TEST_ASN}" -K > "${OUT_DIR}/routeros6--asp.txt" # Not supported 78 | # "${BGPQ4_PATH}" "${TEST_AS_SET}" -f "${TEST_ASN}" -K7 > "${OUT_DIR}/routeros7--asp.txt" # Not supported 79 | "${BGPQ4_PATH}" "${TEST_AS_SET}" -f "${TEST_ASN}" -N > "${OUT_DIR}/sros--asp.txt" 80 | "${BGPQ4_PATH}" "${TEST_AS_SET}" -f "${TEST_ASN}" -n > "${OUT_DIR}/sros-mdcli--asp.txt" 81 | 82 | # Test IRR source scopes 83 | # Limit ASN to valid source: 84 | "${BGPQ4_PATH}" "AS${TEST_ASN}" -S RIPE-NONAUTH > "${OUT_DIR}/as112-ripe-nonauth.txt" 85 | # Limit ASN to invalid sources: 86 | "${BGPQ4_PATH}" "AS${TEST_ASN}" -S APNIC,AFRINIC > "${OUT_DIR}/as112-apnic.txt" 87 | # Limit AS-SET using IRR prefix notation to valid source: 88 | "${BGPQ4_PATH}" "RIPE::${TEST_AS_SET}" > "${OUT_DIR}/as-as112-ripe-notation.txt" 89 | # Limit AS-SET using IRR prefix notation to invalid source: 90 | "${BGPQ4_PATH}" "APNIC::${TEST_AS_SET}" > "${OUT_DIR}/as-as112-apnic-notation.txt" 91 | -------------------------------------------------------------------------------- /tests/reference/as-as112-apnic-notation.txt: -------------------------------------------------------------------------------- 1 | no ip prefix-list NN 2 | ! generated prefix-list NN is empty 3 | ip prefix-list NN deny 0.0.0.0/0 4 | -------------------------------------------------------------------------------- /tests/reference/as-as112-ripe-notation.txt: -------------------------------------------------------------------------------- 1 | no ip prefix-list NN 2 | ip prefix-list NN permit 192.31.196.0/24 3 | ip prefix-list NN permit 192.175.48.0/24 4 | -------------------------------------------------------------------------------- /tests/reference/as112-apnic.txt: -------------------------------------------------------------------------------- 1 | no ip prefix-list NN 2 | ! generated prefix-list NN is empty 3 | ip prefix-list NN deny 0.0.0.0/0 4 | -------------------------------------------------------------------------------- /tests/reference/as112-ripe-nonauth.txt: -------------------------------------------------------------------------------- 1 | no ip prefix-list NN 2 | ip prefix-list NN permit 192.31.196.0/24 3 | ip prefix-list NN permit 192.175.48.0/24 4 | -------------------------------------------------------------------------------- /tests/reference/bird--4.txt: -------------------------------------------------------------------------------- 1 | NN = [ 2 | 192.31.196.0/24, 3 | 192.175.48.0/24 4 | ]; 5 | -------------------------------------------------------------------------------- /tests/reference/bird--6.txt: -------------------------------------------------------------------------------- 1 | NN = [ 2 | 2001:4:112::/48, 3 | 2620:4f:8000::/48 4 | ]; 5 | -------------------------------------------------------------------------------- /tests/reference/bird--asp.txt: -------------------------------------------------------------------------------- 1 | NN = [ 2 | 112 3 | ]; 4 | -------------------------------------------------------------------------------- /tests/reference/eos--4.txt: -------------------------------------------------------------------------------- 1 | no ip prefix-list NN 2 | ip prefix-list NN 3 | seq 1 permit 192.31.196.0/24 4 | seq 2 permit 192.175.48.0/24 5 | -------------------------------------------------------------------------------- /tests/reference/eos--6.txt: -------------------------------------------------------------------------------- 1 | no ipv6 prefix-list NN 2 | ipv6 prefix-list NN 3 | seq 1 permit 2001:4:112::/48 4 | seq 2 permit 2620:4f:8000::/48 5 | -------------------------------------------------------------------------------- /tests/reference/formated--4.txt: -------------------------------------------------------------------------------- 1 | 192.31.196.0/24 192.175.48.0/24 2 | -------------------------------------------------------------------------------- /tests/reference/formated--6.txt: -------------------------------------------------------------------------------- 1 | 2001:4:112::/48 2620:4f:8000::/48 2 | -------------------------------------------------------------------------------- /tests/reference/huawei--4.txt: -------------------------------------------------------------------------------- 1 | undo ip ip-prefix NN 2 | ip ip-prefix NN permit 192.31.196.0 24 3 | ip ip-prefix NN permit 192.175.48.0 24 4 | -------------------------------------------------------------------------------- /tests/reference/huawei--6.txt: -------------------------------------------------------------------------------- 1 | undo ip ipv6-prefix NN 2 | ip ipv6-prefix NN permit 2001:4:112:: 48 3 | ip ipv6-prefix NN permit 2620:4f:8000:: 48 4 | -------------------------------------------------------------------------------- /tests/reference/huawei--asp.txt: -------------------------------------------------------------------------------- 1 | undo ip as-path-filter NN 2 | ip as-path-filter NN permit ^112(_112)*$ 3 | -------------------------------------------------------------------------------- /tests/reference/huawei-xpl--4.txt: -------------------------------------------------------------------------------- 1 | no xpl ip-prefix-list NN 2 | xpl ip-prefix-list NN 3 | 192.31.196.0 24, 4 | 192.175.48.0 24 5 | end-list 6 | -------------------------------------------------------------------------------- /tests/reference/huawei-xpl--6.txt: -------------------------------------------------------------------------------- 1 | no xpl ipv6-prefix-list NN 2 | xpl ipv6-prefix-list NN 3 | 2001:4:112:: 48, 4 | 2620:4f:8000:: 48 5 | end-list 6 | -------------------------------------------------------------------------------- /tests/reference/huawei-xpl--asp.txt: -------------------------------------------------------------------------------- 1 | xpl as-path-list NN 2 | regular ^112(_112)*$ 3 | end-list 4 | -------------------------------------------------------------------------------- /tests/reference/ios--4.txt: -------------------------------------------------------------------------------- 1 | no ip prefix-list NN 2 | ip prefix-list NN permit 192.31.196.0/24 3 | ip prefix-list NN permit 192.175.48.0/24 4 | -------------------------------------------------------------------------------- /tests/reference/ios--6.txt: -------------------------------------------------------------------------------- 1 | no ipv6 prefix-list NN 2 | ipv6 prefix-list NN permit 2001:4:112::/48 3 | ipv6 prefix-list NN permit 2620:4f:8000::/48 4 | -------------------------------------------------------------------------------- /tests/reference/ios--asp.txt: -------------------------------------------------------------------------------- 1 | no ip as-path access-list NN 2 | ip as-path access-list NN permit ^112(_112)*$ 3 | -------------------------------------------------------------------------------- /tests/reference/ios-xr--4.txt: -------------------------------------------------------------------------------- 1 | no prefix-set NN 2 | prefix-set NN 3 | 192.31.196.0/24, 4 | 192.175.48.0/24 5 | end-set 6 | -------------------------------------------------------------------------------- /tests/reference/ios-xr--6.txt: -------------------------------------------------------------------------------- 1 | no prefix-set NN 2 | prefix-set NN 3 | 2001:4:112::/48, 4 | 2620:4f:8000::/48 5 | end-set 6 | -------------------------------------------------------------------------------- /tests/reference/ios-xr--asp.txt: -------------------------------------------------------------------------------- 1 | as-path-set NN 2 | ios-regex '^112(_112)*$' 3 | end-set 4 | -------------------------------------------------------------------------------- /tests/reference/json--4.txt: -------------------------------------------------------------------------------- 1 | { "NN": [ 2 | { "prefix": "192.31.196.0\/24", "exact": true }, 3 | { "prefix": "192.175.48.0\/24", "exact": true } 4 | ] } 5 | -------------------------------------------------------------------------------- /tests/reference/json--6.txt: -------------------------------------------------------------------------------- 1 | { "NN": [ 2 | { "prefix": "2001:4:112::\/48", "exact": true }, 3 | { "prefix": "2620:4f:8000::\/48", "exact": true } 4 | ] } 5 | -------------------------------------------------------------------------------- /tests/reference/json--asp.txt: -------------------------------------------------------------------------------- 1 | {"NN": [ 2 | 112 3 | ]} 4 | -------------------------------------------------------------------------------- /tests/reference/junos--4.txt: -------------------------------------------------------------------------------- 1 | policy-options { 2 | replace: 3 | prefix-list NN { 4 | 192.31.196.0/24; 5 | 192.175.48.0/24; 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /tests/reference/junos--6.txt: -------------------------------------------------------------------------------- 1 | policy-options { 2 | replace: 3 | prefix-list NN { 4 | 2001:4:112::/48; 5 | 2620:4f:8000::/48; 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /tests/reference/junos--asp.txt: -------------------------------------------------------------------------------- 1 | policy-options { 2 | replace: 3 | as-path-group NN { 4 | as-path a0 "^112(112)*$"; 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /tests/reference/openbgpd--4.txt: -------------------------------------------------------------------------------- 1 | prefix { 2 | 192.31.196.0/24 3 | 192.175.48.0/24 4 | } 5 | -------------------------------------------------------------------------------- /tests/reference/openbgpd--6.txt: -------------------------------------------------------------------------------- 1 | prefix { 2 | 2001:4:112::/48 3 | 2620:4f:8000::/48 4 | } 5 | -------------------------------------------------------------------------------- /tests/reference/openbgpd--asp.txt: -------------------------------------------------------------------------------- 1 | allow from AS 112 AS 112 2 | -------------------------------------------------------------------------------- /tests/reference/routeros6--4.txt: -------------------------------------------------------------------------------- 1 | /routing filter add action=accept chain="NN-V4" prefix=192.31.196.0/24 2 | /routing filter add action=accept chain="NN-V4" prefix=192.175.48.0/24 3 | -------------------------------------------------------------------------------- /tests/reference/routeros6--6.txt: -------------------------------------------------------------------------------- 1 | /routing filter add action=accept chain="NN-V6" prefix=2001:4:112::/48 2 | /routing filter add action=accept chain="NN-V6" prefix=2620:4f:8000::/48 3 | -------------------------------------------------------------------------------- /tests/reference/routeros7--4.txt: -------------------------------------------------------------------------------- 1 | /routing filter rule add chain="NN-V4" rule="if (dst==192.31.196.0/24) {accept}" 2 | /routing filter rule add chain="NN-V4" rule="if (dst==192.175.48.0/24) {accept}" 3 | -------------------------------------------------------------------------------- /tests/reference/routeros7--6.txt: -------------------------------------------------------------------------------- 1 | /routing filter rule add chain="NN-V6" rule="if (dst==2001:4:112::/48) {accept}" 2 | /routing filter rule add chain="NN-V6" rule="if (dst==2620:4f:8000::/48) {accept}" 3 | -------------------------------------------------------------------------------- /tests/reference/srlinux--4.txt: -------------------------------------------------------------------------------- 1 | /routing-policy 2 | delete prefix-set "NN" 3 | prefix-set "NN" { 4 | prefix 192.31.196.0/24 mask-length-range exact { } 5 | prefix 192.175.48.0/24 mask-length-range exact { } 6 | } 7 | -------------------------------------------------------------------------------- /tests/reference/srlinux--6.txt: -------------------------------------------------------------------------------- 1 | /routing-policy 2 | delete prefix-set "NN" 3 | prefix-set "NN" { 4 | prefix 2001:4:112::/48 mask-length-range exact { } 5 | prefix 2620:4f:8000::/48 mask-length-range exact { } 6 | } 7 | -------------------------------------------------------------------------------- /tests/reference/sros--4.txt: -------------------------------------------------------------------------------- 1 | configure router policy-options 2 | begin 3 | no prefix-list "NN" 4 | prefix-list "NN" 5 | prefix 192.31.196.0/24 exact 6 | prefix 192.175.48.0/24 exact 7 | exit 8 | commit 9 | -------------------------------------------------------------------------------- /tests/reference/sros--6.txt: -------------------------------------------------------------------------------- 1 | configure router policy-options 2 | begin 3 | no prefix-list "NN" 4 | prefix-list "NN" 5 | prefix 2001:4:112::/48 exact 6 | prefix 2620:4f:8000::/48 exact 7 | exit 8 | commit 9 | -------------------------------------------------------------------------------- /tests/reference/sros--asp.txt: -------------------------------------------------------------------------------- 1 | configure router policy-options 2 | begin 3 | no as-path-group "NN" 4 | as-path-group "NN" 5 | entry 1 expression "112+" 6 | exit 7 | commit 8 | -------------------------------------------------------------------------------- /tests/reference/sros-mdcli--4.txt: -------------------------------------------------------------------------------- 1 | /configure policy-options 2 | delete prefix-list "NN" 3 | prefix-list "NN" { 4 | prefix 192.31.196.0/24 type exact { 5 | } 6 | prefix 192.175.48.0/24 type exact { 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /tests/reference/sros-mdcli--6.txt: -------------------------------------------------------------------------------- 1 | /configure policy-options 2 | delete prefix-list "NN" 3 | prefix-list "NN" { 4 | prefix 2001:4:112::/48 type exact { 5 | } 6 | prefix 2620:4f:8000::/48 type exact { 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /tests/reference/sros-mdcli--asp.txt: -------------------------------------------------------------------------------- 1 | /configure policy-options 2 | delete as-path-group "NN" 3 | as-path-group "NN" { 4 | entry 1 { 5 | expression "112+" 6 | } 7 | } 8 | --------------------------------------------------------------------------------