mirror of
https://github.com/bgp/bgpq4
synced 2025-02-28 08:53:11 +00:00
Compare commits
186 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
b85bea6324 | ||
|
|
6fdae48462 | ||
|
|
89ab54454e | ||
|
|
baddc22f15 | ||
|
|
0deb7f224c | ||
|
|
975b577b2e | ||
|
|
b785c02e37 | ||
|
|
017bae280f | ||
|
|
2921348a98 | ||
|
|
233380d4e9 | ||
|
|
509e2f0acf | ||
|
|
a6b41d9352 | ||
|
|
7ac2068879 | ||
|
|
fc79ff9242 | ||
|
|
277126f5ea | ||
|
|
08b81f7d19 | ||
|
|
24c3e08f0e | ||
|
|
30110bad46 | ||
|
|
5507267c63 | ||
|
|
ab683d75d5 | ||
|
|
fb955c0521 | ||
|
|
4661fab181 | ||
|
|
6484a9a40c | ||
|
|
3ec83e255a | ||
|
|
c01ebfc3cb | ||
|
|
8dfcfb3173 | ||
|
|
c2126343cf | ||
|
|
96079b8901 | ||
|
|
58521eb687 | ||
|
|
8b804f83fd | ||
|
|
f26a04e8dd | ||
|
|
392a44a536 | ||
|
|
2aed3f9e67 | ||
|
|
d285db3c81 | ||
|
|
5d27a24659 | ||
|
|
97d1f2acda | ||
|
|
61a7bcf671 | ||
|
|
e920d74007 | ||
|
|
b1074667e8 | ||
|
|
bdc455d72e | ||
|
|
8cc7ba2751 | ||
|
|
06b430b420 | ||
|
|
8c51669da9 | ||
|
|
dc1270f10f | ||
|
|
362f747f0f | ||
|
|
6c8c6cfec1 | ||
|
|
fb29cd54e1 | ||
|
|
903704ef3b | ||
|
|
b481111cf6 | ||
|
|
80b7dcf4de | ||
|
|
5ce7d7bfea | ||
|
|
8ec260f03f | ||
|
|
675155356d | ||
|
|
da750ed48f | ||
|
|
7fdb481130 | ||
|
|
fa84d146b3 | ||
|
|
c90c59dba8 | ||
|
|
5730525f11 | ||
|
|
78f57867cc | ||
|
|
2391fdb317 | ||
|
|
9f8d82db21 | ||
|
|
a6792d2495 | ||
|
|
af202a821b | ||
|
|
ed8ee8368d | ||
|
|
bf5f10def9 | ||
|
|
1f7572c2f1 | ||
|
|
34cc813446 | ||
|
|
5c57bb2bb6 | ||
|
|
e6bf77058a | ||
|
|
139e0d972b | ||
|
|
99f036b186 | ||
|
|
cb98214f0c | ||
|
|
ed28cf6734 | ||
|
|
9ac2087d24 | ||
|
|
58adb550bd | ||
|
|
773849fc3a | ||
|
|
40656fbbbf | ||
|
|
4a8dc682fd | ||
|
|
450286a010 | ||
|
|
a055f6f2ee | ||
|
|
c4c5866fa3 | ||
|
|
6074f816e0 | ||
|
|
cb15aea505 | ||
|
|
f1a339985a | ||
|
|
24daa48a67 | ||
|
|
b21c8b30ca | ||
|
|
270e04dd23 | ||
|
|
32a3520e62 | ||
|
|
b816c1023b | ||
|
|
85d833ca48 | ||
|
|
791f3d7790 | ||
|
|
6a4207f940 | ||
|
|
af10798afd | ||
|
|
81112ec2de | ||
|
|
08801e2068 | ||
|
|
b1a9f4f089 | ||
|
|
408b7d8852 | ||
|
|
3754a01a2b | ||
|
|
8bc07f3cb0 | ||
|
|
bbcd4ae18f | ||
|
|
f1e9242814 | ||
|
|
222b89e631 | ||
|
|
ea95e6d27a | ||
|
|
dc0e8c32f7 | ||
|
|
21b574fc91 | ||
|
|
757dc33bb0 | ||
|
|
82146e291b | ||
|
|
ac93dcfeef | ||
|
|
e6d488ca5b | ||
|
|
dee79b0408 | ||
|
|
6f7a8db6aa | ||
|
|
276b2cfc52 | ||
|
|
82b44b1304 | ||
|
|
4eb9566de8 | ||
|
|
f473454ff7 | ||
|
|
16fd7f7668 | ||
|
|
a0b9343551 | ||
|
|
d998a73652 | ||
|
|
0b554d4aa1 | ||
|
|
406b984aa4 | ||
|
|
c2223849fc | ||
|
|
56432d3c74 | ||
|
|
65f157581b | ||
|
|
d103cc6e3b | ||
|
|
d4b53ba67e | ||
|
|
9498d7f368 | ||
|
|
5f5ce4cff3 | ||
|
|
f29576d973 | ||
|
|
d000627465 | ||
|
|
884a982718 | ||
|
|
71cbcf7019 | ||
|
|
03bc8d47dc | ||
|
|
b361671c45 | ||
|
|
a624dc9649 | ||
|
|
fd6150a2dd | ||
|
|
c05dea14a7 | ||
|
|
12da3fc231 | ||
|
|
e21b687467 | ||
|
|
7c483de7d3 | ||
|
|
78825a18cf | ||
|
|
95edf74de7 | ||
|
|
a3f0cd39e2 | ||
|
|
a3053a34b7 | ||
|
|
2d96ae5ff8 | ||
|
|
a3ca5cbcc9 | ||
|
|
bb775897d8 | ||
|
|
521aa51262 | ||
|
|
d9a2ee92a6 | ||
|
|
62b2a56af7 | ||
|
|
92d52b9133 | ||
|
|
1d5b375cc3 | ||
|
|
e4d72f0505 | ||
|
|
b71aa3f21d | ||
|
|
d8fbae0643 | ||
|
|
b8e92b5456 | ||
|
|
20d04b5d54 | ||
|
|
de3c94ac9d | ||
|
|
0a49b8a6ea | ||
|
|
cfa1a00433 | ||
|
|
ef9889dda3 | ||
|
|
fb6fde65c8 | ||
|
|
e9f4eeaa57 | ||
|
|
f5ba86dfa5 | ||
|
|
425e8dbdb4 | ||
|
|
d3eb845c9b | ||
|
|
8b5459cf05 | ||
|
|
fd34dd8fa3 | ||
|
|
bc16d6fb45 | ||
|
|
10cdd5656d | ||
|
|
72eed80603 | ||
|
|
4a2fe3d966 | ||
|
|
484ae33948 | ||
|
|
50d4dbaf28 | ||
|
|
824436099d | ||
|
|
3c4f0ad276 | ||
|
|
96eccd146a | ||
|
|
07db38c432 | ||
|
|
7d6c4421de | ||
|
|
156ff46032 | ||
|
|
e5df35884c | ||
|
|
92249a3e6a | ||
|
|
e3974c5469 | ||
|
|
a62f4031c4 | ||
|
|
b94ab9bc20 | ||
|
|
bda0408c9e | ||
|
|
4b4ffafeb1 |
17
.github/images/alpine.Dockerfile
vendored
Normal file
17
.github/images/alpine.Dockerfile
vendored
Normal file
@@ -0,0 +1,17 @@
|
||||
ARG image=alpine:latest
|
||||
FROM $image
|
||||
|
||||
# Install dependencies
|
||||
RUN apk upgrade
|
||||
RUN apk add autoconf automake file gcc gzip libtool make musl-dev
|
||||
|
||||
# Add source code
|
||||
ADD . /src
|
||||
WORKDIR /src
|
||||
|
||||
# Run steps
|
||||
RUN ./bootstrap
|
||||
RUN ./configure
|
||||
RUN make
|
||||
RUN make check
|
||||
RUN make distcheck
|
||||
1
.github/images/alpine:3.17.Dockerfile
vendored
Symbolic link
1
.github/images/alpine:3.17.Dockerfile
vendored
Symbolic link
@@ -0,0 +1 @@
|
||||
alpine.Dockerfile
|
||||
1
.github/images/alpine:edge.Dockerfile
vendored
Symbolic link
1
.github/images/alpine:edge.Dockerfile
vendored
Symbolic link
@@ -0,0 +1 @@
|
||||
alpine.Dockerfile
|
||||
8
.github/images/centos.Dockerfile
vendored
8
.github/images/centos.Dockerfile
vendored
@@ -1,10 +1,9 @@
|
||||
ARG image=centos:8
|
||||
FROM $image
|
||||
ARG image=centos/centos:latest
|
||||
FROM quay.io/$image
|
||||
|
||||
# Install dependencies
|
||||
RUN yum update -y
|
||||
RUN yum groupinstall -y 'Development Tools'
|
||||
RUN yum install -y autoconf automake findutils
|
||||
RUN yum install -y autoconf automake gcc libtool make diffutils file gzip
|
||||
|
||||
# Add source code
|
||||
ADD . /src
|
||||
@@ -16,4 +15,3 @@ RUN ./configure
|
||||
RUN make
|
||||
RUN make check
|
||||
RUN make distcheck
|
||||
|
||||
|
||||
1
.github/images/centos/centos:7.Dockerfile
vendored
Symbolic link
1
.github/images/centos/centos:7.Dockerfile
vendored
Symbolic link
@@ -0,0 +1 @@
|
||||
../centos.Dockerfile
|
||||
1
.github/images/centos/centos:stream8.Dockerfile
vendored
Symbolic link
1
.github/images/centos/centos:stream8.Dockerfile
vendored
Symbolic link
@@ -0,0 +1 @@
|
||||
../centos.Dockerfile
|
||||
1
.github/images/centos/centos:stream9.Dockerfile
vendored
Symbolic link
1
.github/images/centos/centos:stream9.Dockerfile
vendored
Symbolic link
@@ -0,0 +1 @@
|
||||
../centos.Dockerfile
|
||||
1
.github/images/centos:7.Dockerfile
vendored
1
.github/images/centos:7.Dockerfile
vendored
@@ -1 +0,0 @@
|
||||
centos.Dockerfile
|
||||
1
.github/images/centos:8.Dockerfile
vendored
1
.github/images/centos:8.Dockerfile
vendored
@@ -1 +0,0 @@
|
||||
centos.Dockerfile
|
||||
5
.github/images/debian.Dockerfile
vendored
5
.github/images/debian.Dockerfile
vendored
@@ -7,7 +7,10 @@ RUN apt-get update && apt-get install -y locales && rm -rf /var/lib/apt/lists/*
|
||||
ENV LANG en_US.utf8
|
||||
|
||||
# Install dependencies
|
||||
RUN apt-get update && apt-get dist-upgrade -y && apt-get install -y build-essential autoconf automake markdown && rm -rf /var/lib/apt/lists/*
|
||||
RUN apt-get update \
|
||||
&& apt-get dist-upgrade -y \
|
||||
&& apt-get install -y build-essential autoconf libtool automake markdown \
|
||||
&& rm -rf /var/lib/apt/lists/*
|
||||
|
||||
# Add source code
|
||||
ADD . /src
|
||||
|
||||
1
.github/images/fedora/fedora:36.Dockerfile
vendored
Symbolic link
1
.github/images/fedora/fedora:36.Dockerfile
vendored
Symbolic link
@@ -0,0 +1 @@
|
||||
../centos.Dockerfile
|
||||
1
.github/images/fedora/fedora:37.Dockerfile
vendored
Symbolic link
1
.github/images/fedora/fedora:37.Dockerfile
vendored
Symbolic link
@@ -0,0 +1 @@
|
||||
../centos.Dockerfile
|
||||
1
.github/images/fedora/fedora:38.Dockerfile
vendored
Symbolic link
1
.github/images/fedora/fedora:38.Dockerfile
vendored
Symbolic link
@@ -0,0 +1 @@
|
||||
../centos.Dockerfile
|
||||
1
.github/images/fedora:30.Dockerfile
vendored
1
.github/images/fedora:30.Dockerfile
vendored
@@ -1 +0,0 @@
|
||||
centos.Dockerfile
|
||||
1
.github/images/fedora:31.Dockerfile
vendored
1
.github/images/fedora:31.Dockerfile
vendored
@@ -1 +0,0 @@
|
||||
centos.Dockerfile
|
||||
1
.github/images/rockylinux/rockylinux:8.Dockerfile
vendored
Symbolic link
1
.github/images/rockylinux/rockylinux:8.Dockerfile
vendored
Symbolic link
@@ -0,0 +1 @@
|
||||
../centos.Dockerfile
|
||||
1
.github/images/rockylinux/rockylinux:9.Dockerfile
vendored
Symbolic link
1
.github/images/rockylinux/rockylinux:9.Dockerfile
vendored
Symbolic link
@@ -0,0 +1 @@
|
||||
../centos.Dockerfile
|
||||
1
.github/images/ubuntu:focal.Dockerfile
vendored
Symbolic link
1
.github/images/ubuntu:focal.Dockerfile
vendored
Symbolic link
@@ -0,0 +1 @@
|
||||
debian.Dockerfile
|
||||
1
.github/images/ubuntu:jammy.Dockerfile
vendored
Symbolic link
1
.github/images/ubuntu:jammy.Dockerfile
vendored
Symbolic link
@@ -0,0 +1 @@
|
||||
debian.Dockerfile
|
||||
2
.github/workflows/build.yml
vendored
2
.github/workflows/build.yml
vendored
@@ -6,7 +6,7 @@ jobs:
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v1
|
||||
- uses: actions/checkout@v3
|
||||
- name: bootstrap
|
||||
run: ./bootstrap
|
||||
- name: configure
|
||||
|
||||
38
.github/workflows/codeql-analysis.yml
vendored
Normal file
38
.github/workflows/codeql-analysis.yml
vendored
Normal file
@@ -0,0 +1,38 @@
|
||||
name: CodeQL analysis
|
||||
|
||||
on:
|
||||
push:
|
||||
pull_request:
|
||||
schedule:
|
||||
# build the main branch every Tuesday morning
|
||||
- cron: '15 6 * * 2'
|
||||
workflow_dispatch:
|
||||
|
||||
jobs:
|
||||
analyze:
|
||||
name: Analyze
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
actions: read
|
||||
contents: read
|
||||
security-events: write
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
language: [ 'cpp' ]
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v3
|
||||
- name: Initialize CodeQL
|
||||
uses: github/codeql-action/init@v2
|
||||
with:
|
||||
languages: ${{ matrix.language }}
|
||||
- name: Build Application using script
|
||||
run: |
|
||||
./bootstrap
|
||||
./configure
|
||||
make
|
||||
- name: Perform CodeQL Analysis
|
||||
uses: github/codeql-action/analyze@v2
|
||||
with:
|
||||
category: "/language:${{matrix.language}}"
|
||||
22
.github/workflows/matrixbuild.yml
vendored
22
.github/workflows/matrixbuild.yml
vendored
@@ -9,15 +9,23 @@ jobs:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
dockerenv:
|
||||
- debian:bookworm
|
||||
- debian:bullseye
|
||||
- debian:buster
|
||||
- debian:stretch
|
||||
- ubuntu:jammy
|
||||
- ubuntu:focal
|
||||
- ubuntu:bionic
|
||||
- ubuntu:xenial
|
||||
- centos:8
|
||||
- centos:7
|
||||
- fedora:31
|
||||
- fedora:30
|
||||
- fedora/fedora:38
|
||||
- fedora/fedora:37
|
||||
- fedora/fedora:36
|
||||
- centos/centos:stream9
|
||||
- centos/centos:stream8
|
||||
- centos/centos:7
|
||||
- rockylinux/rockylinux:9
|
||||
- rockylinux/rockylinux:8
|
||||
- alpine:edge
|
||||
- alpine:3.17
|
||||
steps:
|
||||
- uses: actions/checkout@v1
|
||||
- uses: actions/checkout@v3
|
||||
- name: Run build on ${{matrix.dockerenv}}
|
||||
run: docker build . --file .github/images/${{matrix.dockerenv}}.Dockerfile --build-arg image=${{matrix.dockerenv}}
|
||||
|
||||
37
.gitignore
vendored
37
.gitignore
vendored
@@ -1,37 +0,0 @@
|
||||
CVS/
|
||||
Makefile
|
||||
config.h
|
||||
*.o
|
||||
bgpq4
|
||||
bgpq4.html
|
||||
Makefile.in
|
||||
.deps
|
||||
/bgpq4-*.tar.gz
|
||||
/ar-lib
|
||||
/mdate-sh
|
||||
/py-compile
|
||||
/test-driver
|
||||
/ylwrap
|
||||
autom4te.cache
|
||||
/autoscan.log
|
||||
/autoscan-*.log
|
||||
/aclocal.m4
|
||||
/compile
|
||||
/config.guess
|
||||
/config.h.in
|
||||
/config.log
|
||||
/config.status
|
||||
/config.sub
|
||||
/configure
|
||||
/configure.scan
|
||||
/depcomp
|
||||
/install-sh
|
||||
/missing
|
||||
/stamp-h1
|
||||
/ltmain.sh
|
||||
/texinfo.tex
|
||||
m4/libtool.m4
|
||||
m4/ltoptions.m4
|
||||
m4/ltsugar.m4
|
||||
m4/ltversion.m4
|
||||
m4/lt~obsolete.m4
|
||||
51
CHANGES
51
CHANGES
@@ -1,12 +1,57 @@
|
||||
1.8 (2023-01-20)
|
||||
- Downgrade 'key not found' to DEBUG level to reduce noise
|
||||
- Re-introduce -p for private ASN support option
|
||||
|
||||
1.7 (2022-11-03)
|
||||
- Support SOURCE:: syntax (contributed by James Bensley)
|
||||
|
||||
1.6 (2022-09-07)
|
||||
- Fix a bug in address prefix range parsing
|
||||
|
||||
1.5 (2022-07-25)
|
||||
- Add support for the new Junos as-path-origins feature
|
||||
|
||||
1.4 (2021-08-20)
|
||||
- Fix BIRD aspath output
|
||||
|
||||
1.3 (2021-08-20)
|
||||
- Change versioning from X.Y.Z to Y.Z
|
||||
- The repository file hierachy has been reorganized (compat/ include/)
|
||||
- Man page has been extended
|
||||
- Large portions of code have been reformatted to adhere to OpenBSD's source
|
||||
file style guide to improve readability.
|
||||
- Refactor: replace two-dimensional array for ASN storage with Red-Black tree
|
||||
- Reduced memory usage
|
||||
|
||||
0.0.9 (2021-08-18)
|
||||
- Fix various memory errors
|
||||
|
||||
0.0.8 (2021-08-17)
|
||||
- Reorganize automake files and includes
|
||||
- Normalize code to adhere to KNF
|
||||
- Fix all compiler warnings
|
||||
|
||||
0.0.6 (2020-03-12):
|
||||
- Bugfixes - Thanks Chris Caputo!
|
||||
|
||||
0.0.5 (2020-01-01):
|
||||
- Bugfixes
|
||||
|
||||
0.0.4 (2019-12-31):
|
||||
- Remove the '-3' command line option, assume all devices are
|
||||
32-bit ASN safe
|
||||
|
||||
0.0.3 (2019-12-30):
|
||||
- Add support for IRRd 4's A query
|
||||
- Remove the '-2' command line option
|
||||
- Significant code reformating
|
||||
- Improve performance by using IRRd 4's A query when available
|
||||
|
||||
0.0.2 (2019-12-14):
|
||||
- Add Mikrotik support
|
||||
- remove ASDDOT support
|
||||
- Remove ASDDOT support
|
||||
|
||||
0.0.1 (2019-12-14):
|
||||
- fork bgpq3 into bgpq4
|
||||
- Fork bgpq3 into bgpq4
|
||||
|
||||
0.1.36-pre (2019-11-08):
|
||||
- minor documentation cleanup: bgpq3 supports much more vendors
|
||||
|
||||
29
IDEAS
Normal file
29
IDEAS
Normal file
@@ -0,0 +1,29 @@
|
||||
Ben Maddison taught me another aggregation trick:
|
||||
|
||||
route-set: AS37271:RS-EXAMPLE
|
||||
mp-members: 192.0.2.0/27
|
||||
mp-members: 192.0.2.32/27
|
||||
mp-members: 192.0.2.64/27
|
||||
mp-members: 192.0.2.96/27
|
||||
mp-members: 192.0.2.128/26
|
||||
mp-members: 192.0.2.128/27
|
||||
mp-members: 192.0.2.160/27
|
||||
mp-members: 192.0.2.192/27
|
||||
mp-members: 192.0.2.224/27
|
||||
descr: Example route-set
|
||||
mnt-by: MAINT-AS37271
|
||||
changed: benm@workonline.africa 20210819
|
||||
source: RADB
|
||||
|
||||
BGPQ4 produces the following:
|
||||
|
||||
$ bgpq4 -A AS37271:RS-EXAMPLE
|
||||
no ip prefix-list NN
|
||||
ip prefix-list NN permit 192.0.2.0/25 ge 27 le 27
|
||||
ip prefix-list NN permit 192.0.2.128/26 le 27
|
||||
ip prefix-list NN permit 192.0.2.192/26 ge 27 le 27
|
||||
|
||||
But the following aggregation also is valid, and shorter:
|
||||
|
||||
ip prefix-list NN permit 192.0.2.0/24 ge 27 le 27
|
||||
ip prefix-list NN permit 192.0.2.128/26
|
||||
40
Makefile.am
40
Makefile.am
@@ -1,19 +1,35 @@
|
||||
SUBDIRS = include
|
||||
ACLOCAL_AMFLAGS = -I m4
|
||||
|
||||
AM_CPPFLAGS = -I$(top_srcdir)/include
|
||||
AM_CPPFLAGS += -I$(top_srcdir)/compat
|
||||
|
||||
AUTOMAKE_OPTIONS=foreign subdir-objects
|
||||
|
||||
bin_PROGRAMS=bgpq4
|
||||
bgpq4_SOURCES=bgpq4.c bgpq4.h config.h bgpq4_printer.c bgpq_expander.c \
|
||||
expander_freeall.c expander_freeall.h strlcpy.c sx_maxsockbuf.c \
|
||||
sx_maxsockbuf.h sx_prefix.c sx_prefix.h sx_report.c sx_report.h \
|
||||
sx_slentry.c sx_slentry.h sys_queue.h sys_tree.h
|
||||
dist_doc_DATA=bgpq4.html
|
||||
dist_man8_MANS=bgpq4.8
|
||||
EXTRA_DIST=bootstrap readme.header README.md CHANGES COPYRIGHT bgpq4.spec
|
||||
CLEANFILES=bgpq4.html
|
||||
|
||||
bgpq4_LDADD = $(PLATFORM_LDADD) $(PROG_LDADD)
|
||||
|
||||
if !HAVE_STRLCPY
|
||||
SUBDIRS += compat
|
||||
bgpq4_LDADD += $(top_builddir)/compat/libcompat.la
|
||||
endif
|
||||
|
||||
bgpq4_SOURCES=main.c extern.h printer.c expander.c \
|
||||
sx_maxsockbuf.c \
|
||||
sx_prefix.c sx_prefix.h \
|
||||
sx_report.c sx_report.h \
|
||||
sx_slentry.c
|
||||
|
||||
|
||||
EXTRA_DIST=bootstrap README.md CHANGES
|
||||
|
||||
MAINTAINERCLEANFILES=configure aclocal.m4 compile \
|
||||
install-sh missing Makefile.in depcomp \
|
||||
stamp-h1 config.h.in
|
||||
|
||||
bgpq4.html: readme.header README.md
|
||||
cat readme.header README.md | @MARKDOWN@ > bgpq4.html
|
||||
stamp-h1 compat/Makefile.in \
|
||||
config.guess config.sub include/Makefile.in \
|
||||
ltmain.sh
|
||||
|
||||
maintainer-clean-local:
|
||||
-rm -rf autom4te.cache
|
||||
-rm -rf m4 autom4te.cache
|
||||
|
||||
570
README.md
570
README.md
@@ -1,246 +1,242 @@
|
||||
NAME
|
||||
----
|
||||
# NAME
|
||||
|
||||
`bgpq4` - bgp filtering automation tool
|
||||
**bgpq4** - bgp filtering automation tool
|
||||
|
||||
SYNOPSIS
|
||||
--------
|
||||
# SYNOPSIS
|
||||
|
||||
```
|
||||
bgpq4 [-h host[:port]] [-S sources] [-EPz] [-f asn | -F fmt | -G asn | -t] [-2346ABbDdJjNnpsUX] [-a asn] [-r len] [-R len] [-m max] [-W len] OBJECTS [...] EXCEPT OBJECTS
|
||||
```
|
||||
**bgpq4**
|
||||
\[**-h** *host\[:port]*]
|
||||
\[**-S** *sources*]
|
||||
\[**-EPz**]
|
||||
\[**-f** *asn* |
|
||||
**-F** *fmt* |
|
||||
**-G** *asn*
|
||||
**-H** *asn*
|
||||
**-t**]
|
||||
\[**-46ABbDdJjNnpsXU**]
|
||||
\[**-a** *asn*]
|
||||
\[**-r** *len*]
|
||||
\[**-R** *len*]
|
||||
\[**-m** *max*]
|
||||
\[**-W** *len*]
|
||||
*OBJECTS*
|
||||
\[...]
|
||||
\[EXCEPT OBJECTS]
|
||||
|
||||
DESCRIPTION
|
||||
-----------
|
||||
# DESCRIPTION
|
||||
|
||||
The bgpq4 utility used to generate configuration (prefix-lists,
|
||||
extended access-lists, policy-statement terms and as-path lists)
|
||||
based on RADB data.
|
||||
The
|
||||
**bgpq4**
|
||||
utility is used to generate configurations (prefix-lists, extended
|
||||
access-lists, policy-statement terms and as-path lists) based on IRR data.
|
||||
|
||||
The options are as follows:
|
||||
It's options are as follows:
|
||||
|
||||
#### -2
|
||||
**-4**
|
||||
|
||||
Allow routes registered for as23456 (transition-as) (default: false)
|
||||
> generate IPv4 prefix/access-lists (default).
|
||||
|
||||
#### -3
|
||||
**-6**
|
||||
|
||||
Assume that your device is asn32-capable.
|
||||
> generate IPv6 prefix/access-lists (IPv4 by default).
|
||||
|
||||
#### -4
|
||||
**-A**
|
||||
|
||||
Generate IPv4 prefix/access-lists (default).
|
||||
> try to aggregate prefix-lists as much as possible (not all output
|
||||
> formats supported).
|
||||
|
||||
#### -6
|
||||
**-a** *asn*
|
||||
|
||||
Generate IPv6 prefix/access-lists (IPv4 by default).
|
||||
> specify what asn shall be denied in case of empty prefix-list (OpenBGPD)
|
||||
|
||||
#### -A
|
||||
**-B**
|
||||
|
||||
Try to aggregate generated filters as much as possible (not all output formats
|
||||
supported).
|
||||
> generate output in OpenBGPD format (default: Cisco)
|
||||
|
||||
#### -a asn
|
||||
**-b**
|
||||
|
||||
Specify ASN that shall be denied in case of empty prefix-list (OpenBGPD).
|
||||
> generate output in BIRD format (default: Cisco).
|
||||
|
||||
#### -B
|
||||
**-d**
|
||||
|
||||
Generate output in OpenBGPD format (default: Cisco).
|
||||
> enable some debugging output.
|
||||
|
||||
#### -b
|
||||
**-e**
|
||||
|
||||
Generate output in BIRD format (default: Cisco).
|
||||
> generate output in Arista EOS format (default: Cisco).
|
||||
|
||||
#### -d
|
||||
**-E**
|
||||
|
||||
Enable some debugging output.
|
||||
> generate extended access-list (Cisco), policy-statement term using
|
||||
> route-filters (Juniper), \[ip|ipv6]-prefix-list (Nokia) or prefix-sets
|
||||
> (OpenBGPd).
|
||||
|
||||
#### -E
|
||||
**-f** *number*
|
||||
|
||||
Generate extended access-list (Cisco) or policy-statement term using
|
||||
route-filters (Juniper), [ip|ipv6]-prefix-list (Nokia) or prefix-filter
|
||||
(OpenBGPD)
|
||||
> generate input as-path access-list.
|
||||
|
||||
#### -f `AS number`
|
||||
**-F** *fmt*
|
||||
|
||||
Generate input as-path access-list for adjacent as `AS number`.
|
||||
> generate output in user-defined format.
|
||||
|
||||
#### -F `fmt`
|
||||
**-G** *number*
|
||||
|
||||
Generate output in user-defined format.
|
||||
> generate output as-path access-list.
|
||||
|
||||
#### -G `number`
|
||||
**-H** *number*
|
||||
|
||||
Generate output as-path access-list.
|
||||
> generate output as-list for JunOS 21.3R1+ `as-path-origin` filter (JunOS only)
|
||||
|
||||
#### -h `host[:port]`
|
||||
**-h** *host\[:port]*
|
||||
|
||||
Host running IRRD database (default: `rr.ntt.net`).
|
||||
> host running IRRD database (default: rr.ntt.net).
|
||||
|
||||
#### -J
|
||||
**-J**
|
||||
|
||||
Generate config for Juniper (default: Cisco).
|
||||
> generate config for Juniper (default: Cisco).
|
||||
|
||||
#### -j
|
||||
**-j**
|
||||
|
||||
Generate output in JSON format (default: Cisco).
|
||||
> generate output in JSON format (default: Cisco).
|
||||
|
||||
#### -K
|
||||
**-K**
|
||||
|
||||
Generate config for MikroTik (default: Cisco).
|
||||
> generate config for Mikrotik ROSv6 (default: Cisco).
|
||||
|
||||
#### -m `length`
|
||||
**-K7**
|
||||
|
||||
Maximum length of accepted prefixes (default: `32` for IPv4, `128` for IPv6).
|
||||
> generate config for Mikrotik ROSv7 (default: Cisco).
|
||||
|
||||
#### -M `match`
|
||||
**-l** *name*
|
||||
|
||||
Extra match conditions for Juniper route-filters. See the examples section.
|
||||
> name of generated entry.
|
||||
|
||||
#### -n
|
||||
**-L** *limit*
|
||||
|
||||
Generate config for Nokia SR OS (former Alcatel-Lucent) MD-CLI (default: Cisco)
|
||||
> limit recursion depth when expanding as-sets.
|
||||
|
||||
#### -N
|
||||
**-m** *len*
|
||||
|
||||
Generate config for Nokia SR OS (former Alcatel-Lucent) classic CLI (default: Cisco)
|
||||
> maximum prefix-length of accepted prefixes (default: 32 for IPv4 and
|
||||
> 128 for IPv6).
|
||||
|
||||
#### -l `name`
|
||||
**-M** *match*
|
||||
|
||||
`Name` of generated configuration stanza.
|
||||
> extra match conditions for Juniper route-filters.
|
||||
|
||||
#### -L `limit`
|
||||
**-n**
|
||||
|
||||
Limit recursion depth when expanding. This slows `bgpq4` a bit, but sometimes
|
||||
is a useful feature to prevent generated filters from growing too big.
|
||||
> generate config for Nokia SR OS MD-CLI (Cisco IOS by default)
|
||||
|
||||
#### -p
|
||||
**-N**
|
||||
|
||||
Enable use of private ASNs and ASNs used for documentation purpose only
|
||||
(default: disabled).
|
||||
> generate config for Nokia SR OS classic CLI (Cisco IOS by default).
|
||||
|
||||
#### -P
|
||||
**-p**
|
||||
|
||||
Generate prefix-list (default behaviour, flag added for backward compatibility
|
||||
only).
|
||||
> emit prefixes where the origin ASN is in the private ASN range
|
||||
> (disabled by default).
|
||||
|
||||
#### -r `length`
|
||||
**-r** *len*
|
||||
|
||||
Allow more-specific routes with masklen starting with specified length.
|
||||
> allow more specific routes starting with specified masklen too.
|
||||
|
||||
#### -R `length`
|
||||
**-R** *len*
|
||||
|
||||
Allow more-specific routes up to specified masklen too. (Please, note: objects
|
||||
with prefix-length greater than specified length will be always allowed.)
|
||||
> allow more specific routes up to specified masklen too.
|
||||
|
||||
#### -s
|
||||
**-s**
|
||||
|
||||
Generate sequence numbers in IOS-style prefix-lists.
|
||||
> generate sequence numbers in IOS-style prefix-lists.
|
||||
|
||||
#### -S `sources`
|
||||
**-S** *sources*
|
||||
|
||||
Use specified sources only (recommended: RADB,RIPE,APNIC).
|
||||
> use specified sources only (recommended: RPKI,AFRINIC,ARIN,APNIC,LACNIC,RIPE).
|
||||
|
||||
#### -t
|
||||
**-t**
|
||||
|
||||
Generate as-sets for OpenBGPD (OpenBSD 6.4+), BIRD and JSON formats.
|
||||
> generate as-sets for OpenBGPd, BIRD and JSON formats.
|
||||
|
||||
#### -T
|
||||
**-T**
|
||||
|
||||
Disable pipelining. (not recommended)
|
||||
> disable pipelining (not recommended).
|
||||
|
||||
#### -U
|
||||
**-W** *len*
|
||||
|
||||
Generate output in Huawei format (default: Cisco).
|
||||
> generate as-path strings of no more than len items (use 0 for infinity).
|
||||
|
||||
#### -W `length`
|
||||
**-U**
|
||||
|
||||
Generate as-path strings of a given length maximum (0 for infinity).
|
||||
> generate config for Huawei devices (Cisco IOS by default)
|
||||
|
||||
#### -X
|
||||
**-u**
|
||||
|
||||
Generate config for Cisco IOS XR devices (plain IOS by default).
|
||||
> generate output in Huawei XPL format.
|
||||
|
||||
#### -z
|
||||
**-X**
|
||||
|
||||
Generate Juniper route-filter-list (JunOS 16.2+).
|
||||
> generate config for Cisco IOS XR devices (plain IOS by default).
|
||||
|
||||
#### `OBJECTS`
|
||||
**-z**
|
||||
|
||||
`OBJECTS` means networks (in prefix format), autonomous systems, as-sets and
|
||||
route-sets. If multiple objects are specified they will be merged.
|
||||
> generate route-filter-lists (JunOS 16.2+).
|
||||
|
||||
#### `EXCEPT OBJECTS`
|
||||
*OBJECTS*
|
||||
|
||||
You can exclude autonomous sets, as-sets and route-sets found during
|
||||
expansion from future expansion.
|
||||
> means networks (in prefix format), autonomous systems, as-sets and route-sets.
|
||||
|
||||
EXAMPLES
|
||||
--------
|
||||
Generating prefix filter for MikroTik for `AS20597`:
|
||||
*EXCEPT OBJECTS*
|
||||
|
||||
user@host:~>./bgpq4 -Kl eltel-v4 AS20597
|
||||
/routing filter add action=accept chain="eltel-v4" prefix=81.9.0.0/20
|
||||
/routing filter add action=accept chain="eltel-v4" prefix=81.9.32.0/20
|
||||
/routing filter add action=accept chain="eltel-v4" prefix=81.9.96.0/20
|
||||
/routing filter add action=accept chain="eltel-v4" prefix=81.222.128.0/20
|
||||
/routing filter add action=accept chain="eltel-v4" prefix=81.222.160.0/20
|
||||
/routing filter add action=accept chain="eltel-v4" prefix=81.222.192.0/18
|
||||
/routing filter add action=accept chain="eltel-v4" prefix=85.249.8.0/21
|
||||
/routing filter add action=accept chain="eltel-v4" prefix=85.249.224.0/19
|
||||
/routing filter add action=accept chain="eltel-v4" prefix=89.112.0.0/17
|
||||
/routing filter add action=accept chain="eltel-v4" prefix=217.170.64.0/19
|
||||
> those objects will be excluded from expansion.
|
||||
|
||||
Generating named Juniper prefix-filter for `AS20597`:
|
||||
# EXAMPLES
|
||||
|
||||
user@host:~>bgpq4 -Jl eltel-v4 AS20597
|
||||
policy-options {
|
||||
replace:
|
||||
prefix-list eltel-v4 {
|
||||
81.9.0.0/20;
|
||||
81.9.32.0/20;
|
||||
81.9.96.0/20;
|
||||
81.222.128.0/20;
|
||||
81.222.192.0/18;
|
||||
85.249.8.0/21;
|
||||
85.249.224.0/19;
|
||||
89.112.0.0/19;
|
||||
89.112.4.0/22;
|
||||
89.112.32.0/19;
|
||||
89.112.64.0/19;
|
||||
217.170.64.0/20;
|
||||
217.170.80.0/20;
|
||||
}
|
||||
}
|
||||
Generating named juniper prefix-filter for AS20597:
|
||||
|
||||
$ bgpq4 -Jl eltel AS20597
|
||||
policy-options {
|
||||
replace:
|
||||
prefix-list eltel {
|
||||
81.9.0.0/20;
|
||||
81.9.32.0/20;
|
||||
81.9.96.0/20;
|
||||
81.222.128.0/20;
|
||||
81.222.192.0/18;
|
||||
85.249.8.0/21;
|
||||
85.249.224.0/19;
|
||||
89.112.0.0/19;
|
||||
89.112.4.0/22;
|
||||
89.112.32.0/19;
|
||||
89.112.64.0/19;
|
||||
217.170.64.0/20;
|
||||
217.170.80.0/20;
|
||||
}
|
||||
}
|
||||
|
||||
For Cisco we can use aggregation (-A) flag to make this prefix-filter
|
||||
more compact:
|
||||
|
||||
user@host:~>bgpq4 -Al eltel-v4 AS20597
|
||||
no ip prefix-list eltel-v4
|
||||
ip prefix-list eltel-v4 permit 81.9.0.0/20
|
||||
ip prefix-list eltel-v4 permit 81.9.32.0/20
|
||||
ip prefix-list eltel-v4 permit 81.9.96.0/20
|
||||
ip prefix-list eltel-v4 permit 81.222.128.0/20
|
||||
ip prefix-list eltel-v4 permit 81.222.192.0/18
|
||||
ip prefix-list eltel-v4 permit 85.249.8.0/21
|
||||
ip prefix-list eltel-v4 permit 85.249.224.0/19
|
||||
ip prefix-list eltel-v4 permit 89.112.0.0/18 ge 19 le 19
|
||||
ip prefix-list eltel-v4 permit 89.112.4.0/22
|
||||
ip prefix-list eltel-v4 permit 89.112.64.0/19
|
||||
ip prefix-list eltel-v4 permit 217.170.64.0/19 ge 20 le 20
|
||||
$ bgpq4 -Al eltel AS20597
|
||||
no ip prefix-list eltel
|
||||
ip prefix-list eltel permit 81.9.0.0/20
|
||||
ip prefix-list eltel permit 81.9.32.0/20
|
||||
ip prefix-list eltel permit 81.9.96.0/20
|
||||
ip prefix-list eltel permit 81.222.128.0/20
|
||||
ip prefix-list eltel permit 81.222.192.0/18
|
||||
ip prefix-list eltel permit 85.249.8.0/21
|
||||
ip prefix-list eltel permit 85.249.224.0/19
|
||||
ip prefix-list eltel permit 89.112.0.0/18 ge 19 le 19
|
||||
ip prefix-list eltel permit 89.112.4.0/22
|
||||
ip prefix-list eltel permit 89.112.64.0/19
|
||||
ip prefix-list eltel permit 217.170.64.0/19 ge 20 le 20
|
||||
|
||||
and, as you see, prefixes `89.112.0.0/19` and `89.112.32.0/19` now aggregated
|
||||
into single entry
|
||||
Prefixes 89.112.0.0/19 and 89.112.32.0/19 now aggregated
|
||||
into single entry 89.112.0.0/18 ge 19 le 19.
|
||||
|
||||
ip prefix-list eltel permit 89.112.0.0/18 ge 19 le 19.
|
||||
Well, for Juniper we can generate even more interesting policy-options,
|
||||
using -M <extra match conditions>, -R <len> and hierarchical names:
|
||||
|
||||
Well, for Juniper we can generate even more interesting policy-statement,
|
||||
using `-M <extra match conditions>`, `-r <len>`, `-R <len>` and hierarchical
|
||||
names:
|
||||
|
||||
user@host:~>bgpq4 -AJEl eltel/specifics -r 29 -R 32 -M "community blackhole" AS20597
|
||||
$ bgpq4 -AJEl eltel/specifics -r 29 -R 32 -M "community blackhole" AS20597
|
||||
policy-options {
|
||||
policy-statement eltel {
|
||||
term specifics {
|
||||
@@ -261,143 +257,245 @@ names:
|
||||
}
|
||||
}
|
||||
|
||||
generated policy-option term now allows all specifics with prefix-length
|
||||
between /29 and /32 for eltel networks if they match with special community
|
||||
blackhole (defined elsewhere in configuration).
|
||||
|
||||
generated policy-option term now allows more-specific routes in range
|
||||
/29 - /32 for eltel networks if they marked with community 'blackhole'
|
||||
(defined elsewhere in configuration).
|
||||
Of course, this version supports IPv6 (-6):
|
||||
|
||||
Of course, `bgpq4` supports IPv6 (-6):
|
||||
$ bgpq4 -6l as-retn-6 AS-RETN6
|
||||
no ipv6 prefix-list as-retn-6
|
||||
ipv6 prefix-list as-retn-6 permit 2001:7fb:fe00::/48
|
||||
ipv6 prefix-list as-retn-6 permit 2001:7fb:fe01::/48
|
||||
[....]
|
||||
|
||||
user@host:~>bgpq4 -6l as-retn-v6 AS-RETN6
|
||||
no ipv6 prefix-list as-retn-v6
|
||||
ipv6 prefix-list as-retn-v6 permit 2001:7fb:fe00::/48
|
||||
ipv6 prefix-list as-retn-v6 permit 2001:7fb:fe01::/48
|
||||
[....]
|
||||
and assumes your device supports 32-bit ASNs
|
||||
|
||||
and ASN32
|
||||
$ bgpq4 -Jf 112 AS-SPACENET
|
||||
policy-options {
|
||||
replace:
|
||||
as-path-group NN {
|
||||
as-path a0 "^112(112)*$";
|
||||
as-path a1 "^112(.)*(1898|5539|8495|8763|8878|12136|12931|15909)$";
|
||||
as-path a2 "^112(.)*(21358|23456|23600|24151|25152|31529|34127|34906)$";
|
||||
as-path a3 "^112(.)*(35052|41720|43628|44450|196611)$";
|
||||
}
|
||||
}
|
||||
|
||||
user@host:~>bgpq4 -J3f 112 AS-SPACENET
|
||||
policy-options {
|
||||
replace:
|
||||
as-path-group NN {
|
||||
as-path a0 "^112(112)*$";
|
||||
as-path a1 "^112(.)*(1898|5539|8495|8763|8878|12136|12931|15909)$";
|
||||
as-path a2 "^112(.)*(21358|23456|23600|24151|25152|31529|34127|34906)$";
|
||||
as-path a3 "^112(.)*(35052|41720|43628|44450|196611)$";
|
||||
}
|
||||
}
|
||||
see \`AS196611\` in the end of the list ? That's a 32-bit ASN.
|
||||
|
||||
see `AS196611` in the end of the list ? That's `AS3.3` in 'asplain' notation.
|
||||
|
||||
If your router does not support ASN32 (yet) you should not use switch -3,
|
||||
and the result will be next:
|
||||
|
||||
user@host:~>bgpq4 -f 112 AS-SPACENET
|
||||
no ip as-path access-list NN
|
||||
ip as-path access-list NN permit ^112( 112)*$
|
||||
ip as-path access-list NN permit ^112( [0-9]+)* (1898|5539|8495|8763)$
|
||||
ip as-path access-list NN permit ^112( [0-9]+)* (8878|12136|12931|15909)$
|
||||
ip as-path access-list NN permit ^112( [0-9]+)* (21358|23456|23600|24151)$
|
||||
ip as-path access-list NN permit ^112( [0-9]+)* (25152|31529|34127|34906)$
|
||||
ip as-path access-list NN permit ^112( [0-9]+)* (35052|41720|43628|44450)$
|
||||
|
||||
`AS196611` is no more in the list, however, `AS23456` (transition AS) would
|
||||
have been added to list if it were not present.
|
||||
|
||||
USER-DEFINED FORMAT
|
||||
-------------------
|
||||
# USER-DEFINED FORMAT
|
||||
|
||||
If you want to generate configuration not for routers, but for some
|
||||
other programs/systems, you may use user-defined formatting, like in
|
||||
example below:
|
||||
|
||||
user@host:~>bgpq4 -F "ipfw add pass all from %n/%l to any\\n" as3254
|
||||
$ bgpq4 -F "ipfw add pass all from %n/%l to any\n" as3254
|
||||
ipfw add pass all from 62.244.0.0/18 to any
|
||||
ipfw add pass all from 91.219.29.0/24 to any
|
||||
ipfw add pass all from 91.219.30.0/24 to any
|
||||
ipfw add pass all from 193.193.192.0/19 to any
|
||||
|
||||
Recognized format characters: '%n' - network, '%l' - mask length,
|
||||
'%N' - object name, '%m' - object mask and '%i' - inversed mask.
|
||||
Recognized escape characters: '\n' - new line, '\t' - tabulation.
|
||||
Recognized format sequences are:
|
||||
|
||||
**%n**
|
||||
|
||||
> network
|
||||
|
||||
**%l**
|
||||
|
||||
> mask length
|
||||
|
||||
**%a**
|
||||
|
||||
> aggregate low mask length
|
||||
|
||||
**%A**
|
||||
|
||||
> aggregate high mask length
|
||||
|
||||
**%N**
|
||||
|
||||
> object name
|
||||
|
||||
**%m**
|
||||
|
||||
> object mask
|
||||
|
||||
**%i**
|
||||
|
||||
> inversed mask
|
||||
|
||||
**\n**
|
||||
|
||||
> new line
|
||||
|
||||
**\t**
|
||||
|
||||
> tabulation
|
||||
|
||||
Please note that no new lines inserted automatically after each sentence,
|
||||
you have to add them into format string manually, elsewhere output will
|
||||
be in one line (sometimes it makes sense):
|
||||
|
||||
user@host:~>bgpq4 -6F "%n/%l; " as-eltel
|
||||
$ bgpq4 -6F "%n/%l; " as-eltel
|
||||
2001:1b00::/32; 2620:4f:8000::/48; 2a04:bac0::/29; 2a05:3a80::/48;
|
||||
|
||||
DIAGNOSTICS
|
||||
-----------
|
||||
# NOTES ON SOURCES
|
||||
|
||||
When everything is OK, `bgpq4` generates result to standard output and
|
||||
exits with status == 0. In case of errors they are printed to stderr and
|
||||
program exits with non-zero status.
|
||||
By default *bgpq4* trusts data from all databases mirrored into NTT's IRR service.
|
||||
Unfortunately, not all these databases are equal in how much can we trust their
|
||||
data.
|
||||
RIR maintained databases (AFRINIC, ARIN, APNIC, LACNIC and RIPE)
|
||||
shall be trusted more than the others because they have the knowledge about
|
||||
which address space is allocated to each ASN, other databases lack this
|
||||
knowledge and can (and actually do) contain some stale data: nobody but RIRs
|
||||
care to remove outdated route-objects when address space is revoked from one
|
||||
ASN and allocated to another. In order to keep their filters both compact and
|
||||
current, *bgpq4 users* are encouraged to use one of two method to limit
|
||||
database sources to only ones they trust.
|
||||
|
||||
NOTES ON ULTRA-LARGE PREFIX-LISTS
|
||||
---------------------------------
|
||||
One option is to use the '-S' flag. This limits all queries to a specific data
|
||||
source. For example, the following command tells IIRd to only use data from
|
||||
the RIPE RIR DB to build the prefix list for the AS-SET:
|
||||
|
||||
To improve `bgpq4` performance when expanding extra-large AS-SETs you
|
||||
$./bgpq4 -S RIPE AS-VOSTRON
|
||||
no ip prefix-list NN
|
||||
ip prefix-list NN permit 89.21.224.0/19
|
||||
ip prefix-list NN permit 134.0.64.0/21
|
||||
|
||||
Be aware though, than an AS-SET may contain members from other data sources.
|
||||
In this case IRRd won't respond to the bgpq4 query will all the prefixes in the
|
||||
AS-SET tree. Make sure to use the '-S' flag with all the data sources required
|
||||
for the AS-SET being expanded:
|
||||
|
||||
$./bgpq4 -S RIPE,ARIN AS-VOSTRON
|
||||
no ip prefix-list NN
|
||||
ip prefix-list NN permit 89.21.224.0/19
|
||||
ip prefix-list NN permit 134.0.64.0/21
|
||||
ip prefix-list NN permit 208.86.232.0/24
|
||||
ip prefix-list NN permit 208.86.233.0/24
|
||||
ip prefix-list NN permit 208.86.234.0/24
|
||||
ip prefix-list NN permit 208.86.235.0/24
|
||||
|
||||
The other option is to specify a source for an AS-SET or Route Set using the
|
||||
"::" notation. When bgpq4 detects this, it will look for "::" in the specified
|
||||
AS-SET or RS on the CLI, and in all members of the AS-SET/RS, and for each
|
||||
member with a data source specified in "::" format, it will set the IRRd data
|
||||
source to the given value, query the AS-SET/RS, then reset the data sources back
|
||||
to the default list for the next object in the tree.
|
||||
|
||||
$./bgpq4 RIPE::AS-VOSTRON
|
||||
no ip prefix-list NN
|
||||
ip prefix-list NN permit 89.21.224.0/19
|
||||
ip prefix-list NN permit 134.0.64.0/21
|
||||
ip prefix-list NN permit 208.86.232.0/22
|
||||
ip prefix-list NN permit 208.86.232.0/24
|
||||
ip prefix-list NN permit 208.86.233.0/24
|
||||
ip prefix-list NN permit 208.86.234.0/24
|
||||
ip prefix-list NN permit 208.86.235.0/24
|
||||
|
||||
In comparison to the '-S' flag, this method return all the prefixes under the
|
||||
AS-SET, but the root of the tree "AS-VOSTRON" was queries from RIPE only. None
|
||||
of the member objects used the "::" notation so they were queries from the
|
||||
default source list (which is all sources).
|
||||
|
||||
|
||||
General recommendations:
|
||||
|
||||
Use minimal set of RIR databases (only those in which you and your
|
||||
customers have registered route-objects).
|
||||
|
||||
Avoid using ARIN-NONAUTH and RIPE-NONAUTH as trusted sources: these records
|
||||
were created in database but for address space allocated to different RIR,
|
||||
so the NONAUTH databases have no chance to confirm validity of this route
|
||||
object.
|
||||
|
||||
$ bgpq4 -S RIPE,RADB as-space
|
||||
no ip prefix-list NN
|
||||
ip prefix-list NN permit 195.190.32.0/19
|
||||
|
||||
$ bgpq4 -S RADB,RIPE as-space
|
||||
no ip prefix-list NN
|
||||
ip prefix-list NN permit 45.4.4.0/22
|
||||
ip prefix-list NN permit 45.4.132.0/22
|
||||
ip prefix-list NN permit 45.6.128.0/22
|
||||
ip prefix-list NN permit 45.65.184.0/22
|
||||
[...]
|
||||
|
||||
When known, use the "::" notation to speicy the authortative data source for
|
||||
an AS-SET or RS instead of the -S flag.
|
||||
|
||||
# PERFORMANCE
|
||||
|
||||
To improve \`bgpq4\` performance when expanding extra-large AS-SETs you
|
||||
shall tune OS settings to enlarge TCP send buffer.
|
||||
|
||||
FreeBSD can be tuned in the following way:
|
||||
|
||||
sysctl -w net.inet.tcp.sendbuf_max=2097152
|
||||
|
||||
sysctl -w net.inet.tcp.sendbuf_max=2097152
|
||||
|
||||
Linux can be tuned in the following way:
|
||||
|
||||
sysctl -w net.ipv4.tcp_window_scaling=1
|
||||
sysctl -w net.core.rmem_max=2097152
|
||||
sysctl -w net.core.wmem_max=2097152
|
||||
sysctl -w net.ipv4.tcp_rmem="4096 87380 2097152"
|
||||
sysctl -w net.ipv4.tcp_wmem="4096 65536 2097152"
|
||||
sysctl -w net.ipv4.tcp_window_scaling=1
|
||||
|
||||
Please note that generated prefix-lists may not fit your router's
|
||||
limitations. For example, JunOS supports only 85,325 prefixes in
|
||||
each prefix-list [4](http://www.juniper.net/techpubs/en_US/junos11.4/topics/reference/configuration-statement/prefix-list-edit-policy-options.html).
|
||||
sysctl -w net.core.rmem_max=2097152
|
||||
|
||||
sysctl -w net.core.wmem_max=2097152
|
||||
|
||||
BUILDING
|
||||
--------
|
||||
sysctl -w net.ipv4.tcp_rmem="4096 87380 2097152"
|
||||
|
||||
sysctl -w net.ipv4.tcp_wmem="4096 65536 2097152"
|
||||
|
||||
# BUILDING
|
||||
|
||||
This project uses autotools. If you are building from the repository,
|
||||
run the following command to prepare the build system:
|
||||
|
||||
./bootstrap
|
||||
./bootstrap
|
||||
|
||||
In order to compile the software, run:
|
||||
|
||||
./configure
|
||||
make
|
||||
make install
|
||||
./configure
|
||||
|
||||
make
|
||||
|
||||
make install
|
||||
|
||||
If you wish to remove the generated build system files from your
|
||||
working tree, run:
|
||||
|
||||
make maintainer-clean
|
||||
make maintainer-clean
|
||||
|
||||
In order to create a distribution archive, run:
|
||||
|
||||
make dist
|
||||
make dist
|
||||
|
||||
SEE ALSO
|
||||
--------
|
||||
# DIAGNOSTICS
|
||||
|
||||
1. [Routing Arbiter](http://www.radb.net/)
|
||||
2. [Cisco documentation](http://www.cisco.com/en/US/prod/collateral/iosswrel/ps6537/ps6554/ps6599/data_sheet_C78-521821.html)
|
||||
for information on Cisco implementation of ASN32.
|
||||
3. [JunOS prefix-lists limitation](http://www.juniper.net/techpubs/en_US/junos11.4/topics/reference/configuration-statement/prefix-list-edit-policy-options.html)
|
||||
When everything is OK,
|
||||
**bgpq4**
|
||||
generates access-list to standard output and exits with status == 0.
|
||||
In case of errors they are printed to stderr and the program exits with
|
||||
non-zero status.
|
||||
|
||||
AUTHORS
|
||||
-------
|
||||
# AUTHORS
|
||||
|
||||
Alexandre Snarskii, Christian David, Claudio Jeker, Job Snijders, Massimiliano Stucchi, Michail Litvak,
|
||||
Peter Schoenmaker, Roelf Wichertjes, and contributions from many others.
|
||||
Alexandre Snarskii, Christian David, Claudio Jeker, Job Snijders,
|
||||
Massimiliano Stucchi, Michail Litvak, Peter Schoenmaker, Roelf Wichertjes,
|
||||
and contributions from many others.
|
||||
|
||||
Project
|
||||
-------
|
||||
# SEE ALSO
|
||||
|
||||
BGPQ4 is maintained by Job Snijders `<job@ntt.net>`.
|
||||
**https://github.com/bgp/bgpq4**
|
||||
BGPQ4 on Github.
|
||||
|
||||
[https://github.com/bgp/bgpq4](https://github.com/bgp/bgpq4)
|
||||
**http://bgpfilterguide.nlnog.net/**
|
||||
NLNOG's BGP Filter Guide.
|
||||
|
||||
**https://tcp0.com/cgi-bin/mailman/listinfo/bgpq4**
|
||||
Users and interested parties can subscribe to the BGPQ4 mailing list bgpq4@tcp0.com
|
||||
|
||||
# PROJECT MAINTAINER
|
||||
|
||||
Job Snijders <job@sobornost.net>
|
||||
|
||||
273
bgpq4.8
273
bgpq4.8
@@ -22,7 +22,7 @@
|
||||
.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||
.\" SUCH DAMAGE.
|
||||
.\"
|
||||
.Dd Dec 1, 2019
|
||||
.Dd December 23, 2020
|
||||
.Dt BGPQ4 8
|
||||
.Os
|
||||
.Sh NAME
|
||||
@@ -36,10 +36,11 @@
|
||||
.Oo
|
||||
.Fl f Ar asn |
|
||||
.Fl F Ar fmt |
|
||||
.Fl G Ar asn
|
||||
.Fl G Ar asn
|
||||
.Fl H Ar asn
|
||||
.Fl t
|
||||
.Oc
|
||||
.Op Fl 2346ABbDdJjNnsXU
|
||||
.Op Fl 46ABbDdJjNnpsXU
|
||||
.Op Fl a Ar asn
|
||||
.Op Fl r Ar len
|
||||
.Op Fl R Ar len
|
||||
@@ -50,19 +51,15 @@
|
||||
.Op EXCEPT OBJECTS
|
||||
.Sh DESCRIPTION
|
||||
The
|
||||
.Nm
|
||||
.Nm
|
||||
utility used to generate configurations (prefix-lists, extended
|
||||
access-lists, policy-statement terms and as-path lists) based on RADB data.
|
||||
access-lists, policy-statement terms and as-path lists) based on IRR data.
|
||||
.Pp
|
||||
The options are as follows:
|
||||
The options are as follows:
|
||||
.Bl -tag -width Ds
|
||||
.It Fl 2
|
||||
accept routes registered for as23456 (transition-as) (default: false)
|
||||
.It Fl 3
|
||||
assume that your device is asn32-safe.
|
||||
.It Fl 4
|
||||
.It Fl 4
|
||||
generate IPv4 prefix/access-lists (default).
|
||||
.It Fl 6
|
||||
.It Fl 6
|
||||
generate IPv6 prefix/access-lists (IPv4 by default).
|
||||
.It Fl A
|
||||
try to aggregate prefix-lists as much as possible (not all output
|
||||
@@ -75,6 +72,8 @@ generate output in OpenBGPD format (default: Cisco)
|
||||
generate output in BIRD format (default: Cisco).
|
||||
.It Fl d
|
||||
enable some debugging output.
|
||||
.It Fl e
|
||||
generate output in Arista EOS format (default: Cisco).
|
||||
.It Fl E
|
||||
generate extended access-list (Cisco), policy-statement term using
|
||||
route-filters (Juniper), [ip|ipv6]-prefix-list (Nokia) or prefix-sets
|
||||
@@ -85,6 +84,10 @@ generate input as-path access-list.
|
||||
generate output in user-defined format.
|
||||
.It Fl G Ar number
|
||||
generate output as-path access-list.
|
||||
.It Fl H Ar number
|
||||
generate output as-list for
|
||||
.Em as-path-origin
|
||||
filter (JunOS 21.3R1+)
|
||||
.It Fl h Ar host[:port]
|
||||
host running IRRD database (default: rr.ntt.net).
|
||||
.It Fl J
|
||||
@@ -92,54 +95,56 @@ generate config for Juniper (default: Cisco).
|
||||
.It Fl j
|
||||
generate output in JSON format (default: Cisco).
|
||||
.It Fl K
|
||||
generate config for Mikrotik (default: Cisco).
|
||||
.It Fl l Ar name
|
||||
generate config for Mikrotik ROSv6 (default: Cisco).
|
||||
.It Fl K7
|
||||
generate config for Mikrotik ROSv7 (default: Cisco).
|
||||
.It Fl l Ar name
|
||||
name of generated entry.
|
||||
.It Fl L Ar limit
|
||||
limit recursion depth when expanding as-sets.
|
||||
.It Fl m Ar len
|
||||
maximum prefix-length of accepted prefixes (default: 32 for IPv4 and
|
||||
maximum prefix-length of accepted prefixes (default: 32 for IPv4 and
|
||||
128 for IPv6).
|
||||
.It Fl M Ar match
|
||||
.It Fl M Ar match
|
||||
extra match conditions for Juniper route-filters.
|
||||
.It Fl n
|
||||
generate config for Nokia SR OS MD-CLI (Cisco IOS by default)
|
||||
.It Fl N
|
||||
generate config for Nokia SR OS classic CLI (Cisco IOS by default).
|
||||
.It Fl p
|
||||
accept routes registered for private ASNs (default: disabled)
|
||||
.It Fl P
|
||||
generate prefix-list (default, backward compatibility).
|
||||
emit prefixes where the origin ASN is in the private ASN range (disabled by default).
|
||||
.It Fl r Ar len
|
||||
allow more specific routes starting with specified masklen too.
|
||||
allow more specific routes starting with specified masklen too.
|
||||
.It Fl R Ar len
|
||||
allow more specific routes up to specified masklen too.
|
||||
allow more specific routes up to specified masklen too.
|
||||
.It Fl s
|
||||
generate sequence numbers in IOS-style prefix-lists.
|
||||
.It Fl S Ar sources
|
||||
use specified sources only (recommended: RADB,RIPE,APNIC).
|
||||
use specified sources only (recommended: RPKI,AFRINIC,APNIC,ARIN,LACNIC,RIPE).
|
||||
.It Fl t
|
||||
generate as-sets for OpenBGPD (OpenBSD 6.4+), BIRD and JSON formats.
|
||||
generate as-sets for OpenBGPd, BIRD and JSON formats.
|
||||
.It Fl T
|
||||
disable pipelining.
|
||||
.It Fl W Ar len
|
||||
generate as-path strings of no more than len items (use 0 for inifinity).
|
||||
disable pipelining (not recommended).
|
||||
.It Fl U
|
||||
generate config for Huawei devices (Cisco IOS by default)
|
||||
.It Fl u
|
||||
generate config for Huawei devices in XPL format (Cisco IOS by default)
|
||||
.It Fl W Ar len
|
||||
generate as-path strings of no more than len items (use 0 for infinity).
|
||||
.It Fl X
|
||||
generate config for Cisco IOS XR devices (plain IOS by default).
|
||||
.It Fl z
|
||||
generate route-filter-lists (JunOS 16.2+).
|
||||
.It Ar OBJECTS
|
||||
.It Ar OBJECTS
|
||||
means networks (in prefix format), autonomous systems, as-sets and route-sets.
|
||||
.It Ar EXCEPT OBJECTS
|
||||
those objects will be excluded from expansion.
|
||||
.El
|
||||
.Sh EXAMPLES
|
||||
Generating named juniper prefix-filter for AS20597:
|
||||
Generating named juniper prefix-filter for AS20597:
|
||||
.nf
|
||||
.RS
|
||||
~>bgpq4 -Jl eltel AS20597
|
||||
.Bd -literal
|
||||
$ bgpq4 -Jl eltel AS20597
|
||||
policy-options {
|
||||
replace:
|
||||
prefix-list eltel {
|
||||
@@ -158,14 +163,14 @@ replace:
|
||||
217.170.80.0/20;
|
||||
}
|
||||
}
|
||||
.RE
|
||||
.Ed
|
||||
.fi
|
||||
.Pp
|
||||
For Cisco we can use aggregation (-A) flag to make this prefix-filter
|
||||
more compact:
|
||||
more compact:
|
||||
.nf
|
||||
.RS
|
||||
~>bgpq4 -Al eltel AS20597
|
||||
.Bd -literal
|
||||
$ bgpq4 -Al eltel AS20597
|
||||
no ip prefix-list eltel
|
||||
ip prefix-list eltel permit 81.9.0.0/20
|
||||
ip prefix-list eltel permit 81.9.32.0/20
|
||||
@@ -178,16 +183,17 @@ ip prefix-list eltel permit 89.112.0.0/18 ge 19 le 19
|
||||
ip prefix-list eltel permit 89.112.4.0/22
|
||||
ip prefix-list eltel permit 89.112.64.0/19
|
||||
ip prefix-list eltel permit 217.170.64.0/19 ge 20 le 20
|
||||
.RE
|
||||
.Ed
|
||||
.fi
|
||||
- you see, prefixes 89.112.0.0/19 and 89.112.32.0/19 now aggregated
|
||||
into single entry 89.112.0.0/18 ge 19 le 19.
|
||||
.Pp
|
||||
Prefixes 89.112.0.0/19 and 89.112.32.0/19 now aggregated
|
||||
into single entry 89.112.0.0/18 ge 19 le 19.
|
||||
.Pp
|
||||
Well, for Juniper we can generate even more interesting policy-options,
|
||||
using -M <extra match conditions>, -R <len> and hierarchical names:
|
||||
using -M <extra match conditions>, -R <len> and hierarchical names:
|
||||
.nf
|
||||
.RS
|
||||
~>bgpq4 -AJEl eltel/specifics -r 29 -R 32 -M "community blackhole" AS20597
|
||||
.Bd -literal
|
||||
$ bgpq4 -AJEl eltel/specifics -r 29 -R 32 -M "community blackhole" AS20597
|
||||
policy-options {
|
||||
policy-statement eltel {
|
||||
term specifics {
|
||||
@@ -207,26 +213,26 @@ replace:
|
||||
}
|
||||
}
|
||||
}
|
||||
.RE
|
||||
.Ed
|
||||
.fi
|
||||
generated policy-option term now allows all specifics with prefix-length
|
||||
between /29 and /32 for eltel networks if they match with special community
|
||||
between /29 and /32 for eltel networks if they match with special community
|
||||
blackhole (defined elsewhere in configuration).
|
||||
.Pp
|
||||
Of course, this version supports IPv6 (-6):
|
||||
Of course, this version supports IPv6 (-6):
|
||||
.nf
|
||||
.RS
|
||||
~>bgpq4 -6l as-retn-6 AS-RETN6
|
||||
.Bd -literal
|
||||
$ bgpq4 -6l as-retn-6 AS-RETN6
|
||||
no ipv6 prefix-list as-retn-6
|
||||
ipv6 prefix-list as-retn-6 permit 2001:7fb:fe00::/48
|
||||
ipv6 prefix-list as-retn-6 permit 2001:7fb:fe01::/48
|
||||
[....]
|
||||
.RE
|
||||
.Ed
|
||||
.fi
|
||||
and support for ASN 32 is also here
|
||||
and assumes your device supports 32-bit ASNs
|
||||
.nf
|
||||
.RS
|
||||
~>bgpq4 -J3f 112 AS-SPACENET
|
||||
.Bd -literal
|
||||
$ bgpq4 -Jf 112 AS-SPACENET
|
||||
policy-options {
|
||||
replace:
|
||||
as-path-group NN {
|
||||
@@ -236,64 +242,147 @@ replace:
|
||||
as-path a3 "^112(.)*(35052|41720|43628|44450|196611)$";
|
||||
}
|
||||
}
|
||||
.RE
|
||||
.Ed
|
||||
.fi
|
||||
see AS196611 in the end of the list ? That's AS3.3 in 'asplain' notation.
|
||||
.Pp
|
||||
For non-ASN32 capable routers you should not use switch -3,
|
||||
and the result will be next:
|
||||
.nf
|
||||
.RS
|
||||
~>bgpq4 -f 112 AS-SPACENET
|
||||
no ip as-path access-list NN
|
||||
ip as-path access-list NN permit ^112(_112)*$
|
||||
ip as-path access-list NN permit ^112(_[0-9]+)*_(1898|5539|8495|8763)$
|
||||
ip as-path access-list NN permit ^112(_[0-9]+)*_(8878|12136|12931|15909)$
|
||||
ip as-path access-list NN permit ^112(_[0-9]+)*_(21358|23456|23600|24151)$
|
||||
ip as-path access-list NN permit ^112(_[0-9]+)*_(25152|31529|34127|34906)$
|
||||
ip as-path access-list NN permit ^112(_[0-9]+)*_(35052|41720|43628|44450)$
|
||||
.RE
|
||||
.fi
|
||||
.Pp
|
||||
AS196611 is no more in the list, however, AS23456 (transition AS)
|
||||
would be added to list if it were not present.
|
||||
see `AS196611` in the end of the list ? That's a 32-bit ASN.
|
||||
.Sh USER-DEFINED FORMAT
|
||||
If you want to generate configuration not for routers, but for some
|
||||
other programs/systems, you may use user-defined formatting, like in
|
||||
example below:
|
||||
.nf
|
||||
.RS
|
||||
user@host:~>bgpq4 -F "ipfw add pass all from %n/%l to any\\n" as3254
|
||||
.Bd -literal
|
||||
$ bgpq4 -F "ipfw add pass all from %n/%l to any\\n" as3254
|
||||
ipfw add pass all from 62.244.0.0/18 to any
|
||||
ipfw add pass all from 91.219.29.0/24 to any
|
||||
ipfw add pass all from 91.219.30.0/24 to any
|
||||
ipfw add pass all from 193.193.192.0/19 to any
|
||||
.RE
|
||||
.Ed
|
||||
.fi
|
||||
.Pp
|
||||
Recognized format characters: %n - network, %l - mask length,
|
||||
%N - object name, %m - object mask and %i - inversed mask.
|
||||
Recognized escape characters: \\n - new line, \\t - tabulation.
|
||||
Please note that no new lines inserted automatically after each sentence,
|
||||
you have to add them into format string manually, elsewhere output will
|
||||
be in one line (sometimes it makes sense):
|
||||
Recognized format sequences are:
|
||||
.Pp
|
||||
.Bl -tag -width Ds -offset indent -compact
|
||||
.It Cm %n
|
||||
network
|
||||
.It Cm %l
|
||||
mask length
|
||||
.It Cm %a
|
||||
aggregate low mask length
|
||||
.It Cm \&%A
|
||||
aggregate high mask length
|
||||
.It Cm \&%N
|
||||
object name
|
||||
.It Cm %m
|
||||
object mask
|
||||
.It Cm %i
|
||||
inversed mask
|
||||
.It Cm \en
|
||||
new line
|
||||
.It Cm \et
|
||||
tabulation
|
||||
.El
|
||||
.Pp
|
||||
Please note that no new lines are inserted automatically after each sentence.
|
||||
You have to add them into format string manually, otherwise the output will
|
||||
be in one single line (sometimes it makes sense):
|
||||
.nf
|
||||
.RS
|
||||
user@host:~>bgpq4 -6F "%n/%l; " as-eltel
|
||||
.Bd -literal
|
||||
$ bgpq4 -6F "%n/%l; " as-eltel
|
||||
2001:1b00::/32; 2620:4f:8000::/48; 2a04:bac0::/29; 2a05:3a80::/48;
|
||||
.RE
|
||||
.Ed
|
||||
.fi
|
||||
.Sh NOTES ON SOURCES
|
||||
By default
|
||||
.Em bgpq4
|
||||
trusts data from all the databases mirrored into NTT's IRR service.
|
||||
Unfortunately, not all these databases are equal in how much we can
|
||||
trust their data.
|
||||
RIR maintained databases (AFRINIC, ARIN, APNIC, LACNIC and RIPE)
|
||||
shall be trusted more than the others because they have the
|
||||
knowledge about who the rightful holders of resources are, while
|
||||
other databases lack this knowledge and can (and, actually do) contain
|
||||
stale data: no one but the RIRs care to remove outdated route-objects
|
||||
when address space is de-allocated or transferred.
|
||||
In order to keep their filters both compact and actual,
|
||||
.Em bgpq4 users
|
||||
are encouraged to use '-S' flag to limit database sources to only
|
||||
the ones they trust.
|
||||
.Pp
|
||||
General recommendations:
|
||||
.Pp
|
||||
Use a minimal set of RIR databases (only those in which you and your
|
||||
customers have registered route-objects).
|
||||
.Pp
|
||||
Avoid using ARIN-NONAUTH and RIPE-NONAUTH as trusted sources: these records
|
||||
were created in the database, but for address space allocated to different RIRs,
|
||||
so the NONAUTH databases have no chance to confirm validity of the route
|
||||
objects they contain.
|
||||
.Bd -literal
|
||||
$ bgpq4 -S RIPE,RADB as-space
|
||||
no ip prefix-list NN
|
||||
ip prefix-list NN permit 195.190.32.0/19
|
||||
|
||||
$ bgpq4 -S RADB,RIPE as-space
|
||||
no ip prefix-list NN
|
||||
ip prefix-list NN permit 45.4.4.0/22
|
||||
ip prefix-list NN permit 45.4.132.0/22
|
||||
ip prefix-list NN permit 45.6.128.0/22
|
||||
ip prefix-list NN permit 45.65.184.0/22
|
||||
[...]
|
||||
.Ed
|
||||
.Sh PERFORMANCE
|
||||
To improve `bgpq4` performance when expanding extra-large AS-SETs you
|
||||
shall tune OS settings to enlarge TCP send buffer.
|
||||
.Pp
|
||||
FreeBSD can be tuned in the following way:
|
||||
.Pp
|
||||
.Dl sysctl -w net.inet.tcp.sendbuf_max=2097152
|
||||
.Pp
|
||||
Linux can be tuned in the following way:
|
||||
.Pp
|
||||
.Dl sysctl -w net.ipv4.tcp_window_scaling=1
|
||||
.Dl sysctl -w net.core.rmem_max=2097152
|
||||
.Dl sysctl -w net.core.wmem_max=2097152
|
||||
.Dl sysctl -w net.ipv4.tcp_rmem="4096 87380 2097152"
|
||||
.Dl sysctl -w net.ipv4.tcp_wmem="4096 65536 2097152"
|
||||
.Sh BUILDING
|
||||
This project uses autotools. If you are building from the repository,
|
||||
run the following command to prepare the build system:
|
||||
.Pp
|
||||
.Dl ./bootstrap
|
||||
.Pp
|
||||
In order to compile the software, run:
|
||||
.Pp
|
||||
.Dl ./configure
|
||||
.Dl make
|
||||
.Dl make install
|
||||
.Pp
|
||||
If you wish to remove the generated build system files from your
|
||||
working tree, run:
|
||||
.Pp
|
||||
.Dl make maintainer-clean
|
||||
.Pp
|
||||
In order to create a distribution archive, run:
|
||||
.Pp
|
||||
.Dl make dist
|
||||
.Sh DIAGNOSTICS
|
||||
When everything is OK,
|
||||
When everything is OK,
|
||||
.Nm
|
||||
generates access-list to standard output and exits with status == 0.
|
||||
In case of errors they are printed to stderr and program exits with
|
||||
non-zero status.
|
||||
.Sh SEE ALSO
|
||||
.Sy http://www.radb.net/
|
||||
Routing Arbiter project
|
||||
.Sy http://www.cisco.com/en/US/docs/ios/12_0s/release/ntes/120SNEWF.html#wp3521658
|
||||
for information on Cisco implementation of ASN32.
|
||||
generates access-list to standard output and exits with status == 0.
|
||||
In case of errors they are printed to stderr and program exits with
|
||||
non-zero status.
|
||||
.Sh AUTHORS
|
||||
.An Job Snijders Aq job@ntt.net
|
||||
.An Alexandre Snarskii Aq snar@snar.spb.ru
|
||||
Alexandre Snarskii, Christian David, Claudio Jeker, Job Snijders,
|
||||
Massimiliano Stucchi, Michail Litvak, Peter Schoenmaker, Roelf Wichertjes,
|
||||
and contributions from many others.
|
||||
.Sh SEE ALSO
|
||||
.Sy https://github.com/bgp/bgpq4
|
||||
BGPQ4 on Github.
|
||||
.Pp
|
||||
.Sy http://bgpfilterguide.nlnog.net/
|
||||
NLNOG's BGP Filter Guide.
|
||||
.Pp
|
||||
.Sy https://tcp0.com/cgi-bin/mailman/listinfo/bgpq4
|
||||
Users and interested parties can subscribe to the BGPQ4 mailing list bgpq4@tcp0.com
|
||||
.Sh PROJECT MAINTAINER
|
||||
.An Job Snijders Aq job@sobornost.net
|
||||
|
||||
645
bgpq4.c
645
bgpq4.c
@@ -1,645 +0,0 @@
|
||||
#if HAVE_CONFIG_H
|
||||
#include <config.h>
|
||||
#endif
|
||||
|
||||
#include <sys/types.h>
|
||||
#include <sys/socket.h>
|
||||
#include <netinet/in.h>
|
||||
#include <arpa/inet.h>
|
||||
|
||||
#include <ctype.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <strings.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include "bgpq4.h"
|
||||
#include "sx_report.h"
|
||||
#include "expander_freeall.h"
|
||||
|
||||
extern int debug_expander;
|
||||
extern int debug_aggregation;
|
||||
extern int pipelining;
|
||||
extern int expand_as23456;
|
||||
extern int expand_special_asn;
|
||||
|
||||
int
|
||||
usage(int ecode)
|
||||
{
|
||||
printf("\nUsage: bgpq4 [-h host[:port]] [-S sources] [-P|E|G <num>|f <num>|t]"
|
||||
" [-2346ABbdJjNnwXz] [-R len] <OBJECTS>...\n");
|
||||
printf(" -2 : allow routes belonging to as23456 (transition-as) "
|
||||
"(default: false)\n");
|
||||
printf(" -3 : assume that your device is asn32-safe\n");
|
||||
printf(" -4 : generate IPv4 prefix-lists (default)\n");
|
||||
printf(" -6 : generate IPv6 prefix-lists (IPv4 by default)\n");
|
||||
printf(" -A : try to aggregate prefix-lists/route-filters\n");
|
||||
printf(" -B : generate OpenBGPD output (Cisco IOS by default)\n");
|
||||
printf(" -b : generate BIRD output (Cisco IOS by default)\n");
|
||||
printf(" -d : generate some debugging output\n");
|
||||
printf(" -E : generate extended access-list(Cisco), "
|
||||
"route-filter(Juniper)\n"
|
||||
" [ip|ipv6]-prefix-list (Nokia) or prefix-set (OpenBGPD)"
|
||||
"\n");
|
||||
printf(" -F fmt : generate output in user-defined format\n");
|
||||
printf(" -f number : generate input as-path access-list\n");
|
||||
printf(" -G number : generate output as-path access-list\n");
|
||||
printf(" -h host : host running IRRD software (rr.ntt.net by "
|
||||
"default)\n"
|
||||
" (use host:port to specify alternate port)\n");
|
||||
printf(" -J : generate config for JunOS (Cisco IOS by default)\n");
|
||||
printf(" -j : generate JSON output (Cisco IOS by default)\n");
|
||||
printf(" -K : generate config for MikroTik RouterOS (Cisco IOS by default)\n");
|
||||
printf(" -M match : extra match conditions for JunOS route-filters\n");
|
||||
printf(" -m len : maximum prefix length (default: 32 for IPv4, "
|
||||
"128 for IPv6)\n");
|
||||
printf(" -L depth : limit recursion depth (default: unlimited)\n"),
|
||||
printf(" -l name : use specified name for generated access/prefix/.."
|
||||
" list\n");
|
||||
printf(" -N : generate config for Nokia SR OS classic CLI "
|
||||
"(Cisco IOS by default)\n");
|
||||
printf(" -n : generate config for Nokia SR OS MD-CLI (Cisco IOS "
|
||||
"by default)\n");
|
||||
printf(" -P : generate prefix-list (default, just for backward"
|
||||
" compatibility)\n");
|
||||
printf(" -R len : allow more specific routes up to specified masklen\n");
|
||||
printf(" -r len : allow more specific routes from masklen specified\n");
|
||||
printf(" -S sources: use only specified sources (recommended:"
|
||||
" RADB,RIPE,APNIC)\n");
|
||||
printf(" -s : generate sequence numbers in prefix-lists (IOS only)\n");
|
||||
printf(" -T : disable pipelining (experimental, faster mode)\n");
|
||||
printf(" -t : generate as-sets for OpenBGPD (OpenBSD 6.4+), BIRD "
|
||||
"and JSON formats\n");
|
||||
printf(" -U : generate config for Huawei (Cisco IOS by default)\n");
|
||||
printf(" -W len : specify max-entries on as-path line (use 0 for "
|
||||
"infinity)\n");
|
||||
printf(" -w : 'validate' AS numbers: accept only ones with "
|
||||
"registered routes\n");
|
||||
printf(" -X : generate config for IOS XR (Cisco IOS by default)\n");
|
||||
printf("\n" PACKAGE_NAME " version: " PACKAGE_VERSION "\n");
|
||||
exit(ecode);
|
||||
};
|
||||
|
||||
void
|
||||
exclusive()
|
||||
{
|
||||
fprintf(stderr,"-E, -f <asnum>, -G <asnum>, -P and -t are mutually "
|
||||
"exclusive\n");
|
||||
exit(1);
|
||||
};
|
||||
|
||||
void
|
||||
vendor_exclusive()
|
||||
{
|
||||
fprintf(stderr, "-b (BIRD), -B (OpenBGPD), -F (formatted), -J (JunOS), "
|
||||
"-j (JSON), -N (Nokia SR OS classic), -n (Nokia SR OS MD-CLI), "
|
||||
"-U (Huawei) and -X (IOS XR) options are mutually exclusive\n");
|
||||
exit(1);
|
||||
};
|
||||
|
||||
int
|
||||
parseasnumber(struct bgpq_expander* expander, char* optarg)
|
||||
{
|
||||
char* eon=NULL;
|
||||
expander->asnumber=strtoul(optarg,&eon,10);
|
||||
if(expander->asnumber<1 || expander->asnumber>(65535ul*65535)) {
|
||||
sx_report(SX_FATAL,"Invalid AS number: %s\n", optarg);
|
||||
exit(1);
|
||||
};
|
||||
if(eon && *eon=='.') {
|
||||
/* -f 3.3, for example */
|
||||
uint32_t loas=strtoul(eon+1,&eon,10);
|
||||
if(expander->asnumber>65535) {
|
||||
/* should prevent incorrect numbers like 65537.1 */
|
||||
sx_report(SX_FATAL,"Invalid AS number: %s\n", optarg);
|
||||
exit(1);
|
||||
};
|
||||
if(loas<1 || loas>65535) {
|
||||
sx_report(SX_FATAL,"Invalid AS number: %s\n", optarg);
|
||||
exit(1);
|
||||
};
|
||||
if(eon && *eon) {
|
||||
sx_report(SX_FATAL,"Invalid symbol in AS number: %c (%s)\n",
|
||||
*eon, optarg);
|
||||
exit(1);
|
||||
};
|
||||
expander->asnumber=(expander->asnumber<<16)+loas;
|
||||
} else if(eon && *eon) {
|
||||
sx_report(SX_FATAL,"Invalid symbol in AS number: %c (%s)\n",
|
||||
*eon, optarg);
|
||||
exit(1);
|
||||
};
|
||||
return 0;
|
||||
};
|
||||
|
||||
int
|
||||
main(int argc, char* argv[])
|
||||
{
|
||||
int c;
|
||||
struct bgpq_expander expander;
|
||||
int af=AF_INET, selectedipv4 = 0, exceptmode = 0;
|
||||
int widthSet=0, aggregate=0, refine=0, refineLow=0;
|
||||
unsigned long maxlen=0;
|
||||
|
||||
bgpq_expander_init(&expander,af);
|
||||
if (getenv("IRRD_SOURCES"))
|
||||
expander.sources=getenv("IRRD_SOURCES");
|
||||
|
||||
while((c=getopt(argc,argv,"2346a:AbBdDEF:S:jJKf:l:L:m:M:NnW:Ppr:R:G:tTh:UwXsz"))
|
||||
!=EOF) {
|
||||
switch(c) {
|
||||
case '2':
|
||||
expand_as23456=1;
|
||||
break;
|
||||
case '3':
|
||||
expander.asn32=1;
|
||||
break;
|
||||
case '4':
|
||||
/* do nothing, expander already configured for IPv4 */
|
||||
if (expander.family == AF_INET6) {
|
||||
sx_report(SX_FATAL, "-4 and -6 are mutually exclusive\n");
|
||||
exit(1);
|
||||
};
|
||||
selectedipv4=1;
|
||||
break;
|
||||
case '6':
|
||||
if (selectedipv4) {
|
||||
sx_report(SX_FATAL, "-4 and -6 are mutually exclusive\n");
|
||||
exit(1);
|
||||
};
|
||||
af=AF_INET6;
|
||||
expander.family=AF_INET6;
|
||||
expander.tree->family=AF_INET6;
|
||||
break;
|
||||
case 'a':
|
||||
parseasnumber(&expander,optarg);
|
||||
break;
|
||||
case 'A':
|
||||
if(aggregate) debug_aggregation++;
|
||||
aggregate=1;
|
||||
break;
|
||||
case 'b':
|
||||
if(expander.vendor) vendor_exclusive();
|
||||
expander.vendor=V_BIRD;
|
||||
break;
|
||||
case 'B':
|
||||
if(expander.vendor) vendor_exclusive();
|
||||
expander.vendor=V_OPENBGPD;
|
||||
expander.asn32=1;
|
||||
break;
|
||||
case 'd': debug_expander++;
|
||||
break;
|
||||
case 'E': if(expander.generation) exclusive();
|
||||
expander.generation=T_EACL;
|
||||
break;
|
||||
case 'F': if(expander.vendor) exclusive();
|
||||
expander.vendor=V_FORMAT;
|
||||
expander.format=optarg;
|
||||
break;
|
||||
case 'f':
|
||||
if(expander.generation) exclusive();
|
||||
expander.generation=T_ASPATH;
|
||||
parseasnumber(&expander,optarg);
|
||||
break;
|
||||
case 'G':
|
||||
if(expander.generation) exclusive();
|
||||
expander.generation=T_OASPATH;
|
||||
parseasnumber(&expander,optarg);
|
||||
break;
|
||||
case 'h': {
|
||||
char* d=strchr(optarg, ':');
|
||||
expander.server=optarg;
|
||||
if(d) {
|
||||
*d=0;
|
||||
expander.port=d+1;
|
||||
};
|
||||
break;
|
||||
};
|
||||
case 'J': if(expander.vendor) vendor_exclusive();
|
||||
expander.vendor=V_JUNIPER;
|
||||
break;
|
||||
case 'j': if(expander.vendor) vendor_exclusive();
|
||||
expander.vendor=V_JSON;
|
||||
break;
|
||||
case 'K': if(expander.vendor) vendor_exclusive();
|
||||
expander.vendor=V_MIKROTIK;
|
||||
break;
|
||||
case 'p':
|
||||
expand_special_asn=1;
|
||||
break;
|
||||
case 'P':
|
||||
if(expander.generation) exclusive();
|
||||
expander.generation=T_PREFIXLIST;
|
||||
break;
|
||||
case 'r':
|
||||
refineLow=strtoul(optarg,NULL,10);
|
||||
if(!refineLow) {
|
||||
sx_report(SX_FATAL,"Invalid refineLow value: %s\n", optarg);
|
||||
exit(1);
|
||||
};
|
||||
break;
|
||||
case 'R':
|
||||
refine=strtoul(optarg,NULL,10);
|
||||
if(!refine) {
|
||||
sx_report(SX_FATAL,"Invalid refine length: %s\n", optarg);
|
||||
exit(1);
|
||||
};
|
||||
break;
|
||||
case 'l': expander.name=optarg;
|
||||
break;
|
||||
case 'L': expander.maxdepth=strtol(optarg, NULL, 10);
|
||||
if (expander.maxdepth < 1) {
|
||||
sx_report(SX_FATAL, "Invalid maximum recursion (-L): %s\n",
|
||||
optarg);
|
||||
exit(1);
|
||||
};
|
||||
break;
|
||||
case 'm': maxlen=strtoul(optarg, NULL, 10);
|
||||
if (!maxlen) {
|
||||
sx_report(SX_FATAL, "Invalid maxlen (-m): %s\n", optarg);
|
||||
exit(1);
|
||||
};
|
||||
break;
|
||||
case 'M': {
|
||||
char* c, *d;
|
||||
expander.match=strdup(optarg);
|
||||
c=d=expander.match;
|
||||
while(*c) {
|
||||
if(*c=='\\') {
|
||||
if(*(c+1)=='n') {
|
||||
*d='\n';
|
||||
d++;
|
||||
c+=2;
|
||||
} else if(*(c+1)=='r') {
|
||||
*d='\r';
|
||||
d++;
|
||||
c+=2;
|
||||
} else if(*(c+1)=='t') {
|
||||
*d='\t';
|
||||
d++;
|
||||
c+=2;
|
||||
} else if(*(c+1)=='\\') {
|
||||
*d='\\';
|
||||
d++;
|
||||
c+=2;
|
||||
} else {
|
||||
sx_report(SX_FATAL, "Unsupported escape \%c (0x%2.2x) "
|
||||
"in '%s'\n", isprint(*c)?*c:20, *c, optarg);
|
||||
exit(1);
|
||||
};
|
||||
} else {
|
||||
if(c!=d) {
|
||||
*d=*c;
|
||||
};
|
||||
d++;
|
||||
c++;
|
||||
};
|
||||
};
|
||||
*d=0;
|
||||
};
|
||||
break;
|
||||
case 'N': if(expander.vendor) vendor_exclusive();
|
||||
expander.vendor=V_NOKIA;
|
||||
break;
|
||||
case 'n': if(expander.vendor) vendor_exclusive();
|
||||
expander.vendor=V_NOKIA_MD;
|
||||
break;
|
||||
case 't':
|
||||
if(expander.generation) exclusive();
|
||||
expander.generation=T_ASSET;
|
||||
break;
|
||||
case 'T': pipelining=0;
|
||||
break;
|
||||
case 's': expander.sequence=1;
|
||||
break;
|
||||
case 'S': expander.sources=optarg;
|
||||
break;
|
||||
case 'U':
|
||||
if(expander.vendor) exclusive();
|
||||
expander.vendor=V_HUAWEI;
|
||||
break;
|
||||
case 'W': expander.aswidth=atoi(optarg);
|
||||
if(expander.aswidth<0) {
|
||||
sx_report(SX_FATAL,"Invalid as-width: %s\n", optarg);
|
||||
exit(1);
|
||||
};
|
||||
widthSet=1;
|
||||
break;
|
||||
case 'w': expander.validate_asns=1;
|
||||
break;
|
||||
case 'X': if(expander.vendor) vendor_exclusive();
|
||||
expander.vendor=V_CISCO_XR;
|
||||
break;
|
||||
case 'z':
|
||||
if(expander.generation) exclusive();
|
||||
expander.generation=T_ROUTE_FILTER_LIST;
|
||||
break;
|
||||
default : usage(1);
|
||||
};
|
||||
};
|
||||
|
||||
argc-=optind;
|
||||
argv+=optind;
|
||||
|
||||
if(!widthSet) {
|
||||
if(expander.generation==T_ASPATH) {
|
||||
if(expander.vendor==V_CISCO) {
|
||||
expander.aswidth=4;
|
||||
} else if(expander.vendor==V_CISCO_XR) {
|
||||
expander.aswidth=6;
|
||||
} else if(expander.vendor==V_JUNIPER) {
|
||||
expander.aswidth=8;
|
||||
} else if(expander.vendor==V_MIKROTIK) {
|
||||
expander.aswidth=4;
|
||||
} else if(expander.vendor==V_BIRD) {
|
||||
expander.aswidth=10;
|
||||
} else if(expander.vendor==V_NOKIA || expander.vendor==V_NOKIA_MD) {
|
||||
expander.aswidth=8;
|
||||
};
|
||||
} else if(expander.generation==T_OASPATH) {
|
||||
if(expander.vendor==V_CISCO) {
|
||||
expander.aswidth=5;
|
||||
} else if(expander.vendor==V_CISCO_XR) {
|
||||
expander.aswidth=7;
|
||||
} else if(expander.vendor==V_JUNIPER) {
|
||||
expander.aswidth=8;
|
||||
} else if(expander.vendor==V_NOKIA || expander.vendor==V_NOKIA_MD) {
|
||||
expander.aswidth=8;
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
if(!expander.generation) {
|
||||
expander.generation=T_PREFIXLIST;
|
||||
};
|
||||
|
||||
if(expander.vendor==V_CISCO_XR && expander.generation!=T_PREFIXLIST &&
|
||||
expander.generation!=T_ASPATH && expander.generation!=T_OASPATH) {
|
||||
sx_report(SX_FATAL, "Sorry, only prefix-sets and as-paths "
|
||||
"supported for IOS XR\n");
|
||||
};
|
||||
if(expander.vendor==V_BIRD && expander.generation!=T_PREFIXLIST &&
|
||||
expander.generation!=T_ASPATH && expander.generation!=T_ASSET) {
|
||||
sx_report(SX_FATAL, "Sorry, only prefix-lists and as-paths/as-sets "
|
||||
"supported for BIRD output\n");
|
||||
};
|
||||
if(expander.vendor==V_JSON && expander.generation!=T_PREFIXLIST &&
|
||||
expander.generation!=T_ASPATH && expander.generation!=T_ASSET) {
|
||||
sx_report(SX_FATAL, "Sorry, only prefix-lists and as-paths/as-sets "
|
||||
"supported for JSON output\n");
|
||||
};
|
||||
if(expander.vendor==V_FORMAT && expander.generation!=T_PREFIXLIST)
|
||||
sx_report(SX_FATAL, "Sorry, only prefix-lists supported in formatted "
|
||||
"output\n");
|
||||
if(expander.vendor==V_FORMAT && (refine || refineLow)) {
|
||||
sx_report(SX_FATAL, "Sorry, formatted output (-F <fmt>) in not "
|
||||
"compatible with -R/-r options\n");
|
||||
exit(1);
|
||||
};
|
||||
if(expander.vendor==V_HUAWEI && expander.generation!=T_ASPATH &&
|
||||
expander.generation!=T_OASPATH && expander.generation != T_PREFIXLIST)
|
||||
sx_report(SX_FATAL, "Sorry, only as-paths and prefix-lists supported "
|
||||
"for Huawei output\n");
|
||||
|
||||
if(expander.generation==T_ROUTE_FILTER_LIST && expander.vendor!=V_JUNIPER) {
|
||||
sx_report(SX_FATAL, "Route-filter-lists (-z) supported for Juniper (-J)"
|
||||
" output only\n");
|
||||
};
|
||||
if(expander.generation==T_ASSET && expander.vendor!=V_JSON &&
|
||||
expander.vendor!=V_OPENBGPD && expander.vendor!=V_BIRD) {
|
||||
sx_report(SX_FATAL, "As-Sets (-t) supported for JSON (-j), OpenBGPD "
|
||||
"(-B) and BIRD (-b) output only\n");
|
||||
};
|
||||
|
||||
if(!expander.asn32 && expander.asnumber>65535) {
|
||||
expander.asnumber=23456;
|
||||
};
|
||||
|
||||
if(aggregate && expander.vendor==V_JUNIPER &&
|
||||
expander.generation==T_PREFIXLIST) {
|
||||
sx_report(SX_FATAL, "Sorry, aggregation (-A) does not work in"
|
||||
" Juniper prefix-lists\nYou can try route-filters (-E) "
|
||||
"or route-filter-lists (-z) instead of prefix-lists "
|
||||
"(-P, default)\n");
|
||||
exit(1);
|
||||
};
|
||||
|
||||
if(aggregate && expander.vendor==V_FORMAT) {
|
||||
sx_report(SX_FATAL, "Sorry, aggregation (-A) is not compatible with "
|
||||
"formatted output (-F <fmt>)\n");
|
||||
exit(1);
|
||||
};
|
||||
|
||||
if(aggregate && (expander.vendor==V_NOKIA_MD || expander.vendor==V_NOKIA) &&
|
||||
expander.generation!=T_PREFIXLIST) {
|
||||
sx_report(SX_FATAL, "Sorry, aggregation (-A) is not supported with "
|
||||
"ip-prefix-lists (-E) on Nokia. You can try prefix-lists (-P) "
|
||||
"instead\n");
|
||||
exit(1);
|
||||
};
|
||||
if(refine && (expander.vendor==V_NOKIA_MD || expander.vendor==V_NOKIA) &&
|
||||
expander.generation!=T_PREFIXLIST) {
|
||||
sx_report(SX_FATAL, "Sorry, more-specifics (-R) is not supported with "
|
||||
"ip-prefix-lists (-E) on Nokia. You can try prefix-lists (-P) "
|
||||
"instead\n");
|
||||
exit(1);
|
||||
};
|
||||
if(refineLow && (expander.vendor==V_NOKIA_MD || expander.vendor==V_NOKIA) &&
|
||||
expander.generation!=T_PREFIXLIST) {
|
||||
sx_report(SX_FATAL, "Sorry, more-specifics (-r) is not supported with "
|
||||
"ip-prefix-lists (-E) on Nokia. You can try prefix-lists (-P) "
|
||||
"instead\n");
|
||||
exit(1);
|
||||
};
|
||||
|
||||
if(aggregate && expander.generation<T_PREFIXLIST) {
|
||||
sx_report(SX_FATAL, "Sorry, aggregation (-A) used only for prefix-"
|
||||
"lists, extended access-lists and route-filters\n");
|
||||
exit(1);
|
||||
};
|
||||
|
||||
if (expander.sequence && expander.vendor!=V_CISCO) {
|
||||
sx_report(SX_FATAL, "Sorry, prefix-lists sequencing (-s) supported"
|
||||
" only for IOS\n");
|
||||
exit(1);
|
||||
};
|
||||
|
||||
if (expander.sequence && expander.generation<T_PREFIXLIST) {
|
||||
sx_report(SX_FATAL, "Sorry, prefix-lists sequencing (-s) can't be "
|
||||
" used for non prefix-list\n");
|
||||
exit(1);
|
||||
};
|
||||
|
||||
if(refineLow && !refine) {
|
||||
if(expander.family == AF_INET)
|
||||
refine = 32;
|
||||
else
|
||||
refine = 128;
|
||||
};
|
||||
|
||||
if (refineLow && refineLow > refine) {
|
||||
sx_report(SX_FATAL, "Incompatible values for -r %u and -R %u\n",
|
||||
refineLow, refine);
|
||||
};
|
||||
|
||||
if(refine || refineLow) {
|
||||
if(expander.family==AF_INET6 && refine>128) {
|
||||
sx_report(SX_FATAL, "Invalid value for refine(-R): %u (1-128 for"
|
||||
" IPv6)\n", refine);
|
||||
} else if(expander.family==AF_INET6 && refineLow>128) {
|
||||
sx_report(SX_FATAL, "Invalid value for refineLow(-r): %u (1-128 for"
|
||||
" IPv6)\n", refineLow);
|
||||
} else if(expander.family==AF_INET && refine>32) {
|
||||
sx_report(SX_FATAL, "Invalid value for refine(-R): %u (1-32 for"
|
||||
" IPv4)\n", refine);
|
||||
} else if(expander.family==AF_INET && refineLow>32) {
|
||||
sx_report(SX_FATAL, "Invalid value for refineLow(-r): %u (1-32 for"
|
||||
" IPv4)\n", refineLow);
|
||||
};
|
||||
|
||||
if(expander.vendor==V_JUNIPER && expander.generation==T_PREFIXLIST) {
|
||||
if(refine) {
|
||||
sx_report(SX_FATAL, "Sorry, more-specific filters (-R %u) "
|
||||
"is not supported for Juniper prefix-lists.\n"
|
||||
"Use router-filters (-E) or route-filter-lists (-z) "
|
||||
"instead\n", refine);
|
||||
} else {
|
||||
sx_report(SX_FATAL, "Sorry, more-specific filters (-r %u) "
|
||||
"is not supported for Juniper prefix-lists.\n"
|
||||
"Use route-filters (-E) or route-filter-lists (-z) "
|
||||
"instead\n", refineLow);
|
||||
};
|
||||
};
|
||||
|
||||
if(expander.generation<T_PREFIXLIST) {
|
||||
if(refine) {
|
||||
sx_report(SX_FATAL, "Sorry, more-specific filter (-R %u) "
|
||||
"supported only with prefix-list generation\n", refine);
|
||||
} else {
|
||||
sx_report(SX_FATAL, "Sorry, more-specific filter (-r %u) "
|
||||
"supported only with prefix-list generation\n", refineLow);
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
if(maxlen) {
|
||||
if((expander.family==AF_INET6 && maxlen>128) ||
|
||||
(expander.family==AF_INET && maxlen>32)) {
|
||||
sx_report(SX_FATAL, "Invalid value for max-prefixlen: %lu (1-128 "
|
||||
"for IPv6, 1-32 for IPv4)\n", maxlen);
|
||||
exit(1);
|
||||
} else if((expander.family==AF_INET6 && maxlen<128) ||
|
||||
(expander.family==AF_INET && maxlen<32)) {
|
||||
/* inet6/128 and inet4/32 does not make sense - all routes will
|
||||
* be accepted, so save some CPU cycles :) */
|
||||
expander.maxlen = maxlen;
|
||||
};
|
||||
} else if (expander.family==AF_INET) {
|
||||
expander.maxlen = 32;
|
||||
} else if (expander.family==AF_INET6) {
|
||||
expander.maxlen = 128;
|
||||
};
|
||||
|
||||
if(expander.generation==T_EACL && expander.vendor==V_CISCO &&
|
||||
expander.family==AF_INET6) {
|
||||
sx_report(SX_FATAL,"Sorry, ipv6 access-lists not supported for Cisco"
|
||||
" yet.\n");
|
||||
};
|
||||
|
||||
if(expander.match != NULL && (expander.vendor != V_JUNIPER ||
|
||||
expander.generation != T_EACL)) {
|
||||
sx_report(SX_FATAL, "Sorry, extra match conditions (-M) can be used "
|
||||
"only with Juniper route-filters\n");
|
||||
};
|
||||
|
||||
if((expander.generation==T_ASPATH || expander.generation==T_OASPATH) &&
|
||||
af != AF_INET && !expander.validate_asns) {
|
||||
sx_report(SX_FATAL, "Sorry, -6 makes no sense with as-path (-f/-G) "
|
||||
"generation\n");
|
||||
};
|
||||
|
||||
if (expander.validate_asns && expander.generation != T_ASPATH &&
|
||||
expander.generation != T_OASPATH) {
|
||||
sx_report(SX_FATAL, "Sorry, -w makes sense only for as-path (-f/-G) "
|
||||
"generation\n");
|
||||
};
|
||||
|
||||
if(!argv[0])
|
||||
usage(1);
|
||||
|
||||
while(argv[0]) {
|
||||
if(!strcmp(argv[0], "EXCEPT")) {
|
||||
exceptmode = 1;
|
||||
} else if (exceptmode) {
|
||||
bgpq_expander_add_stop(&expander,argv[0]);
|
||||
} else if(!strncasecmp(argv[0],"AS-",3)) {
|
||||
bgpq_expander_add_asset(&expander,argv[0]);
|
||||
} else if(!strncasecmp(argv[0],"RS-",3)) {
|
||||
bgpq_expander_add_rset(&expander,argv[0]);
|
||||
} else if(!strncasecmp(argv[0],"AS",2)) {
|
||||
char* c;
|
||||
if((c=strchr(argv[0],':'))) {
|
||||
if(!strncasecmp(c+1,"AS-",3)) {
|
||||
bgpq_expander_add_asset(&expander,argv[0]);
|
||||
} else if(!strncasecmp(c+1,"RS-",3)) {
|
||||
bgpq_expander_add_rset(&expander,argv[0]);
|
||||
} else {
|
||||
SX_DEBUG(debug_expander,"Unknown sub-as object %s\n",
|
||||
argv[0]);
|
||||
};
|
||||
} else {
|
||||
bgpq_expander_add_as(&expander,argv[0]);
|
||||
};
|
||||
} else {
|
||||
char* c = strchr(argv[0], '^');
|
||||
if (!c && !bgpq_expander_add_prefix(&expander,argv[0])) {
|
||||
sx_report(SX_ERROR, "Unable to add prefix %s (bad prefix or "
|
||||
"address-family)\n", argv[0]);
|
||||
exit(1);
|
||||
} else if (c && !bgpq_expander_add_prefix_range(&expander,argv[0])){
|
||||
sx_report(SX_ERROR, "Unable to add prefix-range %s (bad range "
|
||||
"or address-family)\n", argv[0]);
|
||||
exit(1);
|
||||
};
|
||||
};
|
||||
argv++;
|
||||
argc--;
|
||||
};
|
||||
|
||||
if(!bgpq_expand(&expander)) {
|
||||
exit(1);
|
||||
};
|
||||
|
||||
if(refine)
|
||||
sx_radix_tree_refine(expander.tree,refine);
|
||||
|
||||
if(refineLow)
|
||||
sx_radix_tree_refineLow(expander.tree, refineLow);
|
||||
|
||||
if(aggregate)
|
||||
sx_radix_tree_aggregate(expander.tree);
|
||||
|
||||
switch(expander.generation) {
|
||||
case T_NONE: sx_report(SX_FATAL,"Unreachable point... call snar\n");
|
||||
exit(1);
|
||||
case T_ASPATH: bgpq4_print_aspath(stdout,&expander);
|
||||
break;
|
||||
case T_OASPATH: bgpq4_print_oaspath(stdout,&expander);
|
||||
break;
|
||||
case T_ASSET: bgpq4_print_asset(stdout,&expander);
|
||||
break;
|
||||
case T_PREFIXLIST: bgpq4_print_prefixlist(stdout,&expander);
|
||||
break;
|
||||
case T_EACL: bgpq4_print_eacl(stdout,&expander);
|
||||
break;
|
||||
case T_ROUTE_FILTER_LIST:
|
||||
bgpq4_print_route_filter_list(stdout, &expander);
|
||||
break;
|
||||
};
|
||||
|
||||
expander_freeall(&expander);
|
||||
|
||||
return 0;
|
||||
};
|
||||
|
||||
98
bgpq4.h
98
bgpq4.h
@@ -1,98 +0,0 @@
|
||||
#ifndef BGPQ4_H_
|
||||
#define BGPQ4_H_
|
||||
|
||||
#if HAVE_SYS_QUEUE_H && HAVE_STAILQ_IN_SYS_QUEUE
|
||||
#include <sys/queue.h>
|
||||
#else
|
||||
#include "sys_queue.h"
|
||||
#endif
|
||||
|
||||
#include "sx_prefix.h"
|
||||
#include "sx_slentry.h"
|
||||
|
||||
typedef enum {
|
||||
V_CISCO = 0,
|
||||
V_JUNIPER,
|
||||
V_CISCO_XR,
|
||||
V_JSON,
|
||||
V_BIRD,
|
||||
V_OPENBGPD,
|
||||
V_FORMAT,
|
||||
V_NOKIA,
|
||||
V_HUAWEI,
|
||||
V_MIKROTIK,
|
||||
V_NOKIA_MD
|
||||
} bgpq_vendor_t;
|
||||
|
||||
typedef enum {
|
||||
T_NONE = 0,
|
||||
T_ASPATH,
|
||||
T_OASPATH,
|
||||
T_ASSET,
|
||||
T_PREFIXLIST,
|
||||
T_EACL,
|
||||
T_ROUTE_FILTER_LIST
|
||||
} bgpq_gen_t;
|
||||
|
||||
struct bgpq_expander;
|
||||
|
||||
struct bgpq_request {
|
||||
STAILQ_ENTRY(bgpq_request) next;
|
||||
char* request;
|
||||
int size, offset;
|
||||
int (*callback)(char*, struct bgpq_expander*, struct bgpq_request*);
|
||||
void *udata;
|
||||
unsigned depth;
|
||||
};
|
||||
|
||||
struct bgpq_expander {
|
||||
struct sx_radix_tree* tree;
|
||||
STAILQ_HEAD(sx_slentries, sx_slentry) macroses, rsets;
|
||||
RB_HEAD(tentree, sx_tentry) already, stoplist;
|
||||
int family;
|
||||
char* sources;
|
||||
uint32_t asnumber;
|
||||
int aswidth;
|
||||
char* name;
|
||||
bgpq_vendor_t vendor;
|
||||
bgpq_gen_t generation;
|
||||
int identify;
|
||||
int sequence;
|
||||
int maxdepth;
|
||||
int validate_asns;
|
||||
unsigned char asn32;
|
||||
unsigned char* asn32s[65536];
|
||||
struct bgpq_prequest* firstpipe, *lastpipe;
|
||||
int piped;
|
||||
char* match;
|
||||
char* server;
|
||||
char* port;
|
||||
char* format;
|
||||
unsigned maxlen;
|
||||
STAILQ_HEAD(bgpq_requests, bgpq_request) wq, rq;
|
||||
int fd, cdepth;
|
||||
};
|
||||
|
||||
|
||||
int bgpq_expander_init(struct bgpq_expander* b, int af);
|
||||
int bgpq_expander_add_asset(struct bgpq_expander* b, char* set);
|
||||
int bgpq_expander_add_rset(struct bgpq_expander* b, char* set);
|
||||
int bgpq_expander_add_as(struct bgpq_expander* b, char* as);
|
||||
int bgpq_expander_add_prefix(struct bgpq_expander* b, char* prefix);
|
||||
int bgpq_expander_add_prefix_range(struct bgpq_expander* b, char* prefix);
|
||||
int bgpq_expander_add_stop(struct bgpq_expander* b, char* object);
|
||||
|
||||
int bgpq_expand(struct bgpq_expander* b);
|
||||
|
||||
int bgpq4_print_prefixlist(FILE* f, struct bgpq_expander* b);
|
||||
int bgpq4_print_eacl(FILE* f, struct bgpq_expander* b);
|
||||
int bgpq4_print_aspath(FILE* f, struct bgpq_expander* b);
|
||||
int bgpq4_print_asset(FILE* f, struct bgpq_expander* b);
|
||||
int bgpq4_print_oaspath(FILE* f, struct bgpq_expander* b);
|
||||
int bgpq4_print_route_filter_list(FILE* f, struct bgpq_expander* b);
|
||||
|
||||
#ifndef HAVE_STRLCPY
|
||||
size_t strlcpy(char* dst, const char* src, size_t size);
|
||||
#endif
|
||||
|
||||
#endif
|
||||
70
bgpq4.spec
70
bgpq4.spec
@@ -1,70 +0,0 @@
|
||||
Name: bgpq4
|
||||
Version: 0.0.1
|
||||
Release: 0%{?dist}
|
||||
|
||||
Group: System/Utilities
|
||||
Summary: Automate BGP filter generation based on routing database information
|
||||
URL: https://github.com/bgp/bgpq4
|
||||
License: BSD
|
||||
Source0: https://github.com/bgp/bgpq4
|
||||
BuildRoot: %{_tmppath}/%{name}-%{version}-%{release}-root-%(%{__id_u} -n)
|
||||
|
||||
%description
|
||||
You are running BGP in your network and want to automate filter generation for your routers? Well, with BGPQ3 it's easy.
|
||||
|
||||
%prep
|
||||
%setup -q
|
||||
|
||||
%global debug_package %{nil}
|
||||
|
||||
%build
|
||||
./configure --prefix=$RPM_BUILD_ROOT%{_prefix} --mandir=%{_mandir}
|
||||
make
|
||||
|
||||
%install
|
||||
rm -rf $RPM_BUILD_ROOT
|
||||
mkdir -p $RPM_BUILD_ROOT/usr/bin
|
||||
make install
|
||||
|
||||
%files
|
||||
%defattr(-,root,root,-)
|
||||
/usr/bin/bgpq4
|
||||
/usr/man/man8/bgpq4.8.gz
|
||||
%doc COPYRIGHT CHANGES
|
||||
|
||||
%clean
|
||||
rm -rf $RPM_BUILD_ROOT
|
||||
|
||||
%changelog
|
||||
* Sat Dec 14 2019 Job Snijders <job@ntt.net> 0.0.1
|
||||
- fork from bgpq3
|
||||
|
||||
* Tue Nov 30 2018 Alexandre Snarskii <snar@snar.spb.ru> 0.1.35
|
||||
- Version updated
|
||||
|
||||
* Fri Oct 14 2016 Alexandre Snarskii <snar@snar.spb.ru> 0.1.33
|
||||
- Version updated
|
||||
|
||||
* Tue Jun 23 Alexandre Snarskii <snar@snar.spb.ru> 0.1.31
|
||||
- Version updated
|
||||
|
||||
* Tue Mar 10 Alexandre Snarskii <snar@snar.spb.ru> 0.1.28
|
||||
- Version updated
|
||||
|
||||
* Wed Oct 29 Alexandre Snarskii <snar@snar.spb.ru> 0.1.25
|
||||
- Version updated
|
||||
|
||||
* Thu Jun 5 2014 Alexandre Snarskii <snar@snar.spb.ru> 0.1.21-0.snar
|
||||
- Version updated
|
||||
|
||||
* Thu May 9 2013 Alexandre Snarskii <snar@snar.spb.ru> 0.1.19-0.snar
|
||||
- Version updated
|
||||
|
||||
* Sun Feb 24 2013 Alexandre Snarskii <snar@snar.spb.ru> 0.1.18-3.snar
|
||||
- License corrected
|
||||
|
||||
* Wed Feb 20 2013 Arnoud Vermeer <arnoud@tumblr.com> 0.1.18-2.tumblr
|
||||
- Adding missing group info (arnoud@tumblr.com)
|
||||
|
||||
* Wed Feb 20 2013 Arnoud Vermeer <arnoud@tumblr.com> 0.1.18-1.tumblr
|
||||
- new package built with tito
|
||||
1462
bgpq4_printer.c
1462
bgpq4_printer.c
File diff suppressed because it is too large
Load Diff
956
bgpq_expander.c
956
bgpq_expander.c
@@ -1,956 +0,0 @@
|
||||
#if HAVE_CONFIG_H
|
||||
#include "config.h"
|
||||
#endif
|
||||
|
||||
#include <sys/types.h>
|
||||
#include <sys/socket.h>
|
||||
#if HAVE_SYS_SELECT_H
|
||||
#include <sys/select.h>
|
||||
#endif
|
||||
|
||||
#include <assert.h>
|
||||
#include <fcntl.h>
|
||||
#include <inttypes.h>
|
||||
#include <ctype.h>
|
||||
#include <errno.h>
|
||||
#include <netdb.h>
|
||||
#include <limits.h>
|
||||
#include <string.h>
|
||||
#include <strings.h>
|
||||
#include <stdarg.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include "bgpq4.h"
|
||||
#include "sx_report.h"
|
||||
#include "sx_maxsockbuf.h"
|
||||
#include "expander_freeall.h"
|
||||
|
||||
int debug_expander=0;
|
||||
int pipelining=1;
|
||||
int expand_as23456=0;
|
||||
int expand_special_asn=0;
|
||||
|
||||
static inline int
|
||||
tentry_cmp(struct sx_tentry* a, struct sx_tentry* b)
|
||||
{
|
||||
return strcasecmp(a->text, b->text);
|
||||
};
|
||||
|
||||
RB_GENERATE(tentree, sx_tentry, entry, tentry_cmp);
|
||||
|
||||
int
|
||||
bgpq_expander_init(struct bgpq_expander* b, int af)
|
||||
{
|
||||
if(!af) af=AF_INET;
|
||||
if(!b) return 0;
|
||||
|
||||
memset(b,0,sizeof(struct bgpq_expander));
|
||||
|
||||
b->tree=sx_radix_tree_new(af);
|
||||
if(!b->tree) goto fixups;
|
||||
|
||||
b->family=af;
|
||||
b->sources="";
|
||||
b->name="NN";
|
||||
b->aswidth=8;
|
||||
b->asn32s[0]=malloc(8192);
|
||||
if(!b->asn32s[0]) {
|
||||
sx_report(SX_FATAL,"Unable to allocate 8192 bytes: %s\n",
|
||||
strerror(errno));
|
||||
exit(1);
|
||||
};
|
||||
memset(b->asn32s[0],0,8192);
|
||||
b->identify=1;
|
||||
b->server="rr.ntt.net";
|
||||
b->port="43";
|
||||
|
||||
// b->wq = STAILQ_HEAD_INITIALZIER(b->wq);
|
||||
// b->rq = STAILQ_HEAD_INITIALZIER(b->rq);
|
||||
// b->rsets = STAILQ_HEAD_INITIALZIER(b->rsets);
|
||||
// b->macroses = STAILQ_HEAD_INITIALZIER(b->macroses);
|
||||
|
||||
STAILQ_INIT(&b->wq);
|
||||
STAILQ_INIT(&b->rq);
|
||||
STAILQ_INIT(&b->rsets);
|
||||
STAILQ_INIT(&b->macroses);
|
||||
|
||||
return 1;
|
||||
fixups:
|
||||
if(b->tree) sx_radix_tree_freeall(b->tree);
|
||||
b->tree=NULL;
|
||||
free(b);
|
||||
return 0;
|
||||
};
|
||||
|
||||
int
|
||||
bgpq_expander_add_asset(struct bgpq_expander* b, char* as)
|
||||
{
|
||||
struct sx_slentry* le;
|
||||
if(!b || !as) return 0;
|
||||
le=sx_slentry_new(as);
|
||||
STAILQ_INSERT_TAIL(&b->macroses, le, next);
|
||||
return 1;
|
||||
};
|
||||
|
||||
int
|
||||
bgpq_expander_add_rset(struct bgpq_expander* b, char* rs)
|
||||
{
|
||||
struct sx_slentry* le;
|
||||
if(!b || !rs) return 0;
|
||||
le=sx_slentry_new(rs);
|
||||
if(!le) return 0;
|
||||
STAILQ_INSERT_TAIL(&b->rsets, le, next);
|
||||
return 1;
|
||||
};
|
||||
|
||||
int
|
||||
bgpq_expander_add_already(struct bgpq_expander* b, char* rs)
|
||||
{
|
||||
struct sx_tentry* le, lkey;
|
||||
lkey.text = rs;
|
||||
if (RB_FIND(tentree, &b->already, &lkey))
|
||||
return 1;
|
||||
le = sx_tentry_new(rs);
|
||||
RB_INSERT(tentree, &b->already, le);
|
||||
return 1;
|
||||
};
|
||||
|
||||
int
|
||||
bgpq_expander_add_stop(struct bgpq_expander* b, char* rs)
|
||||
{
|
||||
struct sx_tentry* le, lkey;
|
||||
lkey.text = rs;
|
||||
if (RB_FIND(tentree, &b->stoplist, &lkey))
|
||||
return 1;
|
||||
le = sx_tentry_new(rs);
|
||||
RB_INSERT(tentree, &b->stoplist, le);
|
||||
return 1;
|
||||
};
|
||||
|
||||
int
|
||||
bgpq_expander_add_as(struct bgpq_expander* b, char* as)
|
||||
{
|
||||
char* eoa;
|
||||
uint32_t asno;
|
||||
|
||||
if(!b || !as) return 0;
|
||||
|
||||
asno=strtoul(as+2,&eoa,10);
|
||||
if(eoa && (*eoa!='.' && *eoa!=0)) {
|
||||
sx_report(SX_ERROR,"Invalid symbol in AS number: '%c' in %s\n",
|
||||
*eoa, as);
|
||||
return 0;
|
||||
};
|
||||
|
||||
if(*eoa=='.' || asno>65535) {
|
||||
if(b->asn32 || b->generation>=T_PREFIXLIST) {
|
||||
uint32_t asn1;
|
||||
if(asno>65535) {
|
||||
asn1=asno%65536;
|
||||
asno/=65536;
|
||||
} else if(eoa && *(eoa+1)) {
|
||||
asn1=strtoul(eoa+1,&eoa,10);
|
||||
} else {
|
||||
sx_report(SX_ERROR, "Invalid AS number: '%s'\n", as);
|
||||
return 0;
|
||||
};
|
||||
|
||||
if(eoa && *eoa!=0) {
|
||||
sx_report(SX_ERROR,"Invalid symbol in AS number: '%c' in %s\n",
|
||||
*eoa, as);
|
||||
return 0;
|
||||
};
|
||||
if(asn1>65535) {
|
||||
sx_report(SX_ERROR,"Invalid AS number in %s\n", as);
|
||||
return 0;
|
||||
};
|
||||
if(!expand_special_asn && (((asno*65536+asn1)>=4200000000ul) ||
|
||||
((asno*65536+asn1)>=64496 && (asno*65536+asn1) <= 65551))) {
|
||||
return 0;
|
||||
};
|
||||
if(!b->asn32s[asno]) {
|
||||
b->asn32s[asno]=malloc(8192);
|
||||
if(!b->asn32s[asno]) {
|
||||
sx_report(SX_FATAL, "Unable to allocate 8192 bytes: %s."
|
||||
" Unable to add asn32 %s to future expansion\n",
|
||||
strerror(errno), as);
|
||||
return 0;
|
||||
};
|
||||
memset(b->asn32s[asno],0,8192);
|
||||
};
|
||||
b->asn32s[asno][asn1/8]|=(0x80>>(asn1%8));
|
||||
} else if(!b->asn32) {
|
||||
b->asn32s[0][23456/8]|=(0x80>>(23456%8));
|
||||
};
|
||||
return 1;
|
||||
};
|
||||
|
||||
if(asno<1 || asno>65535) {
|
||||
sx_report(SX_ERROR,"Invalid AS number in %s\n", as);
|
||||
return 0;
|
||||
};
|
||||
|
||||
if(asno==23456 && !expand_as23456)
|
||||
return 0;
|
||||
|
||||
if(!expand_special_asn && (asno>=64496 && asno <= 65536))
|
||||
return 0;
|
||||
|
||||
b->asn32s[0][asno/8]|=(0x80>>(asno%8));
|
||||
|
||||
return 1;
|
||||
};
|
||||
|
||||
int
|
||||
bgpq_expander_add_prefix(struct bgpq_expander* b, char* prefix)
|
||||
{
|
||||
struct sx_prefix *p = sx_prefix_alloc(NULL);
|
||||
if(!sx_prefix_parse(p,0,prefix)) {
|
||||
sx_report(SX_ERROR,"Unable to parse prefix %s\n", prefix);
|
||||
return 0;
|
||||
} else if(p->family!=b->family) {
|
||||
SX_DEBUG(debug_expander,"Ignoring prefix %s with wrong address family\n"
|
||||
,prefix);
|
||||
return 0;
|
||||
};
|
||||
if(b->maxlen && p->masklen>b->maxlen) {
|
||||
SX_DEBUG(debug_expander, "Ignoring prefix %s: masklen %i > max "
|
||||
"masklen %u\n", prefix, p->masklen, b->maxlen);
|
||||
return 0;
|
||||
};
|
||||
sx_radix_tree_insert(b->tree,p);
|
||||
if (p) sx_prefix_destroy(p);
|
||||
return 1;
|
||||
};
|
||||
|
||||
int
|
||||
bgpq_expander_add_prefix_range(struct bgpq_expander* b, char* prefix)
|
||||
{
|
||||
return sx_prefix_range_parse(b->tree, b->family, b->maxlen, prefix);
|
||||
};
|
||||
|
||||
int
|
||||
bgpq_expanded_macro(char* as, struct bgpq_expander* ex,
|
||||
struct bgpq_request* req)
|
||||
{
|
||||
bgpq_expander_add_as(ex,as);
|
||||
return 1;
|
||||
};
|
||||
|
||||
struct bgpq_request* bgpq_pipeline(struct bgpq_expander* b,
|
||||
int (*callback)(char*, struct bgpq_expander* b, struct bgpq_request* req),
|
||||
void* udata, char* fmt, ...);
|
||||
int bgpq_expand_irrd(struct bgpq_expander* b,
|
||||
int (*callback)(char*, struct bgpq_expander* b, struct bgpq_request* req),
|
||||
void* udata, char* fmt, ...);
|
||||
|
||||
int
|
||||
bgpq_expanded_macro_limit(char* as, struct bgpq_expander* b,
|
||||
struct bgpq_request* req)
|
||||
{
|
||||
if (!strncasecmp(as, "AS-", 3) || strchr(as, '-') || strchr(as, ':')) {
|
||||
struct sx_tentry tkey = { .text = as };
|
||||
if (RB_FIND(tentree, &b->already, &tkey)) {
|
||||
SX_DEBUG(debug_expander>2,"%s is already expanding, ignore\n", as);
|
||||
return 0;
|
||||
};
|
||||
if (RB_FIND(tentree, &b->stoplist, &tkey)) {
|
||||
SX_DEBUG(debug_expander>2,"%s is in the stoplist, ignore\n", as);
|
||||
return 0;
|
||||
};
|
||||
if(!b->maxdepth ||
|
||||
(b->cdepth + 1 < b->maxdepth && req->depth + 1 < b->maxdepth)) {
|
||||
bgpq_expander_add_already(b,as);
|
||||
if (pipelining) {
|
||||
struct bgpq_request* req1 = bgpq_pipeline(b,
|
||||
bgpq_expanded_macro_limit, NULL, "!i%s\n", as);
|
||||
req1->depth = req->depth+1;
|
||||
} else {
|
||||
b->cdepth++;
|
||||
bgpq_expand_irrd(b, bgpq_expanded_macro_limit, NULL, "!i%s\n",
|
||||
as);
|
||||
b->cdepth--;
|
||||
};
|
||||
} else {
|
||||
SX_DEBUG(debug_expander>2, "ignoring %s at depth %i\n", as,
|
||||
b->cdepth?(b->cdepth+1):(req->depth+1));
|
||||
};
|
||||
} else if(!strncasecmp(as, "AS", 2)) {
|
||||
struct sx_tentry tkey = { .text = as };
|
||||
if (RB_FIND(tentree, &b->stoplist, &tkey)) {
|
||||
SX_DEBUG(debug_expander>2,"%s is in the stoplist, ignore\n", as);
|
||||
return 0;
|
||||
};
|
||||
if(bgpq_expander_add_as(b, as)) {
|
||||
SX_DEBUG(debug_expander>2, ".. added asn %s\n", as);
|
||||
} else {
|
||||
SX_DEBUG(debug_expander, ".. some error adding as %s (in "
|
||||
"response to %s)\n", as, req->request);
|
||||
};
|
||||
} else if (!strcasecmp(as, "ANY")) {
|
||||
return 0;
|
||||
} else {
|
||||
sx_report(SX_ERROR, "unexpected object '%s' in expanded_macro_limit "
|
||||
"(in response to %s)\n", as, req->request);
|
||||
};
|
||||
return 1;
|
||||
};
|
||||
|
||||
int
|
||||
bgpq_expanded_prefix(char* as, struct bgpq_expander* ex,
|
||||
struct bgpq_request* req __attribute__((unused)))
|
||||
{
|
||||
char* d = strchr(as, '^');
|
||||
if (!d)
|
||||
bgpq_expander_add_prefix(ex, as);
|
||||
else
|
||||
bgpq_expander_add_prefix_range(ex, as);
|
||||
return 1;
|
||||
};
|
||||
|
||||
int
|
||||
bgpq_expanded_v6prefix(char* prefix, struct bgpq_expander* ex,
|
||||
struct bgpq_request* req)
|
||||
{
|
||||
char* d = strchr(prefix, '^');
|
||||
if (!d)
|
||||
bgpq_expander_add_prefix(ex,prefix);
|
||||
else
|
||||
bgpq_expander_add_prefix_range(ex,prefix);
|
||||
return 1;
|
||||
};
|
||||
|
||||
int bgpq_pipeline_dequeue(int fd, struct bgpq_expander* b);
|
||||
|
||||
static struct bgpq_request*
|
||||
bgpq_request_alloc(char* request, int (*callback)(char*, struct bgpq_expander*,
|
||||
struct bgpq_request*), void* udata)
|
||||
{
|
||||
struct bgpq_request* bp = malloc(sizeof(struct bgpq_request));
|
||||
if (!bp)
|
||||
return NULL;
|
||||
memset(bp, 0, sizeof(struct bgpq_request));
|
||||
bp->request = strdup(request);
|
||||
bp->offset = 0;
|
||||
bp->size = strlen(bp->request);
|
||||
bp->callback = callback;
|
||||
bp->udata = udata;
|
||||
return bp;
|
||||
};
|
||||
|
||||
static void
|
||||
bgpq_request_free(struct bgpq_request* req)
|
||||
{
|
||||
if (req->request) free(req->request);
|
||||
free(req);
|
||||
};
|
||||
|
||||
struct bgpq_request*
|
||||
bgpq_pipeline(struct bgpq_expander* b,
|
||||
int (*callback)(char*, struct bgpq_expander*, struct bgpq_request*),
|
||||
void* udata, char* fmt, ...)
|
||||
{
|
||||
char request[128];
|
||||
int ret;
|
||||
struct bgpq_request* bp=NULL;
|
||||
va_list ap;
|
||||
va_start(ap,fmt);
|
||||
vsnprintf(request,sizeof(request),fmt,ap);
|
||||
va_end(ap);
|
||||
|
||||
SX_DEBUG(debug_expander,"expander: sending %s", request);
|
||||
|
||||
bp = bgpq_request_alloc(request, callback, udata);
|
||||
if(!bp) {
|
||||
sx_report(SX_FATAL,"Unable to allocate %lu bytes: %s\n",
|
||||
(unsigned long)sizeof(struct bgpq_request),strerror(errno));
|
||||
exit(1);
|
||||
};
|
||||
if (STAILQ_EMPTY(&b->wq)) {
|
||||
ret=write(b->fd, request, bp->size);
|
||||
if (ret < 0) {
|
||||
if (errno == EAGAIN) {
|
||||
STAILQ_INSERT_TAIL(&b->wq, bp, next);
|
||||
return bp;
|
||||
};
|
||||
sx_report(SX_FATAL, "Error writing request: %s\n", strerror(errno));
|
||||
};
|
||||
bp->offset=ret;
|
||||
if (ret == bp->size) {
|
||||
STAILQ_INSERT_TAIL(&b->rq, bp, next);
|
||||
} else {
|
||||
STAILQ_INSERT_TAIL(&b->wq, bp, next);
|
||||
};
|
||||
} else
|
||||
STAILQ_INSERT_TAIL(&b->wq, bp, next);
|
||||
|
||||
return bp;
|
||||
};
|
||||
|
||||
static void
|
||||
bgpq_expander_invalidate_asn(struct bgpq_expander* b, const char* q)
|
||||
{
|
||||
if (!strncmp(q, "!gas", 4) || !strncmp(q, "!6as", 4)) {
|
||||
char* eptr;
|
||||
unsigned long asn = strtoul(q+4, &eptr, 10), asn0, asn1 = 0;
|
||||
if (!asn || asn == ULONG_MAX || asn >= 4294967295 ||
|
||||
(eptr && *eptr != '\n')) {
|
||||
sx_report(SX_ERROR, "some problem invalidating asn %s\n", q);
|
||||
return;
|
||||
};
|
||||
asn1 = asn % 65536;
|
||||
asn0 = asn / 65536;
|
||||
if (!b->asn32s[asn0] ||
|
||||
!(b->asn32s[asn0][asn1/8] & (0x80 >> (asn1 % 8)))) {
|
||||
sx_report(SX_NOTICE, "strange, invalidating inactive asn %lu(%s)\n",
|
||||
asn, q);
|
||||
} else {
|
||||
b->asn32s[asn0][asn1/8] &= ~(0x80 >> (asn1 % 8));
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
static void
|
||||
bgpq_write(struct bgpq_expander* b)
|
||||
{
|
||||
while(!STAILQ_EMPTY(&b->wq)) {
|
||||
struct bgpq_request* req = STAILQ_FIRST(&b->wq);
|
||||
int ret = write(b->fd, req->request+req->offset, req->size-req->offset);
|
||||
if (ret < 0) {
|
||||
if (errno == EAGAIN)
|
||||
return;
|
||||
sx_report(SX_FATAL, "error writing data: %s\n", strerror(errno));
|
||||
};
|
||||
|
||||
if (ret == req->size - req->offset) {
|
||||
/* this request was dequeued */
|
||||
STAILQ_REMOVE_HEAD(&b->wq, next);
|
||||
STAILQ_INSERT_TAIL(&b->rq, req, next);
|
||||
} else {
|
||||
req->offset += ret;
|
||||
break;
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
static int
|
||||
bgpq_selread(struct bgpq_expander* b, char* buffer, int size)
|
||||
{
|
||||
fd_set rfd, wfd;
|
||||
int ret;
|
||||
struct timeval timeout = {10, 0};
|
||||
|
||||
repeat:
|
||||
FD_ZERO(&rfd);
|
||||
FD_SET(b->fd, &rfd);
|
||||
FD_ZERO(&wfd);
|
||||
if (!STAILQ_EMPTY(&b->wq))
|
||||
FD_SET(b->fd, &wfd);
|
||||
|
||||
ret = select(b->fd+1, &rfd, &wfd, NULL, &timeout);
|
||||
if (ret == 0)
|
||||
sx_report(SX_FATAL, "select timeout\n");
|
||||
else if (ret == -1 && errno == EINTR)
|
||||
goto repeat;
|
||||
else if (ret == -1)
|
||||
sx_report(SX_FATAL, "select error %i: %s\n", errno, strerror(errno));
|
||||
|
||||
if (!STAILQ_EMPTY(&b->wq) && FD_ISSET(b->fd, &wfd))
|
||||
bgpq_write(b);
|
||||
|
||||
if (FD_ISSET(b->fd, &rfd))
|
||||
return read(b->fd, buffer, size);
|
||||
goto repeat;
|
||||
};
|
||||
|
||||
int
|
||||
bgpq_read(struct bgpq_expander* b)
|
||||
{
|
||||
static char response[256];
|
||||
static int off = 0;
|
||||
|
||||
if (!STAILQ_EMPTY(&b->wq))
|
||||
bgpq_write(b);
|
||||
|
||||
while(!STAILQ_EMPTY(&b->rq)) {
|
||||
struct bgpq_request* req = STAILQ_FIRST(&b->rq);
|
||||
SX_DEBUG(debug_expander>2, "waiting for answer to %s, init %i '%.*s'\n",
|
||||
req->request, off, off, response);
|
||||
int ret = 0;
|
||||
char* cres;
|
||||
|
||||
if ((cres=strchr(response, '\n'))!=NULL)
|
||||
goto have;
|
||||
repeat:
|
||||
ret = bgpq_selread(b, response+off, sizeof(response)-off);
|
||||
if (ret < 0) {
|
||||
if (errno == EAGAIN)
|
||||
goto repeat;
|
||||
sx_report(SX_FATAL,"Error reading data from IRRd: %s (dequeue)\n",
|
||||
strerror(errno));
|
||||
} else if (ret == 0) {
|
||||
sx_report(SX_FATAL,"EOF from IRRd (dequeue)\n");
|
||||
};
|
||||
off += ret;
|
||||
|
||||
if (!(cres=strchr(response, '\n')))
|
||||
goto repeat;
|
||||
have:
|
||||
SX_DEBUG(debug_expander>5, "got response of %.*s\n", off, response);
|
||||
if(response[0]=='A') {
|
||||
char* eon, *c;
|
||||
unsigned long togot=strtoul(response+1,&eon,10);
|
||||
char* recvbuffer=malloc(togot+2);
|
||||
int offset = 0;
|
||||
if (!recvbuffer) {
|
||||
sx_report(SX_FATAL, "error allocating %lu bytes: %s\n",
|
||||
togot+2, strerror(errno));
|
||||
};
|
||||
memset(recvbuffer,0,togot+2);
|
||||
|
||||
if(!eon || *eon!='\n') {
|
||||
sx_report(SX_ERROR,"A-code finished with wrong char '%c'(%s)\n",
|
||||
eon?*eon:'0',response);
|
||||
exit(1);
|
||||
};
|
||||
|
||||
if (off - ((eon+1) - response) > togot) {
|
||||
/* full response and more data is already in buffer */
|
||||
memcpy(recvbuffer, eon+1, togot);
|
||||
offset = togot;
|
||||
memmove(response, eon+1+togot, off-((eon+1)-response)-togot);
|
||||
off -= togot + ((eon+1)-response);
|
||||
memset(response+off, 0, sizeof(response)-off);
|
||||
} else {
|
||||
/* response is not yet fully buffered */
|
||||
memcpy(recvbuffer, eon+1, off - ((eon+1)-response));
|
||||
offset = off - ((eon+1) - response);
|
||||
memset(response, 0, sizeof(response));
|
||||
off = 0;
|
||||
};
|
||||
|
||||
SX_DEBUG(debug_expander>5,
|
||||
"starting read with ready '%.*s', waiting for %lu\n",
|
||||
offset, recvbuffer, togot-offset);
|
||||
|
||||
if (off > 0)
|
||||
goto have3;
|
||||
if (offset == togot)
|
||||
goto reread2;
|
||||
reread:
|
||||
|
||||
ret = bgpq_selread(b, recvbuffer+offset, togot-offset);
|
||||
if (ret < 0) {
|
||||
if (errno == EAGAIN)
|
||||
goto reread;
|
||||
sx_report(SX_FATAL,"Error reading IRRd: %s (dequeue, result)\n",
|
||||
strerror(errno));
|
||||
} else if (ret == 0) {
|
||||
sx_report(SX_FATAL,"EOF from IRRd (dequeue, result)\n");
|
||||
};
|
||||
SX_DEBUG(debug_expander>5,
|
||||
"Read1: got '%.*s'\n", ret, recvbuffer+offset);
|
||||
offset+=ret;
|
||||
if(offset < togot) {
|
||||
SX_DEBUG(debug_expander>5, "expected %lu, got %lu expanding %s",
|
||||
togot, strlen(recvbuffer), req->request);
|
||||
goto reread;
|
||||
};
|
||||
reread2:
|
||||
ret = bgpq_selread(b, response+off, sizeof(response) - off);
|
||||
if (ret < 0) {
|
||||
if (errno == EAGAIN)
|
||||
goto reread2;
|
||||
sx_report(SX_FATAL,"Error reading IRRd: %s (dequeue,final)\n",
|
||||
strerror(errno));
|
||||
} else if (ret == 0) {
|
||||
sx_report(SX_FATAL,"EOF from IRRd (dequeue,final)\n");
|
||||
};
|
||||
SX_DEBUG(debug_expander>5,
|
||||
"Read2: got '%.*s'\n", ret, response+off);
|
||||
off+=ret;
|
||||
|
||||
have3:
|
||||
if (!(cres = strchr(response, '\n')))
|
||||
goto reread2;
|
||||
|
||||
SX_DEBUG(debug_expander>=3,"Got %s (%lu bytes of %lu) in response "
|
||||
"to %sfinal code: %.*s",recvbuffer,strlen(recvbuffer),togot,
|
||||
req->request,off,response);
|
||||
|
||||
for(c=recvbuffer; c<recvbuffer+togot;) {
|
||||
size_t spn=strcspn(c," \n");
|
||||
if(spn) c[spn]=0;
|
||||
if(c[0]==0) break;
|
||||
req->callback(c, b, req);
|
||||
c+=spn+1;
|
||||
};
|
||||
assert(c == recvbuffer+togot);
|
||||
memset(recvbuffer,0,togot+2);
|
||||
free(recvbuffer);
|
||||
} else if(response[0]=='C') {
|
||||
/* No data */
|
||||
SX_DEBUG(debug_expander,"No data expanding %s\n", req->request);
|
||||
if (b->validate_asns) bgpq_expander_invalidate_asn(b, req->request);
|
||||
} else if(response[0]=='D') {
|
||||
/* .... */
|
||||
SX_DEBUG(debug_expander,"Key not found expanding %s\n",
|
||||
req->request);
|
||||
if (b->validate_asns) bgpq_expander_invalidate_asn(b, req->request);
|
||||
} else if(response[0]=='E') {
|
||||
sx_report(SX_ERROR, "Multiple keys expanding %s: %s\n",
|
||||
req->request, response);
|
||||
} else if(response[0]=='F') {
|
||||
sx_report(SX_ERROR, "Error expanding %s: %s\n",
|
||||
req->request, response);
|
||||
} else {
|
||||
sx_report(SX_ERROR,"Wrong reply: %s to %s\n", response,
|
||||
req->request);
|
||||
exit(1);
|
||||
};
|
||||
memmove(response, cres+1, off-((cres+1)-response));
|
||||
off -= (cres+1)-response;
|
||||
memset(response+off, 0, sizeof(response) - off);
|
||||
SX_DEBUG(debug_expander>5,
|
||||
"fixed response of %i, %.*s\n", off, off, response);
|
||||
|
||||
STAILQ_REMOVE_HEAD(&b->rq, next);
|
||||
b->piped--;
|
||||
bgpq_request_free(req);
|
||||
};
|
||||
return 0;
|
||||
};
|
||||
|
||||
int
|
||||
bgpq_expand_irrd(struct bgpq_expander* b,
|
||||
int (*callback)(char*, struct bgpq_expander*, struct bgpq_request* ),
|
||||
void* udata, char* fmt, ...)
|
||||
{
|
||||
char request[128], response[128];
|
||||
va_list ap;
|
||||
int ret, off = 0;
|
||||
struct bgpq_request *req;
|
||||
|
||||
va_start(ap,fmt);
|
||||
vsnprintf(request,sizeof(request),fmt,ap);
|
||||
va_end(ap);
|
||||
|
||||
req = bgpq_request_alloc(request, callback, udata);
|
||||
|
||||
SX_DEBUG(debug_expander,"expander: sending '%s'\n", request);
|
||||
|
||||
ret=write(b->fd, request, strlen(request));
|
||||
if(ret!=strlen(request)) {
|
||||
sx_report(SX_FATAL,"Partial write to IRRd, only %i bytes written: %s\n",
|
||||
ret, strerror(errno));
|
||||
exit(1);
|
||||
};
|
||||
memset(response,0,sizeof(response));
|
||||
|
||||
repeat:
|
||||
ret = bgpq_selread(b, response+off, sizeof(response)-off);
|
||||
if (ret < 0) {
|
||||
sx_report(SX_ERROR, "Error reading IRRd: %s\n", strerror(errno));
|
||||
exit(1);
|
||||
} else if (ret == 0) {
|
||||
sx_report(SX_FATAL, "EOF reading IRRd\n");
|
||||
exit(1);
|
||||
};
|
||||
off += ret;
|
||||
|
||||
if (strchr(response, '\n') == NULL)
|
||||
goto repeat;
|
||||
|
||||
SX_DEBUG(debug_expander>2,"expander: initially got %lu bytes, '%s'\n",
|
||||
(unsigned long)strlen(response),response);
|
||||
if(response[0]=='A') {
|
||||
char* eon, *c;
|
||||
long togot=strtoul(response+1,&eon,10);
|
||||
char *recvbuffer = malloc(togot+2);
|
||||
int offset = 0;
|
||||
if (!recvbuffer) {
|
||||
sx_report(SX_FATAL, "Error allocating %lu bytes: %s\n",
|
||||
togot+2, strerror(errno));
|
||||
};
|
||||
|
||||
if(eon && *eon!='\n') {
|
||||
sx_report(SX_ERROR,"A-code finised with wrong char '%c' (%s)\n",
|
||||
*eon,response);
|
||||
exit(1);
|
||||
};
|
||||
|
||||
if (off - ((eon+1)-response) > togot) {
|
||||
memcpy(recvbuffer, eon+1, togot);
|
||||
offset = togot;
|
||||
memmove(response, eon+1+togot, off - ((eon+1)-response) - togot);
|
||||
off -= togot + ((eon+1)-response);
|
||||
memset(response+off, 0, sizeof(response)-off);
|
||||
} else {
|
||||
memcpy(recvbuffer, eon+1, off - ((eon+1)-response));
|
||||
offset = off - ((eon+1) - response);
|
||||
memset(response, 0, sizeof(response));
|
||||
off = 0;
|
||||
};
|
||||
|
||||
if (off > 0)
|
||||
goto have3;
|
||||
if (offset == togot)
|
||||
goto reread2;
|
||||
|
||||
reread:
|
||||
ret = bgpq_selread(b, recvbuffer+offset, togot-offset);
|
||||
if (ret == 0) {
|
||||
sx_report(SX_FATAL,"EOF from IRRd (expand,result)\n");
|
||||
} else if (ret < 0) {
|
||||
sx_report(SX_FATAL,"Error reading IRRd: %s (expand,result)\n",
|
||||
strerror(errno));
|
||||
};
|
||||
offset += ret;
|
||||
if (offset < togot)
|
||||
goto reread;
|
||||
|
||||
reread2:
|
||||
ret = bgpq_selread(b, response+off, sizeof(response)-off);
|
||||
if (ret < 0) {
|
||||
sx_report(SX_FATAL, "error reading IRRd: %s\n", strerror(errno));
|
||||
exit(1);
|
||||
} else if (ret == 0) {
|
||||
sx_report(SX_FATAL, "eof reading IRRd\n");
|
||||
exit(1);
|
||||
};
|
||||
off += ret;
|
||||
have3:
|
||||
if (!strchr(response, '\n'))
|
||||
goto reread2;
|
||||
|
||||
SX_DEBUG(debug_expander>2,"expander: final reply of %lu bytes, "
|
||||
"%.*sreturn code %.*s",
|
||||
(unsigned long)strlen(recvbuffer), offset, recvbuffer, off,
|
||||
response);
|
||||
|
||||
for(c=recvbuffer; c<recvbuffer+togot;) {
|
||||
size_t spn=strcspn(c," \n");
|
||||
if(spn) c[spn]=0;
|
||||
if(c[0]==0) break;
|
||||
if(callback) callback(c, b, req);
|
||||
c+=spn+1;
|
||||
};
|
||||
memset(recvbuffer, 0, togot+2);
|
||||
free(recvbuffer);
|
||||
} else if(response[0]=='C') {
|
||||
/* no data */
|
||||
if (b->validate_asns) bgpq_expander_invalidate_asn(b, request);
|
||||
} else if(response[0]=='D') {
|
||||
/* ... */
|
||||
if (b->validate_asns) bgpq_expander_invalidate_asn(b, request);
|
||||
} else if(response[0]=='E') {
|
||||
/* XXXXXX */
|
||||
} else if(response[0]=='F') {
|
||||
/* XXXXXX */
|
||||
} else {
|
||||
sx_report(SX_ERROR,"Wrong reply: %s\n", response);
|
||||
exit(0);
|
||||
};
|
||||
bgpq_request_free(req);
|
||||
return 0;
|
||||
};
|
||||
|
||||
int
|
||||
bgpq_expand(struct bgpq_expander* b)
|
||||
{
|
||||
int fd=-1, err, ret, aquery=0;
|
||||
struct sx_slentry* mc;
|
||||
struct addrinfo hints, *res=NULL, *rp;
|
||||
struct linger sl;
|
||||
sl.l_onoff = 1;
|
||||
sl.l_linger = 5;
|
||||
memset(&hints,0,sizeof(struct addrinfo));
|
||||
|
||||
hints.ai_socktype=SOCK_STREAM;
|
||||
|
||||
err=getaddrinfo(b->server,b->port,&hints,&res);
|
||||
if(err) {
|
||||
sx_report(SX_ERROR,"Unable to resolve %s: %s\n",
|
||||
b->server, gai_strerror(err));
|
||||
exit(1);
|
||||
};
|
||||
|
||||
for(rp=res; rp; rp=rp->ai_next) {
|
||||
fd=socket(rp->ai_family,rp->ai_socktype,0);
|
||||
if(fd==-1) {
|
||||
if(errno==EPROTONOSUPPORT || errno==EAFNOSUPPORT) continue;
|
||||
sx_report(SX_ERROR,"Unable to create socket: %s\n",
|
||||
strerror(errno));
|
||||
exit(1);
|
||||
};
|
||||
if (setsockopt(fd, SOL_SOCKET, SO_LINGER, &sl, sizeof(struct linger))) {
|
||||
sx_report(SX_ERROR,"Unable to set linger on socket: %s\n",
|
||||
strerror(errno));
|
||||
shutdown(fd, SHUT_RDWR);
|
||||
close(fd);
|
||||
exit(1);
|
||||
};
|
||||
err=connect(fd,rp->ai_addr,rp->ai_addrlen);
|
||||
if(err) {
|
||||
shutdown(fd,SHUT_RDWR);
|
||||
close(fd);
|
||||
fd=-1;
|
||||
continue;
|
||||
};
|
||||
err=sx_maxsockbuf(fd,SO_SNDBUF);
|
||||
if(err>0) {
|
||||
SX_DEBUG(debug_expander, "Acquired sendbuf of %i bytes\n", err);
|
||||
} else {
|
||||
shutdown(fd, SHUT_RDWR);
|
||||
close(fd);
|
||||
fd=-1;
|
||||
continue;
|
||||
};
|
||||
break;
|
||||
};
|
||||
freeaddrinfo(res);
|
||||
|
||||
if(fd == -1) {
|
||||
/* all our attempts to connect failed */
|
||||
sx_report(SX_ERROR,"All attempts to connect %s failed, last"
|
||||
" error: %s\n", b->server, strerror(errno));
|
||||
exit(1);
|
||||
};
|
||||
|
||||
b->fd = fd;
|
||||
|
||||
if((ret=write(fd, "!!\n", 3))!=3) {
|
||||
sx_report(SX_ERROR,"Partial write to IRRd: %i bytes, %s\n",
|
||||
ret, strerror(errno));
|
||||
exit(1);
|
||||
};
|
||||
|
||||
if(b->identify) {
|
||||
char ident[128];
|
||||
snprintf(ident,sizeof(ident),"!n" PACKAGE_STRING "\n");
|
||||
write(fd, ident, strlen(ident));
|
||||
read(fd, ident, sizeof(ident));
|
||||
};
|
||||
|
||||
/* Test whether the server has support for the A query */
|
||||
if(b->generation>=T_PREFIXLIST) {
|
||||
char aret[128];
|
||||
char aresp[] = "F Missing required set name for A query";
|
||||
SX_DEBUG(debug_expander, "Testing support for A queries\n");
|
||||
write(fd, "!a\n", 3);
|
||||
memset(aret, 0, sizeof(aret));
|
||||
read(fd, aret, sizeof(aret));
|
||||
if(strncmp(aret, aresp, strlen(aresp)) == 0) {
|
||||
SX_DEBUG(debug_expander, "OK: found support for A queries\n");
|
||||
aquery=1;
|
||||
} else {
|
||||
SX_DEBUG(debug_expander, "No support for A queries\n");
|
||||
}
|
||||
}
|
||||
|
||||
if(b->sources && b->sources[0]!=0) {
|
||||
int slen = strlen(b->sources)+4;
|
||||
if (slen < 128)
|
||||
slen = 128;
|
||||
char sources[slen];
|
||||
snprintf(sources,sizeof(sources),"!s%s\n", b->sources);
|
||||
SX_DEBUG(debug_expander,"Requesting sources %s", sources);
|
||||
write(fd, sources, strlen(sources));
|
||||
memset(sources, 0, slen);
|
||||
read(fd, sources, slen);
|
||||
SX_DEBUG(debug_expander,"Got answer %s", sources);
|
||||
if(sources[0]!='C') {
|
||||
sx_report(SX_ERROR, "Invalid source(s) '%s': %s\n", b->sources,
|
||||
sources);
|
||||
exit(1);
|
||||
};
|
||||
};
|
||||
|
||||
if (pipelining)
|
||||
fcntl(fd, F_SETFL, O_NONBLOCK|(fcntl(fd, F_GETFL)));
|
||||
|
||||
STAILQ_FOREACH(mc, &b->macroses, next) {
|
||||
if (!b->maxdepth && RB_EMPTY(&b->stoplist)) {
|
||||
if(aquery)
|
||||
bgpq_expand_irrd(b, bgpq_expanded_prefix, b,
|
||||
(b->family == AF_INET) ? "!a4%s\n" : "!a6%s\n",
|
||||
mc->text);
|
||||
else
|
||||
bgpq_expand_irrd(b, bgpq_expanded_macro, b, "!i%s,1\n", mc->text);
|
||||
} else {
|
||||
bgpq_expander_add_already(b,mc->text);
|
||||
if (pipelining) {
|
||||
bgpq_pipeline(b, bgpq_expanded_macro_limit, NULL, "!i%s\n",
|
||||
mc->text);
|
||||
} else {
|
||||
bgpq_expand_irrd(b, bgpq_expanded_macro_limit, NULL, "!i%s\n",
|
||||
mc->text);
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
if(pipelining) {
|
||||
if(!STAILQ_EMPTY(&b->wq))
|
||||
bgpq_write(b);
|
||||
if (!STAILQ_EMPTY(&b->rq))
|
||||
bgpq_read(b);
|
||||
};
|
||||
|
||||
if(b->generation>=T_PREFIXLIST || b->validate_asns) {
|
||||
uint32_t i, j, k;
|
||||
STAILQ_FOREACH(mc, &b->rsets, next) {
|
||||
if(b->family==AF_INET) {
|
||||
bgpq_expand_irrd(b, bgpq_expanded_prefix, NULL, "!i%s,1\n",
|
||||
mc->text);
|
||||
} else {
|
||||
bgpq_expand_irrd(b, bgpq_expanded_v6prefix, NULL, "!i%s,1\n",
|
||||
mc->text);
|
||||
};
|
||||
};
|
||||
for(k=0;k<sizeof(b->asn32s)/sizeof(unsigned char*);k++) {
|
||||
if(!b->asn32s[k]) continue;
|
||||
for(i=0;i<8192;i++) {
|
||||
for(j=0;j<8;j++) {
|
||||
if(b->asn32s[k][i]&(0x80>>j)) {
|
||||
if(b->family==AF_INET6) {
|
||||
if(!pipelining) {
|
||||
bgpq_expand_irrd(b, bgpq_expanded_v6prefix,
|
||||
NULL, "!6as%" PRIu32 "\n", (k<<16)+i*8+j);
|
||||
} else {
|
||||
bgpq_pipeline(b, bgpq_expanded_v6prefix,
|
||||
NULL, "!6as%" PRIu32 "\n", (k<<16)+i*8+j);
|
||||
};
|
||||
} else {
|
||||
if(!pipelining) {
|
||||
bgpq_expand_irrd(b, bgpq_expanded_prefix,
|
||||
NULL, "!gas%" PRIu32 "\n", (k<<16)+i*8+j);
|
||||
} else {
|
||||
bgpq_pipeline(b, bgpq_expanded_prefix,
|
||||
NULL, "!gas%" PRIu32 "\n", (k<<16)+i*8+j);
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
if(pipelining) {
|
||||
if(!STAILQ_EMPTY(&b->wq))
|
||||
bgpq_write(b);
|
||||
if (!STAILQ_EMPTY(&b->rq))
|
||||
bgpq_read(b);
|
||||
};
|
||||
};
|
||||
|
||||
write(fd, "!q\n",3);
|
||||
if (pipelining) {
|
||||
int fl = fcntl(fd, F_GETFL);
|
||||
fl &= ~O_NONBLOCK;
|
||||
fcntl(fd, F_SETFL, fl);
|
||||
};
|
||||
shutdown(fd, SHUT_RDWR);
|
||||
close(fd);
|
||||
return 1;
|
||||
};
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
|
||||
bsd_environment() {
|
||||
# Based on https://github.com/rvm/rvm/blob/59fe3b39f0fb5ae01ed5b9aa187201080815ac16/scripts/functions/build_config_system#L123
|
||||
if [[ -z "${AUTOCONF_VERSION:-}" ]]
|
||||
if [ -z "${AUTOCONF_VERSION}" ]
|
||||
then
|
||||
export AUTOCONF_VERSION
|
||||
AUTOCONF_VERSION="$(
|
||||
@@ -17,7 +17,7 @@ bsd_environment() {
|
||||
echo "Using autoconf version: $AUTOCONF_VERSION"
|
||||
fi
|
||||
|
||||
if [[ -z "${AUTOMAKE_VERSION:-}" ]]
|
||||
if [ -z "${AUTOMAKE_VERSION}" ]
|
||||
then
|
||||
export AUTOMAKE_VERSION
|
||||
# FreeBSD might have automake-wrapper
|
||||
|
||||
7
compat/Makefile.am
Normal file
7
compat/Makefile.am
Normal file
@@ -0,0 +1,7 @@
|
||||
AM_CPPFLAGS = -I$(top_srcdir)/include
|
||||
|
||||
noinst_LTLIBRARIES = libcompat.la
|
||||
|
||||
libcompat_la_LIBADD = $(PLATFORM_LDADD)
|
||||
|
||||
libcompat_la_SOURCES = strlcpy.c
|
||||
50
compat/strlcpy.c
Normal file
50
compat/strlcpy.c
Normal file
@@ -0,0 +1,50 @@
|
||||
/* $OpenBSD: strlcpy.c,v 1.16 2019/01/25 00:19:25 millert Exp $ */
|
||||
|
||||
/*
|
||||
* Copyright (c) 1998, 2015 Todd C. Miller <millert@openbsd.org>
|
||||
*
|
||||
* Permission to use, copy, modify, and distribute this software for any
|
||||
* purpose with or without fee is hereby granted, provided that the above
|
||||
* copyright notice and this permission notice appear in all copies.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
|
||||
#include <sys/types.h>
|
||||
#include <string.h>
|
||||
|
||||
/*
|
||||
* Copy string src to buffer dst of size dsize. At most dsize-1
|
||||
* chars will be copied. Always NUL terminates (unless dsize == 0).
|
||||
* Returns strlen(src); if retval >= dsize, truncation occurred.
|
||||
*/
|
||||
size_t
|
||||
strlcpy(char *dst, const char *src, size_t dsize)
|
||||
{
|
||||
const char *osrc = src;
|
||||
size_t nleft = dsize;
|
||||
|
||||
/* Copy as many bytes as will fit. */
|
||||
if (nleft != 0) {
|
||||
while (--nleft != 0) {
|
||||
if ((*dst++ = *src++) == '\0')
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* Not enough room in dst, add NUL and traverse rest of src. */
|
||||
if (nleft == 0) {
|
||||
if (dsize != 0)
|
||||
*dst = '\0'; /* NUL-terminate dst */
|
||||
while (*src++)
|
||||
;
|
||||
}
|
||||
|
||||
return(src - osrc - 1); /* count does not include NUL */
|
||||
}
|
||||
132
configure.ac
132
configure.ac
@@ -1,39 +1,119 @@
|
||||
AC_INIT(bgpq4,0.0.3,job@ntt.net)
|
||||
AM_INIT_AUTOMAKE
|
||||
AC_PACKAGE_URL(https://github.com/bgp/bgpq4)
|
||||
AM_CONFIG_HEADER(config.h)
|
||||
AM_SILENT_RULES([yes])
|
||||
#
|
||||
# Copyright (c) 2019 Brent Cook
|
||||
#
|
||||
# Permission to use, copy, modify, and distribute this software for any
|
||||
# purpose with or without fee is hereby granted, provided that the above
|
||||
# copyright notice and this permission notice appear in all copies.
|
||||
#
|
||||
# THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
# OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
|
||||
AC_INIT([bgpq4], m4_esyscmd([tr -d '\n' < VERSION]), job@sobornost.net)
|
||||
|
||||
AC_CANONICAL_HOST
|
||||
AM_INIT_AUTOMAKE([subdir-objects foreign])
|
||||
AC_CONFIG_MACRO_DIR([m4])
|
||||
m4_ifdef([AM_SILENT_RULES], [AM_SILENT_RULES([yes])])
|
||||
AC_PROG_CC([cc gcc])
|
||||
|
||||
case $host_os in
|
||||
*darwin*)
|
||||
HOST_OS=darwin
|
||||
AC_DEFINE([SPT_TYPE], [SPT_REUSEARGV])
|
||||
;;
|
||||
*freebsd*)
|
||||
HOST_OS=freebsd
|
||||
;;
|
||||
*linux*)
|
||||
HOST_OS=linux
|
||||
CFLAGS="$CFLAGS -D_DEFAULT_SOURCE -D_BSD_SOURCE -D_GNU_SOURCE"
|
||||
AC_DEFINE([SPT_TYPE], [SPT_REUSEARGV])
|
||||
;;
|
||||
*netbsd*)
|
||||
HOST_OS=netbsd
|
||||
;;
|
||||
*openbsd*)
|
||||
HOST_OS=openbsd
|
||||
AC_DEFINE([HAVE_ATTRIBUTE__BOUNDED__], [1], [OpenBSD has __bounded__])
|
||||
AC_DEFINE([HAVE_ATTRIBUTE__DEAD], [1], [OpenBSD has __dead])
|
||||
;;
|
||||
*solaris*)
|
||||
HOST_OS=solaris
|
||||
CFLAGS="$CFLAGS -D__EXTENSIONS__ -D_XOPEN_SOURCE=600 -DBSD_COMP"
|
||||
;;
|
||||
*) ;;
|
||||
esac
|
||||
|
||||
AM_CONDITIONAL([HOST_DARWIN], [test x$HOST_OS = xdarwin])
|
||||
AM_CONDITIONAL([HOST_FREEBSD], [test x$HOST_OS = xfreebsd])
|
||||
AM_CONDITIONAL([HOST_LINUX], [test x$HOST_OS = xlinux])
|
||||
AM_CONDITIONAL([HOST_NETBSD], [test x$HOST_OS = xnetbsd])
|
||||
AM_CONDITIONAL([HOST_SOLARIS], [test x$HOST_OS = xsolaris])
|
||||
|
||||
AC_PROG_CC
|
||||
CC=cc
|
||||
CFLAGS="${CFLAGS} -std=gnu11"
|
||||
|
||||
AC_PROG_CC_STDC
|
||||
AM_PROG_CC_C_O
|
||||
AC_PROG_LIBTOOL
|
||||
AC_PROG_INSTALL
|
||||
|
||||
AC_CHECK_HEADERS([sys/cdefs.h sys/queue.h sys/tree.h sys/select.h])
|
||||
AC_ARG_ENABLE(warnings,
|
||||
AS_HELP_STRING([--disable-warnings],
|
||||
[ enable compiler warnings [default=enabled]]),
|
||||
[case $enableval in
|
||||
yes) enable_warnings=yes;;
|
||||
no) enable_warnings=no;;
|
||||
*) enable_warnings=yes;; esac],
|
||||
enable_warnings=yes)
|
||||
|
||||
AC_MSG_CHECKING([for STAILQ_ interface in queue.h])
|
||||
AC_COMPILE_IFELSE([AC_LANG_PROGRAM([
|
||||
#if HAVE_SYS_QUEUE_H
|
||||
#include <sys/queue.h>
|
||||
#else
|
||||
#include "sys_queue.h"
|
||||
if test "$enable_warnings" = yes; then
|
||||
AM_CFLAGS="$AM_CFLAGS -Wall -Wstrict-prototypes -Wmissing-prototypes -Wmissing-declarations -Wshadow -Wpointer-arith -Wsign-compare -Werror-implicit-function-declaration"
|
||||
save_cflags="$CFLAGS"
|
||||
CFLAGS=-Wno-pointer-sign
|
||||
AC_MSG_CHECKING([whether CC supports -Wno-pointer-sign])
|
||||
AC_COMPILE_IFELSE([AC_LANG_PROGRAM([])],
|
||||
[AC_MSG_RESULT([yes])]
|
||||
[WARN_CFLAGS=-Wno-pointer-sign],
|
||||
[AC_MSG_RESULT([no])]
|
||||
)
|
||||
AM_CFLAGS="$AM_CFLAGS $WARN_CFLAGS"
|
||||
CFLAGS="$save_cflags"
|
||||
fi
|
||||
|
||||
AC_MSG_CHECKING([if compiling with clang])
|
||||
AC_COMPILE_IFELSE([AC_LANG_PROGRAM([], [[
|
||||
#ifndef __clang__
|
||||
not clang
|
||||
#endif
|
||||
struct blah {
|
||||
STAILQ_ENTRY(blah) next;
|
||||
};
|
||||
])],
|
||||
[AC_MSG_RESULT(yes)
|
||||
AC_DEFINE([HAVE_STAILQ_IN_SYS_QUEUE], [1], [sys/queue.h have STAILQ_])],
|
||||
[AC_MSG_RESULT(no)])
|
||||
|
||||
|
||||
AC_CHECK_PROGS([MARKDOWN], [markdown_py markdown2 markdown], [echo])
|
||||
]])],
|
||||
[AC_MSG_RESULT([yes])]
|
||||
[CLANG_FLAGS=-Qunused-arguments],
|
||||
[AC_MSG_RESULT([no])]
|
||||
)
|
||||
AM_CFLAGS="$AM_CFLAGS $CLANG_FLAGS"
|
||||
AM_LDFLAGS="$LDFLAGS $CLANG_FLAGS"
|
||||
AC_SUBST(AM_CFLAGS)
|
||||
AC_SUBST(AM_LDFLAGS)
|
||||
|
||||
AC_CHECK_FUNCS(strlcpy)
|
||||
AC_CHECK_FUNCS(pledge)
|
||||
|
||||
AC_CHECK_LIB(socket,socket)
|
||||
AC_CHECK_LIB(nsl,getaddrinfo)
|
||||
|
||||
AC_OUTPUT(Makefile)
|
||||
AC_CHECK_HEADERS([sys/cdefs.h sys/queue.h sys/tree.h sys/select.h])
|
||||
|
||||
AM_CONDITIONAL([HAVE_PLEDGE], [test "x$ac_cv_func_pledge" = xyes])
|
||||
AM_CONDITIONAL([HAVE_STRLCPY], [test "x$ac_cv_func_strlcpy" = xyes])
|
||||
|
||||
AC_CONFIG_FILES([
|
||||
Makefile
|
||||
include/Makefile
|
||||
compat/Makefile
|
||||
])
|
||||
|
||||
AC_OUTPUT
|
||||
|
||||
1466
expander.c
Normal file
1466
expander.c
Normal file
File diff suppressed because it is too large
Load Diff
@@ -1,76 +0,0 @@
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
|
||||
#include <bgpq4.h>
|
||||
|
||||
void sx_radix_node_freeall(struct sx_radix_node *n) {
|
||||
if (n->l != NULL) {
|
||||
sx_radix_node_freeall(n->l);
|
||||
}
|
||||
if (n->r != NULL) {
|
||||
sx_radix_node_freeall(n->r);
|
||||
}
|
||||
if (n->son != NULL) {
|
||||
sx_radix_node_freeall(n->son);
|
||||
}
|
||||
|
||||
if (n->payload) {
|
||||
free(n->payload);
|
||||
}
|
||||
sx_prefix_destroy(n->prefix);
|
||||
free(n);
|
||||
}
|
||||
|
||||
void sx_radix_tree_freeall(struct sx_radix_tree *t) {
|
||||
sx_radix_node_freeall(t->head);
|
||||
free(t);
|
||||
}
|
||||
|
||||
void bgpq_prequest_freeall(struct bgpq_prequest *bpr) {
|
||||
|
||||
}
|
||||
|
||||
void expander_freeall(struct bgpq_expander *expander) {
|
||||
|
||||
// printf("starting to free all\n");
|
||||
// seg fault here
|
||||
// if (expander->sources != NULL) {
|
||||
// printf("freeing soruces\n");
|
||||
// free(expander->sources);
|
||||
//}
|
||||
// if (expander->name != NULL) {
|
||||
// printf("freeing name\n");
|
||||
// free(expander->name);
|
||||
//}
|
||||
// printf("freeing asn32s\n");
|
||||
for (int i = 0; i < 65536; i++) {
|
||||
if (expander->asn32s[i] != NULL) {
|
||||
free(expander->asn32s[i]);
|
||||
}
|
||||
}
|
||||
// if (expander->match != NULL) {
|
||||
// printf("freeing match\n");
|
||||
// free(expander->match);
|
||||
//}
|
||||
//if (expander->server != NULL) {
|
||||
// printf("freeing server\n");
|
||||
// free(expander->server);
|
||||
//}
|
||||
//if (expander->port != NULL) {
|
||||
// printf("freeing port\n");
|
||||
// free(expander->port);
|
||||
//}
|
||||
//if (expander->format != NULL) {
|
||||
// printf("freeing format\n");
|
||||
// free(expander->format);
|
||||
//}
|
||||
|
||||
sx_radix_tree_freeall(expander->tree);
|
||||
|
||||
bgpq_prequest_freeall(expander->firstpipe);
|
||||
bgpq_prequest_freeall(expander->lastpipe);
|
||||
// printf("finished freeing all\n");
|
||||
|
||||
}
|
||||
|
||||
@@ -1,5 +0,0 @@
|
||||
|
||||
void sx_radix_node_freeall(struct sx_radix_node *n);
|
||||
void sx_radix_tree_freeall(struct sx_radix_tree *t);
|
||||
void bgpq_prequest_freeall(struct bgpq_prequest *bpr);
|
||||
void expander_freeall(struct bgpq_expander *expander);
|
||||
156
extern.h
Normal file
156
extern.h
Normal file
@@ -0,0 +1,156 @@
|
||||
/*
|
||||
* Copyright (c) 2007-2019 Alexandre Snarskii <snar@snar.spb.ru>
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
|
||||
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
||||
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
||||
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||
* SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#include <sys/queue.h>
|
||||
#include <sys/tree.h>
|
||||
|
||||
#include "sx_prefix.h"
|
||||
|
||||
struct slentry {
|
||||
STAILQ_ENTRY(slentry) entry;
|
||||
char *text;
|
||||
};
|
||||
|
||||
struct slentry *sx_slentry_new(char *text);
|
||||
|
||||
struct sx_tentry {
|
||||
RB_ENTRY(sx_tentry) entry;
|
||||
char *text;
|
||||
};
|
||||
|
||||
struct sx_tentry *sx_tentry_new(char *text);
|
||||
|
||||
struct asn_entry {
|
||||
RB_ENTRY(asn_entry) entry;
|
||||
uint32_t asn;
|
||||
};
|
||||
|
||||
typedef enum {
|
||||
V_CISCO = 0,
|
||||
V_JUNIPER,
|
||||
V_CISCO_XR,
|
||||
V_JSON,
|
||||
V_BIRD,
|
||||
V_OPENBGPD,
|
||||
V_FORMAT,
|
||||
V_NOKIA,
|
||||
V_HUAWEI,
|
||||
V_HUAWEI_XPL,
|
||||
V_MIKROTIK6,
|
||||
V_MIKROTIK7,
|
||||
V_NOKIA_MD,
|
||||
V_ARISTA
|
||||
} bgpq_vendor_t;
|
||||
|
||||
typedef enum {
|
||||
T_NONE = 0,
|
||||
T_ASPATH,
|
||||
T_OASPATH,
|
||||
T_ASLIST,
|
||||
T_ASSET,
|
||||
T_PREFIXLIST,
|
||||
T_EACL,
|
||||
T_ROUTE_FILTER_LIST
|
||||
} bgpq_gen_t;
|
||||
|
||||
struct bgpq_expander;
|
||||
|
||||
struct request {
|
||||
STAILQ_ENTRY(request) next;
|
||||
char *request;
|
||||
int size, offset;
|
||||
void *udata;
|
||||
unsigned int depth;
|
||||
int (*callback)(char *, struct bgpq_expander *,
|
||||
struct request *);
|
||||
};
|
||||
|
||||
struct bgpq_expander {
|
||||
struct sx_radix_tree *tree;
|
||||
int family;
|
||||
char *sources;
|
||||
char *defaultsources;
|
||||
unsigned int usesource;
|
||||
uint32_t asnumber;
|
||||
int aswidth;
|
||||
char *name;
|
||||
bgpq_vendor_t vendor;
|
||||
bgpq_gen_t generation;
|
||||
int identify;
|
||||
int sequence;
|
||||
unsigned int maxdepth;
|
||||
unsigned int cdepth;
|
||||
int validate_asns;
|
||||
struct bgpq_prequest *firstpipe, *lastpipe;
|
||||
int piped;
|
||||
char *match;
|
||||
char *server;
|
||||
char *port;
|
||||
char *format;
|
||||
unsigned int maxlen;
|
||||
int fd;
|
||||
RB_HEAD(asn_tree, asn_entry) asnlist;
|
||||
STAILQ_HEAD(requests, request) wq, rq;
|
||||
STAILQ_HEAD(slentries, slentry) macroses, rsets;
|
||||
RB_HEAD(tentree, sx_tentry) already, stoplist;
|
||||
};
|
||||
|
||||
int asn_cmp(struct asn_entry *, struct asn_entry *);
|
||||
RB_PROTOTYPE(asn_tree, asn_entry, entry, asn_cmp);
|
||||
|
||||
int bgpq_expander_init(struct bgpq_expander *b, int af);
|
||||
int bgpq_expander_add_asset(struct bgpq_expander *b, char *set);
|
||||
int bgpq_expander_add_rset(struct bgpq_expander *b, char *set);
|
||||
int bgpq_expander_add_as(struct bgpq_expander *b, char *as);
|
||||
int bgpq_expander_add_prefix(struct bgpq_expander *b, char *prefix);
|
||||
int bgpq_expander_add_prefix_range(struct bgpq_expander *b, char *prefix);
|
||||
int bgpq_expander_add_stop(struct bgpq_expander *b, char *object);
|
||||
|
||||
char* bgpq_get_asset(char *object);
|
||||
char* bgpq_get_rset(char *object);
|
||||
char* bgpq_get_source(char *object);
|
||||
|
||||
int bgpq_expand(struct bgpq_expander *b);
|
||||
|
||||
void bgpq4_print_prefixlist(FILE *f, struct bgpq_expander *b);
|
||||
void bgpq4_print_eacl(FILE *f, struct bgpq_expander *b);
|
||||
void bgpq4_print_aspath(FILE *f, struct bgpq_expander *b);
|
||||
void bgpq4_print_asset(FILE *f, struct bgpq_expander *b);
|
||||
void bgpq4_print_oaspath(FILE *f, struct bgpq_expander *b);
|
||||
void bgpq4_print_aslist(FILE *f, struct bgpq_expander *b);
|
||||
void bgpq4_print_route_filter_list(FILE *f, struct bgpq_expander *b);
|
||||
|
||||
void sx_radix_node_freeall(struct sx_radix_node *n);
|
||||
void sx_radix_tree_freeall(struct sx_radix_tree *t);
|
||||
void bgpq_prequest_freeall(struct bgpq_prequest *bpr);
|
||||
void expander_freeall(struct bgpq_expander *expander);
|
||||
|
||||
/* s - number of opened socket, dir is either SO_SNDBUF or SO_RCVBUF */
|
||||
int sx_maxsockbuf(int s, int dir);
|
||||
|
||||
#ifndef HAVE_STRLCPY
|
||||
size_t strlcpy(char *dst, const char *src, size_t size);
|
||||
#endif
|
||||
6
include/Makefile.am
Normal file
6
include/Makefile.am
Normal file
@@ -0,0 +1,6 @@
|
||||
noinst_HEADERS =
|
||||
noinst_HEADERS += sys/_null.h
|
||||
noinst_HEADERS += sys/queue.h
|
||||
noinst_HEADERS += sys/tree.h
|
||||
noinst_HEADERS += sys/types.h
|
||||
noinst_HEADERS += string.h
|
||||
24
include/string.h
Normal file
24
include/string.h
Normal file
@@ -0,0 +1,24 @@
|
||||
/*
|
||||
* Public domain
|
||||
* string.h compatibility shim
|
||||
*/
|
||||
|
||||
#include_next <string.h>
|
||||
|
||||
#ifndef LIBCOMPAT_STRING_H
|
||||
#define LIBCOMPAT_STRING_H
|
||||
|
||||
#include <sys/types.h>
|
||||
|
||||
#if defined(__sun) || defined(__hpux)
|
||||
/* Some functions historically defined in string.h were placed in strings.h by
|
||||
* SUS. Use the same hack as OS X and FreeBSD use to work around on Solaris and HPUX.
|
||||
*/
|
||||
#include <strings.h>
|
||||
#endif
|
||||
|
||||
#ifndef HAVE_STRLCPY
|
||||
size_t strlcpy(char *dst, const char *src, size_t siz);
|
||||
#endif
|
||||
|
||||
#endif
|
||||
18
include/sys/_null.h
Normal file
18
include/sys/_null.h
Normal file
@@ -0,0 +1,18 @@
|
||||
/* $OpenBSD: _null.h,v 1.2 2016/09/09 22:07:58 millert Exp $ */
|
||||
|
||||
/*
|
||||
* Written by Todd C. Miller, September 9, 2016
|
||||
* Public domain.
|
||||
*/
|
||||
|
||||
#ifndef NULL
|
||||
#if !defined(__cplusplus)
|
||||
#define NULL ((void *)0)
|
||||
#elif __cplusplus >= 201103L
|
||||
#define NULL nullptr
|
||||
#elif defined(__GNUG__)
|
||||
#define NULL __null
|
||||
#else
|
||||
#define NULL 0L
|
||||
#endif
|
||||
#endif
|
||||
633
include/sys/queue.h
Normal file
633
include/sys/queue.h
Normal file
@@ -0,0 +1,633 @@
|
||||
/* $OpenBSD: queue.h,v 1.46 2020/12/30 13:33:12 millert Exp $ */
|
||||
/* $NetBSD: queue.h,v 1.11 1996/05/16 05:17:14 mycroft Exp $ */
|
||||
|
||||
/*
|
||||
* Copyright (c) 1991, 1993
|
||||
* The Regents of the University of California. All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* 3. Neither the name of the University nor the names of its contributors
|
||||
* may be used to endorse or promote products derived from this software
|
||||
* without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
|
||||
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
||||
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
||||
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||
* SUCH DAMAGE.
|
||||
*
|
||||
* @(#)queue.h 8.5 (Berkeley) 8/20/94
|
||||
*/
|
||||
|
||||
#ifndef _SYS_QUEUE_H_
|
||||
#define _SYS_QUEUE_H_
|
||||
|
||||
#include <sys/_null.h>
|
||||
|
||||
/*
|
||||
* This file defines five types of data structures: singly-linked lists,
|
||||
* lists, simple queues, tail queues and XOR simple queues.
|
||||
*
|
||||
*
|
||||
* A singly-linked list is headed by a single forward pointer. The elements
|
||||
* are singly linked for minimum space and pointer manipulation overhead at
|
||||
* the expense of O(n) removal for arbitrary elements. New elements can be
|
||||
* added to the list after an existing element or at the head of the list.
|
||||
* Elements being removed from the head of the list should use the explicit
|
||||
* macro for this purpose for optimum efficiency. A singly-linked list may
|
||||
* only be traversed in the forward direction. Singly-linked lists are ideal
|
||||
* for applications with large datasets and few or no removals or for
|
||||
* implementing a LIFO queue.
|
||||
*
|
||||
* A list is headed by a single forward pointer (or an array of forward
|
||||
* pointers for a hash table header). The elements are doubly linked
|
||||
* so that an arbitrary element can be removed without a need to
|
||||
* traverse the list. New elements can be added to the list before
|
||||
* or after an existing element or at the head of the list. A list
|
||||
* may only be traversed in the forward direction.
|
||||
*
|
||||
* A simple queue is headed by a pair of pointers, one to the head of the
|
||||
* list and the other to the tail of the list. The elements are singly
|
||||
* linked to save space, so elements can only be removed from the
|
||||
* head of the list. New elements can be added to the list before or after
|
||||
* an existing element, at the head of the list, or at the end of the
|
||||
* list. A simple queue may only be traversed in the forward direction.
|
||||
*
|
||||
* A tail queue is headed by a pair of pointers, one to the head of the
|
||||
* list and the other to the tail of the list. The elements are doubly
|
||||
* linked so that an arbitrary element can be removed without a need to
|
||||
* traverse the list. New elements can be added to the list before or
|
||||
* after an existing element, at the head of the list, or at the end of
|
||||
* the list. A tail queue may be traversed in either direction.
|
||||
*
|
||||
* An XOR simple queue is used in the same way as a regular simple queue.
|
||||
* The difference is that the head structure also includes a "cookie" that
|
||||
* is XOR'd with the queue pointer (first, last or next) to generate the
|
||||
* real pointer value.
|
||||
*
|
||||
* For details on the use of these macros, see the queue(3) manual page.
|
||||
*/
|
||||
|
||||
#if defined(QUEUE_MACRO_DEBUG) || (defined(_KERNEL) && defined(DIAGNOSTIC))
|
||||
#define _Q_INVALID ((void *)-1)
|
||||
#define _Q_INVALIDATE(a) (a) = _Q_INVALID
|
||||
#else
|
||||
#define _Q_INVALIDATE(a)
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Singly-linked List definitions.
|
||||
*/
|
||||
#define SLIST_HEAD(name, type) \
|
||||
struct name { \
|
||||
struct type *slh_first; /* first element */ \
|
||||
}
|
||||
|
||||
#define SLIST_HEAD_INITIALIZER(head) \
|
||||
{ NULL }
|
||||
|
||||
#define SLIST_ENTRY(type) \
|
||||
struct { \
|
||||
struct type *sle_next; /* next element */ \
|
||||
}
|
||||
|
||||
/*
|
||||
* Singly-linked List access methods.
|
||||
*/
|
||||
#define SLIST_FIRST(head) ((head)->slh_first)
|
||||
#define SLIST_END(head) NULL
|
||||
#define SLIST_EMPTY(head) (SLIST_FIRST(head) == SLIST_END(head))
|
||||
#define SLIST_NEXT(elm, field) ((elm)->field.sle_next)
|
||||
|
||||
#define SLIST_FOREACH(var, head, field) \
|
||||
for((var) = SLIST_FIRST(head); \
|
||||
(var) != SLIST_END(head); \
|
||||
(var) = SLIST_NEXT(var, field))
|
||||
|
||||
#define SLIST_FOREACH_SAFE(var, head, field, tvar) \
|
||||
for ((var) = SLIST_FIRST(head); \
|
||||
(var) && ((tvar) = SLIST_NEXT(var, field), 1); \
|
||||
(var) = (tvar))
|
||||
|
||||
/*
|
||||
* Singly-linked List functions.
|
||||
*/
|
||||
#define SLIST_INIT(head) { \
|
||||
SLIST_FIRST(head) = SLIST_END(head); \
|
||||
}
|
||||
|
||||
#define SLIST_INSERT_AFTER(slistelm, elm, field) do { \
|
||||
(elm)->field.sle_next = (slistelm)->field.sle_next; \
|
||||
(slistelm)->field.sle_next = (elm); \
|
||||
} while (0)
|
||||
|
||||
#define SLIST_INSERT_HEAD(head, elm, field) do { \
|
||||
(elm)->field.sle_next = (head)->slh_first; \
|
||||
(head)->slh_first = (elm); \
|
||||
} while (0)
|
||||
|
||||
#define SLIST_REMOVE_AFTER(elm, field) do { \
|
||||
(elm)->field.sle_next = (elm)->field.sle_next->field.sle_next; \
|
||||
} while (0)
|
||||
|
||||
#define SLIST_REMOVE_HEAD(head, field) do { \
|
||||
(head)->slh_first = (head)->slh_first->field.sle_next; \
|
||||
} while (0)
|
||||
|
||||
#define SLIST_REMOVE(head, elm, type, field) do { \
|
||||
if ((head)->slh_first == (elm)) { \
|
||||
SLIST_REMOVE_HEAD((head), field); \
|
||||
} else { \
|
||||
struct type *curelm = (head)->slh_first; \
|
||||
\
|
||||
while (curelm->field.sle_next != (elm)) \
|
||||
curelm = curelm->field.sle_next; \
|
||||
curelm->field.sle_next = \
|
||||
curelm->field.sle_next->field.sle_next; \
|
||||
} \
|
||||
_Q_INVALIDATE((elm)->field.sle_next); \
|
||||
} while (0)
|
||||
|
||||
/*
|
||||
* List definitions.
|
||||
*/
|
||||
#define LIST_HEAD(name, type) \
|
||||
struct name { \
|
||||
struct type *lh_first; /* first element */ \
|
||||
}
|
||||
|
||||
#define LIST_HEAD_INITIALIZER(head) \
|
||||
{ NULL }
|
||||
|
||||
#define LIST_ENTRY(type) \
|
||||
struct { \
|
||||
struct type *le_next; /* next element */ \
|
||||
struct type **le_prev; /* address of previous next element */ \
|
||||
}
|
||||
|
||||
/*
|
||||
* List access methods.
|
||||
*/
|
||||
#define LIST_FIRST(head) ((head)->lh_first)
|
||||
#define LIST_END(head) NULL
|
||||
#define LIST_EMPTY(head) (LIST_FIRST(head) == LIST_END(head))
|
||||
#define LIST_NEXT(elm, field) ((elm)->field.le_next)
|
||||
|
||||
#define LIST_FOREACH(var, head, field) \
|
||||
for((var) = LIST_FIRST(head); \
|
||||
(var)!= LIST_END(head); \
|
||||
(var) = LIST_NEXT(var, field))
|
||||
|
||||
#define LIST_FOREACH_SAFE(var, head, field, tvar) \
|
||||
for ((var) = LIST_FIRST(head); \
|
||||
(var) && ((tvar) = LIST_NEXT(var, field), 1); \
|
||||
(var) = (tvar))
|
||||
|
||||
/*
|
||||
* List functions.
|
||||
*/
|
||||
#define LIST_INIT(head) do { \
|
||||
LIST_FIRST(head) = LIST_END(head); \
|
||||
} while (0)
|
||||
|
||||
#define LIST_INSERT_AFTER(listelm, elm, field) do { \
|
||||
if (((elm)->field.le_next = (listelm)->field.le_next) != NULL) \
|
||||
(listelm)->field.le_next->field.le_prev = \
|
||||
&(elm)->field.le_next; \
|
||||
(listelm)->field.le_next = (elm); \
|
||||
(elm)->field.le_prev = &(listelm)->field.le_next; \
|
||||
} while (0)
|
||||
|
||||
#define LIST_INSERT_BEFORE(listelm, elm, field) do { \
|
||||
(elm)->field.le_prev = (listelm)->field.le_prev; \
|
||||
(elm)->field.le_next = (listelm); \
|
||||
*(listelm)->field.le_prev = (elm); \
|
||||
(listelm)->field.le_prev = &(elm)->field.le_next; \
|
||||
} while (0)
|
||||
|
||||
#define LIST_INSERT_HEAD(head, elm, field) do { \
|
||||
if (((elm)->field.le_next = (head)->lh_first) != NULL) \
|
||||
(head)->lh_first->field.le_prev = &(elm)->field.le_next;\
|
||||
(head)->lh_first = (elm); \
|
||||
(elm)->field.le_prev = &(head)->lh_first; \
|
||||
} while (0)
|
||||
|
||||
#define LIST_REMOVE(elm, field) do { \
|
||||
if ((elm)->field.le_next != NULL) \
|
||||
(elm)->field.le_next->field.le_prev = \
|
||||
(elm)->field.le_prev; \
|
||||
*(elm)->field.le_prev = (elm)->field.le_next; \
|
||||
_Q_INVALIDATE((elm)->field.le_prev); \
|
||||
_Q_INVALIDATE((elm)->field.le_next); \
|
||||
} while (0)
|
||||
|
||||
#define LIST_REPLACE(elm, elm2, field) do { \
|
||||
if (((elm2)->field.le_next = (elm)->field.le_next) != NULL) \
|
||||
(elm2)->field.le_next->field.le_prev = \
|
||||
&(elm2)->field.le_next; \
|
||||
(elm2)->field.le_prev = (elm)->field.le_prev; \
|
||||
*(elm2)->field.le_prev = (elm2); \
|
||||
_Q_INVALIDATE((elm)->field.le_prev); \
|
||||
_Q_INVALIDATE((elm)->field.le_next); \
|
||||
} while (0)
|
||||
|
||||
/*
|
||||
* Simple queue definitions.
|
||||
*/
|
||||
#define SIMPLEQ_HEAD(name, type) \
|
||||
struct name { \
|
||||
struct type *sqh_first; /* first element */ \
|
||||
struct type **sqh_last; /* addr of last next element */ \
|
||||
}
|
||||
|
||||
#define SIMPLEQ_HEAD_INITIALIZER(head) \
|
||||
{ NULL, &(head).sqh_first }
|
||||
|
||||
#define SIMPLEQ_ENTRY(type) \
|
||||
struct { \
|
||||
struct type *sqe_next; /* next element */ \
|
||||
}
|
||||
|
||||
/*
|
||||
* Simple queue access methods.
|
||||
*/
|
||||
#define SIMPLEQ_FIRST(head) ((head)->sqh_first)
|
||||
#define SIMPLEQ_END(head) NULL
|
||||
#define SIMPLEQ_EMPTY(head) (SIMPLEQ_FIRST(head) == SIMPLEQ_END(head))
|
||||
#define SIMPLEQ_NEXT(elm, field) ((elm)->field.sqe_next)
|
||||
|
||||
#define SIMPLEQ_FOREACH(var, head, field) \
|
||||
for((var) = SIMPLEQ_FIRST(head); \
|
||||
(var) != SIMPLEQ_END(head); \
|
||||
(var) = SIMPLEQ_NEXT(var, field))
|
||||
|
||||
#define SIMPLEQ_FOREACH_SAFE(var, head, field, tvar) \
|
||||
for ((var) = SIMPLEQ_FIRST(head); \
|
||||
(var) && ((tvar) = SIMPLEQ_NEXT(var, field), 1); \
|
||||
(var) = (tvar))
|
||||
|
||||
/*
|
||||
* Simple queue functions.
|
||||
*/
|
||||
#define SIMPLEQ_INIT(head) do { \
|
||||
(head)->sqh_first = NULL; \
|
||||
(head)->sqh_last = &(head)->sqh_first; \
|
||||
} while (0)
|
||||
|
||||
#define SIMPLEQ_INSERT_HEAD(head, elm, field) do { \
|
||||
if (((elm)->field.sqe_next = (head)->sqh_first) == NULL) \
|
||||
(head)->sqh_last = &(elm)->field.sqe_next; \
|
||||
(head)->sqh_first = (elm); \
|
||||
} while (0)
|
||||
|
||||
#define SIMPLEQ_INSERT_TAIL(head, elm, field) do { \
|
||||
(elm)->field.sqe_next = NULL; \
|
||||
*(head)->sqh_last = (elm); \
|
||||
(head)->sqh_last = &(elm)->field.sqe_next; \
|
||||
} while (0)
|
||||
|
||||
#define SIMPLEQ_INSERT_AFTER(head, listelm, elm, field) do { \
|
||||
if (((elm)->field.sqe_next = (listelm)->field.sqe_next) == NULL)\
|
||||
(head)->sqh_last = &(elm)->field.sqe_next; \
|
||||
(listelm)->field.sqe_next = (elm); \
|
||||
} while (0)
|
||||
|
||||
#define SIMPLEQ_REMOVE_HEAD(head, field) do { \
|
||||
if (((head)->sqh_first = (head)->sqh_first->field.sqe_next) == NULL) \
|
||||
(head)->sqh_last = &(head)->sqh_first; \
|
||||
} while (0)
|
||||
|
||||
#define SIMPLEQ_REMOVE_AFTER(head, elm, field) do { \
|
||||
if (((elm)->field.sqe_next = (elm)->field.sqe_next->field.sqe_next) \
|
||||
== NULL) \
|
||||
(head)->sqh_last = &(elm)->field.sqe_next; \
|
||||
} while (0)
|
||||
|
||||
#define SIMPLEQ_CONCAT(head1, head2) do { \
|
||||
if (!SIMPLEQ_EMPTY((head2))) { \
|
||||
*(head1)->sqh_last = (head2)->sqh_first; \
|
||||
(head1)->sqh_last = (head2)->sqh_last; \
|
||||
SIMPLEQ_INIT((head2)); \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
/*
|
||||
* XOR Simple queue definitions.
|
||||
*/
|
||||
#define XSIMPLEQ_HEAD(name, type) \
|
||||
struct name { \
|
||||
struct type *sqx_first; /* first element */ \
|
||||
struct type **sqx_last; /* addr of last next element */ \
|
||||
unsigned long sqx_cookie; \
|
||||
}
|
||||
|
||||
#define XSIMPLEQ_ENTRY(type) \
|
||||
struct { \
|
||||
struct type *sqx_next; /* next element */ \
|
||||
}
|
||||
|
||||
/*
|
||||
* XOR Simple queue access methods.
|
||||
*/
|
||||
#define XSIMPLEQ_XOR(head, ptr) ((__typeof(ptr))((head)->sqx_cookie ^ \
|
||||
(unsigned long)(ptr)))
|
||||
#define XSIMPLEQ_FIRST(head) XSIMPLEQ_XOR(head, ((head)->sqx_first))
|
||||
#define XSIMPLEQ_END(head) NULL
|
||||
#define XSIMPLEQ_EMPTY(head) (XSIMPLEQ_FIRST(head) == XSIMPLEQ_END(head))
|
||||
#define XSIMPLEQ_NEXT(head, elm, field) XSIMPLEQ_XOR(head, ((elm)->field.sqx_next))
|
||||
|
||||
|
||||
#define XSIMPLEQ_FOREACH(var, head, field) \
|
||||
for ((var) = XSIMPLEQ_FIRST(head); \
|
||||
(var) != XSIMPLEQ_END(head); \
|
||||
(var) = XSIMPLEQ_NEXT(head, var, field))
|
||||
|
||||
#define XSIMPLEQ_FOREACH_SAFE(var, head, field, tvar) \
|
||||
for ((var) = XSIMPLEQ_FIRST(head); \
|
||||
(var) && ((tvar) = XSIMPLEQ_NEXT(head, var, field), 1); \
|
||||
(var) = (tvar))
|
||||
|
||||
/*
|
||||
* XOR Simple queue functions.
|
||||
*/
|
||||
#define XSIMPLEQ_INIT(head) do { \
|
||||
arc4random_buf(&(head)->sqx_cookie, sizeof((head)->sqx_cookie)); \
|
||||
(head)->sqx_first = XSIMPLEQ_XOR(head, NULL); \
|
||||
(head)->sqx_last = XSIMPLEQ_XOR(head, &(head)->sqx_first); \
|
||||
} while (0)
|
||||
|
||||
#define XSIMPLEQ_INSERT_HEAD(head, elm, field) do { \
|
||||
if (((elm)->field.sqx_next = (head)->sqx_first) == \
|
||||
XSIMPLEQ_XOR(head, NULL)) \
|
||||
(head)->sqx_last = XSIMPLEQ_XOR(head, &(elm)->field.sqx_next); \
|
||||
(head)->sqx_first = XSIMPLEQ_XOR(head, (elm)); \
|
||||
} while (0)
|
||||
|
||||
#define XSIMPLEQ_INSERT_TAIL(head, elm, field) do { \
|
||||
(elm)->field.sqx_next = XSIMPLEQ_XOR(head, NULL); \
|
||||
*(XSIMPLEQ_XOR(head, (head)->sqx_last)) = XSIMPLEQ_XOR(head, (elm)); \
|
||||
(head)->sqx_last = XSIMPLEQ_XOR(head, &(elm)->field.sqx_next); \
|
||||
} while (0)
|
||||
|
||||
#define XSIMPLEQ_INSERT_AFTER(head, listelm, elm, field) do { \
|
||||
if (((elm)->field.sqx_next = (listelm)->field.sqx_next) == \
|
||||
XSIMPLEQ_XOR(head, NULL)) \
|
||||
(head)->sqx_last = XSIMPLEQ_XOR(head, &(elm)->field.sqx_next); \
|
||||
(listelm)->field.sqx_next = XSIMPLEQ_XOR(head, (elm)); \
|
||||
} while (0)
|
||||
|
||||
#define XSIMPLEQ_REMOVE_HEAD(head, field) do { \
|
||||
if (((head)->sqx_first = XSIMPLEQ_XOR(head, \
|
||||
(head)->sqx_first)->field.sqx_next) == XSIMPLEQ_XOR(head, NULL)) \
|
||||
(head)->sqx_last = XSIMPLEQ_XOR(head, &(head)->sqx_first); \
|
||||
} while (0)
|
||||
|
||||
#define XSIMPLEQ_REMOVE_AFTER(head, elm, field) do { \
|
||||
if (((elm)->field.sqx_next = XSIMPLEQ_XOR(head, \
|
||||
(elm)->field.sqx_next)->field.sqx_next) \
|
||||
== XSIMPLEQ_XOR(head, NULL)) \
|
||||
(head)->sqx_last = \
|
||||
XSIMPLEQ_XOR(head, &(elm)->field.sqx_next); \
|
||||
} while (0)
|
||||
|
||||
|
||||
/*
|
||||
* Tail queue definitions.
|
||||
*/
|
||||
#define TAILQ_HEAD(name, type) \
|
||||
struct name { \
|
||||
struct type *tqh_first; /* first element */ \
|
||||
struct type **tqh_last; /* addr of last next element */ \
|
||||
}
|
||||
|
||||
#define TAILQ_HEAD_INITIALIZER(head) \
|
||||
{ NULL, &(head).tqh_first }
|
||||
|
||||
#define TAILQ_ENTRY(type) \
|
||||
struct { \
|
||||
struct type *tqe_next; /* next element */ \
|
||||
struct type **tqe_prev; /* address of previous next element */ \
|
||||
}
|
||||
|
||||
/*
|
||||
* Tail queue access methods.
|
||||
*/
|
||||
#define TAILQ_FIRST(head) ((head)->tqh_first)
|
||||
#define TAILQ_END(head) NULL
|
||||
#define TAILQ_NEXT(elm, field) ((elm)->field.tqe_next)
|
||||
#define TAILQ_LAST(head, headname) \
|
||||
(*(((struct headname *)((head)->tqh_last))->tqh_last))
|
||||
/* XXX */
|
||||
#define TAILQ_PREV(elm, headname, field) \
|
||||
(*(((struct headname *)((elm)->field.tqe_prev))->tqh_last))
|
||||
#define TAILQ_EMPTY(head) \
|
||||
(TAILQ_FIRST(head) == TAILQ_END(head))
|
||||
|
||||
#define TAILQ_FOREACH(var, head, field) \
|
||||
for((var) = TAILQ_FIRST(head); \
|
||||
(var) != TAILQ_END(head); \
|
||||
(var) = TAILQ_NEXT(var, field))
|
||||
|
||||
#define TAILQ_FOREACH_SAFE(var, head, field, tvar) \
|
||||
for ((var) = TAILQ_FIRST(head); \
|
||||
(var) != TAILQ_END(head) && \
|
||||
((tvar) = TAILQ_NEXT(var, field), 1); \
|
||||
(var) = (tvar))
|
||||
|
||||
|
||||
#define TAILQ_FOREACH_REVERSE(var, head, headname, field) \
|
||||
for((var) = TAILQ_LAST(head, headname); \
|
||||
(var) != TAILQ_END(head); \
|
||||
(var) = TAILQ_PREV(var, headname, field))
|
||||
|
||||
#define TAILQ_FOREACH_REVERSE_SAFE(var, head, headname, field, tvar) \
|
||||
for ((var) = TAILQ_LAST(head, headname); \
|
||||
(var) != TAILQ_END(head) && \
|
||||
((tvar) = TAILQ_PREV(var, headname, field), 1); \
|
||||
(var) = (tvar))
|
||||
|
||||
/*
|
||||
* Tail queue functions.
|
||||
*/
|
||||
#define TAILQ_INIT(head) do { \
|
||||
(head)->tqh_first = NULL; \
|
||||
(head)->tqh_last = &(head)->tqh_first; \
|
||||
} while (0)
|
||||
|
||||
#define TAILQ_INSERT_HEAD(head, elm, field) do { \
|
||||
if (((elm)->field.tqe_next = (head)->tqh_first) != NULL) \
|
||||
(head)->tqh_first->field.tqe_prev = \
|
||||
&(elm)->field.tqe_next; \
|
||||
else \
|
||||
(head)->tqh_last = &(elm)->field.tqe_next; \
|
||||
(head)->tqh_first = (elm); \
|
||||
(elm)->field.tqe_prev = &(head)->tqh_first; \
|
||||
} while (0)
|
||||
|
||||
#define TAILQ_INSERT_TAIL(head, elm, field) do { \
|
||||
(elm)->field.tqe_next = NULL; \
|
||||
(elm)->field.tqe_prev = (head)->tqh_last; \
|
||||
*(head)->tqh_last = (elm); \
|
||||
(head)->tqh_last = &(elm)->field.tqe_next; \
|
||||
} while (0)
|
||||
|
||||
#define TAILQ_INSERT_AFTER(head, listelm, elm, field) do { \
|
||||
if (((elm)->field.tqe_next = (listelm)->field.tqe_next) != NULL)\
|
||||
(elm)->field.tqe_next->field.tqe_prev = \
|
||||
&(elm)->field.tqe_next; \
|
||||
else \
|
||||
(head)->tqh_last = &(elm)->field.tqe_next; \
|
||||
(listelm)->field.tqe_next = (elm); \
|
||||
(elm)->field.tqe_prev = &(listelm)->field.tqe_next; \
|
||||
} while (0)
|
||||
|
||||
#define TAILQ_INSERT_BEFORE(listelm, elm, field) do { \
|
||||
(elm)->field.tqe_prev = (listelm)->field.tqe_prev; \
|
||||
(elm)->field.tqe_next = (listelm); \
|
||||
*(listelm)->field.tqe_prev = (elm); \
|
||||
(listelm)->field.tqe_prev = &(elm)->field.tqe_next; \
|
||||
} while (0)
|
||||
|
||||
#define TAILQ_REMOVE(head, elm, field) do { \
|
||||
if (((elm)->field.tqe_next) != NULL) \
|
||||
(elm)->field.tqe_next->field.tqe_prev = \
|
||||
(elm)->field.tqe_prev; \
|
||||
else \
|
||||
(head)->tqh_last = (elm)->field.tqe_prev; \
|
||||
*(elm)->field.tqe_prev = (elm)->field.tqe_next; \
|
||||
_Q_INVALIDATE((elm)->field.tqe_prev); \
|
||||
_Q_INVALIDATE((elm)->field.tqe_next); \
|
||||
} while (0)
|
||||
|
||||
#define TAILQ_REPLACE(head, elm, elm2, field) do { \
|
||||
if (((elm2)->field.tqe_next = (elm)->field.tqe_next) != NULL) \
|
||||
(elm2)->field.tqe_next->field.tqe_prev = \
|
||||
&(elm2)->field.tqe_next; \
|
||||
else \
|
||||
(head)->tqh_last = &(elm2)->field.tqe_next; \
|
||||
(elm2)->field.tqe_prev = (elm)->field.tqe_prev; \
|
||||
*(elm2)->field.tqe_prev = (elm2); \
|
||||
_Q_INVALIDATE((elm)->field.tqe_prev); \
|
||||
_Q_INVALIDATE((elm)->field.tqe_next); \
|
||||
} while (0)
|
||||
|
||||
#define TAILQ_CONCAT(head1, head2, field) do { \
|
||||
if (!TAILQ_EMPTY(head2)) { \
|
||||
*(head1)->tqh_last = (head2)->tqh_first; \
|
||||
(head2)->tqh_first->field.tqe_prev = (head1)->tqh_last; \
|
||||
(head1)->tqh_last = (head2)->tqh_last; \
|
||||
TAILQ_INIT((head2)); \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
/*
|
||||
* Singly-linked Tail queue declarations.
|
||||
*/
|
||||
#define STAILQ_HEAD(name, type) \
|
||||
struct name { \
|
||||
struct type *stqh_first; /* first element */ \
|
||||
struct type **stqh_last; /* addr of last next element */ \
|
||||
}
|
||||
|
||||
#define STAILQ_HEAD_INITIALIZER(head) \
|
||||
{ NULL, &(head).stqh_first }
|
||||
|
||||
#define STAILQ_ENTRY(type) \
|
||||
struct { \
|
||||
struct type *stqe_next; /* next element */ \
|
||||
}
|
||||
|
||||
/*
|
||||
* Singly-linked Tail queue access methods.
|
||||
*/
|
||||
#define STAILQ_FIRST(head) ((head)->stqh_first)
|
||||
#define STAILQ_END(head) NULL
|
||||
#define STAILQ_EMPTY(head) (STAILQ_FIRST(head) == STAILQ_END(head))
|
||||
#define STAILQ_NEXT(elm, field) ((elm)->field.stqe_next)
|
||||
|
||||
#define STAILQ_FOREACH(var, head, field) \
|
||||
for ((var) = STAILQ_FIRST(head); \
|
||||
(var) != STAILQ_END(head); \
|
||||
(var) = STAILQ_NEXT(var, field))
|
||||
|
||||
#define STAILQ_FOREACH_SAFE(var, head, field, tvar) \
|
||||
for ((var) = STAILQ_FIRST(head); \
|
||||
(var) && ((tvar) = STAILQ_NEXT(var, field), 1); \
|
||||
(var) = (tvar))
|
||||
|
||||
/*
|
||||
* Singly-linked Tail queue functions.
|
||||
*/
|
||||
#define STAILQ_INIT(head) do { \
|
||||
STAILQ_FIRST((head)) = NULL; \
|
||||
(head)->stqh_last = &STAILQ_FIRST((head)); \
|
||||
} while (0)
|
||||
|
||||
#define STAILQ_INSERT_HEAD(head, elm, field) do { \
|
||||
if ((STAILQ_NEXT((elm), field) = STAILQ_FIRST((head))) == NULL) \
|
||||
(head)->stqh_last = &STAILQ_NEXT((elm), field); \
|
||||
STAILQ_FIRST((head)) = (elm); \
|
||||
} while (0)
|
||||
|
||||
#define STAILQ_INSERT_TAIL(head, elm, field) do { \
|
||||
STAILQ_NEXT((elm), field) = NULL; \
|
||||
*(head)->stqh_last = (elm); \
|
||||
(head)->stqh_last = &STAILQ_NEXT((elm), field); \
|
||||
} while (0)
|
||||
|
||||
#define STAILQ_INSERT_AFTER(head, listelm, elm, field) do { \
|
||||
if ((STAILQ_NEXT((elm), field) = STAILQ_NEXT((elm), field)) == NULL)\
|
||||
(head)->stqh_last = &STAILQ_NEXT((elm), field); \
|
||||
STAILQ_NEXT((elm), field) = (elm); \
|
||||
} while (0)
|
||||
|
||||
#define STAILQ_REMOVE_HEAD(head, field) do { \
|
||||
if ((STAILQ_FIRST((head)) = \
|
||||
STAILQ_NEXT(STAILQ_FIRST((head)), field)) == NULL) \
|
||||
(head)->stqh_last = &STAILQ_FIRST((head)); \
|
||||
} while (0)
|
||||
|
||||
#define STAILQ_REMOVE_AFTER(head, elm, field) do { \
|
||||
if ((STAILQ_NEXT(elm, field) = \
|
||||
STAILQ_NEXT(STAILQ_NEXT(elm, field), field)) == NULL) \
|
||||
(head)->stqh_last = &STAILQ_NEXT((elm), field); \
|
||||
} while (0)
|
||||
|
||||
#define STAILQ_REMOVE(head, elm, type, field) do { \
|
||||
if (STAILQ_FIRST((head)) == (elm)) { \
|
||||
STAILQ_REMOVE_HEAD((head), field); \
|
||||
} else { \
|
||||
struct type *curelm = (head)->stqh_first; \
|
||||
while (STAILQ_NEXT(curelm, field) != (elm)) \
|
||||
curelm = STAILQ_NEXT(curelm, field); \
|
||||
STAILQ_REMOVE_AFTER(head, curelm, field); \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
#define STAILQ_CONCAT(head1, head2) do { \
|
||||
if (!STAILQ_EMPTY((head2))) { \
|
||||
*(head1)->stqh_last = (head2)->stqh_first; \
|
||||
(head1)->stqh_last = (head2)->stqh_last; \
|
||||
STAILQ_INIT((head2)); \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
#define STAILQ_LAST(head, type, field) \
|
||||
(STAILQ_EMPTY((head)) ? NULL : \
|
||||
((struct type *)(void *) \
|
||||
((char *)((head)->stqh_last) - offsetof(struct type, field))))
|
||||
|
||||
#endif /* !_SYS_QUEUE_H_ */
|
||||
@@ -1,8 +1,5 @@
|
||||
/* $NetBSD: tree.h,v 1.8 2004/03/28 19:38:30 provos Exp $ */
|
||||
/* $OpenBSD: tree.h,v 1.7 2002/10/17 21:51:54 art Exp $ */
|
||||
/* $FreeBSD: stable/10/sys/sys/tree.h 189204 2009-03-01 04:57:23Z bms $ */
|
||||
|
||||
/*-
|
||||
/* $OpenBSD: tree.h,v 1.30 2020/10/10 18:03:41 otto Exp $ */
|
||||
/*
|
||||
* Copyright 2002 Niels Provos <provos@citi.umich.edu>
|
||||
* All rights reserved.
|
||||
*
|
||||
@@ -30,9 +27,7 @@
|
||||
#ifndef _SYS_TREE_H_
|
||||
#define _SYS_TREE_H_
|
||||
|
||||
#if HAVE_SYS_CDEFS_H
|
||||
#include <sys/cdefs.h>
|
||||
#endif
|
||||
#include <sys/_null.h>
|
||||
|
||||
/*
|
||||
* This file defines data structures for different types of trees:
|
||||
@@ -71,7 +66,7 @@ struct name { \
|
||||
|
||||
#define SPLAY_INIT(root) do { \
|
||||
(root)->sph_root = NULL; \
|
||||
} while (/*CONSTCOND*/ 0)
|
||||
} while (0)
|
||||
|
||||
#define SPLAY_ENTRY(type) \
|
||||
struct { \
|
||||
@@ -89,32 +84,32 @@ struct { \
|
||||
SPLAY_LEFT((head)->sph_root, field) = SPLAY_RIGHT(tmp, field); \
|
||||
SPLAY_RIGHT(tmp, field) = (head)->sph_root; \
|
||||
(head)->sph_root = tmp; \
|
||||
} while (/*CONSTCOND*/ 0)
|
||||
|
||||
} while (0)
|
||||
|
||||
#define SPLAY_ROTATE_LEFT(head, tmp, field) do { \
|
||||
SPLAY_RIGHT((head)->sph_root, field) = SPLAY_LEFT(tmp, field); \
|
||||
SPLAY_LEFT(tmp, field) = (head)->sph_root; \
|
||||
(head)->sph_root = tmp; \
|
||||
} while (/*CONSTCOND*/ 0)
|
||||
} while (0)
|
||||
|
||||
#define SPLAY_LINKLEFT(head, tmp, field) do { \
|
||||
SPLAY_LEFT(tmp, field) = (head)->sph_root; \
|
||||
tmp = (head)->sph_root; \
|
||||
(head)->sph_root = SPLAY_LEFT((head)->sph_root, field); \
|
||||
} while (/*CONSTCOND*/ 0)
|
||||
} while (0)
|
||||
|
||||
#define SPLAY_LINKRIGHT(head, tmp, field) do { \
|
||||
SPLAY_RIGHT(tmp, field) = (head)->sph_root; \
|
||||
tmp = (head)->sph_root; \
|
||||
(head)->sph_root = SPLAY_RIGHT((head)->sph_root, field); \
|
||||
} while (/*CONSTCOND*/ 0)
|
||||
} while (0)
|
||||
|
||||
#define SPLAY_ASSEMBLE(head, node, left, right, field) do { \
|
||||
SPLAY_RIGHT(left, field) = SPLAY_LEFT((head)->sph_root, field); \
|
||||
SPLAY_LEFT(right, field) = SPLAY_RIGHT((head)->sph_root, field);\
|
||||
SPLAY_LEFT((head)->sph_root, field) = SPLAY_RIGHT(node, field); \
|
||||
SPLAY_RIGHT((head)->sph_root, field) = SPLAY_LEFT(node, field); \
|
||||
} while (/*CONSTCOND*/ 0)
|
||||
} while (0)
|
||||
|
||||
/* Generates prototypes and inline functions */
|
||||
|
||||
@@ -125,7 +120,7 @@ struct type *name##_SPLAY_INSERT(struct name *, struct type *); \
|
||||
struct type *name##_SPLAY_REMOVE(struct name *, struct type *); \
|
||||
\
|
||||
/* Finds the node with the same key as elm */ \
|
||||
static __inline struct type * \
|
||||
static __unused __inline struct type * \
|
||||
name##_SPLAY_FIND(struct name *head, struct type *elm) \
|
||||
{ \
|
||||
if (SPLAY_EMPTY(head)) \
|
||||
@@ -136,7 +131,7 @@ name##_SPLAY_FIND(struct name *head, struct type *elm) \
|
||||
return (NULL); \
|
||||
} \
|
||||
\
|
||||
static __inline struct type * \
|
||||
static __unused __inline struct type * \
|
||||
name##_SPLAY_NEXT(struct name *head, struct type *elm) \
|
||||
{ \
|
||||
name##_SPLAY(head, elm); \
|
||||
@@ -150,7 +145,7 @@ name##_SPLAY_NEXT(struct name *head, struct type *elm) \
|
||||
return (elm); \
|
||||
} \
|
||||
\
|
||||
static __inline struct type * \
|
||||
static __unused __inline struct type * \
|
||||
name##_SPLAY_MIN_MAX(struct name *head, int val) \
|
||||
{ \
|
||||
name##_SPLAY_MINMAX(head, val); \
|
||||
@@ -215,7 +210,7 @@ name##_SPLAY(struct name *head, struct type *elm) \
|
||||
SPLAY_LEFT(&__node, field) = SPLAY_RIGHT(&__node, field) = NULL;\
|
||||
__left = __right = &__node; \
|
||||
\
|
||||
while ((__comp = (cmp)(elm, (head)->sph_root)) != 0) { \
|
||||
while ((__comp = (cmp)(elm, (head)->sph_root))) { \
|
||||
if (__comp < 0) { \
|
||||
__tmp = SPLAY_LEFT((head)->sph_root, field); \
|
||||
if (__tmp == NULL) \
|
||||
@@ -305,7 +300,7 @@ struct name { \
|
||||
|
||||
#define RB_INIT(root) do { \
|
||||
(root)->rbh_root = NULL; \
|
||||
} while (/*CONSTCOND*/ 0)
|
||||
} while (0)
|
||||
|
||||
#define RB_BLACK 0
|
||||
#define RB_RED 1
|
||||
@@ -328,12 +323,12 @@ struct { \
|
||||
RB_PARENT(elm, field) = parent; \
|
||||
RB_LEFT(elm, field) = RB_RIGHT(elm, field) = NULL; \
|
||||
RB_COLOR(elm, field) = RB_RED; \
|
||||
} while (/*CONSTCOND*/ 0)
|
||||
} while (0)
|
||||
|
||||
#define RB_SET_BLACKRED(black, red, field) do { \
|
||||
RB_COLOR(black, field) = RB_BLACK; \
|
||||
RB_COLOR(red, field) = RB_RED; \
|
||||
} while (/*CONSTCOND*/ 0)
|
||||
} while (0)
|
||||
|
||||
#ifndef RB_AUGMENT
|
||||
#define RB_AUGMENT(x) do {} while (0)
|
||||
@@ -341,11 +336,11 @@ struct { \
|
||||
|
||||
#define RB_ROTATE_LEFT(head, elm, tmp, field) do { \
|
||||
(tmp) = RB_RIGHT(elm, field); \
|
||||
if ((RB_RIGHT(elm, field) = RB_LEFT(tmp, field)) != NULL) { \
|
||||
if ((RB_RIGHT(elm, field) = RB_LEFT(tmp, field))) { \
|
||||
RB_PARENT(RB_LEFT(tmp, field), field) = (elm); \
|
||||
} \
|
||||
RB_AUGMENT(elm); \
|
||||
if ((RB_PARENT(tmp, field) = RB_PARENT(elm, field)) != NULL) { \
|
||||
if ((RB_PARENT(tmp, field) = RB_PARENT(elm, field))) { \
|
||||
if ((elm) == RB_LEFT(RB_PARENT(elm, field), field)) \
|
||||
RB_LEFT(RB_PARENT(elm, field), field) = (tmp); \
|
||||
else \
|
||||
@@ -357,15 +352,15 @@ struct { \
|
||||
RB_AUGMENT(tmp); \
|
||||
if ((RB_PARENT(tmp, field))) \
|
||||
RB_AUGMENT(RB_PARENT(tmp, field)); \
|
||||
} while (/*CONSTCOND*/ 0)
|
||||
} while (0)
|
||||
|
||||
#define RB_ROTATE_RIGHT(head, elm, tmp, field) do { \
|
||||
(tmp) = RB_LEFT(elm, field); \
|
||||
if ((RB_LEFT(elm, field) = RB_RIGHT(tmp, field)) != NULL) { \
|
||||
if ((RB_LEFT(elm, field) = RB_RIGHT(tmp, field))) { \
|
||||
RB_PARENT(RB_RIGHT(tmp, field), field) = (elm); \
|
||||
} \
|
||||
RB_AUGMENT(elm); \
|
||||
if ((RB_PARENT(tmp, field) = RB_PARENT(elm, field)) != NULL) { \
|
||||
if ((RB_PARENT(tmp, field) = RB_PARENT(elm, field))) { \
|
||||
if ((elm) == RB_LEFT(RB_PARENT(elm, field), field)) \
|
||||
RB_LEFT(RB_PARENT(elm, field), field) = (tmp); \
|
||||
else \
|
||||
@@ -377,13 +372,13 @@ struct { \
|
||||
RB_AUGMENT(tmp); \
|
||||
if ((RB_PARENT(tmp, field))) \
|
||||
RB_AUGMENT(RB_PARENT(tmp, field)); \
|
||||
} while (/*CONSTCOND*/ 0)
|
||||
} while (0)
|
||||
|
||||
/* Generates prototypes and inline functions */
|
||||
#define RB_PROTOTYPE(name, type, field, cmp) \
|
||||
RB_PROTOTYPE_INTERNAL(name, type, field, cmp,)
|
||||
#define RB_PROTOTYPE_STATIC(name, type, field, cmp) \
|
||||
RB_PROTOTYPE_INTERNAL(name, type, field, cmp, __unused static)
|
||||
RB_PROTOTYPE_INTERNAL(name, type, field, cmp, __attribute__((__unused__)) static)
|
||||
#define RB_PROTOTYPE_INTERNAL(name, type, field, cmp, attr) \
|
||||
attr void name##_RB_INSERT_COLOR(struct name *, struct type *); \
|
||||
attr void name##_RB_REMOVE_COLOR(struct name *, struct type *, struct type *);\
|
||||
@@ -402,13 +397,13 @@ attr struct type *name##_RB_MINMAX(struct name *, int); \
|
||||
#define RB_GENERATE(name, type, field, cmp) \
|
||||
RB_GENERATE_INTERNAL(name, type, field, cmp,)
|
||||
#define RB_GENERATE_STATIC(name, type, field, cmp) \
|
||||
RB_GENERATE_INTERNAL(name, type, field, cmp, __unused static)
|
||||
RB_GENERATE_INTERNAL(name, type, field, cmp, __attribute__((__unused__)) static)
|
||||
#define RB_GENERATE_INTERNAL(name, type, field, cmp, attr) \
|
||||
attr void \
|
||||
name##_RB_INSERT_COLOR(struct name *head, struct type *elm) \
|
||||
{ \
|
||||
struct type *parent, *gparent, *tmp; \
|
||||
while ((parent = RB_PARENT(elm, field)) != NULL && \
|
||||
while ((parent = RB_PARENT(elm, field)) && \
|
||||
RB_COLOR(parent, field) == RB_RED) { \
|
||||
gparent = RB_PARENT(parent, field); \
|
||||
if (parent == RB_LEFT(gparent, field)) { \
|
||||
@@ -472,8 +467,7 @@ name##_RB_REMOVE_COLOR(struct name *head, struct type *parent, struct type *elm)
|
||||
if (RB_RIGHT(tmp, field) == NULL || \
|
||||
RB_COLOR(RB_RIGHT(tmp, field), field) == RB_BLACK) {\
|
||||
struct type *oleft; \
|
||||
if ((oleft = RB_LEFT(tmp, field)) \
|
||||
!= NULL) \
|
||||
if ((oleft = RB_LEFT(tmp, field)))\
|
||||
RB_COLOR(oleft, field) = RB_BLACK;\
|
||||
RB_COLOR(tmp, field) = RB_RED; \
|
||||
RB_ROTATE_RIGHT(head, tmp, oleft, field);\
|
||||
@@ -505,8 +499,7 @@ name##_RB_REMOVE_COLOR(struct name *head, struct type *parent, struct type *elm)
|
||||
if (RB_LEFT(tmp, field) == NULL || \
|
||||
RB_COLOR(RB_LEFT(tmp, field), field) == RB_BLACK) {\
|
||||
struct type *oright; \
|
||||
if ((oright = RB_RIGHT(tmp, field)) \
|
||||
!= NULL) \
|
||||
if ((oright = RB_RIGHT(tmp, field)))\
|
||||
RB_COLOR(oright, field) = RB_BLACK;\
|
||||
RB_COLOR(tmp, field) = RB_RED; \
|
||||
RB_ROTATE_LEFT(head, tmp, oright, field);\
|
||||
@@ -538,7 +531,7 @@ name##_RB_REMOVE(struct name *head, struct type *elm) \
|
||||
else { \
|
||||
struct type *left; \
|
||||
elm = RB_RIGHT(elm, field); \
|
||||
while ((left = RB_LEFT(elm, field)) != NULL) \
|
||||
while ((left = RB_LEFT(elm, field))) \
|
||||
elm = left; \
|
||||
child = RB_RIGHT(elm, field); \
|
||||
parent = RB_PARENT(elm, field); \
|
||||
@@ -571,7 +564,7 @@ name##_RB_REMOVE(struct name *head, struct type *elm) \
|
||||
left = parent; \
|
||||
do { \
|
||||
RB_AUGMENT(left); \
|
||||
} while ((left = RB_PARENT(left, field)) != NULL); \
|
||||
} while ((left = RB_PARENT(left, field))); \
|
||||
} \
|
||||
goto color; \
|
||||
} \
|
||||
@@ -739,14 +732,9 @@ name##_RB_MINMAX(struct name *head, int val) \
|
||||
(x) != NULL; \
|
||||
(x) = name##_RB_NEXT(x))
|
||||
|
||||
#define RB_FOREACH_FROM(x, name, y) \
|
||||
for ((x) = (y); \
|
||||
((x) != NULL) && ((y) = name##_RB_NEXT(x), (x) != NULL); \
|
||||
(x) = (y))
|
||||
|
||||
#define RB_FOREACH_SAFE(x, name, head, y) \
|
||||
for ((x) = RB_MIN(name, head); \
|
||||
((x) != NULL) && ((y) = name##_RB_NEXT(x), (x) != NULL); \
|
||||
((x) != NULL) && ((y) = name##_RB_NEXT(x), 1); \
|
||||
(x) = (y))
|
||||
|
||||
#define RB_FOREACH_REVERSE(x, name, head) \
|
||||
@@ -754,14 +742,265 @@ name##_RB_MINMAX(struct name *head, int val) \
|
||||
(x) != NULL; \
|
||||
(x) = name##_RB_PREV(x))
|
||||
|
||||
#define RB_FOREACH_REVERSE_FROM(x, name, y) \
|
||||
for ((x) = (y); \
|
||||
((x) != NULL) && ((y) = name##_RB_PREV(x), (x) != NULL); \
|
||||
(x) = (y))
|
||||
|
||||
#define RB_FOREACH_REVERSE_SAFE(x, name, head, y) \
|
||||
for ((x) = RB_MAX(name, head); \
|
||||
((x) != NULL) && ((y) = name##_RB_PREV(x), (x) != NULL); \
|
||||
((x) != NULL) && ((y) = name##_RB_PREV(x), 1); \
|
||||
(x) = (y))
|
||||
|
||||
|
||||
/*
|
||||
* Copyright (c) 2016 David Gwynne <dlg@openbsd.org>
|
||||
*
|
||||
* Permission to use, copy, modify, and distribute this software for any
|
||||
* purpose with or without fee is hereby granted, provided that the above
|
||||
* copyright notice and this permission notice appear in all copies.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
|
||||
struct rb_type {
|
||||
int (*t_compare)(const void *, const void *);
|
||||
void (*t_augment)(void *);
|
||||
unsigned int t_offset; /* offset of rb_entry in type */
|
||||
};
|
||||
|
||||
struct rb_tree {
|
||||
struct rb_entry *rbt_root;
|
||||
};
|
||||
|
||||
struct rb_entry {
|
||||
struct rb_entry *rbt_parent;
|
||||
struct rb_entry *rbt_left;
|
||||
struct rb_entry *rbt_right;
|
||||
unsigned int rbt_color;
|
||||
};
|
||||
|
||||
#define RBT_HEAD(_name, _type) \
|
||||
struct _name { \
|
||||
struct rb_tree rbh_root; \
|
||||
}
|
||||
|
||||
#define RBT_ENTRY(_type) struct rb_entry
|
||||
|
||||
static inline void
|
||||
_rb_init(struct rb_tree *rbt)
|
||||
{
|
||||
rbt->rbt_root = NULL;
|
||||
}
|
||||
|
||||
static inline int
|
||||
_rb_empty(struct rb_tree *rbt)
|
||||
{
|
||||
return (rbt->rbt_root == NULL);
|
||||
}
|
||||
|
||||
void *_rb_insert(const struct rb_type *, struct rb_tree *, void *);
|
||||
void *_rb_remove(const struct rb_type *, struct rb_tree *, void *);
|
||||
void *_rb_find(const struct rb_type *, struct rb_tree *, const void *);
|
||||
void *_rb_nfind(const struct rb_type *, struct rb_tree *, const void *);
|
||||
void *_rb_root(const struct rb_type *, struct rb_tree *);
|
||||
void *_rb_min(const struct rb_type *, struct rb_tree *);
|
||||
void *_rb_max(const struct rb_type *, struct rb_tree *);
|
||||
void *_rb_next(const struct rb_type *, void *);
|
||||
void *_rb_prev(const struct rb_type *, void *);
|
||||
void *_rb_left(const struct rb_type *, void *);
|
||||
void *_rb_right(const struct rb_type *, void *);
|
||||
void *_rb_parent(const struct rb_type *, void *);
|
||||
void _rb_set_left(const struct rb_type *, void *, void *);
|
||||
void _rb_set_right(const struct rb_type *, void *, void *);
|
||||
void _rb_set_parent(const struct rb_type *, void *, void *);
|
||||
void _rb_poison(const struct rb_type *, void *, unsigned long);
|
||||
int _rb_check(const struct rb_type *, void *, unsigned long);
|
||||
|
||||
#define RBT_INITIALIZER(_head) { { NULL } }
|
||||
|
||||
#define RBT_PROTOTYPE(_name, _type, _field, _cmp) \
|
||||
extern const struct rb_type *const _name##_RBT_TYPE; \
|
||||
\
|
||||
__unused static inline void \
|
||||
_name##_RBT_INIT(struct _name *head) \
|
||||
{ \
|
||||
_rb_init(&head->rbh_root); \
|
||||
} \
|
||||
\
|
||||
__unused static inline struct _type * \
|
||||
_name##_RBT_INSERT(struct _name *head, struct _type *elm) \
|
||||
{ \
|
||||
return _rb_insert(_name##_RBT_TYPE, &head->rbh_root, elm); \
|
||||
} \
|
||||
\
|
||||
__unused static inline struct _type * \
|
||||
_name##_RBT_REMOVE(struct _name *head, struct _type *elm) \
|
||||
{ \
|
||||
return _rb_remove(_name##_RBT_TYPE, &head->rbh_root, elm); \
|
||||
} \
|
||||
\
|
||||
__unused static inline struct _type * \
|
||||
_name##_RBT_FIND(struct _name *head, const struct _type *key) \
|
||||
{ \
|
||||
return _rb_find(_name##_RBT_TYPE, &head->rbh_root, key); \
|
||||
} \
|
||||
\
|
||||
__unused static inline struct _type * \
|
||||
_name##_RBT_NFIND(struct _name *head, const struct _type *key) \
|
||||
{ \
|
||||
return _rb_nfind(_name##_RBT_TYPE, &head->rbh_root, key); \
|
||||
} \
|
||||
\
|
||||
__unused static inline struct _type * \
|
||||
_name##_RBT_ROOT(struct _name *head) \
|
||||
{ \
|
||||
return _rb_root(_name##_RBT_TYPE, &head->rbh_root); \
|
||||
} \
|
||||
\
|
||||
__unused static inline int \
|
||||
_name##_RBT_EMPTY(struct _name *head) \
|
||||
{ \
|
||||
return _rb_empty(&head->rbh_root); \
|
||||
} \
|
||||
\
|
||||
__unused static inline struct _type * \
|
||||
_name##_RBT_MIN(struct _name *head) \
|
||||
{ \
|
||||
return _rb_min(_name##_RBT_TYPE, &head->rbh_root); \
|
||||
} \
|
||||
\
|
||||
__unused static inline struct _type * \
|
||||
_name##_RBT_MAX(struct _name *head) \
|
||||
{ \
|
||||
return _rb_max(_name##_RBT_TYPE, &head->rbh_root); \
|
||||
} \
|
||||
\
|
||||
__unused static inline struct _type * \
|
||||
_name##_RBT_NEXT(struct _type *elm) \
|
||||
{ \
|
||||
return _rb_next(_name##_RBT_TYPE, elm); \
|
||||
} \
|
||||
\
|
||||
__unused static inline struct _type * \
|
||||
_name##_RBT_PREV(struct _type *elm) \
|
||||
{ \
|
||||
return _rb_prev(_name##_RBT_TYPE, elm); \
|
||||
} \
|
||||
\
|
||||
__unused static inline struct _type * \
|
||||
_name##_RBT_LEFT(struct _type *elm) \
|
||||
{ \
|
||||
return _rb_left(_name##_RBT_TYPE, elm); \
|
||||
} \
|
||||
\
|
||||
__unused static inline struct _type * \
|
||||
_name##_RBT_RIGHT(struct _type *elm) \
|
||||
{ \
|
||||
return _rb_right(_name##_RBT_TYPE, elm); \
|
||||
} \
|
||||
\
|
||||
__unused static inline struct _type * \
|
||||
_name##_RBT_PARENT(struct _type *elm) \
|
||||
{ \
|
||||
return _rb_parent(_name##_RBT_TYPE, elm); \
|
||||
} \
|
||||
\
|
||||
__unused static inline void \
|
||||
_name##_RBT_SET_LEFT(struct _type *elm, struct _type *left) \
|
||||
{ \
|
||||
_rb_set_left(_name##_RBT_TYPE, elm, left); \
|
||||
} \
|
||||
\
|
||||
__unused static inline void \
|
||||
_name##_RBT_SET_RIGHT(struct _type *elm, struct _type *right) \
|
||||
{ \
|
||||
_rb_set_right(_name##_RBT_TYPE, elm, right); \
|
||||
} \
|
||||
\
|
||||
__unused static inline void \
|
||||
_name##_RBT_SET_PARENT(struct _type *elm, struct _type *parent) \
|
||||
{ \
|
||||
_rb_set_parent(_name##_RBT_TYPE, elm, parent); \
|
||||
} \
|
||||
\
|
||||
__unused static inline void \
|
||||
_name##_RBT_POISON(struct _type *elm, unsigned long poison) \
|
||||
{ \
|
||||
_rb_poison(_name##_RBT_TYPE, elm, poison); \
|
||||
} \
|
||||
\
|
||||
__unused static inline int \
|
||||
_name##_RBT_CHECK(struct _type *elm, unsigned long poison) \
|
||||
{ \
|
||||
return _rb_check(_name##_RBT_TYPE, elm, poison); \
|
||||
}
|
||||
|
||||
#define RBT_GENERATE_INTERNAL(_name, _type, _field, _cmp, _aug) \
|
||||
static int \
|
||||
_name##_RBT_COMPARE(const void *lptr, const void *rptr) \
|
||||
{ \
|
||||
const struct _type *l = lptr, *r = rptr; \
|
||||
return _cmp(l, r); \
|
||||
} \
|
||||
static const struct rb_type _name##_RBT_INFO = { \
|
||||
_name##_RBT_COMPARE, \
|
||||
_aug, \
|
||||
offsetof(struct _type, _field), \
|
||||
}; \
|
||||
const struct rb_type *const _name##_RBT_TYPE = &_name##_RBT_INFO
|
||||
|
||||
#define RBT_GENERATE_AUGMENT(_name, _type, _field, _cmp, _aug) \
|
||||
static void \
|
||||
_name##_RBT_AUGMENT(void *ptr) \
|
||||
{ \
|
||||
struct _type *p = ptr; \
|
||||
return _aug(p); \
|
||||
} \
|
||||
RBT_GENERATE_INTERNAL(_name, _type, _field, _cmp, _name##_RBT_AUGMENT)
|
||||
|
||||
#define RBT_GENERATE(_name, _type, _field, _cmp) \
|
||||
RBT_GENERATE_INTERNAL(_name, _type, _field, _cmp, NULL)
|
||||
|
||||
#define RBT_INIT(_name, _head) _name##_RBT_INIT(_head)
|
||||
#define RBT_INSERT(_name, _head, _elm) _name##_RBT_INSERT(_head, _elm)
|
||||
#define RBT_REMOVE(_name, _head, _elm) _name##_RBT_REMOVE(_head, _elm)
|
||||
#define RBT_FIND(_name, _head, _key) _name##_RBT_FIND(_head, _key)
|
||||
#define RBT_NFIND(_name, _head, _key) _name##_RBT_NFIND(_head, _key)
|
||||
#define RBT_ROOT(_name, _head) _name##_RBT_ROOT(_head)
|
||||
#define RBT_EMPTY(_name, _head) _name##_RBT_EMPTY(_head)
|
||||
#define RBT_MIN(_name, _head) _name##_RBT_MIN(_head)
|
||||
#define RBT_MAX(_name, _head) _name##_RBT_MAX(_head)
|
||||
#define RBT_NEXT(_name, _elm) _name##_RBT_NEXT(_elm)
|
||||
#define RBT_PREV(_name, _elm) _name##_RBT_PREV(_elm)
|
||||
#define RBT_LEFT(_name, _elm) _name##_RBT_LEFT(_elm)
|
||||
#define RBT_RIGHT(_name, _elm) _name##_RBT_RIGHT(_elm)
|
||||
#define RBT_PARENT(_name, _elm) _name##_RBT_PARENT(_elm)
|
||||
#define RBT_SET_LEFT(_name, _elm, _l) _name##_RBT_SET_LEFT(_elm, _l)
|
||||
#define RBT_SET_RIGHT(_name, _elm, _r) _name##_RBT_SET_RIGHT(_elm, _r)
|
||||
#define RBT_SET_PARENT(_name, _elm, _p) _name##_RBT_SET_PARENT(_elm, _p)
|
||||
#define RBT_POISON(_name, _elm, _p) _name##_RBT_POISON(_elm, _p)
|
||||
#define RBT_CHECK(_name, _elm, _p) _name##_RBT_CHECK(_elm, _p)
|
||||
|
||||
#define RBT_FOREACH(_e, _name, _head) \
|
||||
for ((_e) = RBT_MIN(_name, (_head)); \
|
||||
(_e) != NULL; \
|
||||
(_e) = RBT_NEXT(_name, (_e)))
|
||||
|
||||
#define RBT_FOREACH_SAFE(_e, _name, _head, _n) \
|
||||
for ((_e) = RBT_MIN(_name, (_head)); \
|
||||
(_e) != NULL && ((_n) = RBT_NEXT(_name, (_e)), 1); \
|
||||
(_e) = (_n))
|
||||
|
||||
#define RBT_FOREACH_REVERSE(_e, _name, _head) \
|
||||
for ((_e) = RBT_MAX(_name, (_head)); \
|
||||
(_e) != NULL; \
|
||||
(_e) = RBT_PREV(_name, (_e)))
|
||||
|
||||
#define RBT_FOREACH_REVERSE_SAFE(_e, _name, _head, _n) \
|
||||
for ((_e) = RBT_MAX(_name, (_head)); \
|
||||
(_e) != NULL && ((_n) = RBT_PREV(_name, (_e)), 1); \
|
||||
(_e) = (_n))
|
||||
|
||||
#endif /* _SYS_TREE_H_ */
|
||||
25
include/sys/types.h
Normal file
25
include/sys/types.h
Normal file
@@ -0,0 +1,25 @@
|
||||
/*
|
||||
* Public domain
|
||||
* sys/types.h compatibility shim
|
||||
*/
|
||||
|
||||
#include_next <sys/types.h>
|
||||
|
||||
#ifndef LIBCOMPAT_SYS_TYPES_H
|
||||
#define LIBCOMPAT_SYS_TYPES_H
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#ifdef __MINGW32__
|
||||
#include <_bsd_types.h>
|
||||
#endif
|
||||
|
||||
#if !defined(HAVE_ATTRIBUTE__DEAD) && !defined(__dead)
|
||||
#define __dead __attribute__((__noreturn__))
|
||||
#endif
|
||||
|
||||
#if !defined(HAVE_ATTRIBUTE__BOUNDED__) && !defined(__bounded__)
|
||||
# define __bounded__(x, y, z)
|
||||
#endif
|
||||
|
||||
#endif
|
||||
794
main.c
Normal file
794
main.c
Normal file
@@ -0,0 +1,794 @@
|
||||
/*
|
||||
* Copyright (c) 2019-2021 Job Snijders <job@sobornost.net>
|
||||
* Copyright (c) 2007-2019 Alexandre Snarskii <snar@snar.spb.ru>
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
|
||||
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
||||
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
||||
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||
* SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#if HAVE_CONFIG_H
|
||||
#include <config.h>
|
||||
#endif
|
||||
|
||||
#include <sys/types.h>
|
||||
#include <sys/socket.h>
|
||||
|
||||
#include <ctype.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <strings.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include "extern.h"
|
||||
#include "sx_report.h"
|
||||
|
||||
extern int debug_expander;
|
||||
extern int debug_aggregation;
|
||||
extern int pipelining;
|
||||
extern int expand_special_asn;
|
||||
|
||||
static int
|
||||
usage(int ecode)
|
||||
{
|
||||
printf("\nUsage: bgpq4 [-h host[:port]] [-S sources] [-E|G|H <num>"
|
||||
"|f <num>|t] [-46ABbdJjKNnpwXz] [-R len] <OBJECTS> ... "
|
||||
"[EXCEPT <OBJECTS> ...]\n");
|
||||
printf("\nVendor targets:\n");
|
||||
printf(" no option : Cisco IOS Classic (default)\n");
|
||||
printf(" -X : Cisco IOS XR\n");
|
||||
printf(" -U : Huawei\n");
|
||||
printf(" -u : Huawei XPL\n");
|
||||
printf(" -j : JSON\n");
|
||||
printf(" -J : Juniper Junos\n");
|
||||
printf(" -K : MikroTik RouterOSv6\n");
|
||||
printf(" -K7 : MikroTik RouterOSv7\n");
|
||||
printf(" -b : NIC.CZ BIRD\n");
|
||||
printf(" -N : Nokia SR OS (Classic CLI)\n");
|
||||
printf(" -n : Nokia SR OS (MD-CLI)\n");
|
||||
printf(" -B : OpenBSD OpenBGPD\n");
|
||||
printf(" -e : Arista EOS\n");
|
||||
printf(" -F fmt : User defined format (example: '-F %%n/%%l')\n");
|
||||
|
||||
printf("\nInput filters:\n");
|
||||
printf(" -4 : generate IPv4 prefix-lists (default)\n");
|
||||
printf(" -6 : generate IPv6 prefix-lists\n");
|
||||
printf(" -m len : maximum prefix length (default: 32 for IPv4, "
|
||||
"128 for IPv6)\n");
|
||||
printf(" -L depth : limit recursion depth (default: unlimited)\n"),
|
||||
printf(" -S sources: only use specified IRR sources, in the specified "
|
||||
"order (comma separated)\n");
|
||||
printf(" -w : 'validate' AS numbers: accept only ones with "
|
||||
"registered routes\n");
|
||||
|
||||
printf("\nOutput modifiers:\n");
|
||||
printf(" -A : try to aggregate prefix-lists/route-filters\n");
|
||||
printf(" -E : generate extended access-list (Cisco), "
|
||||
"route-filter (Juniper)\n"
|
||||
" [ip|ipv6]-prefix-list (Nokia) or prefix-set "
|
||||
"(OpenBGPD)\n");
|
||||
printf(" -f number : generate input as-path access-list\n");
|
||||
printf(" -G number : generate output as-path access-list\n");
|
||||
printf(" -H number : generate origin as-lists (JunOS only)\n");
|
||||
printf(" -M match : extra match conditions for JunOS route-filters\n");
|
||||
printf(" -l name : use specified name for generated access/prefix/.."
|
||||
" list\n");
|
||||
printf(" -R len : allow more specific routes up to specified masklen\n");
|
||||
printf(" -r len : allow more specific routes from masklen specified\n");
|
||||
printf(" -s : generate sequence numbers in prefix-lists (IOS only)\n");
|
||||
printf(" -t : generate as-sets for OpenBGPD (OpenBGPD 6.4+), BIRD "
|
||||
"and JSON formats\n");
|
||||
printf(" -z : generate route-filter-list (Junos only)\n");
|
||||
printf(" -W len : specify max-entries on as-path/as-list line (use 0 for "
|
||||
"infinity)\n");
|
||||
|
||||
printf("\nUtility operations:\n");
|
||||
printf(" -d : generate some debugging output\n");
|
||||
printf(" -h host : host running IRRD software (default: rr.ntt.net)\n"
|
||||
" use 'host:port' to specify alternate port\n");
|
||||
printf(" -T : disable pipelining (not recommended)\n");
|
||||
printf(" -v : print version and exit\n");
|
||||
printf("\n" PACKAGE_NAME " version: " PACKAGE_VERSION " "
|
||||
"(https://github.com/bgp/bgpq4)\n");
|
||||
exit(ecode);
|
||||
}
|
||||
|
||||
static void
|
||||
version(void)
|
||||
{
|
||||
printf(PACKAGE_NAME " - a versatile utility to generate BGP filters\n"
|
||||
"version: " PACKAGE_VERSION "\n"
|
||||
"website: https://github.com/bgp/bgpq4\n"
|
||||
"maintainer: Job Snijders <job@sobornost.net>\n");
|
||||
exit(0);
|
||||
}
|
||||
|
||||
static void
|
||||
exclusive(void)
|
||||
{
|
||||
fprintf(stderr,"-E, -F, -K , -f <asnum>, -G <asnum>, and -t are mutually"
|
||||
" exclusive\n");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
static void
|
||||
vendor_exclusive(void)
|
||||
{
|
||||
fprintf(stderr, "-b (BIRD), -B (OpenBGPD), -F (formatted), -J (Junos),"
|
||||
" -j (JSON), -K[7] (Microtik ROS), -N (Nokia SR OS Classic),"
|
||||
" -n (Nokia SR OS MD-CLI), -U (Huawei), -u (Huawei XPL),"
|
||||
"-e (Arista) and -X (IOS XR) options are mutually exclusive\n");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
static int
|
||||
parseasnumber(struct bgpq_expander *expander, char *asnstr)
|
||||
{
|
||||
char *eon = NULL;
|
||||
|
||||
expander->asnumber = strtoul(asnstr, &eon, 10);
|
||||
if (expander->asnumber < 1 || expander->asnumber > (65535ul * 65535)) {
|
||||
sx_report(SX_FATAL, "Invalid AS number: %s\n", asnstr);
|
||||
exit(1);
|
||||
}
|
||||
if (eon && *eon == '.') {
|
||||
/* -f 3.3, for example */
|
||||
uint32_t loas = strtoul(eon + 1, &eon, 10);
|
||||
if (expander->asnumber > 65535) {
|
||||
/* should prevent incorrect numbers like 65537.1 */
|
||||
sx_report(SX_FATAL,"Invalid AS number: %s\n", asnstr);
|
||||
exit(1);
|
||||
}
|
||||
if (loas < 1 || loas > 65535) {
|
||||
sx_report(SX_FATAL,"Invalid AS number: %s\n", asnstr);
|
||||
exit(1);
|
||||
}
|
||||
if (eon && *eon) {
|
||||
sx_report(SX_FATAL,"Invalid symbol in AS number: "
|
||||
"%c (%s)\n", *eon, asnstr);
|
||||
exit(1);
|
||||
}
|
||||
expander->asnumber=(expander->asnumber << 16) + loas;
|
||||
} else if (eon && *eon) {
|
||||
sx_report(SX_FATAL,"Invalid symbol in AS number: %c (%s)\n",
|
||||
*eon, asnstr);
|
||||
exit(1);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
main(int argc, char* argv[])
|
||||
{
|
||||
int c;
|
||||
struct bgpq_expander expander;
|
||||
int af = AF_INET, selectedipv4 = 0, exceptmode = 0;
|
||||
int widthSet = 0, aggregate = 0, refine = 0, refineLow = 0;
|
||||
unsigned long maxlen = 0;
|
||||
|
||||
#ifdef HAVE_PLEDGE
|
||||
if (pledge("stdio inet dns", NULL) == -1) {
|
||||
sx_report(SX_ERROR, "pledge() failed");
|
||||
exit(1);
|
||||
}
|
||||
#endif
|
||||
|
||||
bgpq_expander_init(&expander, af);
|
||||
|
||||
if (getenv("IRRD_SOURCES"))
|
||||
expander.sources=getenv("IRRD_SOURCES");
|
||||
|
||||
while ((c = getopt(argc, argv,
|
||||
"467a:AbBdDEeF:S:jJKf:l:L:m:M:NnpW:r:R:G:H:tTh:UuwXsvz")) != EOF) {
|
||||
switch (c) {
|
||||
case '4':
|
||||
/* do nothing, expander already configured for IPv4 */
|
||||
if (expander.family == AF_INET6) {
|
||||
sx_report(SX_FATAL, "-4 and -6 are mutually "
|
||||
"exclusive\n");
|
||||
exit(1);
|
||||
}
|
||||
selectedipv4 = 1;
|
||||
break;
|
||||
case '6':
|
||||
if (selectedipv4) {
|
||||
sx_report(SX_FATAL, "-4 and -6 are mutually "
|
||||
"exclusive\n");
|
||||
exit(1);
|
||||
}
|
||||
af = AF_INET6;
|
||||
expander.family = AF_INET6;
|
||||
expander.tree->family = AF_INET6;
|
||||
break;
|
||||
case '7':
|
||||
if (expander.vendor != V_MIKROTIK6) {
|
||||
sx_report(SX_FATAL, "'7' can only be used after -K\n");
|
||||
exit(1);
|
||||
}
|
||||
expander.vendor = V_MIKROTIK7;
|
||||
break;
|
||||
case 'a':
|
||||
parseasnumber(&expander, optarg);
|
||||
break;
|
||||
case 'A':
|
||||
if (aggregate)
|
||||
debug_aggregation++;
|
||||
aggregate = 1;
|
||||
break;
|
||||
case 'b':
|
||||
if (expander.vendor)
|
||||
vendor_exclusive();
|
||||
expander.vendor = V_BIRD;
|
||||
break;
|
||||
case 'B':
|
||||
if (expander.vendor)
|
||||
vendor_exclusive();
|
||||
expander.vendor = V_OPENBGPD;
|
||||
break;
|
||||
case 'd':
|
||||
debug_expander++;
|
||||
break;
|
||||
case 'E':
|
||||
if (expander.generation)
|
||||
exclusive();
|
||||
expander.generation = T_EACL;
|
||||
break;
|
||||
case 'e':
|
||||
if (expander.vendor)
|
||||
vendor_exclusive();
|
||||
expander.vendor = V_ARISTA;
|
||||
expander.sequence = 1;
|
||||
break;
|
||||
case 'F':
|
||||
if (expander.vendor)
|
||||
exclusive();
|
||||
expander.vendor = V_FORMAT;
|
||||
expander.format = optarg;
|
||||
break;
|
||||
case 'f':
|
||||
if (expander.generation)
|
||||
exclusive();
|
||||
expander.generation = T_ASPATH;
|
||||
parseasnumber(&expander, optarg);
|
||||
break;
|
||||
case 'G':
|
||||
if (expander.generation)
|
||||
exclusive();
|
||||
expander.generation = T_OASPATH;
|
||||
parseasnumber(&expander, optarg);
|
||||
break;
|
||||
case 'H':
|
||||
if (expander.generation)
|
||||
exclusive();
|
||||
expander.generation = T_ASLIST;
|
||||
parseasnumber(&expander, optarg);
|
||||
break;
|
||||
case 'h':
|
||||
{
|
||||
char *d = strchr(optarg, ':');
|
||||
expander.server = optarg;
|
||||
if (d) {
|
||||
*d = 0;
|
||||
expander.port = d + 1;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case 'J':
|
||||
if (expander.vendor)
|
||||
vendor_exclusive();
|
||||
expander.vendor = V_JUNIPER;
|
||||
break;
|
||||
case 'j':
|
||||
if (expander.vendor)
|
||||
vendor_exclusive();
|
||||
expander.vendor = V_JSON;
|
||||
break;
|
||||
case 'K':
|
||||
if (expander.vendor)
|
||||
vendor_exclusive();
|
||||
expander.vendor = V_MIKROTIK6;
|
||||
break;
|
||||
case 'r':
|
||||
refineLow = strtoul(optarg, NULL, 10);
|
||||
if (!refineLow) {
|
||||
sx_report(SX_FATAL, "Invalid refineLow value:"
|
||||
" %s\n", optarg);
|
||||
exit(1);
|
||||
}
|
||||
break;
|
||||
case 'R':
|
||||
refine = strtoul(optarg, NULL, 10);
|
||||
if (!refine) {
|
||||
sx_report(SX_FATAL,"Invalid refine length:"
|
||||
" %s\n", optarg);
|
||||
exit(1);
|
||||
}
|
||||
break;
|
||||
case 'l':
|
||||
expander.name = optarg;
|
||||
break;
|
||||
case 'L':
|
||||
expander.maxdepth = strtol(optarg, NULL, 10);
|
||||
if (expander.maxdepth < 1) {
|
||||
sx_report(SX_FATAL, "Invalid maximum recursion"
|
||||
" (-L): %s\n", optarg);
|
||||
exit(1);
|
||||
}
|
||||
break;
|
||||
case 'm':
|
||||
maxlen=strtoul(optarg, NULL, 10);
|
||||
if (!maxlen) {
|
||||
sx_report(SX_FATAL, "Invalid maxlen (-m): %s\n",
|
||||
optarg);
|
||||
exit(1);
|
||||
}
|
||||
break;
|
||||
case 'M':
|
||||
{
|
||||
char *mc, *md;
|
||||
expander.match = strdup(optarg);
|
||||
mc = md = expander.match;
|
||||
while (*mc) {
|
||||
if (*mc == '\\') {
|
||||
if (*(mc + 1) == '\n') {
|
||||
*md = '\n';
|
||||
md++;
|
||||
mc += 2;
|
||||
} else if (*(mc + 1) == 'r') {
|
||||
*md = '\r';
|
||||
md++;
|
||||
mc += 2;
|
||||
} else if (*(mc + 1) == 't') {
|
||||
*md = '\t';
|
||||
md++;
|
||||
mc += 2;
|
||||
} else if (*(mc + 1) == '\\') {
|
||||
*md = '\\';
|
||||
md++;
|
||||
mc += 2;
|
||||
} else {
|
||||
sx_report(SX_FATAL, "Unsupported"
|
||||
" escape \%c (0x%2.2x) in "
|
||||
"'%s'\n",
|
||||
isprint(*mc) ? *mc : 20,
|
||||
*mc, optarg);
|
||||
exit(1);
|
||||
}
|
||||
} else {
|
||||
if (mc != md) {
|
||||
*md = *mc;
|
||||
}
|
||||
md++;
|
||||
mc++;
|
||||
}
|
||||
}
|
||||
*md = 0;
|
||||
}
|
||||
break;
|
||||
case 'N':
|
||||
if (expander.vendor)
|
||||
vendor_exclusive();
|
||||
expander.vendor = V_NOKIA;
|
||||
break;
|
||||
case 'n':
|
||||
if (expander.vendor)
|
||||
vendor_exclusive();
|
||||
expander.vendor = V_NOKIA_MD;
|
||||
break;
|
||||
case 'p':
|
||||
expand_special_asn = 1;
|
||||
break;
|
||||
case 't':
|
||||
if (expander.generation)
|
||||
exclusive();
|
||||
expander.generation = T_ASSET;
|
||||
break;
|
||||
case 'T':
|
||||
pipelining = 0;
|
||||
break;
|
||||
case 's':
|
||||
expander.sequence = 1;
|
||||
break;
|
||||
case 'S':
|
||||
expander.sources = optarg;
|
||||
break;
|
||||
case 'U':
|
||||
if (expander.vendor)
|
||||
exclusive();
|
||||
expander.vendor = V_HUAWEI;
|
||||
break;
|
||||
case 'u':
|
||||
if (expander.vendor)
|
||||
exclusive();
|
||||
expander.vendor = V_HUAWEI_XPL;
|
||||
break;
|
||||
case 'W':
|
||||
expander.aswidth = atoi(optarg);
|
||||
if (expander.aswidth < 0) {
|
||||
sx_report(SX_FATAL,"Invalid as-width: %s\n", optarg);
|
||||
exit(1);
|
||||
}
|
||||
widthSet = 1;
|
||||
break;
|
||||
case 'w':
|
||||
expander.validate_asns = 1;
|
||||
break;
|
||||
case 'X':
|
||||
if (expander.vendor)
|
||||
vendor_exclusive();
|
||||
expander.vendor = V_CISCO_XR;
|
||||
break;
|
||||
case 'v':
|
||||
version();
|
||||
break;
|
||||
case 'z':
|
||||
if (expander.generation)
|
||||
exclusive();
|
||||
expander.generation = T_ROUTE_FILTER_LIST;
|
||||
break;
|
||||
default:
|
||||
usage(1);
|
||||
}
|
||||
}
|
||||
|
||||
argc -= optind;
|
||||
argv += optind;
|
||||
|
||||
if (!widthSet) {
|
||||
if (expander.generation == T_ASPATH) {
|
||||
int vendor = expander.vendor;
|
||||
switch (vendor) {
|
||||
case V_ARISTA:
|
||||
case V_CISCO:
|
||||
case V_MIKROTIK6:
|
||||
case V_MIKROTIK7:
|
||||
expander.aswidth = 4;
|
||||
break;
|
||||
case V_CISCO_XR:
|
||||
expander.aswidth = 6;
|
||||
break;
|
||||
case V_JUNIPER:
|
||||
case V_NOKIA:
|
||||
case V_NOKIA_MD:
|
||||
expander.aswidth = 8;
|
||||
break;
|
||||
case V_BIRD:
|
||||
expander.aswidth = 10;
|
||||
break;
|
||||
}
|
||||
} else if (expander.generation == T_OASPATH) {
|
||||
int vendor = expander.vendor;
|
||||
switch (vendor) {
|
||||
case V_ARISTA:
|
||||
case V_CISCO:
|
||||
expander.aswidth = 5;
|
||||
break;
|
||||
case V_CISCO_XR:
|
||||
expander.aswidth = 7;
|
||||
break;
|
||||
case V_JUNIPER:
|
||||
case V_NOKIA:
|
||||
case V_NOKIA_MD:
|
||||
expander.aswidth = 8;
|
||||
break;
|
||||
}
|
||||
} else if (expander.generation == T_ASLIST) {
|
||||
int vendor = expander.vendor;
|
||||
switch (vendor) {
|
||||
case V_JUNIPER:
|
||||
expander.aswidth = 8;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!expander.generation)
|
||||
expander.generation = T_PREFIXLIST;
|
||||
|
||||
if (expander.vendor == V_CISCO_XR
|
||||
&& expander.generation != T_PREFIXLIST
|
||||
&& expander.generation != T_ASPATH
|
||||
&& expander.generation != T_OASPATH) {
|
||||
sx_report(SX_FATAL, "Sorry, only prefix-sets and as-paths "
|
||||
"supported for IOS XR\n");
|
||||
}
|
||||
if (expander.vendor == V_BIRD
|
||||
&& expander.generation != T_PREFIXLIST
|
||||
&& expander.generation != T_ASPATH
|
||||
&& expander.generation != T_ASSET) {
|
||||
sx_report(SX_FATAL, "Sorry, only prefix-lists and as-paths/as-sets "
|
||||
"supported for BIRD output\n");
|
||||
}
|
||||
if (expander.vendor == V_JSON
|
||||
&& expander.generation != T_PREFIXLIST
|
||||
&& expander.generation != T_ASPATH
|
||||
&& expander.generation != T_ASSET) {
|
||||
sx_report(SX_FATAL, "Sorry, only prefix-lists and as-paths/as-sets "
|
||||
"supported for JSON output\n");
|
||||
}
|
||||
|
||||
if (expander.vendor == V_FORMAT
|
||||
&& expander.generation != T_PREFIXLIST)
|
||||
sx_report(SX_FATAL, "Sorry, only prefix-lists supported in formatted "
|
||||
"output\n");
|
||||
|
||||
if (expander.vendor == V_HUAWEI
|
||||
&& expander.generation != T_ASPATH
|
||||
&& expander.generation != T_OASPATH
|
||||
&& expander.generation != T_PREFIXLIST)
|
||||
sx_report(SX_FATAL, "Sorry, only as-paths and prefix-lists supported "
|
||||
"for Huawei output\n");
|
||||
|
||||
if (expander.generation == T_ROUTE_FILTER_LIST
|
||||
&& expander.vendor != V_JUNIPER)
|
||||
sx_report(SX_FATAL, "Route-filter-lists (-z) supported for Juniper (-J)"
|
||||
" output only\n");
|
||||
|
||||
if (expander.generation == T_ASSET
|
||||
&& expander.vendor != V_JSON
|
||||
&& expander.vendor != V_OPENBGPD
|
||||
&& expander.vendor != V_BIRD)
|
||||
sx_report(SX_FATAL, "As-Sets (-t) supported for JSON (-j), OpenBGPD "
|
||||
"(-B) and BIRD (-b) output only\n");
|
||||
|
||||
if (aggregate
|
||||
&& expander.vendor == V_JUNIPER
|
||||
&& expander.generation == T_PREFIXLIST) {
|
||||
sx_report(SX_FATAL, "Sorry, aggregation (-A) does not work in"
|
||||
" Juniper prefix-lists\nYou can try route-filters (-E) "
|
||||
"or route-filter-lists (-z) instead of prefix-lists\n.");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
if (aggregate
|
||||
&& (expander.vendor == V_NOKIA_MD || expander.vendor == V_NOKIA)
|
||||
&& expander.generation != T_PREFIXLIST) {
|
||||
sx_report(SX_FATAL, "Sorry, aggregation (-A) is not supported with "
|
||||
"ip-prefix-lists (-E) on Nokia.\n");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
if (refine
|
||||
&& (expander.vendor == V_NOKIA_MD || expander.vendor == V_NOKIA)
|
||||
&& expander.generation != T_PREFIXLIST) {
|
||||
sx_report(SX_FATAL, "Sorry, more-specifics (-R) is not supported with "
|
||||
"ip-prefix-lists (-E) on Nokia.\n");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
if (refineLow
|
||||
&& (expander.vendor == V_NOKIA_MD || expander.vendor == V_NOKIA)
|
||||
&& expander.generation != T_PREFIXLIST) {
|
||||
sx_report(SX_FATAL, "Sorry, more-specifics (-r) is not supported with "
|
||||
"ip-prefix-lists (-E) on Nokia.\n");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
if (aggregate && expander.generation < T_PREFIXLIST) {
|
||||
sx_report(SX_FATAL, "Sorry, aggregation (-A) used only for prefix-"
|
||||
"lists, extended access-lists and route-filters\n");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
if (expander.sequence
|
||||
&& (expander.vendor != V_CISCO && expander.vendor != V_ARISTA)) {
|
||||
sx_report(SX_FATAL, "Sorry, prefix-lists sequencing (-s) supported"
|
||||
" only for IOS and EOS\n");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
if (expander.sequence && expander.generation < T_PREFIXLIST) {
|
||||
sx_report(SX_FATAL, "Sorry, prefix-lists sequencing (-s) can't be "
|
||||
" used for non prefix-list\n");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
if (refineLow && !refine) {
|
||||
if (expander.family == AF_INET)
|
||||
refine = 32;
|
||||
else
|
||||
refine = 128;
|
||||
}
|
||||
|
||||
if (refineLow && refineLow > refine)
|
||||
sx_report(SX_FATAL, "Incompatible values for -r %u and -R %u\n",
|
||||
refineLow, refine);
|
||||
|
||||
if (refine || refineLow) {
|
||||
if (expander.family == AF_INET6 && refine > 128) {
|
||||
sx_report(SX_FATAL, "Invalid value for refine(-R): %u (1-128 for"
|
||||
" IPv6)\n", refine);
|
||||
} else if (expander.family == AF_INET6 && refineLow > 128) {
|
||||
sx_report(SX_FATAL, "Invalid value for refineLow(-r): %u (1-128 for"
|
||||
" IPv6)\n", refineLow);
|
||||
} else if (expander.family == AF_INET && refine > 32) {
|
||||
sx_report(SX_FATAL, "Invalid value for refine(-R): %u (1-32 for"
|
||||
" IPv4)\n", refine);
|
||||
} else if (expander.family == AF_INET && refineLow > 32) {
|
||||
sx_report(SX_FATAL, "Invalid value for refineLow(-r): %u (1-32 for"
|
||||
" IPv4)\n", refineLow);
|
||||
}
|
||||
|
||||
if (expander.vendor == V_JUNIPER && expander.generation == T_PREFIXLIST) {
|
||||
if (refine) {
|
||||
sx_report(SX_FATAL, "Sorry, more-specific filters (-R %u) "
|
||||
"is not supported for Juniper prefix-lists.\n"
|
||||
"Use router-filters (-E) or route-filter-lists (-z) "
|
||||
"instead\n", refine);
|
||||
} else {
|
||||
sx_report(SX_FATAL, "Sorry, more-specific filters (-r %u) "
|
||||
"is not supported for Juniper prefix-lists.\n"
|
||||
"Use route-filters (-E) or route-filter-lists (-z) "
|
||||
"instead\n", refineLow);
|
||||
}
|
||||
}
|
||||
|
||||
if (expander.generation < T_PREFIXLIST) {
|
||||
if (refine)
|
||||
sx_report(SX_FATAL, "Sorry, more-specific filter (-R %u) "
|
||||
"supported only with prefix-list generation\n", refine);
|
||||
else
|
||||
sx_report(SX_FATAL, "Sorry, more-specific filter (-r %u) "
|
||||
"supported only with prefix-list generation\n", refineLow);
|
||||
}
|
||||
}
|
||||
|
||||
if (maxlen) {
|
||||
if ((expander.family == AF_INET6 && maxlen > 128)
|
||||
|| (expander.family == AF_INET && maxlen > 32)) {
|
||||
sx_report(SX_FATAL, "Invalid value for max-prefixlen: %lu (1-128 "
|
||||
"for IPv6, 1-32 for IPv4)\n", maxlen);
|
||||
exit(1);
|
||||
} else if ((expander.family == AF_INET6 && maxlen < 128)
|
||||
|| (expander.family == AF_INET && maxlen < 32)) {
|
||||
/*
|
||||
* inet6/128 and inet4/32 does not make sense - all
|
||||
* routes will be accepted, so save some CPU cycles :)
|
||||
*/
|
||||
expander.maxlen = maxlen;
|
||||
}
|
||||
} else if (expander.family == AF_INET)
|
||||
expander.maxlen = 32;
|
||||
else if (expander.family == AF_INET6)
|
||||
expander.maxlen = 128;
|
||||
|
||||
if (expander.generation == T_EACL && expander.vendor == V_CISCO
|
||||
&& expander.family == AF_INET6) {
|
||||
sx_report(SX_FATAL,"Sorry, ipv6 access-lists not supported "
|
||||
"for Cisco yet.\n");
|
||||
}
|
||||
|
||||
if (expander.match != NULL
|
||||
&& (expander.vendor != V_JUNIPER || expander.generation != T_EACL)) {
|
||||
sx_report(SX_FATAL, "Sorry, extra match conditions (-M) can be used "
|
||||
"only with Juniper route-filters\n");
|
||||
}
|
||||
|
||||
if ((expander.generation == T_ASPATH
|
||||
|| expander.generation == T_OASPATH
|
||||
|| expander.generation == T_ASLIST)
|
||||
&& af != AF_INET && !expander.validate_asns) {
|
||||
sx_report(SX_FATAL, "Sorry, -6 makes no sense with as-path (-f/-G) or as-list (-H) "
|
||||
"generation\n");
|
||||
}
|
||||
|
||||
if (expander.validate_asns
|
||||
&& expander.generation != T_ASPATH
|
||||
&& expander.generation != T_OASPATH
|
||||
&& expander.generation != T_ASLIST) {
|
||||
sx_report(SX_FATAL, "Sorry, -w makes sense only for as-path "
|
||||
"(-f/-G) generation\n");
|
||||
}
|
||||
|
||||
if (!argv[0])
|
||||
usage(1);
|
||||
|
||||
while (argv[0]) {
|
||||
char *obj = argv[0];
|
||||
char *delim = strstr(argv[0], "::");
|
||||
if (delim) {
|
||||
expander.usesource = 1;
|
||||
obj = delim + 2;
|
||||
}
|
||||
if (!strcmp(argv[0], "EXCEPT")) {
|
||||
exceptmode = 1;
|
||||
} else if (exceptmode) {
|
||||
bgpq_expander_add_stop(&expander, argv[0]);
|
||||
} else if (!strncasecmp(obj, "AS-", 3)) {
|
||||
bgpq_expander_add_asset(&expander, argv[0]);
|
||||
} else if (!strncasecmp(obj, "RS-", 3)) {
|
||||
bgpq_expander_add_rset(&expander, argv[0]);
|
||||
} else if (!strncasecmp(obj, "AS", 2)) {
|
||||
char *ec;
|
||||
if ((ec = strchr(obj, ':'))) {
|
||||
if (!strncasecmp(ec + 1, "AS-", 3)) {
|
||||
bgpq_expander_add_asset(&expander, argv[0]);
|
||||
} else if (!strncasecmp(ec + 1, "RS-", 3)) {
|
||||
bgpq_expander_add_rset(&expander, argv[0]);
|
||||
} else {
|
||||
SX_DEBUG(debug_expander,"Unknown sub-as"
|
||||
" object %s\n", argv[0]);
|
||||
}
|
||||
} else {
|
||||
bgpq_expander_add_as(&expander, argv[0]);
|
||||
}
|
||||
} else {
|
||||
char *ec = strchr(argv[0], '^');
|
||||
if (!ec && !bgpq_expander_add_prefix(&expander, argv[0])) {
|
||||
sx_report(SX_ERROR, "Unable to add prefix %s "
|
||||
"(bad prefix or address-family)\n", argv[0]);
|
||||
exit(1);
|
||||
} else if (ec && !bgpq_expander_add_prefix_range(&expander,
|
||||
argv[0])) {
|
||||
sx_report(SX_ERROR, "Unable to add prefix-range "
|
||||
"%s (bad range or address-family)\n",
|
||||
argv[0]);
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
argv++;
|
||||
argc--;
|
||||
}
|
||||
|
||||
if (!bgpq_expand(&expander))
|
||||
exit(1);
|
||||
|
||||
if (refine)
|
||||
sx_radix_tree_refine(expander.tree, refine);
|
||||
|
||||
if (refineLow)
|
||||
sx_radix_tree_refineLow(expander.tree, refineLow);
|
||||
|
||||
if (aggregate)
|
||||
sx_radix_tree_aggregate(expander.tree);
|
||||
|
||||
switch (expander.generation) {
|
||||
case T_NONE:
|
||||
sx_report(SX_FATAL,"Unreachable point");
|
||||
exit(1);
|
||||
case T_ASPATH:
|
||||
bgpq4_print_aspath(stdout, &expander);
|
||||
break;
|
||||
case T_OASPATH:
|
||||
bgpq4_print_oaspath(stdout, &expander);
|
||||
break;
|
||||
case T_ASLIST:
|
||||
bgpq4_print_aslist(stdout, &expander);
|
||||
break;
|
||||
case T_ASSET:
|
||||
bgpq4_print_asset(stdout, &expander);
|
||||
break;
|
||||
case T_PREFIXLIST:
|
||||
bgpq4_print_prefixlist(stdout, &expander);
|
||||
break;
|
||||
case T_EACL:
|
||||
bgpq4_print_eacl(stdout, &expander);
|
||||
break;
|
||||
case T_ROUTE_FILTER_LIST:
|
||||
bgpq4_print_route_filter_list(stdout, &expander);
|
||||
break;
|
||||
}
|
||||
|
||||
expander_freeall(&expander);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -1,13 +0,0 @@
|
||||
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.or
|
||||
g/TR/html4/loose.dtd">
|
||||
<html><head><style type='text/css'>
|
||||
h1 { color: #3c78b5; border-bottom: 3px solid #3c78b5; font-size: 180%; }
|
||||
h2 { color: #3c78b5; border-bottom: 2px solid #3c78b5; font-size: 140%; }
|
||||
h3 { color: #3c78b5; border-bottom: 1px dotted #3c78b5; font-size: 129%; }
|
||||
em { color: #0000FF; }
|
||||
code { font-size:12px; background-color:#f8f8ff; border:1px; }
|
||||
pre { border: 1px dotted #3c78b5; background-color: #f8f8ff; margin: 1em 1em;}
|
||||
body { width: 80%; margin: 0 3em; }
|
||||
ul { list-style: none; }
|
||||
</style></head><body>
|
||||
|
||||
72
strlcpy.c
72
strlcpy.c
@@ -1,72 +0,0 @@
|
||||
/* $OpenBSD: strlcpy.c,v 1.4 1999/05/01 18:56:41 millert Exp $ */
|
||||
|
||||
/*
|
||||
* Copyright (c) 1998 Todd C. Miller <Todd.Miller@courtesan.com>
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* 3. The name of the author may not be used to endorse or promote products
|
||||
* derived from this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
|
||||
* INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
|
||||
* AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
|
||||
* THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
||||
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
|
||||
* OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
|
||||
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
|
||||
* OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
|
||||
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#if HAVE_CONFIG_H
|
||||
#include "config.h"
|
||||
#endif
|
||||
|
||||
#ifndef HAVE_STRLCPY
|
||||
|
||||
#include <sys/types.h>
|
||||
#include <string.h>
|
||||
|
||||
/*
|
||||
* Copy src to string dst of size siz. At most siz-1 characters
|
||||
* will be copied. Always NUL terminates (unless siz == 0).
|
||||
* Returns strlen(src); if retval >= siz, truncation occurred.
|
||||
*/
|
||||
size_t
|
||||
strlcpy(dst, src, siz)
|
||||
char *dst;
|
||||
const char *src;
|
||||
size_t siz;
|
||||
{
|
||||
char *d = dst;
|
||||
const char *s = src;
|
||||
size_t n = siz;
|
||||
|
||||
/* Copy as many bytes as will fit */
|
||||
if (n != 0 && --n != 0) {
|
||||
do {
|
||||
if ((*d++ = *s++) == 0)
|
||||
break;
|
||||
} while (--n != 0);
|
||||
}
|
||||
|
||||
/* Not enough room in dst, add NUL and traverse rest of src */
|
||||
if (n == 0) {
|
||||
if (siz != 0)
|
||||
*d = '\0'; /* NUL-terminate dst */
|
||||
while (*s++)
|
||||
;
|
||||
}
|
||||
|
||||
return(s - src - 1); /* count does not include NUL */
|
||||
}
|
||||
#endif
|
||||
148
sx_maxsockbuf.c
148
sx_maxsockbuf.c
@@ -1,3 +1,30 @@
|
||||
/*
|
||||
* Copyright (c) 2019-2020 Job Snijders <job@sobornost.net>
|
||||
* Copyright (c) 2007-2019 Alexandre Snarskii <snar@snar.spb.ru>
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
|
||||
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
||||
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
||||
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||
* SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#include <sys/types.h>
|
||||
#include <sys/socket.h>
|
||||
#include <netinet/in.h>
|
||||
@@ -9,81 +36,82 @@
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include "extern.h"
|
||||
#include "sx_report.h"
|
||||
|
||||
#ifndef SX_MAXSOCKBUF_MAX
|
||||
#define SX_MAXSOCKBUF_MAX (2*1024*1024)
|
||||
#endif
|
||||
|
||||
int
|
||||
sx_maxsockbuf(int s, int dir)
|
||||
{
|
||||
int optval=0, voptval;
|
||||
int hiconf=-1, loconf=-1;
|
||||
unsigned int voptlen;
|
||||
int phase=0, iterations=0;
|
||||
{
|
||||
int optval = 0, voptval;
|
||||
int hiconf = -1, loconf = -1;
|
||||
unsigned int voptlen;
|
||||
int phase = 0, iterations = 0;
|
||||
|
||||
if(s<0) {
|
||||
sx_report(SX_FATAL,"Unable to maximize sockbuf on invalid socket %i\n",
|
||||
s);
|
||||
if (s < 0) {
|
||||
sx_report(SX_FATAL,"Unable to maximize sockbuf on invalid "
|
||||
"socket %i\n", s);
|
||||
exit(1);
|
||||
};
|
||||
}
|
||||
|
||||
voptlen = sizeof(optval);
|
||||
|
||||
voptlen=sizeof(optval);
|
||||
if(getsockopt(s,SOL_SOCKET,dir,(void*)&optval,&voptlen)==-1) {
|
||||
sx_report(SX_ERROR,"initial getsockopt failed: %s\n", strerror(errno));
|
||||
if (getsockopt(s, SOL_SOCKET, dir, (void*)&optval, &voptlen) == -1) {
|
||||
sx_report(SX_ERROR,"initial getsockopt failed: %s\n",
|
||||
strerror(errno));
|
||||
return -1;
|
||||
};
|
||||
}
|
||||
|
||||
for(;;) {
|
||||
for (;;) {
|
||||
iterations++;
|
||||
if(phase==0) optval<<=1;
|
||||
else {
|
||||
if(optval==(hiconf+loconf)/2) break;
|
||||
optval=(hiconf+loconf)/2;
|
||||
};
|
||||
if(optval>SX_MAXSOCKBUF_MAX && phase==0)
|
||||
break;
|
||||
|
||||
if(setsockopt(s,SOL_SOCKET,dir,(void*)&optval,sizeof(optval))==-1)
|
||||
{
|
||||
if(phase==0) phase=1;
|
||||
hiconf=optval;
|
||||
if (phase == 0)
|
||||
optval <<= 1;
|
||||
else {
|
||||
if (optval == (hiconf + loconf) / 2)
|
||||
break;
|
||||
optval = (hiconf + loconf) / 2;
|
||||
}
|
||||
|
||||
if (setsockopt(s, SOL_SOCKET, dir, (void*)&optval,
|
||||
sizeof(optval)) == -1) {
|
||||
|
||||
if (phase == 0)
|
||||
phase = 1;
|
||||
|
||||
hiconf = optval;
|
||||
|
||||
continue;
|
||||
} else {
|
||||
loconf=optval;
|
||||
};
|
||||
} else {
|
||||
loconf = optval;
|
||||
}
|
||||
|
||||
voptlen=sizeof(voptval);
|
||||
voptlen = sizeof(voptval);
|
||||
|
||||
if(getsockopt(s,SOL_SOCKET,dir,(void*)&voptval,&voptlen)==-1) {
|
||||
sx_report(SX_ERROR,"getsockopt failed: %s\n", strerror(errno));
|
||||
if (getsockopt(s, SOL_SOCKET, dir, (void*)&voptval,
|
||||
&voptlen) == -1) {
|
||||
sx_report(SX_ERROR,"getsockopt failed: %s\n",
|
||||
strerror(errno));
|
||||
return -1;
|
||||
} else if(voptval<optval) {
|
||||
if(phase==0) {
|
||||
phase=1; optval>>=1; continue;
|
||||
} else if(phase==1) {
|
||||
phase=2; optval-=2048; continue;
|
||||
} else break;
|
||||
} else if(voptval>=SX_MAXSOCKBUF_MAX) {
|
||||
/* ... and getsockopt not failed and voptval>=optval. Do not allow
|
||||
* to increase sockbuf too much even in case OS permits it */
|
||||
break;
|
||||
};
|
||||
};
|
||||
} else if (voptval < optval) {
|
||||
if (phase == 0) {
|
||||
phase = 1;
|
||||
optval >>= 1;
|
||||
continue;
|
||||
} else if (phase == 1) {
|
||||
phase = 2;
|
||||
optval -= 2048;
|
||||
continue;
|
||||
} else
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
voptlen=sizeof(voptval);
|
||||
if(getsockopt(s,SOL_SOCKET,dir,(void*)&voptval,&voptlen)==-1) {
|
||||
sx_report(SX_ERROR,"getsockopt(final stage) failed: %s\n",
|
||||
strerror(errno));
|
||||
voptlen = sizeof(voptval);
|
||||
if (getsockopt(s, SOL_SOCKET, dir, (void*)&voptval,
|
||||
&voptlen) == -1) {
|
||||
sx_report(SX_ERROR,"getsockopt(final stage) failed: %s\n",
|
||||
strerror(errno));
|
||||
return -1;
|
||||
} else {
|
||||
/*
|
||||
printf("Finally got %i bytes of recvspace in %i interations\n",
|
||||
voptval, iterations);
|
||||
*/
|
||||
};
|
||||
return voptval;
|
||||
};
|
||||
} else
|
||||
return voptval;
|
||||
}
|
||||
|
||||
@@ -1,8 +0,0 @@
|
||||
#ifndef SX_MAXSOCKBUF_H_
|
||||
#define SX_MAXSOCKBUF_H_
|
||||
|
||||
/* s - number of opened socket, dir is either SO_SNDBUF or SO_RCVBUF */
|
||||
int sx_maxsockbuf(int s, int dir);
|
||||
|
||||
#endif
|
||||
|
||||
1899
sx_prefix.c
1899
sx_prefix.c
File diff suppressed because it is too large
Load Diff
115
sx_prefix.h
115
sx_prefix.h
@@ -1,3 +1,29 @@
|
||||
/*
|
||||
* Copyright (c) 2007-2019 Alexandre Snarskii <snar@snar.spb.ru>
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
|
||||
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
||||
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
||||
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||
* SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#ifndef _SX_PREFIX_H_
|
||||
#define _SX_PREFIX_H_
|
||||
|
||||
@@ -8,7 +34,7 @@
|
||||
|
||||
typedef struct sx_prefix {
|
||||
int family;
|
||||
int masklen;
|
||||
unsigned int masklen;
|
||||
union {
|
||||
struct in_addr addr;
|
||||
struct in6_addr addr6;
|
||||
@@ -17,58 +43,59 @@ typedef struct sx_prefix {
|
||||
} sx_prefix_t;
|
||||
|
||||
typedef struct sx_radix_node {
|
||||
struct sx_radix_node* parent, *l, *r, *son;
|
||||
void* payload;
|
||||
unsigned int isGlue:1;
|
||||
unsigned int isAggregated:1;
|
||||
unsigned int isAggregate:1;
|
||||
unsigned int aggregateLow;
|
||||
unsigned int aggregateHi;
|
||||
struct sx_prefix *prefix;
|
||||
struct sx_radix_node *parent, *l, *r, *son;
|
||||
void *payload;
|
||||
unsigned int isGlue:1;
|
||||
unsigned int isAggregated:1;
|
||||
unsigned int isAggregate:1;
|
||||
unsigned int aggregateLow;
|
||||
unsigned int aggregateHi;
|
||||
struct sx_prefix *prefix;
|
||||
} sx_radix_node_t;
|
||||
|
||||
typedef struct sx_radix_tree {
|
||||
int family;
|
||||
struct sx_radix_node* head;
|
||||
int family;
|
||||
struct sx_radix_node *head;
|
||||
} sx_radix_tree_t;
|
||||
|
||||
/* most common operations with the tree is to: lookup/insert/unlink */
|
||||
struct sx_radix_node* sx_radix_tree_lookup(struct sx_radix_tree* tree,
|
||||
struct sx_prefix* prefix);
|
||||
struct sx_radix_node* sx_radix_tree_insert(struct sx_radix_tree* tree,
|
||||
struct sx_prefix* prefix);
|
||||
void sx_radix_tree_unlink(struct sx_radix_tree* t, struct sx_radix_node* n);
|
||||
struct sx_radix_node* sx_radix_tree_lookup_exact(struct sx_radix_tree* tree,
|
||||
struct sx_prefix* prefix);
|
||||
struct sx_radix_node *sx_radix_tree_lookup(struct sx_radix_tree *tree,
|
||||
struct sx_prefix *prefix);
|
||||
struct sx_radix_node *sx_radix_tree_insert(struct sx_radix_tree *tree,
|
||||
struct sx_prefix *prefix);
|
||||
void sx_radix_tree_unlink(struct sx_radix_tree *t, struct sx_radix_node *n);
|
||||
struct sx_radix_node *sx_radix_tree_lookup_exact(struct sx_radix_tree *tree,
|
||||
struct sx_prefix *prefix);
|
||||
|
||||
struct sx_prefix* sx_prefix_alloc(struct sx_prefix* p);
|
||||
void sx_prefix_destroy(struct sx_prefix* p);
|
||||
void sx_radix_node_destroy(struct sx_radix_node* p);
|
||||
void sx_prefix_adjust_masklen(struct sx_prefix* p);
|
||||
struct sx_prefix* sx_prefix_new(int af, char* text);
|
||||
int sx_prefix_parse(struct sx_prefix* p, int af, char* text);
|
||||
int sx_prefix_range_parse(struct sx_radix_tree* t, int af, int ml, char* text);
|
||||
int sx_prefix_fprint(FILE* f, struct sx_prefix* p);
|
||||
int sx_prefix_snprintf(struct sx_prefix* p, char* rbuffer, int srb);
|
||||
int sx_prefix_snprintf_sep(struct sx_prefix* p, char* rbuffer, int srb, char*);
|
||||
int sx_prefix_snprintf_fmt(struct sx_prefix* p, char* rbuffer, int srb,
|
||||
const char* name, const char* fmt);
|
||||
int sx_prefix_jsnprintf(struct sx_prefix* p, char* rbuffer, int srb);
|
||||
struct sx_radix_tree* sx_radix_tree_new(int af);
|
||||
struct sx_radix_node* sx_radix_node_new(struct sx_prefix* prefix);
|
||||
struct sx_prefix* sx_prefix_overlay(struct sx_prefix* p, int n);
|
||||
int sx_radix_tree_empty(struct sx_radix_tree* t);
|
||||
void sx_radix_node_fprintf(struct sx_radix_node* node, void* udata);
|
||||
int sx_radix_node_foreach(struct sx_radix_node* node,
|
||||
void (*func)(struct sx_radix_node*, void*), void* udata);
|
||||
int sx_radix_tree_foreach(struct sx_radix_tree* tree,
|
||||
void (*func)(struct sx_radix_node*, void*), void* udata);
|
||||
int sx_radix_tree_aggregate(struct sx_radix_tree* tree);
|
||||
int sx_radix_tree_refine(struct sx_radix_tree* tree, unsigned refine);
|
||||
int sx_radix_tree_refineLow(struct sx_radix_tree* tree, unsigned refineLow);
|
||||
struct sx_prefix *sx_prefix_alloc(struct sx_prefix *p);
|
||||
void sx_prefix_free(struct sx_prefix *p);
|
||||
void sx_radix_node_destroy(struct sx_radix_node *p);
|
||||
void sx_prefix_adjust_masklen(struct sx_prefix *p);
|
||||
struct sx_prefix *sx_prefix_new(int af, char *text);
|
||||
int sx_prefix_parse(struct sx_prefix *p, int af, char *text);
|
||||
int sx_prefix_range_parse(struct sx_radix_tree *t, int af, unsigned int ml, char *text);
|
||||
int sx_prefix_fprint(FILE *f, struct sx_prefix *p);
|
||||
int sx_prefix_snprintf(struct sx_prefix *p, char *rbuffer, int srb);
|
||||
int sx_prefix_snprintf_sep(struct sx_prefix *p, char *rbuffer, int srb, char *);
|
||||
void sx_prefix_snprintf_fmt(struct sx_prefix *p, FILE *f,
|
||||
const char *name, const char *fmt, unsigned int aggregateLow,
|
||||
unsigned int aggregateHi);
|
||||
int sx_prefix_jsnprintf(struct sx_prefix *p, char *rbuffer, int srb);
|
||||
struct sx_radix_tree *sx_radix_tree_new(int af);
|
||||
struct sx_radix_node *sx_radix_node_new(struct sx_prefix *prefix);
|
||||
struct sx_prefix *sx_prefix_overlay(struct sx_prefix *p, int n);
|
||||
int sx_radix_tree_empty(struct sx_radix_tree *t);
|
||||
void sx_radix_node_fprintf(struct sx_radix_node *node, void *udata);
|
||||
int sx_radix_node_foreach(struct sx_radix_node *node,
|
||||
void (*func)(struct sx_radix_node *, void *), void *udata);
|
||||
int sx_radix_tree_foreach(struct sx_radix_tree *tree,
|
||||
void (*func)(struct sx_radix_node *, void *), void *udata);
|
||||
int sx_radix_tree_aggregate(struct sx_radix_tree *tree);
|
||||
int sx_radix_tree_refine(struct sx_radix_tree *tree, unsigned refine);
|
||||
int sx_radix_tree_refineLow(struct sx_radix_tree *tree, unsigned refineLow);
|
||||
|
||||
#ifndef HAVE_STRLCPY
|
||||
size_t strlcpy(char* dst, const char* src, size_t size);
|
||||
size_t strlcpy(char *dst, const char *src, size_t size);
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
||||
119
sx_report.c
119
sx_report.c
@@ -1,6 +1,28 @@
|
||||
#if HAVE_CONFIG_H
|
||||
#include <config.h>
|
||||
#endif
|
||||
/*
|
||||
* Copyright (c) 2007-2019 Alexandre Snarskii <snar@snar.spb.ru>
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
|
||||
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
||||
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
||||
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||
* SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#include <errno.h>
|
||||
#include <inttypes.h>
|
||||
@@ -18,80 +40,87 @@ static int reportStderr=1;
|
||||
static char const*
|
||||
sx_report_name(sx_report_t t)
|
||||
{
|
||||
switch(t) {
|
||||
case SX_MISFEATURE: return "MISSING FEATURE:";
|
||||
case SX_FATAL: return "FATAL ERROR:";
|
||||
case SX_ERROR: return "ERROR:";
|
||||
case SX_NOTICE: return "Notice:";
|
||||
case SX_DEBUG: return "Debug:";
|
||||
};
|
||||
switch (t) {
|
||||
case SX_MISFEATURE:
|
||||
return "MISSING FEATURE:";
|
||||
case SX_FATAL:
|
||||
return "FATAL ERROR:";
|
||||
case SX_ERROR:
|
||||
return "ERROR:";
|
||||
case SX_NOTICE:
|
||||
return "Notice:";
|
||||
case SX_DEBUG:
|
||||
return "Debug:";
|
||||
}
|
||||
|
||||
return "...... HMMMMM.... ERROR... \n";
|
||||
};
|
||||
}
|
||||
|
||||
int
|
||||
sx_report(sx_report_t t, char* fmt, ...)
|
||||
{
|
||||
char buffer[65536];
|
||||
va_list ap;
|
||||
va_start(ap,fmt);
|
||||
va_start(ap, fmt);
|
||||
|
||||
vsnprintf(buffer,sizeof(buffer),fmt,ap);
|
||||
vsnprintf(buffer, sizeof(buffer), fmt, ap);
|
||||
va_end(ap);
|
||||
|
||||
if(reportStderr) {
|
||||
fputs(sx_report_name(t),stderr);
|
||||
fputs(buffer,stderr);
|
||||
if (reportStderr) {
|
||||
fputs(sx_report_name(t), stderr);
|
||||
fputs(buffer, stderr);
|
||||
} else {
|
||||
switch(t) {
|
||||
case SX_FATAL:
|
||||
syslog(LOG_ERR,"FATAL ERROR: %s", buffer);
|
||||
break;
|
||||
case SX_MISFEATURE:
|
||||
case SX_ERROR:
|
||||
syslog(LOG_ERR,"ERROR: %s", buffer);
|
||||
break;
|
||||
case SX_NOTICE:
|
||||
syslog(LOG_WARNING,"Notice: %s", buffer);
|
||||
break;
|
||||
case SX_DEBUG:
|
||||
syslog(LOG_DEBUG,"Debug: %s", buffer);
|
||||
break;
|
||||
};
|
||||
};
|
||||
case SX_FATAL:
|
||||
syslog(LOG_ERR,"FATAL ERROR: %s", buffer);
|
||||
break;
|
||||
case SX_MISFEATURE:
|
||||
case SX_ERROR:
|
||||
syslog(LOG_ERR,"ERROR: %s", buffer);
|
||||
break;
|
||||
case SX_NOTICE:
|
||||
syslog(LOG_WARNING,"Notice: %s", buffer);
|
||||
break;
|
||||
case SX_DEBUG:
|
||||
syslog(LOG_DEBUG,"Debug: %s", buffer);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if(t==SX_FATAL) exit(-1);
|
||||
if (t == SX_FATAL)
|
||||
exit(-1);
|
||||
|
||||
return 0;
|
||||
};
|
||||
}
|
||||
|
||||
int
|
||||
sx_debug(char const* const file, char const* const func, int const line,
|
||||
char* fmt, ...)
|
||||
char* fmt, ...)
|
||||
{
|
||||
char buffer[65536];
|
||||
char bline[65536];
|
||||
|
||||
va_list ap;
|
||||
va_start(ap,fmt);
|
||||
va_start(ap, fmt);
|
||||
|
||||
vsnprintf(buffer,sizeof(buffer),fmt,ap);
|
||||
vsnprintf(buffer, sizeof(buffer), fmt, ap);
|
||||
va_end(ap);
|
||||
|
||||
snprintf(bline,sizeof(bline),"DEBUG: %s:%i %s ", file, line, func);
|
||||
if(reportStderr) {
|
||||
fputs(bline,stderr);
|
||||
fputs(buffer,stderr);
|
||||
snprintf(bline, sizeof(bline), "DEBUG: %s:%i %s ", file, line, func);
|
||||
if (reportStderr) {
|
||||
fputs(bline, stderr);
|
||||
fputs(buffer, stderr);
|
||||
} else {
|
||||
syslog(LOG_DEBUG,"%s %s", bline, buffer);
|
||||
};
|
||||
}
|
||||
|
||||
return 0;
|
||||
};
|
||||
}
|
||||
|
||||
void
|
||||
sx_openlog(char* progname)
|
||||
{
|
||||
openlog(progname?progname:"<unknown>",LOG_PID,LOG_DAEMON);
|
||||
reportStderr=0;
|
||||
};
|
||||
openlog(progname ? progname : "<unknown>", LOG_PID, LOG_DAEMON);
|
||||
reportStderr = 0;
|
||||
}
|
||||
|
||||
|
||||
26
sx_report.h
26
sx_report.h
@@ -1,3 +1,29 @@
|
||||
/*
|
||||
* Copyright (c) 2007-2019 Alexandre Snarskii <snar@snar.spb.ru>
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
|
||||
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
||||
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
||||
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||
* SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#ifndef SX_REPORT_H_
|
||||
#define SX_REPORT_H_
|
||||
|
||||
|
||||
59
sx_slentry.c
59
sx_slentry.c
@@ -1,27 +1,62 @@
|
||||
/*
|
||||
* Copyright (c) 2007-2019 Alexandre Snarskii <snar@snar.spb.ru>
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
|
||||
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
||||
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
||||
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||
* SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <strings.h>
|
||||
|
||||
#include "sx_slentry.h"
|
||||
#include "extern.h"
|
||||
|
||||
struct sx_slentry*
|
||||
sx_slentry_new(char* t)
|
||||
struct slentry *
|
||||
sx_slentry_new(char *t)
|
||||
{
|
||||
struct sx_slentry* e=malloc(sizeof(struct sx_slentry));
|
||||
if(!e) return NULL;
|
||||
memset(e,0,sizeof(struct sx_slentry));
|
||||
if(t) e->text=strdup(t);
|
||||
struct slentry *e = malloc(sizeof(struct slentry));
|
||||
|
||||
if (!e)
|
||||
return NULL;
|
||||
|
||||
memset(e, 0, sizeof(struct slentry));
|
||||
|
||||
e->text = strdup(t);
|
||||
|
||||
return e;
|
||||
};
|
||||
}
|
||||
|
||||
struct sx_tentry*
|
||||
sx_tentry_new(char* t)
|
||||
struct sx_tentry *
|
||||
sx_tentry_new(char *t)
|
||||
{
|
||||
struct sx_tentry* te = malloc(sizeof(struct sx_tentry));
|
||||
struct sx_tentry *te = malloc(sizeof(struct sx_tentry));
|
||||
|
||||
if (!te)
|
||||
return NULL;
|
||||
|
||||
memset(te, 0, sizeof(struct sx_tentry));
|
||||
|
||||
te->text = strdup(t);
|
||||
|
||||
return te;
|
||||
};
|
||||
}
|
||||
|
||||
30
sx_slentry.h
30
sx_slentry.h
@@ -1,30 +0,0 @@
|
||||
#ifndef SX_SLENTRY_H_
|
||||
#define SX_SLENTRY_H_
|
||||
|
||||
#if HAVE_SYS_QUEUE_H && HAVE_STAILQ_IN_SYS_QUEUE
|
||||
#include <sys/queue.h>
|
||||
#else
|
||||
#include "sys_queue.h"
|
||||
#endif
|
||||
|
||||
#if HAVE_SYS_TREE_H
|
||||
#include <sys/tree.h>
|
||||
#else
|
||||
#include "sys_tree.h"
|
||||
#endif
|
||||
|
||||
struct sx_slentry {
|
||||
STAILQ_ENTRY(sx_slentry) next;
|
||||
char* text;
|
||||
};
|
||||
|
||||
struct sx_slentry* sx_slentry_new(char* text);
|
||||
|
||||
struct sx_tentry {
|
||||
RB_ENTRY(sx_tentry) entry;
|
||||
char* text;
|
||||
};
|
||||
|
||||
struct sx_tentry* sx_tentry_new(char* text);
|
||||
|
||||
#endif
|
||||
696
sys_queue.h
696
sys_queue.h
@@ -1,696 +0,0 @@
|
||||
/*-
|
||||
* Copyright (c) 1991, 1993
|
||||
* The Regents of the University of California. All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* 4. Neither the name of the University nor the names of its contributors
|
||||
* may be used to endorse or promote products derived from this software
|
||||
* without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
|
||||
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
||||
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
||||
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||
* SUCH DAMAGE.
|
||||
*
|
||||
* @(#)queue.h 8.5 (Berkeley) 8/20/94
|
||||
* $FreeBSD: stable/10/sys/sys/queue.h 251887 2013-06-18 02:57:56Z lstewart $
|
||||
*/
|
||||
|
||||
#ifndef _SYS_QUEUE_H_
|
||||
#define _SYS_QUEUE_H_
|
||||
|
||||
#if HAVE_SYS_CDEFS_H
|
||||
#include <sys/cdefs.h>
|
||||
#endif
|
||||
|
||||
/*
|
||||
* This file defines four types of data structures: singly-linked lists,
|
||||
* singly-linked tail queues, lists and tail queues.
|
||||
*
|
||||
* A singly-linked list is headed by a single forward pointer. The elements
|
||||
* are singly linked for minimum space and pointer manipulation overhead at
|
||||
* the expense of O(n) removal for arbitrary elements. New elements can be
|
||||
* added to the list after an existing element or at the head of the list.
|
||||
* Elements being removed from the head of the list should use the explicit
|
||||
* macro for this purpose for optimum efficiency. A singly-linked list may
|
||||
* only be traversed in the forward direction. Singly-linked lists are ideal
|
||||
* for applications with large datasets and few or no removals or for
|
||||
* implementing a LIFO queue.
|
||||
*
|
||||
* A singly-linked tail queue is headed by a pair of pointers, one to the
|
||||
* head of the list and the other to the tail of the list. The elements are
|
||||
* singly linked for minimum space and pointer manipulation overhead at the
|
||||
* expense of O(n) removal for arbitrary elements. New elements can be added
|
||||
* to the list after an existing element, at the head of the list, or at the
|
||||
* end of the list. Elements being removed from the head of the tail queue
|
||||
* should use the explicit macro for this purpose for optimum efficiency.
|
||||
* A singly-linked tail queue may only be traversed in the forward direction.
|
||||
* Singly-linked tail queues are ideal for applications with large datasets
|
||||
* and few or no removals or for implementing a FIFO queue.
|
||||
*
|
||||
* A list is headed by a single forward pointer (or an array of forward
|
||||
* pointers for a hash table header). The elements are doubly linked
|
||||
* so that an arbitrary element can be removed without a need to
|
||||
* traverse the list. New elements can be added to the list before
|
||||
* or after an existing element or at the head of the list. A list
|
||||
* may be traversed in either direction.
|
||||
*
|
||||
* A tail queue is headed by a pair of pointers, one to the head of the
|
||||
* list and the other to the tail of the list. The elements are doubly
|
||||
* linked so that an arbitrary element can be removed without a need to
|
||||
* traverse the list. New elements can be added to the list before or
|
||||
* after an existing element, at the head of the list, or at the end of
|
||||
* the list. A tail queue may be traversed in either direction.
|
||||
*
|
||||
* For details on the use of these macros, see the queue(3) manual page.
|
||||
*
|
||||
*
|
||||
* SLIST LIST STAILQ TAILQ
|
||||
* _HEAD + + + +
|
||||
* _HEAD_INITIALIZER + + + +
|
||||
* _ENTRY + + + +
|
||||
* _INIT + + + +
|
||||
* _EMPTY + + + +
|
||||
* _FIRST + + + +
|
||||
* _NEXT + + + +
|
||||
* _PREV - + - +
|
||||
* _LAST - - + +
|
||||
* _FOREACH + + + +
|
||||
* _FOREACH_FROM + + + +
|
||||
* _FOREACH_SAFE + + + +
|
||||
* _FOREACH_FROM_SAFE + + + +
|
||||
* _FOREACH_REVERSE - - - +
|
||||
* _FOREACH_REVERSE_FROM - - - +
|
||||
* _FOREACH_REVERSE_SAFE - - - +
|
||||
* _FOREACH_REVERSE_FROM_SAFE - - - +
|
||||
* _INSERT_HEAD + + + +
|
||||
* _INSERT_BEFORE - + - +
|
||||
* _INSERT_AFTER + + + +
|
||||
* _INSERT_TAIL - - + +
|
||||
* _CONCAT - - + +
|
||||
* _REMOVE_AFTER + - + -
|
||||
* _REMOVE_HEAD + - + -
|
||||
* _REMOVE + + + +
|
||||
* _SWAP + + + +
|
||||
*
|
||||
*/
|
||||
#ifdef QUEUE_MACRO_DEBUG
|
||||
/* Store the last 2 places the queue element or head was altered */
|
||||
struct qm_trace {
|
||||
unsigned long lastline;
|
||||
unsigned long prevline;
|
||||
const char *lastfile;
|
||||
const char *prevfile;
|
||||
};
|
||||
|
||||
#define TRACEBUF struct qm_trace trace;
|
||||
#define TRACEBUF_INITIALIZER { __FILE__, __LINE__, NULL, 0 } ,
|
||||
#define TRASHIT(x) do {(x) = (void *)-1;} while (0)
|
||||
#define QMD_SAVELINK(name, link) void **name = (void *)&(link)
|
||||
|
||||
#define QMD_TRACE_HEAD(head) do { \
|
||||
(head)->trace.prevline = (head)->trace.lastline; \
|
||||
(head)->trace.prevfile = (head)->trace.lastfile; \
|
||||
(head)->trace.lastline = __LINE__; \
|
||||
(head)->trace.lastfile = __FILE__; \
|
||||
} while (0)
|
||||
|
||||
#define QMD_TRACE_ELEM(elem) do { \
|
||||
(elem)->trace.prevline = (elem)->trace.lastline; \
|
||||
(elem)->trace.prevfile = (elem)->trace.lastfile; \
|
||||
(elem)->trace.lastline = __LINE__; \
|
||||
(elem)->trace.lastfile = __FILE__; \
|
||||
} while (0)
|
||||
|
||||
#else
|
||||
#define QMD_TRACE_ELEM(elem)
|
||||
#define QMD_TRACE_HEAD(head)
|
||||
#define QMD_SAVELINK(name, link)
|
||||
#define TRACEBUF
|
||||
#define TRACEBUF_INITIALIZER
|
||||
#define TRASHIT(x)
|
||||
#endif /* QUEUE_MACRO_DEBUG */
|
||||
|
||||
/*
|
||||
* Singly-linked List declarations.
|
||||
*/
|
||||
#define SLIST_HEAD(name, type) \
|
||||
struct name { \
|
||||
struct type *slh_first; /* first element */ \
|
||||
}
|
||||
|
||||
#define SLIST_HEAD_INITIALIZER(head) \
|
||||
{ NULL }
|
||||
|
||||
#define SLIST_ENTRY(type) \
|
||||
struct { \
|
||||
struct type *sle_next; /* next element */ \
|
||||
}
|
||||
|
||||
/*
|
||||
* Singly-linked List functions.
|
||||
*/
|
||||
#define SLIST_EMPTY(head) ((head)->slh_first == NULL)
|
||||
|
||||
#define SLIST_FIRST(head) ((head)->slh_first)
|
||||
|
||||
#define SLIST_FOREACH(var, head, field) \
|
||||
for ((var) = SLIST_FIRST((head)); \
|
||||
(var); \
|
||||
(var) = SLIST_NEXT((var), field))
|
||||
|
||||
#define SLIST_FOREACH_FROM(var, head, field) \
|
||||
for ((var) = ((var) ? (var) : SLIST_FIRST((head))); \
|
||||
(var); \
|
||||
(var) = SLIST_NEXT((var), field))
|
||||
|
||||
#define SLIST_FOREACH_SAFE(var, head, field, tvar) \
|
||||
for ((var) = SLIST_FIRST((head)); \
|
||||
(var) && ((tvar) = SLIST_NEXT((var), field), 1); \
|
||||
(var) = (tvar))
|
||||
|
||||
#define SLIST_FOREACH_FROM_SAFE(var, head, field, tvar) \
|
||||
for ((var) = ((var) ? (var) : SLIST_FIRST((head))); \
|
||||
(var) && ((tvar) = SLIST_NEXT((var), field), 1); \
|
||||
(var) = (tvar))
|
||||
|
||||
#define SLIST_FOREACH_PREVPTR(var, varp, head, field) \
|
||||
for ((varp) = &SLIST_FIRST((head)); \
|
||||
((var) = *(varp)) != NULL; \
|
||||
(varp) = &SLIST_NEXT((var), field))
|
||||
|
||||
#define SLIST_INIT(head) do { \
|
||||
SLIST_FIRST((head)) = NULL; \
|
||||
} while (0)
|
||||
|
||||
#define SLIST_INSERT_AFTER(slistelm, elm, field) do { \
|
||||
SLIST_NEXT((elm), field) = SLIST_NEXT((slistelm), field); \
|
||||
SLIST_NEXT((slistelm), field) = (elm); \
|
||||
} while (0)
|
||||
|
||||
#define SLIST_INSERT_HEAD(head, elm, field) do { \
|
||||
SLIST_NEXT((elm), field) = SLIST_FIRST((head)); \
|
||||
SLIST_FIRST((head)) = (elm); \
|
||||
} while (0)
|
||||
|
||||
#define SLIST_NEXT(elm, field) ((elm)->field.sle_next)
|
||||
|
||||
#define SLIST_REMOVE(head, elm, type, field) do { \
|
||||
QMD_SAVELINK(oldnext, (elm)->field.sle_next); \
|
||||
if (SLIST_FIRST((head)) == (elm)) { \
|
||||
SLIST_REMOVE_HEAD((head), field); \
|
||||
} \
|
||||
else { \
|
||||
struct type *curelm = SLIST_FIRST((head)); \
|
||||
while (SLIST_NEXT(curelm, field) != (elm)) \
|
||||
curelm = SLIST_NEXT(curelm, field); \
|
||||
SLIST_REMOVE_AFTER(curelm, field); \
|
||||
} \
|
||||
TRASHIT(*oldnext); \
|
||||
} while (0)
|
||||
|
||||
#define SLIST_REMOVE_AFTER(elm, field) do { \
|
||||
SLIST_NEXT(elm, field) = \
|
||||
SLIST_NEXT(SLIST_NEXT(elm, field), field); \
|
||||
} while (0)
|
||||
|
||||
#define SLIST_REMOVE_HEAD(head, field) do { \
|
||||
SLIST_FIRST((head)) = SLIST_NEXT(SLIST_FIRST((head)), field); \
|
||||
} while (0)
|
||||
|
||||
#define SLIST_SWAP(head1, head2, type) do { \
|
||||
struct type *swap_first = SLIST_FIRST(head1); \
|
||||
SLIST_FIRST(head1) = SLIST_FIRST(head2); \
|
||||
SLIST_FIRST(head2) = swap_first; \
|
||||
} while (0)
|
||||
|
||||
/*
|
||||
* Singly-linked Tail queue declarations.
|
||||
*/
|
||||
#define STAILQ_HEAD(name, type) \
|
||||
struct name { \
|
||||
struct type *stqh_first;/* first element */ \
|
||||
struct type **stqh_last;/* addr of last next element */ \
|
||||
}
|
||||
|
||||
#define STAILQ_HEAD_INITIALIZER(head) \
|
||||
{ NULL, &(head).stqh_first }
|
||||
|
||||
#define STAILQ_ENTRY(type) \
|
||||
struct { \
|
||||
struct type *stqe_next; /* next element */ \
|
||||
}
|
||||
|
||||
/*
|
||||
* Singly-linked Tail queue functions.
|
||||
*/
|
||||
#define STAILQ_CONCAT(head1, head2) do { \
|
||||
if (!STAILQ_EMPTY((head2))) { \
|
||||
*(head1)->stqh_last = (head2)->stqh_first; \
|
||||
(head1)->stqh_last = (head2)->stqh_last; \
|
||||
STAILQ_INIT((head2)); \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
#define STAILQ_EMPTY(head) ((head)->stqh_first == NULL)
|
||||
|
||||
#define STAILQ_FIRST(head) ((head)->stqh_first)
|
||||
|
||||
#define STAILQ_FOREACH(var, head, field) \
|
||||
for((var) = STAILQ_FIRST((head)); \
|
||||
(var); \
|
||||
(var) = STAILQ_NEXT((var), field))
|
||||
|
||||
#define STAILQ_FOREACH_FROM(var, head, field) \
|
||||
for ((var) = ((var) ? (var) : STAILQ_FIRST((head))); \
|
||||
(var); \
|
||||
(var) = STAILQ_NEXT((var), field))
|
||||
|
||||
#define STAILQ_FOREACH_SAFE(var, head, field, tvar) \
|
||||
for ((var) = STAILQ_FIRST((head)); \
|
||||
(var) && ((tvar) = STAILQ_NEXT((var), field), 1); \
|
||||
(var) = (tvar))
|
||||
|
||||
#define STAILQ_FOREACH_FROM_SAFE(var, head, field, tvar) \
|
||||
for ((var) = ((var) ? (var) : STAILQ_FIRST((head))); \
|
||||
(var) && ((tvar) = STAILQ_NEXT((var), field), 1); \
|
||||
(var) = (tvar))
|
||||
|
||||
#define STAILQ_INIT(head) do { \
|
||||
STAILQ_FIRST((head)) = NULL; \
|
||||
(head)->stqh_last = &STAILQ_FIRST((head)); \
|
||||
} while (0)
|
||||
|
||||
#define STAILQ_INSERT_AFTER(head, tqelm, elm, field) do { \
|
||||
if ((STAILQ_NEXT((elm), field) = STAILQ_NEXT((tqelm), field)) == NULL)\
|
||||
(head)->stqh_last = &STAILQ_NEXT((elm), field); \
|
||||
STAILQ_NEXT((tqelm), field) = (elm); \
|
||||
} while (0)
|
||||
|
||||
#define STAILQ_INSERT_HEAD(head, elm, field) do { \
|
||||
if ((STAILQ_NEXT((elm), field) = STAILQ_FIRST((head))) == NULL) \
|
||||
(head)->stqh_last = &STAILQ_NEXT((elm), field); \
|
||||
STAILQ_FIRST((head)) = (elm); \
|
||||
} while (0)
|
||||
|
||||
#define STAILQ_INSERT_TAIL(head, elm, field) do { \
|
||||
STAILQ_NEXT((elm), field) = NULL; \
|
||||
*(head)->stqh_last = (elm); \
|
||||
(head)->stqh_last = &STAILQ_NEXT((elm), field); \
|
||||
} while (0)
|
||||
|
||||
#define STAILQ_LAST(head, type, field) \
|
||||
(STAILQ_EMPTY((head)) ? NULL : \
|
||||
__containerof((head)->stqh_last, struct type, field.stqe_next))
|
||||
|
||||
#define STAILQ_NEXT(elm, field) ((elm)->field.stqe_next)
|
||||
|
||||
#define STAILQ_REMOVE(head, elm, type, field) do { \
|
||||
QMD_SAVELINK(oldnext, (elm)->field.stqe_next); \
|
||||
if (STAILQ_FIRST((head)) == (elm)) { \
|
||||
STAILQ_REMOVE_HEAD((head), field); \
|
||||
} \
|
||||
else { \
|
||||
struct type *curelm = STAILQ_FIRST((head)); \
|
||||
while (STAILQ_NEXT(curelm, field) != (elm)) \
|
||||
curelm = STAILQ_NEXT(curelm, field); \
|
||||
STAILQ_REMOVE_AFTER(head, curelm, field); \
|
||||
} \
|
||||
TRASHIT(*oldnext); \
|
||||
} while (0)
|
||||
|
||||
#define STAILQ_REMOVE_AFTER(head, elm, field) do { \
|
||||
if ((STAILQ_NEXT(elm, field) = \
|
||||
STAILQ_NEXT(STAILQ_NEXT(elm, field), field)) == NULL) \
|
||||
(head)->stqh_last = &STAILQ_NEXT((elm), field); \
|
||||
} while (0)
|
||||
|
||||
#define STAILQ_REMOVE_HEAD(head, field) do { \
|
||||
if ((STAILQ_FIRST((head)) = \
|
||||
STAILQ_NEXT(STAILQ_FIRST((head)), field)) == NULL) \
|
||||
(head)->stqh_last = &STAILQ_FIRST((head)); \
|
||||
} while (0)
|
||||
|
||||
#define STAILQ_SWAP(head1, head2, type) do { \
|
||||
struct type *swap_first = STAILQ_FIRST(head1); \
|
||||
struct type **swap_last = (head1)->stqh_last; \
|
||||
STAILQ_FIRST(head1) = STAILQ_FIRST(head2); \
|
||||
(head1)->stqh_last = (head2)->stqh_last; \
|
||||
STAILQ_FIRST(head2) = swap_first; \
|
||||
(head2)->stqh_last = swap_last; \
|
||||
if (STAILQ_EMPTY(head1)) \
|
||||
(head1)->stqh_last = &STAILQ_FIRST(head1); \
|
||||
if (STAILQ_EMPTY(head2)) \
|
||||
(head2)->stqh_last = &STAILQ_FIRST(head2); \
|
||||
} while (0)
|
||||
|
||||
|
||||
/*
|
||||
* List declarations.
|
||||
*/
|
||||
#define LIST_HEAD(name, type) \
|
||||
struct name { \
|
||||
struct type *lh_first; /* first element */ \
|
||||
}
|
||||
|
||||
#define LIST_HEAD_INITIALIZER(head) \
|
||||
{ NULL }
|
||||
|
||||
#define LIST_ENTRY(type) \
|
||||
struct { \
|
||||
struct type *le_next; /* next element */ \
|
||||
struct type **le_prev; /* address of previous next element */ \
|
||||
}
|
||||
|
||||
/*
|
||||
* List functions.
|
||||
*/
|
||||
|
||||
#if (defined(_KERNEL) && defined(INVARIANTS))
|
||||
#define QMD_LIST_CHECK_HEAD(head, field) do { \
|
||||
if (LIST_FIRST((head)) != NULL && \
|
||||
LIST_FIRST((head))->field.le_prev != \
|
||||
&LIST_FIRST((head))) \
|
||||
panic("Bad list head %p first->prev != head", (head)); \
|
||||
} while (0)
|
||||
|
||||
#define QMD_LIST_CHECK_NEXT(elm, field) do { \
|
||||
if (LIST_NEXT((elm), field) != NULL && \
|
||||
LIST_NEXT((elm), field)->field.le_prev != \
|
||||
&((elm)->field.le_next)) \
|
||||
panic("Bad link elm %p next->prev != elm", (elm)); \
|
||||
} while (0)
|
||||
|
||||
#define QMD_LIST_CHECK_PREV(elm, field) do { \
|
||||
if (*(elm)->field.le_prev != (elm)) \
|
||||
panic("Bad link elm %p prev->next != elm", (elm)); \
|
||||
} while (0)
|
||||
#else
|
||||
#define QMD_LIST_CHECK_HEAD(head, field)
|
||||
#define QMD_LIST_CHECK_NEXT(elm, field)
|
||||
#define QMD_LIST_CHECK_PREV(elm, field)
|
||||
#endif /* (_KERNEL && INVARIANTS) */
|
||||
|
||||
#define LIST_EMPTY(head) ((head)->lh_first == NULL)
|
||||
|
||||
#define LIST_FIRST(head) ((head)->lh_first)
|
||||
|
||||
#define LIST_FOREACH(var, head, field) \
|
||||
for ((var) = LIST_FIRST((head)); \
|
||||
(var); \
|
||||
(var) = LIST_NEXT((var), field))
|
||||
|
||||
#define LIST_FOREACH_FROM(var, head, field) \
|
||||
for ((var) = ((var) ? (var) : LIST_FIRST((head))); \
|
||||
(var); \
|
||||
(var) = LIST_NEXT((var), field))
|
||||
|
||||
#define LIST_FOREACH_SAFE(var, head, field, tvar) \
|
||||
for ((var) = LIST_FIRST((head)); \
|
||||
(var) && ((tvar) = LIST_NEXT((var), field), 1); \
|
||||
(var) = (tvar))
|
||||
|
||||
#define LIST_FOREACH_FROM_SAFE(var, head, field, tvar) \
|
||||
for ((var) = ((var) ? (var) : LIST_FIRST((head))); \
|
||||
(var) && ((tvar) = LIST_NEXT((var), field), 1); \
|
||||
(var) = (tvar))
|
||||
|
||||
#define LIST_INIT(head) do { \
|
||||
LIST_FIRST((head)) = NULL; \
|
||||
} while (0)
|
||||
|
||||
#define LIST_INSERT_AFTER(listelm, elm, field) do { \
|
||||
QMD_LIST_CHECK_NEXT(listelm, field); \
|
||||
if ((LIST_NEXT((elm), field) = LIST_NEXT((listelm), field)) != NULL)\
|
||||
LIST_NEXT((listelm), field)->field.le_prev = \
|
||||
&LIST_NEXT((elm), field); \
|
||||
LIST_NEXT((listelm), field) = (elm); \
|
||||
(elm)->field.le_prev = &LIST_NEXT((listelm), field); \
|
||||
} while (0)
|
||||
|
||||
#define LIST_INSERT_BEFORE(listelm, elm, field) do { \
|
||||
QMD_LIST_CHECK_PREV(listelm, field); \
|
||||
(elm)->field.le_prev = (listelm)->field.le_prev; \
|
||||
LIST_NEXT((elm), field) = (listelm); \
|
||||
*(listelm)->field.le_prev = (elm); \
|
||||
(listelm)->field.le_prev = &LIST_NEXT((elm), field); \
|
||||
} while (0)
|
||||
|
||||
#define LIST_INSERT_HEAD(head, elm, field) do { \
|
||||
QMD_LIST_CHECK_HEAD((head), field); \
|
||||
if ((LIST_NEXT((elm), field) = LIST_FIRST((head))) != NULL) \
|
||||
LIST_FIRST((head))->field.le_prev = &LIST_NEXT((elm), field);\
|
||||
LIST_FIRST((head)) = (elm); \
|
||||
(elm)->field.le_prev = &LIST_FIRST((head)); \
|
||||
} while (0)
|
||||
|
||||
#define LIST_NEXT(elm, field) ((elm)->field.le_next)
|
||||
|
||||
#define LIST_PREV(elm, head, type, field) \
|
||||
((elm)->field.le_prev == &LIST_FIRST((head)) ? NULL : \
|
||||
__containerof((elm)->field.le_prev, struct type, field.le_next))
|
||||
|
||||
#define LIST_REMOVE(elm, field) do { \
|
||||
QMD_SAVELINK(oldnext, (elm)->field.le_next); \
|
||||
QMD_SAVELINK(oldprev, (elm)->field.le_prev); \
|
||||
QMD_LIST_CHECK_NEXT(elm, field); \
|
||||
QMD_LIST_CHECK_PREV(elm, field); \
|
||||
if (LIST_NEXT((elm), field) != NULL) \
|
||||
LIST_NEXT((elm), field)->field.le_prev = \
|
||||
(elm)->field.le_prev; \
|
||||
*(elm)->field.le_prev = LIST_NEXT((elm), field); \
|
||||
TRASHIT(*oldnext); \
|
||||
TRASHIT(*oldprev); \
|
||||
} while (0)
|
||||
|
||||
#define LIST_SWAP(head1, head2, type, field) do { \
|
||||
struct type *swap_tmp = LIST_FIRST((head1)); \
|
||||
LIST_FIRST((head1)) = LIST_FIRST((head2)); \
|
||||
LIST_FIRST((head2)) = swap_tmp; \
|
||||
if ((swap_tmp = LIST_FIRST((head1))) != NULL) \
|
||||
swap_tmp->field.le_prev = &LIST_FIRST((head1)); \
|
||||
if ((swap_tmp = LIST_FIRST((head2))) != NULL) \
|
||||
swap_tmp->field.le_prev = &LIST_FIRST((head2)); \
|
||||
} while (0)
|
||||
|
||||
/*
|
||||
* Tail queue declarations.
|
||||
*/
|
||||
#define TAILQ_HEAD(name, type) \
|
||||
struct name { \
|
||||
struct type *tqh_first; /* first element */ \
|
||||
struct type **tqh_last; /* addr of last next element */ \
|
||||
TRACEBUF \
|
||||
}
|
||||
|
||||
#define TAILQ_HEAD_INITIALIZER(head) \
|
||||
{ NULL, &(head).tqh_first, TRACEBUF_INITIALIZER }
|
||||
|
||||
#define TAILQ_ENTRY(type) \
|
||||
struct { \
|
||||
struct type *tqe_next; /* next element */ \
|
||||
struct type **tqe_prev; /* address of previous next element */ \
|
||||
TRACEBUF \
|
||||
}
|
||||
|
||||
/*
|
||||
* Tail queue functions.
|
||||
*/
|
||||
#if (defined(_KERNEL) && defined(INVARIANTS))
|
||||
#define QMD_TAILQ_CHECK_HEAD(head, field) do { \
|
||||
if (!TAILQ_EMPTY(head) && \
|
||||
TAILQ_FIRST((head))->field.tqe_prev != \
|
||||
&TAILQ_FIRST((head))) \
|
||||
panic("Bad tailq head %p first->prev != head", (head)); \
|
||||
} while (0)
|
||||
|
||||
#define QMD_TAILQ_CHECK_TAIL(head, field) do { \
|
||||
if (*(head)->tqh_last != NULL) \
|
||||
panic("Bad tailq NEXT(%p->tqh_last) != NULL", (head)); \
|
||||
} while (0)
|
||||
|
||||
#define QMD_TAILQ_CHECK_NEXT(elm, field) do { \
|
||||
if (TAILQ_NEXT((elm), field) != NULL && \
|
||||
TAILQ_NEXT((elm), field)->field.tqe_prev != \
|
||||
&((elm)->field.tqe_next)) \
|
||||
panic("Bad link elm %p next->prev != elm", (elm)); \
|
||||
} while (0)
|
||||
|
||||
#define QMD_TAILQ_CHECK_PREV(elm, field) do { \
|
||||
if (*(elm)->field.tqe_prev != (elm)) \
|
||||
panic("Bad link elm %p prev->next != elm", (elm)); \
|
||||
} while (0)
|
||||
#else
|
||||
#define QMD_TAILQ_CHECK_HEAD(head, field)
|
||||
#define QMD_TAILQ_CHECK_TAIL(head, headname)
|
||||
#define QMD_TAILQ_CHECK_NEXT(elm, field)
|
||||
#define QMD_TAILQ_CHECK_PREV(elm, field)
|
||||
#endif /* (_KERNEL && INVARIANTS) */
|
||||
|
||||
#define TAILQ_CONCAT(head1, head2, field) do { \
|
||||
if (!TAILQ_EMPTY(head2)) { \
|
||||
*(head1)->tqh_last = (head2)->tqh_first; \
|
||||
(head2)->tqh_first->field.tqe_prev = (head1)->tqh_last; \
|
||||
(head1)->tqh_last = (head2)->tqh_last; \
|
||||
TAILQ_INIT((head2)); \
|
||||
QMD_TRACE_HEAD(head1); \
|
||||
QMD_TRACE_HEAD(head2); \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
#define TAILQ_EMPTY(head) ((head)->tqh_first == NULL)
|
||||
|
||||
#define TAILQ_FIRST(head) ((head)->tqh_first)
|
||||
|
||||
#define TAILQ_FOREACH(var, head, field) \
|
||||
for ((var) = TAILQ_FIRST((head)); \
|
||||
(var); \
|
||||
(var) = TAILQ_NEXT((var), field))
|
||||
|
||||
#define TAILQ_FOREACH_FROM(var, head, field) \
|
||||
for ((var) = ((var) ? (var) : TAILQ_FIRST((head))); \
|
||||
(var); \
|
||||
(var) = TAILQ_NEXT((var), field))
|
||||
|
||||
#define TAILQ_FOREACH_SAFE(var, head, field, tvar) \
|
||||
for ((var) = TAILQ_FIRST((head)); \
|
||||
(var) && ((tvar) = TAILQ_NEXT((var), field), 1); \
|
||||
(var) = (tvar))
|
||||
|
||||
#define TAILQ_FOREACH_FROM_SAFE(var, head, field, tvar) \
|
||||
for ((var) = ((var) ? (var) : TAILQ_FIRST((head))); \
|
||||
(var) && ((tvar) = TAILQ_NEXT((var), field), 1); \
|
||||
(var) = (tvar))
|
||||
|
||||
#define TAILQ_FOREACH_REVERSE(var, head, headname, field) \
|
||||
for ((var) = TAILQ_LAST((head), headname); \
|
||||
(var); \
|
||||
(var) = TAILQ_PREV((var), headname, field))
|
||||
|
||||
#define TAILQ_FOREACH_REVERSE_FROM(var, head, headname, field) \
|
||||
for ((var) = ((var) ? (var) : TAILQ_LAST((head), headname)); \
|
||||
(var); \
|
||||
(var) = TAILQ_PREV((var), headname, field))
|
||||
|
||||
#define TAILQ_FOREACH_REVERSE_SAFE(var, head, headname, field, tvar) \
|
||||
for ((var) = TAILQ_LAST((head), headname); \
|
||||
(var) && ((tvar) = TAILQ_PREV((var), headname, field), 1); \
|
||||
(var) = (tvar))
|
||||
|
||||
#define TAILQ_FOREACH_REVERSE_FROM_SAFE(var, head, headname, field, tvar) \
|
||||
for ((var) = ((var) ? (var) : TAILQ_LAST((head), headname)); \
|
||||
(var) && ((tvar) = TAILQ_PREV((var), headname, field), 1); \
|
||||
(var) = (tvar))
|
||||
|
||||
#define TAILQ_INIT(head) do { \
|
||||
TAILQ_FIRST((head)) = NULL; \
|
||||
(head)->tqh_last = &TAILQ_FIRST((head)); \
|
||||
QMD_TRACE_HEAD(head); \
|
||||
} while (0)
|
||||
|
||||
#define TAILQ_INSERT_AFTER(head, listelm, elm, field) do { \
|
||||
QMD_TAILQ_CHECK_NEXT(listelm, field); \
|
||||
if ((TAILQ_NEXT((elm), field) = TAILQ_NEXT((listelm), field)) != NULL)\
|
||||
TAILQ_NEXT((elm), field)->field.tqe_prev = \
|
||||
&TAILQ_NEXT((elm), field); \
|
||||
else { \
|
||||
(head)->tqh_last = &TAILQ_NEXT((elm), field); \
|
||||
QMD_TRACE_HEAD(head); \
|
||||
} \
|
||||
TAILQ_NEXT((listelm), field) = (elm); \
|
||||
(elm)->field.tqe_prev = &TAILQ_NEXT((listelm), field); \
|
||||
QMD_TRACE_ELEM(&(elm)->field); \
|
||||
QMD_TRACE_ELEM(&listelm->field); \
|
||||
} while (0)
|
||||
|
||||
#define TAILQ_INSERT_BEFORE(listelm, elm, field) do { \
|
||||
QMD_TAILQ_CHECK_PREV(listelm, field); \
|
||||
(elm)->field.tqe_prev = (listelm)->field.tqe_prev; \
|
||||
TAILQ_NEXT((elm), field) = (listelm); \
|
||||
*(listelm)->field.tqe_prev = (elm); \
|
||||
(listelm)->field.tqe_prev = &TAILQ_NEXT((elm), field); \
|
||||
QMD_TRACE_ELEM(&(elm)->field); \
|
||||
QMD_TRACE_ELEM(&listelm->field); \
|
||||
} while (0)
|
||||
|
||||
#define TAILQ_INSERT_HEAD(head, elm, field) do { \
|
||||
QMD_TAILQ_CHECK_HEAD(head, field); \
|
||||
if ((TAILQ_NEXT((elm), field) = TAILQ_FIRST((head))) != NULL) \
|
||||
TAILQ_FIRST((head))->field.tqe_prev = \
|
||||
&TAILQ_NEXT((elm), field); \
|
||||
else \
|
||||
(head)->tqh_last = &TAILQ_NEXT((elm), field); \
|
||||
TAILQ_FIRST((head)) = (elm); \
|
||||
(elm)->field.tqe_prev = &TAILQ_FIRST((head)); \
|
||||
QMD_TRACE_HEAD(head); \
|
||||
QMD_TRACE_ELEM(&(elm)->field); \
|
||||
} while (0)
|
||||
|
||||
#define TAILQ_INSERT_TAIL(head, elm, field) do { \
|
||||
QMD_TAILQ_CHECK_TAIL(head, field); \
|
||||
TAILQ_NEXT((elm), field) = NULL; \
|
||||
(elm)->field.tqe_prev = (head)->tqh_last; \
|
||||
*(head)->tqh_last = (elm); \
|
||||
(head)->tqh_last = &TAILQ_NEXT((elm), field); \
|
||||
QMD_TRACE_HEAD(head); \
|
||||
QMD_TRACE_ELEM(&(elm)->field); \
|
||||
} while (0)
|
||||
|
||||
#define TAILQ_LAST(head, headname) \
|
||||
(*(((struct headname *)((head)->tqh_last))->tqh_last))
|
||||
|
||||
#define TAILQ_NEXT(elm, field) ((elm)->field.tqe_next)
|
||||
|
||||
#define TAILQ_PREV(elm, headname, field) \
|
||||
(*(((struct headname *)((elm)->field.tqe_prev))->tqh_last))
|
||||
|
||||
#define TAILQ_REMOVE(head, elm, field) do { \
|
||||
QMD_SAVELINK(oldnext, (elm)->field.tqe_next); \
|
||||
QMD_SAVELINK(oldprev, (elm)->field.tqe_prev); \
|
||||
QMD_TAILQ_CHECK_NEXT(elm, field); \
|
||||
QMD_TAILQ_CHECK_PREV(elm, field); \
|
||||
if ((TAILQ_NEXT((elm), field)) != NULL) \
|
||||
TAILQ_NEXT((elm), field)->field.tqe_prev = \
|
||||
(elm)->field.tqe_prev; \
|
||||
else { \
|
||||
(head)->tqh_last = (elm)->field.tqe_prev; \
|
||||
QMD_TRACE_HEAD(head); \
|
||||
} \
|
||||
*(elm)->field.tqe_prev = TAILQ_NEXT((elm), field); \
|
||||
TRASHIT(*oldnext); \
|
||||
TRASHIT(*oldprev); \
|
||||
QMD_TRACE_ELEM(&(elm)->field); \
|
||||
} while (0)
|
||||
|
||||
#define TAILQ_SWAP(head1, head2, type, field) do { \
|
||||
struct type *swap_first = (head1)->tqh_first; \
|
||||
struct type **swap_last = (head1)->tqh_last; \
|
||||
(head1)->tqh_first = (head2)->tqh_first; \
|
||||
(head1)->tqh_last = (head2)->tqh_last; \
|
||||
(head2)->tqh_first = swap_first; \
|
||||
(head2)->tqh_last = swap_last; \
|
||||
if ((swap_first = (head1)->tqh_first) != NULL) \
|
||||
swap_first->field.tqe_prev = &(head1)->tqh_first; \
|
||||
else \
|
||||
(head1)->tqh_last = &(head1)->tqh_first; \
|
||||
if ((swap_first = (head2)->tqh_first) != NULL) \
|
||||
swap_first->field.tqe_prev = &(head2)->tqh_first; \
|
||||
else \
|
||||
(head2)->tqh_last = &(head2)->tqh_first; \
|
||||
} while (0)
|
||||
|
||||
#endif /* !_SYS_QUEUE_H_ */
|
||||
Reference in New Issue
Block a user