mirror of
https://gitlab.nic.cz/labs/bird.git
synced 2024-11-14 23:28:43 +00:00
Merge branch 'int-new' into mj-coro
This commit is contained in:
commit
e907d7cb7d
2
.gitignore
vendored
2
.gitignore
vendored
@ -11,3 +11,5 @@
|
||||
/config.status
|
||||
/configure
|
||||
/sysdep/autoconf.h.in
|
||||
/sysdep/autoconf.h.in~
|
||||
/cscope.*
|
||||
|
309
.gitlab-ci.yml
Normal file
309
.gitlab-ci.yml
Normal file
@ -0,0 +1,309 @@
|
||||
variables:
|
||||
DEBIAN_FRONTEND: noninteractive
|
||||
LC_ALL: C
|
||||
GIT_STRATEGY: fetch
|
||||
DOCKER_CMD: docker --config="$HOME/.docker/$CI_JOB_ID/"
|
||||
IMG_BASE: registry.labs.nic.cz/labs/bird
|
||||
|
||||
stages:
|
||||
- image
|
||||
- build
|
||||
|
||||
.docker: &docker_build
|
||||
stage: image
|
||||
allow_failure: true
|
||||
script:
|
||||
- $DOCKER_CMD login -u gitlab-ci-token -p $CI_BUILD_TOKEN registry.labs.nic.cz
|
||||
# Make sure we refresh the base image if it updates (eg. security updates, etc)
|
||||
# If we do just the build, cache is always reused and the freshness of the
|
||||
# base image is never checked. However, pull always asks and updates the
|
||||
# image only if it changed ‒ therefore, the cache is used unless there's a
|
||||
# change.
|
||||
- $DOCKER_CMD pull `sed -ne 's/^FROM //p' "misc/docker/$IMG_NAME/Dockerfile"`
|
||||
- $DOCKER_CMD build -t "bird:$IMG_NAME" "misc/docker/$IMG_NAME"
|
||||
- $DOCKER_CMD tag "bird:$IMG_NAME" "$IMG_BASE:$IMG_NAME"
|
||||
- $DOCKER_CMD push "$IMG_BASE:$IMG_NAME"
|
||||
after_script:
|
||||
- rm -f "$HOME/.docker/$CI_JOB_ID/" # cleanup the credentials
|
||||
tags:
|
||||
# That's Docker in Docker
|
||||
- dind
|
||||
|
||||
docker_debian-7-amd64:
|
||||
variables:
|
||||
IMG_NAME: "debian-7-amd64"
|
||||
<<: *docker_build
|
||||
|
||||
docker_debian-8-amd64:
|
||||
variables:
|
||||
IMG_NAME: "debian-8-amd64"
|
||||
<<: *docker_build
|
||||
|
||||
docker_debian-9-amd64:
|
||||
variables:
|
||||
IMG_NAME: "debian-9-amd64"
|
||||
<<: *docker_build
|
||||
|
||||
docker_debian-testing-amd64:
|
||||
variables:
|
||||
IMG_NAME: "debian-testing-amd64"
|
||||
<<: *docker_build
|
||||
|
||||
docker_debian-7-i386:
|
||||
variables:
|
||||
IMG_NAME: "debian-7-i386"
|
||||
<<: *docker_build
|
||||
|
||||
docker_debian-8-i386:
|
||||
variables:
|
||||
IMG_NAME: "debian-8-i386"
|
||||
<<: *docker_build
|
||||
|
||||
docker_debian-9-i386:
|
||||
variables:
|
||||
IMG_NAME: "debian-9-i386"
|
||||
<<: *docker_build
|
||||
|
||||
docker_debian-testing-i386:
|
||||
variables:
|
||||
IMG_NAME: "debian-testing-i386"
|
||||
<<: *docker_build
|
||||
|
||||
docker_fedora-25-amd64:
|
||||
variables:
|
||||
IMG_NAME: "fedora-25-amd64"
|
||||
<<: *docker_build
|
||||
|
||||
docker_fedora-26-amd64:
|
||||
variables:
|
||||
IMG_NAME: "fedora-26-amd64"
|
||||
<<: *docker_build
|
||||
|
||||
docker_centos-6-amd64:
|
||||
variables:
|
||||
IMG_NAME: "centos-6-amd64"
|
||||
<<: *docker_build
|
||||
|
||||
docker_centos-7-amd64:
|
||||
variables:
|
||||
IMG_NAME: "centos-7-amd64"
|
||||
<<: *docker_build
|
||||
|
||||
docker_opensuse-42_3-amd64:
|
||||
variables:
|
||||
IMG_NAME: "opensuse-42.3-amd64"
|
||||
<<: *docker_build
|
||||
|
||||
docker_ubuntu-14_04-amd64:
|
||||
variables:
|
||||
IMG_NAME: "ubuntu-14.04-amd64"
|
||||
<<: *docker_build
|
||||
|
||||
docker_ubuntu-16_04-amd64:
|
||||
variables:
|
||||
IMG_NAME: "ubuntu-16.04-amd64"
|
||||
<<: *docker_build
|
||||
|
||||
.debian-7-i386: &debian-7-i386_env
|
||||
image: registry.labs.nic.cz/labs/bird:debian-7-i386
|
||||
tags:
|
||||
- docker
|
||||
- linux
|
||||
- amd64
|
||||
|
||||
.debian-8-i386: &debian-8-i386_env
|
||||
image: registry.labs.nic.cz/labs/bird:debian-8-i386
|
||||
tags:
|
||||
- docker
|
||||
- linux
|
||||
- amd64
|
||||
|
||||
.debian-9-i386: &debian-9-i386_env
|
||||
image: registry.labs.nic.cz/labs/bird:debian-9-i386
|
||||
tags:
|
||||
- docker
|
||||
- linux
|
||||
- amd64
|
||||
|
||||
.debian-testing-i386: &debian-testing-i386_env
|
||||
image: registry.labs.nic.cz/labs/bird:debian-testing-i386
|
||||
tags:
|
||||
- docker
|
||||
- linux
|
||||
- amd64
|
||||
|
||||
.debian-7-amd64: &debian-7-amd64_env
|
||||
image: registry.labs.nic.cz/labs/bird:debian-7-amd64
|
||||
tags:
|
||||
- docker
|
||||
- linux
|
||||
- amd64
|
||||
|
||||
.debian-8-amd64: &debian-8-amd64_env
|
||||
image: registry.labs.nic.cz/labs/bird:debian-8-amd64
|
||||
tags:
|
||||
- docker
|
||||
- linux
|
||||
- amd64
|
||||
|
||||
.debian-9-amd64: &debian-9-amd64_env
|
||||
image: registry.labs.nic.cz/labs/bird:debian-9-amd64
|
||||
tags:
|
||||
- docker
|
||||
- linux
|
||||
- amd64
|
||||
|
||||
.debian-testing-amd64: &debian-testing-amd64_env
|
||||
image: registry.labs.nic.cz/labs/bird:debian-testing-amd64
|
||||
tags:
|
||||
- docker
|
||||
- linux
|
||||
- amd64
|
||||
|
||||
.fedora-25-amd64: &fedora-25-amd64_env
|
||||
image: registry.labs.nic.cz/labs/bird:fedora-25-amd64
|
||||
tags:
|
||||
- docker
|
||||
- linux
|
||||
- amd64
|
||||
|
||||
.fedora-26-amd64: &fedora-26-amd64_env
|
||||
image: registry.labs.nic.cz/labs/bird:fedora-26-amd64
|
||||
tags:
|
||||
- docker
|
||||
- linux
|
||||
- amd64
|
||||
|
||||
.centos-6-amd64: ¢os-6-amd64_env
|
||||
image: registry.labs.nic.cz/labs/bird:centos-6-amd64
|
||||
tags:
|
||||
- docker
|
||||
- linux
|
||||
- amd64
|
||||
|
||||
.centos-7-amd64: ¢os-7-amd64_env
|
||||
image: registry.labs.nic.cz/labs/bird:centos-7-amd64
|
||||
tags:
|
||||
- docker
|
||||
- linux
|
||||
- amd64
|
||||
|
||||
.opensuse-42_3-amd64: &opensuse-42_3-amd64_env
|
||||
image: registry.labs.nic.cz/labs/bird:opensuse-42.3-amd64
|
||||
tags:
|
||||
- docker
|
||||
- linux
|
||||
- amd64
|
||||
|
||||
.ubuntu-14_04-amd64: &ubuntu-14_04-amd64_env
|
||||
image: registry.labs.nic.cz/labs/bird:ubuntu-14.04-amd64
|
||||
tags:
|
||||
- docker
|
||||
- linux
|
||||
- amd64
|
||||
|
||||
.ubuntu-16_04-amd64: &ubuntu-16_04-amd64_env
|
||||
image: registry.labs.nic.cz/labs/bird:ubuntu-16.04-amd64
|
||||
tags:
|
||||
- docker
|
||||
- linux
|
||||
- amd64
|
||||
|
||||
# TODO We want to copy these BSDs to our own virtual machines, to make sure someone doesn't update them by accident.
|
||||
.freebsd-11-i386: &freebsd-11-i386_env
|
||||
tags:
|
||||
- freebsd
|
||||
- i386
|
||||
#only:
|
||||
#- master
|
||||
#- triggers
|
||||
#- tags
|
||||
|
||||
.freebsd-11-amd64: &freebsd-11-amd64_env
|
||||
tags:
|
||||
- freebsd
|
||||
- amd64
|
||||
#only:
|
||||
#- master
|
||||
#- triggers
|
||||
#- tags
|
||||
|
||||
.build: &build_job
|
||||
stage: build
|
||||
script:
|
||||
- autoreconf
|
||||
- ./configure CPPFLAGS="$CPPFLAGS" LDFLAGS="$LDFLAGS"
|
||||
# Detect which make is available
|
||||
- MAKE=make
|
||||
- which gmake 2>/dev/null >/dev/null && MAKE=gmake
|
||||
- $MAKE
|
||||
# Run tests if they are available
|
||||
- $MAKE check
|
||||
|
||||
build-debian-7-amd64:
|
||||
<<: *debian-7-amd64_env
|
||||
<<: *build_job
|
||||
|
||||
build-debian-8-amd64:
|
||||
<<: *debian-8-amd64_env
|
||||
<<: *build_job
|
||||
|
||||
build-debian-9-amd64:
|
||||
<<: *debian-9-amd64_env
|
||||
<<: *build_job
|
||||
|
||||
build-debian-testing-amd64:
|
||||
<<: *debian-testing-amd64_env
|
||||
<<: *build_job
|
||||
|
||||
build-fedora-25-amd64:
|
||||
<<: *fedora-25-amd64_env
|
||||
<<: *build_job
|
||||
|
||||
build-fedora-26-amd64:
|
||||
<<: *fedora-26-amd64_env
|
||||
<<: *build_job
|
||||
|
||||
build-centos-6-amd64:
|
||||
<<: *centos-6-amd64_env
|
||||
<<: *build_job
|
||||
|
||||
build-centos-7-amd64:
|
||||
<<: *centos-7-amd64_env
|
||||
<<: *build_job
|
||||
|
||||
build-opensuse-42_3-amd64:
|
||||
<<: *opensuse-42_3-amd64_env
|
||||
<<: *build_job
|
||||
|
||||
build-ubuntu-14_04-amd64:
|
||||
<<: *ubuntu-14_04-amd64_env
|
||||
<<: *build_job
|
||||
|
||||
build-ubuntu-16_04-amd64:
|
||||
<<: *ubuntu-16_04-amd64_env
|
||||
<<: *build_job
|
||||
|
||||
build-debian-7-i386:
|
||||
<<: *debian-7-i386_env
|
||||
<<: *build_job
|
||||
|
||||
build-debian-8-i386:
|
||||
<<: *debian-8-i386_env
|
||||
<<: *build_job
|
||||
|
||||
build-debian-9-i386:
|
||||
<<: *debian-9-i386_env
|
||||
<<: *build_job
|
||||
|
||||
build-debian-testing-i386:
|
||||
<<: *debian-testing-i386_env
|
||||
<<: *build_job
|
||||
|
||||
build-freebsd-11-amd64:
|
||||
<<: *freebsd-11-amd64_env
|
||||
<<: *build_job
|
||||
|
||||
build-freebsd-11-i386:
|
||||
<<: *freebsd-11-i386_env
|
||||
<<: *build_job
|
27
Makefile.in
27
Makefile.in
@ -9,6 +9,8 @@ MAKEFLAGS += -r
|
||||
CPPFLAGS=-I$(objdir) -I$(srcdir) @CPPFLAGS@
|
||||
CFLAGS=$(CPPFLAGS) @CFLAGS@
|
||||
LDFLAGS=@LDFLAGS@
|
||||
M4FLAGS=@M4FLAGS@
|
||||
BISONFLAGS=@BISONFLAGS@
|
||||
LIBS=@LIBS@
|
||||
DAEMON_LIBS=@DAEMON_LIBS@
|
||||
CLIENT_LIBS=@CLIENT_LIBS@
|
||||
@ -37,6 +39,11 @@ srcdir := @srcdir@
|
||||
objdir := @objdir@
|
||||
exedir := @exedir@
|
||||
|
||||
git-label:=$(strip $(shell cd $(srcdir) && [ "$$(git rev-parse --show-toplevel)" = "$$(readlink -f .)" ] && git describe --always --dirty=-x 2>/dev/null))
|
||||
ifneq ($(git-label),)
|
||||
CFLAGS += -DGIT_LABEL="$(git-label)"
|
||||
endif
|
||||
|
||||
ifeq ($(objdir),.)
|
||||
objdir := $(realpath .)
|
||||
endif
|
||||
@ -49,11 +56,15 @@ else
|
||||
Q:=
|
||||
endif
|
||||
|
||||
ifneq ($(COLOR),)
|
||||
CFLAGS += -fdiagnostics-color=always
|
||||
endif
|
||||
|
||||
# Meta rules
|
||||
docgoals := docs userdocs progdocs
|
||||
testgoals := check test tests tests_run
|
||||
cleangoals := clean distclean testsclean
|
||||
.PHONY: all daemon cli $(docgoals) $(testgoals) $(cleangoals) tags
|
||||
.PHONY: all daemon cli $(docgoals) $(testgoals) $(cleangoals) tags cscope
|
||||
all: daemon cli
|
||||
|
||||
daemon: $(daemon)
|
||||
@ -90,7 +101,7 @@ clean = $(eval $(call clean_in,$(1)))
|
||||
include $(addsuffix /Makefile,$(addprefix $(srcdir)/,$(dirs)))
|
||||
|
||||
# Generic rules
|
||||
|
||||
# Object file rules
|
||||
$(objdir)/%.o: $(srcdir)/%.c $(objdir)/.dir-stamp $(objdir)/sysdep/paths.h
|
||||
$(E)echo CC -o $@ -c $<
|
||||
$(Q)$(CC) $(CFLAGS) -MMD -MP -o $@ -c $<
|
||||
@ -99,7 +110,16 @@ $(objdir)/%.o: $(objdir)/%.c $(objdir)/.dir-stamp $(objdir)/sysdep/paths.h
|
||||
$(E)echo CC -o $@ -c $<
|
||||
$(Q)$(CC) $(CFLAGS) -MMD -MP -o $@ -c $<
|
||||
|
||||
# Debug: Preprocessed source rules
|
||||
$(objdir)/%.E: $(srcdir)/%.c $(objdir)/.dir-stamp $(objdir)/sysdep/paths.h
|
||||
$(E)echo CC -o $@ -E $<
|
||||
$(Q)$(CC) $(CFLAGS) -MMD -MP -o $@ -E $<
|
||||
|
||||
$(objdir)/%.E: $(objdir)/%.c $(objdir)/.dir-stamp $(objdir)/sysdep/paths.h
|
||||
$(E)echo CC -o $@ -E $<
|
||||
$(Q)$(CC) $(CFLAGS) -MMD -MP -o $@ -E $<
|
||||
|
||||
# Debug: Assembler object rules
|
||||
$(objdir)/%.S: $(srcdir)/%.c $(objdir)/.dir-stamp $(objdir)/sysdep/paths.h
|
||||
$(E)echo CC -o $@ -S $<
|
||||
$(Q)$(CC) $(CFLAGS) -MMD -MP -o $@ -S $<
|
||||
@ -157,6 +177,9 @@ endif
|
||||
tags:
|
||||
cd $(srcdir) ; etags -lc `find $(dirs) -name *.[chY]`
|
||||
|
||||
cscope:
|
||||
cd $(srcdir) ; find $(dirs) -name *.[chY] > cscope.files ; cscope -b
|
||||
|
||||
# Install
|
||||
|
||||
install: all
|
||||
|
81
NEWS
81
NEWS
@ -1,45 +1,70 @@
|
||||
Version 2.0.0-pre1 (2017-04-29)
|
||||
Version 2.0.2 (2018-03-22)
|
||||
o Source-specific routing support for Linux kernel and Babel
|
||||
o BGP: New option 'disable after cease'
|
||||
o Filter: Allow silent filter execution
|
||||
o Filter: Fixed stack overflow in BGP mask expressions.
|
||||
o Several bugfixes
|
||||
|
||||
Notes:
|
||||
|
||||
Syntax prefix:netmask for IPv4 prefixes was dropped. Just use prefix/pxlen.
|
||||
|
||||
|
||||
Version 2.0.1 (2018-01-16)
|
||||
o Linux MPLS kernel support
|
||||
o Better handling of channels inherited from templates
|
||||
o Default EBGP Route Propagation Behavior without Policies (RFC 8212)
|
||||
o Many bugfixes
|
||||
|
||||
Notes:
|
||||
|
||||
To satisfy requirements of RFC 8212, external BGP protocols now require
|
||||
explicit configuration of import and export policies.
|
||||
|
||||
|
||||
Version 2.0.0 (2017-12-11)
|
||||
o Integrated IPv4 + IPv6 design
|
||||
o Support for MPLS next hops
|
||||
o VPNv4 and VPNv6 network types
|
||||
o Support for VPNv4 and VPNv6 networks
|
||||
o Microsecond timers infrastructure
|
||||
o Basic VRF support
|
||||
o Babel: Support for dual-stack IPv4/IPv6
|
||||
o Babel: Many improvements and bugfixes
|
||||
o Major BGP protocol redesign
|
||||
o Full support for Multiprotocol BGP
|
||||
o BGP multicast support (SAFI 2)
|
||||
o BGP flowspec support (RFC 5575)
|
||||
o BGP with MPLS labels (RFC 3107)
|
||||
o BGP MPLS/VPN support (RFC 4364)
|
||||
o BGP 6PE - IPv6 NLRI over IPv4 MPLS (RFC 4798)
|
||||
o BGP IPv4 NLRI with an IPv6 Next Hop (RFC 5549)
|
||||
o BGP Confederations (RFC 5065)
|
||||
o BGP: Simplify igp table options
|
||||
o BGP Shutdown communication (RFC 8203)
|
||||
o BGP: Allow exchanging LOCAL_PREF with eBGP peers
|
||||
o BGP: Allow to specify interface for regular sessions
|
||||
o Babel support restored
|
||||
o Static: Minor overhaul
|
||||
o Netlink: Default kernel metric changed to 32
|
||||
o Flowspec: Limit tcp mask length to 12 bits
|
||||
o Update of show route command
|
||||
|
||||
Notes:
|
||||
|
||||
Definitions of OSPFv2, OSPFv3 and RIP NG protocols now use keywords
|
||||
'ospf v2', 'ospf v3' and 'rip ng' instead of 'ospf2', 'ospf3' and 'ripng'.
|
||||
|
||||
Flows and ROAs no longer use phony next hops, so there is no need to use
|
||||
'drop' or 'unreachable' in their static route definitions.
|
||||
|
||||
See doc/bird.conf.example2 for configuration examples.
|
||||
|
||||
|
||||
Version 2.0.0-pre0 (2016-12-07)
|
||||
o Integrated IPv4 + IPv6 design
|
||||
o Major BGP protocol redesign
|
||||
o BGP multicast support (SAFI 2)
|
||||
o BGP flowspec support (RFC 5575)
|
||||
o OSPF: Support of address families in OSPFv3
|
||||
o OSPF: Enable ECMP and Link detection by default
|
||||
o RAdv: Support for more specific routes (RFC 4191)
|
||||
o RAdv: Proper handling of prefix retraction
|
||||
o RIP: Enable ECMP and Link detection by default
|
||||
o Redesign of RPKI handling
|
||||
o New RPKI-Router protocol
|
||||
o Static: Minor overhaul
|
||||
o Static: Support for all new route types
|
||||
o Kenrel: Default Linux kernel metric changed to 32
|
||||
o Kernel: Fix IPv6 ECMP handling with Linux 4.11+
|
||||
o Update of show route command
|
||||
o BIRD client persistent history
|
||||
o New build system
|
||||
o Unit tests
|
||||
o ...
|
||||
|
||||
Notes:
|
||||
|
||||
Protocols and tables are now connected using explicit channels, most related
|
||||
protocol options (table, import, export, ...) are now channel options. See
|
||||
doc/bird.conf.example2 for configuration examples.
|
||||
Tables are now defined with appropriate net type keyword. Protocols and tables
|
||||
are now connected by explicit channels, most related protocol options (table,
|
||||
import, export, ...) are now channel options. See doc/bird.conf.example2 for
|
||||
configuration examples. Some options were removed/replaced.
|
||||
|
||||
|
||||
Version 1.6.3 (2016-12-21)
|
||||
|
4
README
4
README
@ -6,7 +6,7 @@
|
||||
(c) 1998--2008 Martin Mares <mj@ucw.cz>
|
||||
(c) 1998--2000 Pavel Machek <pavel@ucw.cz>
|
||||
(c) 1998--2008 Ondrej Filip <feela@network.cz>
|
||||
(c) 2009--2016 CZ.NIC z.s.p.o.
|
||||
(c) 2009--2017 CZ.NIC z.s.p.o.
|
||||
|
||||
================================================================================
|
||||
|
||||
@ -19,7 +19,7 @@ Public License.
|
||||
What do we support
|
||||
==================
|
||||
|
||||
o Both IPv4 and IPv6 (use --enable-ipv6 when configuring)
|
||||
o Both IPv4 and IPv6
|
||||
o Multiple routing tables
|
||||
o Border Gateway Protocol (BGPv4)
|
||||
o Routing Information Protocol (RIPv2, RIPng)
|
||||
|
45
TODO
45
TODO
@ -1,45 +0,0 @@
|
||||
Core
|
||||
~~~~
|
||||
- socket open failure should not be fatal
|
||||
- &&,||: priorities
|
||||
- static: allow specifying a per-route filter program for setting route attributes?
|
||||
|
||||
Globals
|
||||
~~~~~~~
|
||||
- right usage of DBG vs. debug
|
||||
- logging and tracing; use appropriate log levels
|
||||
- check incoming packets and log errors!!
|
||||
- check log calls for trailing newlines and log levels followed by comma
|
||||
- check if all protocols set proper packet priorities and TTL's.
|
||||
- try compiling with -Wunused
|
||||
- does everybody test return value of sk_open?
|
||||
- protocols: implement CLI hooks and per-procotol CLI commands
|
||||
- protocols: implement reconfigure hook
|
||||
- protocols: use locking
|
||||
- check use of system includes and sprintf()
|
||||
|
||||
Various ideas
|
||||
~~~~~~~~~~~~~
|
||||
- client: Ctrl-R eats one more enter
|
||||
- bgp: timing of updates?
|
||||
- netlink: import Linux route attributes to our rta's, so that they can be filtered?
|
||||
- config: executable config files
|
||||
- filters: user defined attributes?
|
||||
- io: use poll if available
|
||||
- route recalculation timing and flap dampening [see RFC2439 for algorithms]
|
||||
- aggregate engine: standard route aggregation and summarization [RFC2519]
|
||||
- aggregate engine: injection of manually configured pseudo-static routes
|
||||
- generate default route if any working BGP connection exists (aggregate engine again?)
|
||||
- generate default route to IGP's (aggregate engine yet another time?)
|
||||
- look at RFC 2386 (QoS-based routing)
|
||||
- cli: show tables?
|
||||
|
||||
OSPF
|
||||
~~~~
|
||||
- check incoming packets using neighbor cache
|
||||
- RFC2328 appendix E: Use a better algorithm
|
||||
- automatic generation of external route tags (RFC1403)
|
||||
- RFC2370 opaque LSA's
|
||||
- Limit export rate of external LSAs (like Gated does)
|
||||
- Bugfix in link state retransmission list (aging)
|
||||
- Graceful OSPF restart - RFC3623
|
121
aclocal.m4
vendored
121
aclocal.m4
vendored
@ -31,6 +31,106 @@ AC_DEFUN([BIRD_CHECK_PTHREADS],
|
||||
CFLAGS="$bird_tmp_cflags"
|
||||
])
|
||||
|
||||
AC_DEFUN([BIRD_CHECK_MPLS_KERNEL],
|
||||
[
|
||||
AC_CACHE_CHECK(
|
||||
[for Linux MPLS headers],
|
||||
[bird_cv_mpls_kernel],
|
||||
[
|
||||
AC_COMPILE_IFELSE(
|
||||
[
|
||||
AC_LANG_PROGRAM(
|
||||
[
|
||||
#include <linux/lwtunnel.h>
|
||||
#include <linux/netlink.h>
|
||||
#include <linux/rtnetlink.h>
|
||||
#include <sys/socket.h>
|
||||
void t(int arg);
|
||||
],
|
||||
[
|
||||
t(AF_MPLS);
|
||||
t(RTA_VIA);
|
||||
t(RTA_NEWDST);
|
||||
t(RTA_ENCAP_TYPE);
|
||||
t(RTA_ENCAP);
|
||||
struct rtvia rtvia;
|
||||
t(LWTUNNEL_ENCAP_MPLS);
|
||||
]
|
||||
)
|
||||
],
|
||||
[bird_cv_mpls_kernel=yes],
|
||||
[bird_cv_mpls_kernel=no]
|
||||
)
|
||||
]
|
||||
)
|
||||
])
|
||||
|
||||
AC_DEFUN([BIRD_CHECK_ANDROID_GLOB],
|
||||
[
|
||||
AC_CACHE_CHECK(
|
||||
[for glob.h],
|
||||
[bird_cv_lib_glob],
|
||||
AC_LINK_IFELSE([
|
||||
AC_LANG_PROGRAM(
|
||||
[
|
||||
#include <glob.h>
|
||||
#include <stdlib.h>
|
||||
],
|
||||
[ glob(NULL, 0, NULL, NULL); ]
|
||||
)
|
||||
],
|
||||
[bird_cv_lib_glob=yes],
|
||||
[
|
||||
bird_tmp_libs="$LIBS"
|
||||
LIBS="$LIBS -landroid-glob"
|
||||
AC_LINK_IFELSE([
|
||||
AC_LANG_PROGRAM(
|
||||
[
|
||||
#include <glob.h>
|
||||
#include <stdlib.h>
|
||||
],
|
||||
[ glob(NULL, 0, NULL, NULL); ]
|
||||
)
|
||||
],
|
||||
[bird_cv_lib_glob=-landroid-glob],
|
||||
[bird_cv_lib_glob=no]
|
||||
)
|
||||
LIBS="$bird_tmp_libs"
|
||||
]
|
||||
)
|
||||
)
|
||||
])
|
||||
|
||||
AC_DEFUN([BIRD_CHECK_ANDROID_LOG],
|
||||
[
|
||||
AC_CACHE_CHECK(
|
||||
[for syslog lib flags],
|
||||
[bird_cv_lib_log],
|
||||
AC_LINK_IFELSE([
|
||||
AC_LANG_PROGRAM(
|
||||
[ #include <sys/syslog.h> ],
|
||||
[ syslog(0, ""); ]
|
||||
)
|
||||
],
|
||||
[bird_cv_lib_log=yes],
|
||||
[
|
||||
bird_tmp_libs="$LIBS"
|
||||
LIBS="$LIBS -llog"
|
||||
AC_LINK_IFELSE([
|
||||
AC_LANG_PROGRAM(
|
||||
[ #include <sys/syslog.h> ],
|
||||
[ syslog(0, ""); ]
|
||||
)
|
||||
],
|
||||
[bird_cv_lib_log=-llog],
|
||||
[bird_cv_lib_log=no]
|
||||
)
|
||||
LIBS="$bird_tmp_libs"
|
||||
]
|
||||
)
|
||||
)
|
||||
])
|
||||
|
||||
AC_DEFUN([BIRD_CHECK_GCC_OPTION],
|
||||
[
|
||||
bird_tmp_cflags="$CFLAGS"
|
||||
@ -75,3 +175,24 @@ AC_DEFUN([BIRD_CHECK_PROG_FLAVOR_GNU],
|
||||
)
|
||||
esac
|
||||
])
|
||||
|
||||
AC_DEFUN([BIRD_CHECK_BISON_VERSION],
|
||||
[
|
||||
$1=`bison --version | ( read line; echo ${line##* } )`
|
||||
case "$$1" in
|
||||
1.* | 2.0* | 2.1* | 2.2* | 2.3*)
|
||||
AC_MSG_ERROR([Provided Bison version $$1 is too old, need at least 2.4])
|
||||
;;
|
||||
2.*)
|
||||
bird_bison_synclines=no
|
||||
bird_bison_enhanced_error=no
|
||||
;;
|
||||
3.* | 4.* | 5.* | 6.* | 7.* | 8.* | 9.*)
|
||||
bird_bison_synclines=yes
|
||||
bird_bison_enhanced_error=yes
|
||||
;;
|
||||
*)
|
||||
AC_MSG_ERROR([Couldn't parse Bison version $$1. Call the developers for help.])
|
||||
;;
|
||||
esac
|
||||
])
|
||||
|
@ -22,13 +22,14 @@ protocol direct {
|
||||
|
||||
# Feed routes to kernel FIB
|
||||
protocol kernel {
|
||||
ipv4 { export all; };
|
||||
# learn; # Learn all routes from the kernel
|
||||
ipv4 { export all; import all; };
|
||||
learn; # Learn all routes from the kernel
|
||||
# scan time 10; # Scan kernel tables every 10 seconds
|
||||
}
|
||||
|
||||
protocol kernel {
|
||||
ipv6;
|
||||
ipv6 { import all; };
|
||||
learn;
|
||||
}
|
||||
|
||||
# Static route feed
|
||||
@ -52,6 +53,6 @@ protocol rip {
|
||||
ipv4;
|
||||
}
|
||||
|
||||
protocol ripng {
|
||||
protocol rip ng {
|
||||
ipv6;
|
||||
}
|
||||
|
@ -25,9 +25,10 @@
|
||||
#include <fcntl.h>
|
||||
#include <unistd.h>
|
||||
#include <errno.h>
|
||||
#include <sys/select.h>
|
||||
#include <sys/socket.h>
|
||||
#include <sys/un.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/un.h>
|
||||
|
||||
#include "nest/bird.h"
|
||||
#include "lib/resource.h"
|
||||
|
@ -11,7 +11,7 @@ BISON_DEBUG=-t
|
||||
endif
|
||||
|
||||
$(conf-y-targets): $(s)confbase.Y $(s)flowspec.Y
|
||||
$(M4) -P $| $^ >$@
|
||||
$(M4) $(M4FLAGS) -P $| $^ >$@
|
||||
|
||||
$(o)cf-parse.y: | $(s)gen_parser.m4
|
||||
$(o)keywords.h: | $(s)gen_keywords.m4
|
||||
@ -20,7 +20,7 @@ $(o)commands.h: | $(s)gen_commands.m4 $(srcdir)/client/cmds.m4
|
||||
$(o)cf-parse.tab.h: $(o)cf-parse.tab.c
|
||||
|
||||
$(o)cf-parse.tab.c: $(o)cf-parse.y
|
||||
$(BISON) $(BISON_DEBUG) -dv -pcf_ -b $(@:.tab.c=) $<
|
||||
$(BISON) $(BISON_DEBUG) $(BISONFLAGS) -dv -pcf_ -b $(@:.tab.c=) $<
|
||||
|
||||
$(o)cf-lex.c: $(s)cf-lex.l
|
||||
$(FLEX) $(FLEX_DEBUG) -s -B -8 -Pcf_ -o$@ $<
|
||||
|
23
conf/conf.c
23
conf/conf.c
@ -52,7 +52,7 @@
|
||||
#include "lib/resource.h"
|
||||
#include "lib/string.h"
|
||||
#include "lib/event.h"
|
||||
#include "sysdep/unix/timer.h"
|
||||
#include "lib/timer.h"
|
||||
#include "conf/conf.h"
|
||||
#include "filter/filter.h"
|
||||
|
||||
@ -102,9 +102,9 @@ config_alloc(const char *name)
|
||||
c->pool = p;
|
||||
c->mem = l;
|
||||
c->file_name = ndup;
|
||||
c->load_time = now;
|
||||
c->tf_route = c->tf_proto = (struct timeformat){"%T", "%F", 20*3600};
|
||||
c->tf_base = c->tf_log = (struct timeformat){"%F %T", NULL, 0};
|
||||
c->load_time = current_time();
|
||||
c->tf_route = c->tf_proto = TM_ISO_SHORT_MS;
|
||||
c->tf_base = c->tf_log = TM_ISO_LONG_MS;
|
||||
c->gr_wait = DEFAULT_GR_WAIT;
|
||||
|
||||
return c;
|
||||
@ -219,11 +219,6 @@ global_commit(struct config *new, struct config *old)
|
||||
if (!old)
|
||||
return 0;
|
||||
|
||||
if (!ipa_equal(old->listen_bgp_addr, new->listen_bgp_addr) ||
|
||||
(old->listen_bgp_port != new->listen_bgp_port) ||
|
||||
(old->listen_bgp_flags != new->listen_bgp_flags))
|
||||
log(L_WARN "Reconfiguration of BGP listening socket not implemented, please restart BIRD.");
|
||||
|
||||
if (!new->router_id)
|
||||
{
|
||||
new->router_id = old->router_id;
|
||||
@ -307,7 +302,7 @@ config_done(void *unused UNUSED)
|
||||
* config_commit - commit a configuration
|
||||
* @c: new configuration
|
||||
* @type: type of reconfiguration (RECONFIG_SOFT or RECONFIG_HARD)
|
||||
* @timeout: timeout for undo (or 0 for no timeout)
|
||||
* @timeout: timeout for undo (in seconds; or 0 for no timeout)
|
||||
*
|
||||
* When a configuration is parsed and prepared for use, the
|
||||
* config_commit() function starts the process of reconfiguration.
|
||||
@ -331,7 +326,7 @@ config_done(void *unused UNUSED)
|
||||
* are accepted.
|
||||
*/
|
||||
int
|
||||
config_commit(struct config *c, int type, int timeout)
|
||||
config_commit(struct config *c, int type, uint timeout)
|
||||
{
|
||||
if (shutting_down)
|
||||
{
|
||||
@ -340,8 +335,8 @@ config_commit(struct config *c, int type, int timeout)
|
||||
}
|
||||
|
||||
undo_available = 1;
|
||||
if (timeout > 0)
|
||||
tm_start(config_timer, timeout);
|
||||
if (timeout)
|
||||
tm_start(config_timer, timeout S);
|
||||
else
|
||||
tm_stop(config_timer);
|
||||
|
||||
@ -452,7 +447,7 @@ config_undo(void)
|
||||
extern void cmd_reconfig_undo_notify(void);
|
||||
|
||||
static void
|
||||
config_timeout(struct timer *t UNUSED)
|
||||
config_timeout(timer *t UNUSED)
|
||||
{
|
||||
log(L_INFO "Config timeout expired, starting undo");
|
||||
cmd_reconfig_undo_notify();
|
||||
|
11
conf/conf.h
11
conf/conf.h
@ -13,7 +13,7 @@
|
||||
#include "lib/ip.h"
|
||||
#include "lib/hash.h"
|
||||
#include "lib/resource.h"
|
||||
#include "sysdep/unix/timer.h"
|
||||
#include "lib/timer.h"
|
||||
|
||||
|
||||
/* Configuration structure */
|
||||
@ -32,16 +32,13 @@ struct config {
|
||||
struct iface_patt *router_id_from; /* Configured list of router ID iface patterns */
|
||||
|
||||
u32 router_id; /* Our Router ID */
|
||||
ip_addr listen_bgp_addr; /* Listening BGP socket should use this address */
|
||||
unsigned listen_bgp_port; /* Listening BGP socket should use this port (0 is default) */
|
||||
u32 listen_bgp_flags; /* Listening BGP socket should use these flags */
|
||||
unsigned proto_default_debug; /* Default protocol debug mask */
|
||||
unsigned proto_default_mrtdump; /* Default protocol mrtdump mask */
|
||||
struct timeformat tf_route; /* Time format for 'show route' */
|
||||
struct timeformat tf_proto; /* Time format for 'show protocol' */
|
||||
struct timeformat tf_log; /* Time format for the logfile */
|
||||
struct timeformat tf_base; /* Time format for other purposes */
|
||||
u32 gr_wait; /* Graceful restart wait timeout */
|
||||
u32 gr_wait; /* Graceful restart wait timeout (sec) */
|
||||
|
||||
int cli_debug; /* Tracing of CLI connections and commands */
|
||||
int latency_debug; /* I/O loop tracks duration of each event */
|
||||
@ -57,7 +54,7 @@ struct config {
|
||||
struct config *fallback; /* Link to regular config for CLI parsing */
|
||||
int obstacle_count; /* Number of items blocking freeing of this config */
|
||||
int shutdown; /* This is a pseudo-config for daemon shutdown */
|
||||
bird_clock_t load_time; /* When we've got this configuration */
|
||||
btime load_time; /* When we've got this configuration */
|
||||
};
|
||||
|
||||
/* Please don't use these variables in protocols. Use proto_config->global instead. */
|
||||
@ -68,7 +65,7 @@ struct config *config_alloc(const char *name);
|
||||
int config_parse(struct config *);
|
||||
int cli_parse(struct config *);
|
||||
void config_free(struct config *);
|
||||
int config_commit(struct config *, int type, int timeout);
|
||||
int config_commit(struct config *, int type, uint timeout);
|
||||
int config_confirm(void);
|
||||
int config_undo(void);
|
||||
void config_init(void);
|
||||
|
@ -14,7 +14,7 @@ CF_HDR
|
||||
#include "conf/conf.h"
|
||||
#include "lib/resource.h"
|
||||
#include "lib/socket.h"
|
||||
#include "sysdep/unix/timer.h"
|
||||
#include "lib/timer.h"
|
||||
#include "lib/string.h"
|
||||
#include "nest/protocol.h"
|
||||
#include "nest/iface.h"
|
||||
@ -49,6 +49,8 @@ CF_DECLS
|
||||
struct rtable_config *r;
|
||||
struct channel_config *cc;
|
||||
struct f_inst *x;
|
||||
struct f_dynamic_attr fda;
|
||||
struct f_static_attr fsa;
|
||||
struct filter *f;
|
||||
struct f_tree *e;
|
||||
struct f_trie *trie;
|
||||
@ -60,7 +62,7 @@ CF_DECLS
|
||||
struct lsadb_show_data *ld;
|
||||
struct iface *iface;
|
||||
void *g;
|
||||
bird_clock_t time;
|
||||
btime time;
|
||||
struct f_prefix px;
|
||||
struct proto_spec ps;
|
||||
struct channel_limit cl;
|
||||
@ -80,11 +82,10 @@ CF_DECLS
|
||||
%type <iface> ipa_scope
|
||||
|
||||
%type <i> expr bool pxlen4
|
||||
%type <i32> expr_us
|
||||
%type <time> datetime
|
||||
%type <time> expr_us time
|
||||
%type <a> ipa
|
||||
%type <net> net_ip4_ net_ip6_ net_ip6 net_ip_ net_ip net_or_ipa
|
||||
%type <net_ptr> net_ net_any net_vpn4_ net_vpn6_ net_vpn_ net_roa4_ net_roa6_ net_roa_
|
||||
%type <net_ptr> net_ net_any net_vpn4_ net_vpn6_ net_vpn_ net_roa4_ net_roa6_ net_roa_ net_ip6_sadr_ net_mpls_
|
||||
%type <mls> label_stack_start label_stack
|
||||
|
||||
%type <t> text opttext
|
||||
@ -97,7 +98,7 @@ CF_DECLS
|
||||
%left '!'
|
||||
%nonassoc '.'
|
||||
|
||||
CF_KEYWORDS(DEFINE, ON, OFF, YES, NO, S, MS, US, PORT, VPN)
|
||||
CF_KEYWORDS(DEFINE, ON, OFF, YES, NO, S, MS, US, PORT, VPN, MPLS, FROM)
|
||||
|
||||
CF_GRAMMAR
|
||||
|
||||
@ -112,12 +113,13 @@ conf_entries:
|
||||
| conf_entries conf
|
||||
;
|
||||
|
||||
CF_ADDTO(conf, ';')
|
||||
conf: ';' ;
|
||||
|
||||
|
||||
/* Constant expressions */
|
||||
|
||||
CF_ADDTO(conf, definition)
|
||||
conf: definition ;
|
||||
|
||||
definition:
|
||||
DEFINE SYM '=' term ';' {
|
||||
struct f_val *val = cfg_alloc(sizeof(struct f_val));
|
||||
@ -137,9 +139,9 @@ expr:
|
||||
|
||||
|
||||
expr_us:
|
||||
expr S { $$ = (u32) $1 * 1000000; }
|
||||
| expr MS { $$ = (u32) $1 * 1000; }
|
||||
| expr US { $$ = (u32) $1 * 1; }
|
||||
expr S { $$ = $1 S_; }
|
||||
| expr MS { $$ = $1 MS_; }
|
||||
| expr US { $$ = $1 US_; }
|
||||
;
|
||||
|
||||
/* Switches */
|
||||
@ -178,10 +180,6 @@ pxlen4:
|
||||
if ($2 > IP4_MAX_PREFIX_LENGTH) cf_error("Invalid prefix length %u", $2);
|
||||
$$ = $2;
|
||||
}
|
||||
| ':' IP4 {
|
||||
$$ = ip4_masklen($2);
|
||||
if ($$ == 255) cf_error("Invalid netmask %I4", $2);
|
||||
}
|
||||
;
|
||||
|
||||
net_ip4_: IP4 pxlen4
|
||||
@ -207,6 +205,25 @@ net_ip6_: IP6 '/' NUM
|
||||
n->prefix, n->pxlen, ip6_and(n->prefix, ip6_mkmask(n->pxlen)), n->pxlen);
|
||||
};
|
||||
|
||||
net_ip6_sadr_: IP6 '/' NUM FROM IP6 '/' NUM
|
||||
{
|
||||
if ($3 > IP6_MAX_PREFIX_LENGTH)
|
||||
cf_error("Invalid prefix length %u", $3);
|
||||
|
||||
if ($7 > IP6_MAX_PREFIX_LENGTH)
|
||||
cf_error("Invalid prefix length %u", $7);
|
||||
|
||||
$$ = cfg_alloc(sizeof(net_addr_ip6_sadr));
|
||||
net_fill_ip6_sadr($$, $1, $3, $5, $7);
|
||||
|
||||
net_addr_ip6_sadr *n = (void *) $$;
|
||||
if (!net_validate_ip6_sadr(n))
|
||||
cf_error("Invalid SADR IPv6 prefix %I6/%d from %I6/%d, maybe you wanted %I6/%d from %I6/%d",
|
||||
n->dst_prefix, n->dst_pxlen, n->src_prefix, n->src_pxlen,
|
||||
ip6_and(n->dst_prefix, ip6_mkmask(n->dst_pxlen)), n->dst_pxlen,
|
||||
ip6_and(n->src_prefix, ip6_mkmask(n->src_pxlen)), n->src_pxlen);
|
||||
};
|
||||
|
||||
net_vpn4_: VPN_RD net_ip4_
|
||||
{
|
||||
$$ = cfg_alloc(sizeof(net_addr_vpn4));
|
||||
@ -235,6 +252,12 @@ net_roa6_: net_ip6_ MAX NUM AS NUM
|
||||
cf_error("Invalid max prefix length %u", $3);
|
||||
};
|
||||
|
||||
net_mpls_: MPLS NUM
|
||||
{
|
||||
$$ = cfg_alloc(sizeof(net_addr_roa6));
|
||||
net_fill_mpls($$, $2);
|
||||
}
|
||||
|
||||
net_ip_: net_ip4_ | net_ip6_ ;
|
||||
net_vpn_: net_vpn4_ | net_vpn6_ ;
|
||||
net_roa_: net_roa4_ | net_roa6_ ;
|
||||
@ -244,6 +267,8 @@ net_:
|
||||
| net_vpn_
|
||||
| net_roa_
|
||||
| net_flow_
|
||||
| net_ip6_sadr_
|
||||
| net_mpls_
|
||||
;
|
||||
|
||||
|
||||
@ -308,11 +333,11 @@ label_stack:
|
||||
}
|
||||
;
|
||||
|
||||
datetime:
|
||||
time:
|
||||
TEXT {
|
||||
$$ = tm_parse_datetime($1);
|
||||
$$ = tm_parse_time($1);
|
||||
if (!$$)
|
||||
cf_error("Invalid date and time");
|
||||
cf_error("Invalid date/time");
|
||||
}
|
||||
;
|
||||
|
||||
|
@ -11,16 +11,6 @@ CF_HDR
|
||||
#define PARSER 1
|
||||
|
||||
#include "nest/bird.h"
|
||||
#include "conf/conf.h"
|
||||
#include "lib/resource.h"
|
||||
#include "lib/socket.h"
|
||||
#include "sysdep/unix/timer.h"
|
||||
#include "lib/string.h"
|
||||
#include "nest/protocol.h"
|
||||
#include "nest/iface.h"
|
||||
#include "nest/route.h"
|
||||
#include "nest/cli.h"
|
||||
#include "filter/filter.h"
|
||||
#include "lib/flowspec.h"
|
||||
|
||||
|
||||
|
@ -34,15 +34,10 @@ m4_define(CF_keywd, `m4_ifdef([[CF_tok_$1]],,[[m4_define([[CF_tok_$1]],1)m4_defi
|
||||
m4_define(CF_KEYWORDS, `m4_define([[CF_toks]],[[]])CF_iterate([[CF_keywd]], [[$@]])m4_ifelse(CF_toks,,,%token[[]]CF_toks
|
||||
)DNL')
|
||||
|
||||
# Dynamic syntax rules
|
||||
m4_define(CF_dyn_rules,)
|
||||
m4_define(CF_ADDTO, `m4_define([[CF_rule_$1]],m4_ifdef([[CF_rule_$1]],CF_rule_$1 | ,[[m4_define([[CF_dyn_rules]],CF_dyn_rules[[CF_RULE($1)
|
||||
]])]])$2)DNL')
|
||||
|
||||
# CLI commands
|
||||
m4_define(CF_CLI, `m4_define([[CF_cmd]], cmd_[[]]m4_translit($1, [[ ]], _))DNL
|
||||
m4_divert(2)CF_KEYWORDS(m4_translit($1, [[ ]], [[,]]))
|
||||
m4_divert(3)CF_ADDTO(cli_cmd, CF_cmd)
|
||||
m4_divert(3)cli_cmd: CF_cmd
|
||||
CF_cmd: $1 $2 END')
|
||||
m4_define(CF_CLI_CMD, `')
|
||||
m4_define(CF_CLI_HELP, `')
|
||||
@ -62,10 +57,6 @@ m4_undivert(2)DNL
|
||||
%%
|
||||
m4_undivert(3)DNL
|
||||
|
||||
/* Dynamic rules */
|
||||
|
||||
m4_define(CF_RULE, [[$1: CF_rule_$1 ;]])
|
||||
CF_dyn_rules
|
||||
%%
|
||||
m4_undivert(4)DNL
|
||||
')
|
||||
|
71
configure.ac
71
configure.ac
@ -36,6 +36,12 @@ AC_ARG_ENABLE([libssh],
|
||||
[enable_libssh=try]
|
||||
)
|
||||
|
||||
AC_ARG_ENABLE([mpls-kernel],
|
||||
[AS_HELP_STRING([--enable-mpls-kernel], [enable MPLS support in kernel protocol @<:@try@:>@])],
|
||||
[],
|
||||
[enable_mpls_kernel=try]
|
||||
)
|
||||
|
||||
AC_ARG_WITH([protocols],
|
||||
[AS_HELP_STRING([--with-protocols=LIST], [include specified routing protocols @<:@all@:>@])],
|
||||
[],
|
||||
@ -151,6 +157,20 @@ test -z "$FLEX" && AC_MSG_ERROR([Flex is missing.])
|
||||
test -z "$BISON" && AC_MSG_ERROR([Bison is missing.])
|
||||
test -z "$M4" && AC_MSG_ERROR([M4 is missing.])
|
||||
|
||||
AC_MSG_CHECKING([bison version])
|
||||
BIRD_CHECK_BISON_VERSION(BISON_VERSION)
|
||||
AC_MSG_RESULT([$BISON_VERSION])
|
||||
if test "$bird_bison_synclines" = yes; then
|
||||
M4FLAGS="$M4FLAGS -s"
|
||||
fi
|
||||
|
||||
if test "$bird_bison_enhanced_error" = yes; then
|
||||
BISONFLAGS="$BISONFLAGS -Dparse.lac=full -Dparse.error=verbose"
|
||||
fi
|
||||
|
||||
AC_SUBST([M4FLAGS])
|
||||
AC_SUBST([BISONFLAGS])
|
||||
|
||||
BIRD_CHECK_PROG_FLAVOR_GNU([$M4],
|
||||
[],
|
||||
[AC_MSG_ERROR([Provided M4 is not GNU M4.])]
|
||||
@ -175,6 +195,8 @@ else
|
||||
;;
|
||||
freebsd*)
|
||||
sysdesc=bsd
|
||||
CPPFLAGS="$CPPFLAGS -I/usr/local/include"
|
||||
LDFLAGS="$LDFLAGS -L/usr/local/lib"
|
||||
;;
|
||||
kfreebsd*)
|
||||
sysdesc=bsd
|
||||
@ -239,6 +261,20 @@ if test "$enable_libssh" != no ; then
|
||||
fi
|
||||
fi
|
||||
|
||||
if test "$enable_mpls_kernel" != no ; then
|
||||
BIRD_CHECK_MPLS_KERNEL
|
||||
|
||||
if test "$bird_cv_mpls_kernel" = yes ; then
|
||||
AC_DEFINE([HAVE_MPLS_KERNEL], [1], [Define to 1 if kernel is MPLS capable])
|
||||
elif test "$enable_mpls_kernel" = yes ; then
|
||||
AC_MSG_ERROR([Kernel MPLS support not found.])
|
||||
fi
|
||||
|
||||
if test "$enable_mpls_kernel" = try ; then
|
||||
enable_mpls_kernel="$bird_cv_mpls_kernel"
|
||||
fi
|
||||
fi
|
||||
|
||||
all_protocols="$proto_bfd babel bgp ospf pipe radv rip $proto_rpki static"
|
||||
|
||||
all_protocols=`echo $all_protocols | sed 's/ /,/g'`
|
||||
@ -287,24 +323,26 @@ esac
|
||||
AC_CHECK_HEADERS_ONCE([alloca.h syslog.h])
|
||||
AC_CHECK_MEMBERS([struct sockaddr.sa_len], [], [], [#include <sys/socket.h>])
|
||||
|
||||
AC_CHECK_HEADERS([linux/lwtunnel.h],
|
||||
[AC_DEFINE([HAVE_LWTUNNEL], [1], [Define to 1 if you have the <linux/lwtunnel.h> header file.])],
|
||||
[],
|
||||
[] dnl Force new AC_CHECK_HEADER semantics
|
||||
)
|
||||
|
||||
AC_CHECK_MEMBERS([struct rtvia.rtvia_family],
|
||||
[AC_DEFINE([HAVE_STRUCT_RTVIA], [1], [Define to 1 if you have rtvia structure.])],
|
||||
[],
|
||||
[#include <linux/rtnetlink.h>]
|
||||
)
|
||||
|
||||
AC_C_BIGENDIAN(
|
||||
[AC_DEFINE([CPU_BIG_ENDIAN], [1], [Define to 1 if cpu is big endian])],
|
||||
[AC_DEFINE([CPU_LITTLE_ENDIAN], [1], [Define to 1 if cpu is little endian])],
|
||||
[AC_MSG_ERROR([Cannot determine CPU endianity.])]
|
||||
)
|
||||
|
||||
BIRD_CHECK_ANDROID_GLOB
|
||||
if test "$bird_cv_lib_glob" = no ; then
|
||||
AC_MSG_ERROR([glob.h not found.])
|
||||
elif test "$bird_cv_lib_glob" != yes ; then
|
||||
LIBS="$LIBS $bird_cv_lib_glob"
|
||||
fi
|
||||
|
||||
BIRD_CHECK_ANDROID_LOG
|
||||
if test "$bird_cv_lib_log" = no ; then
|
||||
AC_MSG_ERROR([don't know how to link syslog.])
|
||||
elif test "$bird_cv_lib_log" != yes ; then
|
||||
LIBS="$LIBS $bird_cv_lib_log"
|
||||
fi
|
||||
|
||||
if test "$enable_debug" = yes ; then
|
||||
AC_DEFINE([DEBUGGING], [1], [Define to 1 if debugging is enabled])
|
||||
LDFLAGS="$LDFLAGS -rdynamic"
|
||||
@ -358,12 +396,6 @@ if test "$enable_client" = yes ; then
|
||||
[$TINFO_LIBS]
|
||||
)
|
||||
|
||||
AC_SEARCH_LIBS([add_history], [history readline],
|
||||
[HISTORY_LIBS="$LIBS"; LIBS=""],
|
||||
[AC_MSG_ERROR([The client requires GNU Readline library. Either install the library or use --disable-client to compile without the client.])],
|
||||
[$TINFO_LIBS]
|
||||
)
|
||||
|
||||
AC_CHECK_LIB([readline], [rl_crlf],
|
||||
[AC_DEFINE([HAVE_RL_CRLF], [1], [Define to 1 if you have rl_crlf()])],
|
||||
[],
|
||||
@ -377,7 +409,7 @@ if test "$enable_client" = yes ; then
|
||||
)
|
||||
|
||||
LIBS="$BASE_LIBS"
|
||||
CLIENT_LIBS="$HISTORY_LIBS $READLINE_LIBS $TINFO_LIBS"
|
||||
CLIENT_LIBS="$READLINE_LIBS $TINFO_LIBS"
|
||||
fi
|
||||
AC_SUBST([CLIENT])
|
||||
AC_SUBST([CLIENT_LIBS])
|
||||
@ -396,6 +428,7 @@ AC_MSG_RESULT([ System configuration: $sysdesc])
|
||||
AC_MSG_RESULT([ Debugging: $enable_debug])
|
||||
AC_MSG_RESULT([ POSIX threads: $enable_pthreads])
|
||||
AC_MSG_RESULT([ Routing protocols: $protocols])
|
||||
AC_MSG_RESULT([ Kernel MPLS support: $enable_mpls_kernel])
|
||||
AC_MSG_RESULT([ Client: $enable_client])
|
||||
|
||||
rm -f $objdir/.*-stamp
|
||||
|
@ -1,222 +1,204 @@
|
||||
/*
|
||||
* This is an example configuration file
|
||||
* (for version 1.x.x, obsolete)
|
||||
*/
|
||||
|
||||
# Yes, even shell-like comments work...
|
||||
# This is a basic configuration file, which contains boilerplate options and
|
||||
# some basic examples. It allows the BIRD daemon to start but will not cause
|
||||
# anything else to happen.
|
||||
#
|
||||
# Please refer to the BIRD User's Guide documentation, which is also available
|
||||
# online at http://bird.network.cz/ in HTML format, for more information on
|
||||
# configuring BIRD and adding routing protocols.
|
||||
|
||||
# Configure logging
|
||||
#log syslog { debug, trace, info, remote, warning, error, auth, fatal, bug };
|
||||
#log stderr all;
|
||||
#log "tmp" all;
|
||||
log syslog all;
|
||||
# log "/var/log/bird.log" { debug, trace, info, remote, warning, error, auth, fatal, bug };
|
||||
|
||||
# Override router ID
|
||||
#router id 198.51.100.1;
|
||||
# Set router ID. It is a unique identification of your router, usually one of
|
||||
# IPv4 addresses of the router. It is recommended to configure it explicitly.
|
||||
# router id 198.51.100.1;
|
||||
|
||||
# You can define your own symbols...
|
||||
#define xyzzy = (120+10);
|
||||
#define '1a-a1' = (30+40);
|
||||
|
||||
# Define a route filter...
|
||||
#filter test_filter {
|
||||
# if net ~ 10.0.0.0/16 then accept;
|
||||
# else reject;
|
||||
#}
|
||||
|
||||
#filter sink { reject; }
|
||||
#filter okay { accept; }
|
||||
|
||||
#include "filters.conf";
|
||||
|
||||
# Define another routing table
|
||||
#table testable;
|
||||
|
||||
# Turn on global debugging of all protocols
|
||||
#debug protocols all;
|
||||
# Turn on global debugging of all protocols (all messages or just selected classes)
|
||||
# debug protocols all;
|
||||
# debug protocols { events, states };
|
||||
|
||||
# Turn on internal watchdog
|
||||
#watchdog warning 5 s;
|
||||
#watchdog timeout 30 s;
|
||||
# watchdog warning 5 s;
|
||||
# watchdog timeout 30 s;
|
||||
|
||||
# The direct protocol automatically generates device routes to
|
||||
# all network interfaces. Can exist in as many instances as you wish
|
||||
# if you want to populate multiple routing tables with device routes.
|
||||
#protocol direct {
|
||||
# interface "-eth*", "*"; # Restrict network interfaces it works with
|
||||
#}
|
||||
# You can define your own constants
|
||||
# define my_asn = 65000;
|
||||
# define my_addr = 198.51.100.1;
|
||||
|
||||
# This pseudo-protocol performs synchronization between BIRD's routing
|
||||
# tables and the kernel. If your kernel supports multiple routing tables
|
||||
# (as Linux 2.2.x does), you can run multiple instances of the kernel
|
||||
# protocol and synchronize different kernel tables with different BIRD tables.
|
||||
protocol kernel {
|
||||
# learn; # Learn all alien routes from the kernel
|
||||
persist; # Don't remove routes on bird shutdown
|
||||
scan time 20; # Scan kernel routing table every 20 seconds
|
||||
# import none; # Default is import all
|
||||
export all; # Default is export none
|
||||
# kernel table 5; # Kernel table to synchronize with (default: main)
|
||||
}
|
||||
# Tables master4 and master6 are defined by default
|
||||
# ipv4 table master4;
|
||||
# ipv6 table master6;
|
||||
|
||||
# This pseudo-protocol watches all interface up/down events.
|
||||
# Define more tables, e.g. for policy routing or as MRIB
|
||||
# ipv4 table mrib4;
|
||||
# ipv6 table mrib6;
|
||||
|
||||
# The Device protocol is not a real routing protocol. It does not generate any
|
||||
# routes and it only serves as a module for getting information about network
|
||||
# interfaces from the kernel. It is necessary in almost any configuration.
|
||||
protocol device {
|
||||
scan time 10; # Scan interfaces every 10 seconds
|
||||
}
|
||||
|
||||
# Static routes (again, there can be multiple instances, so that you
|
||||
# can disable/enable various groups of static routes on the fly).
|
||||
# The direct protocol is not a real routing protocol. It automatically generates
|
||||
# direct routes to all network interfaces. Can exist in as many instances as you
|
||||
# wish if you want to populate multiple routing tables with direct routes.
|
||||
protocol direct {
|
||||
disabled; # Disable by default
|
||||
ipv4; # Connect to default IPv4 table
|
||||
ipv6; # ... and to default IPv6 table
|
||||
}
|
||||
|
||||
# The Kernel protocol is not a real routing protocol. Instead of communicating
|
||||
# with other routers in the network, it performs synchronization of BIRD
|
||||
# routing tables with the OS kernel. One instance per table.
|
||||
protocol kernel {
|
||||
ipv4 { # Connect protocol to IPv4 table by channel
|
||||
# table master4; # Default IPv4 table is master4
|
||||
# import all; # Import to table, default is import all
|
||||
export all; # Export to protocol. default is export none
|
||||
};
|
||||
# learn; # Learn alien routes from the kernel
|
||||
# kernel table 10; # Kernel table to synchronize with (default: main)
|
||||
}
|
||||
|
||||
# Another instance for IPv6, skipping default options
|
||||
protocol kernel {
|
||||
ipv6 { export all; };
|
||||
}
|
||||
|
||||
# Static routes (Again, there can be multiple instances, for different address
|
||||
# families and to disable/enable various groups of static routes on the fly).
|
||||
protocol static {
|
||||
# disabled; # Disable by default
|
||||
# table testable; # Connect to a non-default table
|
||||
# preference 1000; # Default preference of routes
|
||||
# debug { states, routes, filters, interfaces, events, packets };
|
||||
# debug all;
|
||||
# route 0.0.0.0/0 via 198.51.100.13;
|
||||
# route 198.51.100.0/25 unreachable;
|
||||
ipv4; # Again, IPv4 channel with default options
|
||||
|
||||
# route 0.0.0.0/0 via 198.51.100.10;
|
||||
# route 192.0.2.0/24 blackhole;
|
||||
# route 10.0.0.0/8 unreachable;
|
||||
# route 10.1.1.0:255.255.255.0 via 198.51.100.3;
|
||||
# route 10.1.2.0:255.255.255.0 via 198.51.100.3;
|
||||
# route 10.1.3.0:255.255.255.0 via 198.51.100.4;
|
||||
# route 10.2.0.0/24 via "arc0";
|
||||
# route 10.2.0.0/24 via "eth0";
|
||||
# # Static routes can be defined with optional attributes
|
||||
# route 10.1.1.0/24 via 198.51.100.3 { rip_metric = 3; };
|
||||
# route 10.1.2.0/24 via 198.51.100.3 { ospf_metric1 = 100; };
|
||||
# route 10.1.3.0/24 via 198.51.100.4 { ospf_metric2 = 100; };
|
||||
}
|
||||
|
||||
# Pipe protocol connects two routing tables... Beware of loops.
|
||||
#protocol pipe {
|
||||
# peer table testable;
|
||||
# Define what routes do we export to this protocol / import from it.
|
||||
# import all; # default is all
|
||||
# export all; # default is none
|
||||
# import none; # If you wish to disable imports
|
||||
# import filter test_filter; # Use named filter
|
||||
# import where source = RTS_DEVICE; # Use explicit filter
|
||||
#}
|
||||
# Pipe protocol connects two routing tables. Beware of loops.
|
||||
# protocol pipe {
|
||||
# table master4; # No ipv4/ipv6 channel definition like in other protocols
|
||||
# peer table mrib4;
|
||||
# import all; # Direction peer table -> table
|
||||
# export all; # Direction table -> peer table
|
||||
# }
|
||||
|
||||
# RIP aka Rest In Pieces...
|
||||
#protocol rip MyRIP { # You can also use an explicit name
|
||||
# preference xyzzy;
|
||||
# debug all;
|
||||
# port 1520;
|
||||
# period 7;
|
||||
# infinity 16;
|
||||
# garbage time 60;
|
||||
# interface "*" { mode broadcast; };
|
||||
# honor neighbor; # To whom do we agree to send the routing table
|
||||
# honor always;
|
||||
# honor never;
|
||||
# passwords {
|
||||
# password "nazdar";
|
||||
# RIP example, both RIP and RIPng are supported
|
||||
# protocol rip {
|
||||
# ipv4 {
|
||||
# # Export direct, static routes and ones from RIP itself
|
||||
# import all;
|
||||
# export where source ~ [ RTS_DEVICE, RTS_STATIC, RTS_RIP ];
|
||||
# };
|
||||
# authentication none;
|
||||
# import filter { print "importing"; accept; };
|
||||
# export filter { print "exporting"; accept; };
|
||||
#}
|
||||
# interface "eth*" {
|
||||
# update time 10; # Default period is 30
|
||||
# timeout time 60; # Default timeout is 180
|
||||
# authentication cryptographic; # No authentication by default
|
||||
# password "hello" { algorithm hmac sha256; }; # Default is MD5
|
||||
# };
|
||||
# }
|
||||
|
||||
#protocol ospf MyOSPF {
|
||||
# tick 2;
|
||||
# rfc1583compat yes;
|
||||
# area 0.0.0.0 {
|
||||
# stub no;
|
||||
# OSPF example, both OSPFv2 and OSPFv3 are supported
|
||||
# protocol ospf v3 {
|
||||
# ipv6 {
|
||||
# import all;
|
||||
# export where source = RTS_STATIC;
|
||||
# };
|
||||
# area 0 {
|
||||
# interface "eth*" {
|
||||
# hello 9;
|
||||
# retransmit 6;
|
||||
# cost 10;
|
||||
# transmit delay 5;
|
||||
# dead count 5;
|
||||
# wait 50;
|
||||
# type broadcast;
|
||||
# authentication simple;
|
||||
# password "pass";
|
||||
# type broadcast; # Detected by default
|
||||
# cost 10; # Interface metric
|
||||
# hello 5; # Default hello perid 10 is too long
|
||||
# };
|
||||
# interface "arc0" {
|
||||
# rx buffer large;
|
||||
# type nonbroadcast;
|
||||
# poll 14;
|
||||
# dead 75;
|
||||
# neighbors {
|
||||
# 10.1.1.2 eligible;
|
||||
# 10.1.1.4;
|
||||
# };
|
||||
# strict nonbroadcast yes;
|
||||
# interface "tun*" {
|
||||
# type ptp; # PtP mode, avoids DR selection
|
||||
# cost 100; # Interface metric
|
||||
# hello 5; # Default hello perid 10 is too long
|
||||
# };
|
||||
# interface "xxx0" {
|
||||
# passwords {
|
||||
# password "abc" {
|
||||
# id 1;
|
||||
# generate to "22-04-2003 11:00:06";
|
||||
# accept to "17-01-2004 12:01:05";
|
||||
# };
|
||||
# password "def" {
|
||||
# id 2;
|
||||
# generate from "22-04-2003 11:00:07";
|
||||
# accept from "17-01-2003 12:01:05";
|
||||
# };
|
||||
# };
|
||||
# authentication cryptographic;
|
||||
# interface "dummy0" {
|
||||
# stub; # Stub interface, just propagate it
|
||||
# };
|
||||
# };
|
||||
# area 20 {
|
||||
# stub 1;
|
||||
# interface "ppp1" {
|
||||
# hello 8;
|
||||
# authentication none;
|
||||
# };
|
||||
# interface "fr*";
|
||||
# virtual link 192.168.0.1 {
|
||||
# password "sdsdffsdfg";
|
||||
# authentication cryptographic;
|
||||
# };
|
||||
# };
|
||||
#}
|
||||
|
||||
# Define simple filter as an example for BGP import filter
|
||||
# See https://gitlab.labs.nic.cz/labs/bird/wikis/BGP_filtering for more examples
|
||||
# filter rt_import
|
||||
# {
|
||||
# if bgp_path.first != 64496 then accept;
|
||||
# if bgp_path.len > 64 then accept;
|
||||
# if bgp_next_hop != from then accept;
|
||||
# reject;
|
||||
# }
|
||||
|
||||
#protocol bgp {
|
||||
# disabled;
|
||||
# BGP example, explicit name 'uplink1' is used instead of default 'bgp1'
|
||||
# protocol bgp uplink1 {
|
||||
# description "My BGP uplink";
|
||||
# local as 65000;
|
||||
# neighbor 198.51.100.130 as 64496;
|
||||
# multihop;
|
||||
# hold time 240;
|
||||
# startup hold time 240;
|
||||
# connect retry time 120;
|
||||
# keepalive time 80; # defaults to hold time / 3
|
||||
# start delay time 5; # How long do we wait before initial connect
|
||||
# error wait time 60, 300;# Minimum and maximum time we wait after an error (when consecutive
|
||||
# # errors occur, we increase the delay exponentially ...
|
||||
# error forget time 300; # ... until this timeout expires)
|
||||
# disable after error; # Disable the protocol automatically when an error occurs
|
||||
# next hop self; # Disable next hop processing and always advertise our local address as nexthop
|
||||
# path metric 1; # Prefer routes with shorter paths (like Cisco does)
|
||||
# default bgp_med 0; # MED value we use for comparison when none is defined
|
||||
# default bgp_local_pref 0; # The same for local preference
|
||||
# source address 198.51.100.14; # What local address we use for the TCP connection
|
||||
# local 198.51.100.1 as 65000;
|
||||
# neighbor 198.51.100.10 as 64496;
|
||||
# hold time 90; # Default is 240
|
||||
# password "secret"; # Password used for MD5 authentication
|
||||
# rr client; # I am a route reflector and the neighor is my client
|
||||
# rr cluster id 1.0.0.1; # Use this value for cluster id instead of my router id
|
||||
# export where source=RTS_STATIC;
|
||||
# export filter {
|
||||
# if source = RTS_STATIC then {
|
||||
# bgp_community = -empty-; bgp_community = add(bgp_community,(65000,5678));
|
||||
# bgp_origin = 0;
|
||||
# bgp_community = -empty-; bgp_community.add((65000,5678));
|
||||
# if (65000,64501) ~ bgp_community then
|
||||
# bgp_community.add((0, 1));
|
||||
# if bgp_path ~ [= 65000 =] then
|
||||
# bgp_path.prepend(65000);
|
||||
# accept;
|
||||
# }
|
||||
# reject;
|
||||
#
|
||||
# ipv4 { # regular IPv4 unicast (1/1)
|
||||
# import filter rt_import;
|
||||
# export where source ~ [ RTS_STATIC, RTS_BGP ];
|
||||
# };
|
||||
#
|
||||
# ipv6 { # regular IPv6 unicast (2/1)
|
||||
# import filter rt_import;
|
||||
# export filter { # The same as 'where' expression above
|
||||
# if source ~ [ RTS_STATIC, RTS_BGP ]
|
||||
# then accept;
|
||||
# else reject;
|
||||
# };
|
||||
# };
|
||||
#
|
||||
# ipv4 multicast { # IPv4 multicast topology (1/2)
|
||||
# table mrib4; # explicit IPv4 table
|
||||
# import filter rt_import;
|
||||
# export all;
|
||||
# };
|
||||
#
|
||||
# ipv6 multicast { # IPv6 multicast topology (2/2)
|
||||
# table mrib6; # explicit IPv6 table
|
||||
# import filter rt_import;
|
||||
# export all;
|
||||
# };
|
||||
#}
|
||||
#
|
||||
# Template usage example
|
||||
#template bgp rr_client {
|
||||
# disabled;
|
||||
# local as 65000;
|
||||
# multihop;
|
||||
|
||||
# Template example. Using templates to define IBGP route reflector clients.
|
||||
# template bgp rr_clients {
|
||||
# local 10.0.0.1 as 65000;
|
||||
# neighbor as 65000;
|
||||
# rr client;
|
||||
# rr cluster id 1.0.0.1;
|
||||
#}
|
||||
#
|
||||
#protocol bgp rr_abcd from rr_client {
|
||||
# neighbor 10.1.4.7 as 65000;
|
||||
#}
|
||||
# ipv4 {
|
||||
# import all;
|
||||
# export where source = RTS_BGP;
|
||||
# };
|
||||
#
|
||||
# ipv6 {
|
||||
# import all;
|
||||
# export where source = RTS_BGP;
|
||||
# };
|
||||
# }
|
||||
#
|
||||
# protocol bgp client1 from rr_clients {
|
||||
# neighbor 10.0.1.1;
|
||||
# }
|
||||
#
|
||||
# protocol bgp client2 from rr_clients {
|
||||
# neighbor 10.0.2.1;
|
||||
# }
|
||||
#
|
||||
# protocol bgp client3 from rr_clients {
|
||||
# neighbor 10.0.3.1;
|
||||
# }
|
||||
|
@ -28,20 +28,15 @@ flow6 table flowtab6;
|
||||
|
||||
|
||||
protocol device {
|
||||
scan time 10;
|
||||
}
|
||||
|
||||
protocol kernel kernel4 {
|
||||
scan time 20;
|
||||
|
||||
ipv4 {
|
||||
export all;
|
||||
};
|
||||
}
|
||||
|
||||
protocol kernel kernel6 {
|
||||
scan time 20;
|
||||
|
||||
ipv6 {
|
||||
export all;
|
||||
};
|
||||
@ -169,8 +164,6 @@ protocol pipe {
|
||||
}
|
||||
|
||||
protocol ospf v2 ospf4 {
|
||||
# ecmp;
|
||||
|
||||
ipv4 {
|
||||
import all;
|
||||
# export where source = RTS_STATIC;
|
||||
@ -186,8 +179,6 @@ protocol ospf v2 ospf4 {
|
||||
|
||||
|
||||
protocol ospf v3 ospf6 {
|
||||
# ecmp;
|
||||
|
||||
ipv6 {
|
||||
import all;
|
||||
# export where source = RTS_STATIC;
|
||||
@ -251,7 +242,7 @@ protocol bgp {
|
||||
};
|
||||
|
||||
# IPv6 with MPLS labels (2/4)
|
||||
ipv6 multicast {
|
||||
ipv6 mpls {
|
||||
# explicit IPv6 table
|
||||
table mtab6;
|
||||
import all;
|
||||
|
1675
doc/bird.sgml
1675
doc/bird.sgml
File diff suppressed because it is too large
Load Diff
311
filter/config.Y
311
filter/config.Y
@ -12,8 +12,6 @@ CF_HDR
|
||||
|
||||
CF_DEFINES
|
||||
|
||||
#define P(a,b) ((a << 8) | b)
|
||||
|
||||
static inline u32 pair(u32 a, u32 b) { return (a << 16) | b; }
|
||||
static inline u32 pair_a(u32 p) { return p >> 16; }
|
||||
static inline u32 pair_b(u32 p) { return p & 0xFFFF; }
|
||||
@ -112,7 +110,7 @@ f_new_ec_item(u32 kind, u32 ipv4_used, u32 key, u32 vf, u32 vt)
|
||||
{
|
||||
u64 fm, to;
|
||||
|
||||
if (ipv4_used || (key >= 0x10000)) {
|
||||
if ((kind != EC_GENERIC) && (ipv4_used || (key >= 0x10000))) {
|
||||
check_u16(vf);
|
||||
if (vt == EC_ALL)
|
||||
vt = 0xFFFF;
|
||||
@ -157,12 +155,11 @@ f_new_lc_item(u32 f1, u32 t1, u32 f2, u32 t2, u32 f3, u32 t3)
|
||||
}
|
||||
|
||||
static inline struct f_inst *
|
||||
f_generate_empty(struct f_inst *dyn)
|
||||
f_generate_empty(struct f_dynamic_attr dyn)
|
||||
{
|
||||
struct f_inst *e = f_new_inst();
|
||||
e->code = 'E';
|
||||
struct f_inst *e = f_new_inst(FI_EMPTY);
|
||||
|
||||
switch (dyn->aux & EAF_TYPE_MASK) {
|
||||
switch (dyn.type & EAF_TYPE_MASK) {
|
||||
case EAF_TYPE_AS_PATH:
|
||||
e->aux = T_PATH;
|
||||
break;
|
||||
@ -179,9 +176,9 @@ f_generate_empty(struct f_inst *dyn)
|
||||
cf_error("Can't empty that attribute");
|
||||
}
|
||||
|
||||
dyn->code = P('e','S');
|
||||
dyn->a1.p = e;
|
||||
return dyn;
|
||||
struct f_inst *s = f_new_inst_da(FI_EA_SET, dyn);
|
||||
s->a1.p = e;
|
||||
return s;
|
||||
}
|
||||
|
||||
|
||||
@ -190,21 +187,19 @@ f_generate_dpair(struct f_inst *t1, struct f_inst *t2)
|
||||
{
|
||||
struct f_inst *rv;
|
||||
|
||||
if ((t1->code == 'c') && (t2->code == 'c')) {
|
||||
if ((t1->fi_code == FI_CONSTANT) && (t2->fi_code == FI_CONSTANT)) {
|
||||
if ((t1->aux != T_INT) || (t2->aux != T_INT))
|
||||
cf_error( "Can't operate with value of non-integer type in pair constructor");
|
||||
|
||||
check_u16(t1->a2.i);
|
||||
check_u16(t2->a2.i);
|
||||
|
||||
rv = f_new_inst();
|
||||
rv->code = 'c';
|
||||
rv = f_new_inst(FI_CONSTANT);
|
||||
rv->aux = T_PAIR;
|
||||
rv->a2.i = pair(t1->a2.i, t2->a2.i);
|
||||
}
|
||||
else {
|
||||
rv = f_new_inst();
|
||||
rv->code = P('m', 'p');
|
||||
rv = f_new_inst(FI_PAIR_CONSTRUCT);
|
||||
rv->a1.p = t1;
|
||||
rv->a2.p = t2;
|
||||
}
|
||||
@ -219,7 +214,7 @@ f_generate_ec(u16 kind, struct f_inst *tk, struct f_inst *tv)
|
||||
int c1 = 0, c2 = 0, ipv4_used = 0;
|
||||
u32 key = 0, val2 = 0;
|
||||
|
||||
if (tk->code == 'c') {
|
||||
if (tk->fi_code == FI_CONSTANT) {
|
||||
c1 = 1;
|
||||
|
||||
if (tk->aux == T_INT) {
|
||||
@ -233,7 +228,7 @@ f_generate_ec(u16 kind, struct f_inst *tk, struct f_inst *tv)
|
||||
}
|
||||
|
||||
/* IP->Quad implicit conversion */
|
||||
else if (tk->code == 'C') {
|
||||
else if (tk->fi_code == FI_CONSTANT_INDIRECT) {
|
||||
c1 = 1;
|
||||
struct f_val *val = tk->a1.p;
|
||||
|
||||
@ -250,7 +245,7 @@ f_generate_ec(u16 kind, struct f_inst *tk, struct f_inst *tv)
|
||||
cf_error("Can't operate with key of non-integer/IPv4 type in EC constructor");
|
||||
}
|
||||
|
||||
if (tv->code == 'c') {
|
||||
if (tv->fi_code == FI_CONSTANT) {
|
||||
if (tv->aux != T_INT)
|
||||
cf_error("Can't operate with value of non-integer type in EC constructor");
|
||||
c2 = 1;
|
||||
@ -276,15 +271,13 @@ f_generate_ec(u16 kind, struct f_inst *tk, struct f_inst *tv)
|
||||
}
|
||||
|
||||
NEW_F_VAL;
|
||||
rv = f_new_inst();
|
||||
rv->code = 'C';
|
||||
rv = f_new_inst(FI_CONSTANT_INDIRECT);
|
||||
rv->a1.p = val;
|
||||
val->type = T_EC;
|
||||
val->val.ec = ec;
|
||||
}
|
||||
else {
|
||||
rv = f_new_inst();
|
||||
rv->code = P('m','c');
|
||||
rv = f_new_inst(FI_EC_CONSTRUCT);
|
||||
rv->aux = kind;
|
||||
rv->a1.p = tk;
|
||||
rv->a2.p = tv;
|
||||
@ -298,12 +291,11 @@ f_generate_lc(struct f_inst *t1, struct f_inst *t2, struct f_inst *t3)
|
||||
{
|
||||
struct f_inst *rv;
|
||||
|
||||
if ((t1->code == 'c') && (t2->code == 'c') && (t3->code == 'c')) {
|
||||
if ((t1->fi_code == FI_CONSTANT) && (t2->fi_code == FI_CONSTANT) && (t3->fi_code == FI_CONSTANT)) {
|
||||
if ((t1->aux != T_INT) || (t2->aux != T_INT) || (t3->aux != T_INT))
|
||||
cf_error( "LC - Can't operate with value of non-integer type in tuple constructor");
|
||||
|
||||
rv = f_new_inst();
|
||||
rv->code = 'C';
|
||||
rv = f_new_inst(FI_CONSTANT_INDIRECT);
|
||||
|
||||
NEW_F_VAL;
|
||||
rv->a1.p = val;
|
||||
@ -312,17 +304,36 @@ f_generate_lc(struct f_inst *t1, struct f_inst *t2, struct f_inst *t3)
|
||||
}
|
||||
else
|
||||
{
|
||||
rv = cfg_allocz(sizeof(struct f_inst3));
|
||||
rv->lineno = ifs->lino;
|
||||
rv->code = P('m','l');
|
||||
rv = f_new_inst(FI_LC_CONSTRUCT);
|
||||
rv->a1.p = t1;
|
||||
rv->a2.p = t2;
|
||||
INST3(rv).p = t3;
|
||||
rv->a3.p = t3;
|
||||
}
|
||||
|
||||
return rv;
|
||||
}
|
||||
|
||||
static inline struct f_inst *
|
||||
f_generate_path_mask(struct f_path_mask *t)
|
||||
{
|
||||
for (struct f_path_mask *tt = t; tt; tt = tt->next) {
|
||||
if (tt->kind == PM_ASN_EXPR) {
|
||||
struct f_inst *mrv = f_new_inst(FI_PATHMASK_CONSTRUCT);
|
||||
mrv->a1.p = t;
|
||||
return mrv;
|
||||
}
|
||||
}
|
||||
|
||||
NEW_F_VAL;
|
||||
val->type = T_PATH_MASK;
|
||||
val->val.path_mask = t;
|
||||
|
||||
struct f_inst *rv = f_new_inst(FI_CONSTANT_INDIRECT);
|
||||
rv->a1.p = val;
|
||||
|
||||
return rv;
|
||||
}
|
||||
|
||||
/*
|
||||
* Remove all new lines and doubled whitespaces
|
||||
* and convert all tabulators to spaces
|
||||
@ -372,8 +383,7 @@ static struct f_inst *
|
||||
assert_done(struct f_inst *expr, const char *start, const char *end)
|
||||
{
|
||||
struct f_inst *i;
|
||||
i = f_new_inst();
|
||||
i->code = P('a','s');
|
||||
i = f_new_inst(FI_ASSERT);
|
||||
i->a1.p = expr;
|
||||
|
||||
if (end >= start)
|
||||
@ -399,7 +409,7 @@ CF_KEYWORDS(FUNCTION, PRINT, PRINTN, UNSET, RETURN,
|
||||
TRUE, FALSE, RT, RO, UNKNOWN, GENERIC,
|
||||
FROM, GW, NET, MASK, PROTO, SOURCE, SCOPE, DEST, IFNAME, IFINDEX,
|
||||
PREFERENCE,
|
||||
ROA_CHECK, ASN,
|
||||
ROA_CHECK, ASN, SRC,
|
||||
IS_V4, IS_V6,
|
||||
LEN, MAXLEN,
|
||||
DEFINED,
|
||||
@ -412,7 +422,9 @@ CF_KEYWORDS(FUNCTION, PRINT, PRINTN, UNSET, RETURN,
|
||||
%nonassoc THEN
|
||||
%nonassoc ELSE
|
||||
|
||||
%type <x> term block cmds cmds_int cmd function_body constant constructor print_one print_list var_list var_listn dynamic_attr static_attr function_call symbol bgp_path_expr
|
||||
%type <x> term block cmds cmds_int cmd function_body constant constructor print_one print_list var_list var_listn function_call symbol bgp_path_expr
|
||||
%type <fda> dynamic_attr
|
||||
%type <fsa> static_attr
|
||||
%type <f> filter filter_body where_filter
|
||||
%type <i> type break_command ec_kind
|
||||
%type <i32> cnum
|
||||
@ -421,12 +433,12 @@ CF_KEYWORDS(FUNCTION, PRINT, PRINTN, UNSET, RETURN,
|
||||
%type <v> set_atom switch_atom fipa
|
||||
%type <px> fprefix
|
||||
%type <s> decls declsn one_decl function_params
|
||||
%type <h> bgp_path bgp_path_tail1 bgp_path_tail2
|
||||
%type <h> bgp_path bgp_path_tail
|
||||
%type <t> get_cf_position
|
||||
|
||||
CF_GRAMMAR
|
||||
|
||||
CF_ADDTO(conf, filter_def)
|
||||
conf: filter_def ;
|
||||
filter_def:
|
||||
FILTER SYM { $2 = cf_define_symbol($2, SYM_FILTER, NULL); cf_push_scope( $2 ); }
|
||||
filter_body {
|
||||
@ -437,12 +449,12 @@ filter_def:
|
||||
}
|
||||
;
|
||||
|
||||
CF_ADDTO(conf, filter_eval)
|
||||
conf: filter_eval ;
|
||||
filter_eval:
|
||||
EVAL term { f_eval_int($2); }
|
||||
;
|
||||
|
||||
CF_ADDTO(conf, bt_test_suite)
|
||||
conf: bt_test_suite ;
|
||||
bt_test_suite:
|
||||
BT_TEST_SUITE '(' SYM ',' text ')' {
|
||||
if (!($3->class & SYM_FUNCTION))
|
||||
@ -543,16 +555,13 @@ where_filter:
|
||||
/* Construct 'IF term THEN ACCEPT; REJECT;' */
|
||||
struct filter *f = cfg_alloc(sizeof(struct filter));
|
||||
struct f_inst *i, *acc, *rej;
|
||||
acc = f_new_inst(); /* ACCEPT */
|
||||
acc->code = P('p',',');
|
||||
acc = f_new_inst(FI_PRINT_AND_DIE); /* ACCEPT */
|
||||
acc->a1.p = NULL;
|
||||
acc->a2.i = F_ACCEPT;
|
||||
rej = f_new_inst(); /* REJECT */
|
||||
rej->code = P('p',',');
|
||||
rej = f_new_inst(FI_PRINT_AND_DIE); /* REJECT */
|
||||
rej->a1.p = NULL;
|
||||
rej->a2.i = F_REJECT;
|
||||
i = f_new_inst(); /* IF */
|
||||
i->code = '?';
|
||||
i = f_new_inst(FI_CONDITION); /* IF */
|
||||
i->a1.p = $2;
|
||||
i->a2.p = acc;
|
||||
i->next = rej;
|
||||
@ -571,8 +580,7 @@ function_body:
|
||||
decls '{' cmds '}' {
|
||||
if ($1) {
|
||||
/* Prepend instruction to clear local variables */
|
||||
$$ = f_new_inst();
|
||||
$$->code = P('c','v');
|
||||
$$ = f_new_inst(FI_CLEAR_LOCAL_VARS);
|
||||
$$->a1.p = $1;
|
||||
$$->next = $3;
|
||||
} else
|
||||
@ -580,7 +588,7 @@ function_body:
|
||||
}
|
||||
;
|
||||
|
||||
CF_ADDTO(conf, function_def)
|
||||
conf: function_def ;
|
||||
function_def:
|
||||
FUNCTION SYM { DBG( "Beginning of function %s\n", $2->name );
|
||||
$2 = cf_define_symbol($2, SYM_FUNCTION, NULL);
|
||||
@ -755,7 +763,7 @@ switch_body: /* EMPTY */ { $$ = NULL; }
|
||||
}
|
||||
;
|
||||
|
||||
/* CONST '(' expr ')' { $$ = f_new_inst(); $$->code = 'c'; $$->aux = T_INT; $$->a2.i = $3; } */
|
||||
/* CONST '(' expr ')' { $$ = f_new_inst(FI_CONSTANT); $$->aux = T_INT; $$->a2.i = $3; } */
|
||||
|
||||
bgp_path_expr:
|
||||
symbol { $$ = $1; }
|
||||
@ -763,53 +771,39 @@ bgp_path_expr:
|
||||
;
|
||||
|
||||
bgp_path:
|
||||
PO bgp_path_tail1 PC { $$ = $2; }
|
||||
| '/' bgp_path_tail2 '/' { $$ = $2; }
|
||||
PO bgp_path_tail PC { $$ = $2; }
|
||||
;
|
||||
|
||||
bgp_path_tail1:
|
||||
NUM bgp_path_tail1 { $$ = cfg_allocz(sizeof(struct f_path_mask)); $$->next = $2; $$->kind = PM_ASN; $$->val = $1; }
|
||||
| NUM DDOT NUM bgp_path_tail1 { $$ = cfg_allocz(sizeof(struct f_path_mask)); $$->next = $4; $$->kind = PM_ASN_RANGE; $$->val = $1; $$->val2 = $3; }
|
||||
| '*' bgp_path_tail1 { $$ = cfg_allocz(sizeof(struct f_path_mask)); $$->next = $2; $$->kind = PM_ASTERISK; }
|
||||
| '?' bgp_path_tail1 { $$ = cfg_allocz(sizeof(struct f_path_mask)); $$->next = $2; $$->kind = PM_QUESTION; }
|
||||
| bgp_path_expr bgp_path_tail1 { $$ = cfg_allocz(sizeof(struct f_path_mask)); $$->next = $2; $$->kind = PM_ASN_EXPR; $$->val = (uintptr_t) $1; }
|
||||
bgp_path_tail:
|
||||
NUM bgp_path_tail { $$ = cfg_allocz(sizeof(struct f_path_mask)); $$->next = $2; $$->kind = PM_ASN; $$->val = $1; }
|
||||
| NUM DDOT NUM bgp_path_tail { $$ = cfg_allocz(sizeof(struct f_path_mask)); $$->next = $4; $$->kind = PM_ASN_RANGE; $$->val = $1; $$->val2 = $3; }
|
||||
| '*' bgp_path_tail { $$ = cfg_allocz(sizeof(struct f_path_mask)); $$->next = $2; $$->kind = PM_ASTERISK; }
|
||||
| '?' bgp_path_tail { $$ = cfg_allocz(sizeof(struct f_path_mask)); $$->next = $2; $$->kind = PM_QUESTION; }
|
||||
| bgp_path_expr bgp_path_tail { $$ = cfg_allocz(sizeof(struct f_path_mask)); $$->next = $2; $$->kind = PM_ASN_EXPR; $$->val = (uintptr_t) $1; }
|
||||
| { $$ = NULL; }
|
||||
;
|
||||
|
||||
bgp_path_tail2:
|
||||
NUM bgp_path_tail2 { $$ = cfg_allocz(sizeof(struct f_path_mask)); $$->next = $2; $$->kind = PM_ASN; $$->val = $1; }
|
||||
| '?' bgp_path_tail2 { $$ = cfg_allocz(sizeof(struct f_path_mask)); $$->next = $2; $$->kind = PM_ASTERISK; }
|
||||
| { $$ = NULL; }
|
||||
;
|
||||
|
||||
constant:
|
||||
NUM { $$ = f_new_inst(); $$->code = 'c'; $$->aux = T_INT; $$->a2.i = $1; }
|
||||
| TRUE { $$ = f_new_inst(); $$->code = 'c'; $$->aux = T_BOOL; $$->a2.i = 1; }
|
||||
| FALSE { $$ = f_new_inst(); $$->code = 'c'; $$->aux = T_BOOL; $$->a2.i = 0; }
|
||||
| TEXT { $$ = f_new_inst(); $$->code = 'c'; $$->aux = T_STRING; $$->a2.p = $1; }
|
||||
| fipa { NEW_F_VAL; $$ = f_new_inst(); $$->code = 'C'; $$->a1.p = val; *val = $1; }
|
||||
| VPN_RD { NEW_F_VAL; $$ = f_new_inst(); $$->code = 'C'; val->type = T_RD; val->val.ec = $1; $$->a1.p = val; }
|
||||
| net_ { NEW_F_VAL; $$ = f_new_inst(); $$->code = 'C'; val->type = T_NET; val->val.net = $1; $$->a1.p = val; }
|
||||
| '[' set_items ']' { DBG( "We've got a set here..." ); $$ = f_new_inst(); $$->code = 'c'; $$->aux = T_SET; $$->a2.p = build_tree($2); DBG( "ook\n" ); }
|
||||
| '[' fprefix_set ']' { $$ = f_new_inst(); $$->code = 'c'; $$->aux = T_PREFIX_SET; $$->a2.p = $2; }
|
||||
| ENUM { $$ = f_new_inst(); $$->code = 'c'; $$->aux = $1 >> 16; $$->a2.i = $1 & 0xffff; }
|
||||
| bgp_path { NEW_F_VAL; $$ = f_new_inst(); $$->code = 'C'; val->type = T_PATH_MASK; val->val.path_mask = $1; $$->a1.p = val; }
|
||||
NUM { $$ = f_new_inst(FI_CONSTANT); $$->aux = T_INT; $$->a2.i = $1; }
|
||||
| TRUE { $$ = f_new_inst(FI_CONSTANT); $$->aux = T_BOOL; $$->a2.i = 1; }
|
||||
| FALSE { $$ = f_new_inst(FI_CONSTANT); $$->aux = T_BOOL; $$->a2.i = 0; }
|
||||
| TEXT { $$ = f_new_inst(FI_CONSTANT); $$->aux = T_STRING; $$->a2.p = $1; }
|
||||
| fipa { NEW_F_VAL; $$ = f_new_inst(FI_CONSTANT_INDIRECT); $$->a1.p = val; *val = $1; }
|
||||
| VPN_RD { NEW_F_VAL; $$ = f_new_inst(FI_CONSTANT_INDIRECT); val->type = T_RD; val->val.ec = $1; $$->a1.p = val; }
|
||||
| net_ { NEW_F_VAL; $$ = f_new_inst(FI_CONSTANT_INDIRECT); val->type = T_NET; val->val.net = $1; $$->a1.p = val; }
|
||||
| '[' set_items ']' { DBG( "We've got a set here..." ); $$ = f_new_inst(FI_CONSTANT); $$->aux = T_SET; $$->a2.p = build_tree($2); DBG( "ook\n" ); }
|
||||
| '[' fprefix_set ']' { $$ = f_new_inst(FI_CONSTANT); $$->aux = T_PREFIX_SET; $$->a2.p = $2; }
|
||||
| ENUM { $$ = f_new_inst(FI_CONSTANT); $$->aux = $1 >> 16; $$->a2.i = $1 & 0xffff; }
|
||||
;
|
||||
|
||||
constructor:
|
||||
'(' term ',' term ')' { $$ = f_generate_dpair($2, $4); }
|
||||
| '(' ec_kind ',' term ',' term ')' { $$ = f_generate_ec($2, $4, $6); }
|
||||
| '(' term ',' term ',' term ')' { $$ = f_generate_lc($2, $4, $6); }
|
||||
| bgp_path { $$ = f_generate_path_mask($1); }
|
||||
;
|
||||
|
||||
|
||||
/*
|
||||
* Maybe there are no dynamic attributes defined by protocols.
|
||||
* For such cases, we force the dynamic_attr list to contain
|
||||
* at least an invalid token, so it is syntantically correct.
|
||||
*/
|
||||
CF_ADDTO(dynamic_attr, INVALID_TOKEN { $$ = NULL; })
|
||||
|
||||
rtadot: /* EMPTY, we are not permitted RTA. prefix */
|
||||
;
|
||||
|
||||
@ -820,8 +814,7 @@ function_call:
|
||||
if ($1->class != SYM_FUNCTION)
|
||||
cf_error("You can't call something which is not a function. Really.");
|
||||
DBG("You are calling function %s\n", $1->name);
|
||||
$$ = f_new_inst();
|
||||
$$->code = P('c','a');
|
||||
$$ = f_new_inst(FI_CALL);
|
||||
$$->a1.p = inst;
|
||||
$$->a2.p = $1->def;
|
||||
sym = $1->aux2;
|
||||
@ -838,11 +831,9 @@ function_call:
|
||||
|
||||
symbol:
|
||||
SYM {
|
||||
$$ = f_new_inst();
|
||||
|
||||
switch ($1->class & 0xff00) {
|
||||
case SYM_CONSTANT: $$->code = 'C'; break;
|
||||
case SYM_VARIABLE: $$->code = 'V'; break;
|
||||
case SYM_CONSTANT: $$ = f_new_inst(FI_CONSTANT_INDIRECT); break;
|
||||
case SYM_VARIABLE: $$ = f_new_inst(FI_VARIABLE); break;
|
||||
default: cf_error("%s: variable expected.", $1->name);
|
||||
}
|
||||
|
||||
@ -851,57 +842,58 @@ symbol:
|
||||
}
|
||||
|
||||
static_attr:
|
||||
FROM { $$ = f_new_inst(); $$->aux = T_IP; $$->a2.i = SA_FROM; $$->a1.i = 1; }
|
||||
| GW { $$ = f_new_inst(); $$->aux = T_IP; $$->a2.i = SA_GW; $$->a1.i = 1; }
|
||||
| NET { $$ = f_new_inst(); $$->aux = T_NET; $$->a2.i = SA_NET; }
|
||||
| PROTO { $$ = f_new_inst(); $$->aux = T_STRING; $$->a2.i = SA_PROTO; }
|
||||
| SOURCE { $$ = f_new_inst(); $$->aux = T_ENUM_RTS; $$->a2.i = SA_SOURCE; }
|
||||
| SCOPE { $$ = f_new_inst(); $$->aux = T_ENUM_SCOPE; $$->a2.i = SA_SCOPE; $$->a1.i = 1; }
|
||||
| DEST { $$ = f_new_inst(); $$->aux = T_ENUM_RTD; $$->a2.i = SA_DEST; $$->a1.i = 1; }
|
||||
| IFNAME { $$ = f_new_inst(); $$->aux = T_STRING; $$->a2.i = SA_IFNAME; }
|
||||
| IFINDEX { $$ = f_new_inst(); $$->aux = T_INT; $$->a2.i = SA_IFINDEX; }
|
||||
FROM { $$ = f_new_static_attr(T_IP, SA_FROM, 1); }
|
||||
| GW { $$ = f_new_static_attr(T_IP, SA_GW, 1); }
|
||||
| NET { $$ = f_new_static_attr(T_NET, SA_NET, 0); }
|
||||
| PROTO { $$ = f_new_static_attr(T_STRING, SA_PROTO, 0); }
|
||||
| SOURCE { $$ = f_new_static_attr(T_ENUM_RTS, SA_SOURCE, 0); }
|
||||
| SCOPE { $$ = f_new_static_attr(T_ENUM_SCOPE, SA_SCOPE, 1); }
|
||||
| DEST { $$ = f_new_static_attr(T_ENUM_RTD, SA_DEST, 1); }
|
||||
| IFNAME { $$ = f_new_static_attr(T_STRING, SA_IFNAME, 0); }
|
||||
| IFINDEX { $$ = f_new_static_attr(T_INT, SA_IFINDEX, 0); }
|
||||
;
|
||||
|
||||
term:
|
||||
'(' term ')' { $$ = $2; }
|
||||
| term '+' term { $$ = f_new_inst(); $$->code = '+'; $$->a1.p = $1; $$->a2.p = $3; }
|
||||
| term '-' term { $$ = f_new_inst(); $$->code = '-'; $$->a1.p = $1; $$->a2.p = $3; }
|
||||
| term '*' term { $$ = f_new_inst(); $$->code = '*'; $$->a1.p = $1; $$->a2.p = $3; }
|
||||
| term '/' term { $$ = f_new_inst(); $$->code = '/'; $$->a1.p = $1; $$->a2.p = $3; }
|
||||
| term AND term { $$ = f_new_inst(); $$->code = '&'; $$->a1.p = $1; $$->a2.p = $3; }
|
||||
| term OR term { $$ = f_new_inst(); $$->code = '|'; $$->a1.p = $1; $$->a2.p = $3; }
|
||||
| term '=' term { $$ = f_new_inst(); $$->code = P('=','='); $$->a1.p = $1; $$->a2.p = $3; }
|
||||
| term NEQ term { $$ = f_new_inst(); $$->code = P('!','='); $$->a1.p = $1; $$->a2.p = $3; }
|
||||
| term '<' term { $$ = f_new_inst(); $$->code = '<'; $$->a1.p = $1; $$->a2.p = $3; }
|
||||
| term LEQ term { $$ = f_new_inst(); $$->code = P('<','='); $$->a1.p = $1; $$->a2.p = $3; }
|
||||
| term '>' term { $$ = f_new_inst(); $$->code = '<'; $$->a1.p = $3; $$->a2.p = $1; }
|
||||
| term GEQ term { $$ = f_new_inst(); $$->code = P('<','='); $$->a1.p = $3; $$->a2.p = $1; }
|
||||
| term '~' term { $$ = f_new_inst(); $$->code = '~'; $$->a1.p = $1; $$->a2.p = $3; }
|
||||
| term NMA term { $$ = f_new_inst(); $$->code = P('!','~'); $$->a1.p = $1; $$->a2.p = $3; }
|
||||
| '!' term { $$ = f_new_inst(); $$->code = '!'; $$->a1.p = $2; }
|
||||
| DEFINED '(' term ')' { $$ = f_new_inst(); $$->code = P('d','e'); $$->a1.p = $3; }
|
||||
'(' term ')' { $$ = $2; }
|
||||
| term '+' term { $$ = f_new_inst(FI_ADD); $$->a1.p = $1; $$->a2.p = $3; }
|
||||
| term '-' term { $$ = f_new_inst(FI_SUBTRACT); $$->a1.p = $1; $$->a2.p = $3; }
|
||||
| term '*' term { $$ = f_new_inst(FI_MULTIPLY); $$->a1.p = $1; $$->a2.p = $3; }
|
||||
| term '/' term { $$ = f_new_inst(FI_DIVIDE); $$->a1.p = $1; $$->a2.p = $3; }
|
||||
| term AND term { $$ = f_new_inst(FI_AND); $$->a1.p = $1; $$->a2.p = $3; }
|
||||
| term OR term { $$ = f_new_inst(FI_OR); $$->a1.p = $1; $$->a2.p = $3; }
|
||||
| term '=' term { $$ = f_new_inst(FI_EQ); $$->a1.p = $1; $$->a2.p = $3; }
|
||||
| term NEQ term { $$ = f_new_inst(FI_NEQ); $$->a1.p = $1; $$->a2.p = $3; }
|
||||
| term '<' term { $$ = f_new_inst(FI_LT); $$->a1.p = $1; $$->a2.p = $3; }
|
||||
| term LEQ term { $$ = f_new_inst(FI_LTE); $$->a1.p = $1; $$->a2.p = $3; }
|
||||
| term '>' term { $$ = f_new_inst(FI_LT); $$->a1.p = $3; $$->a2.p = $1; }
|
||||
| term GEQ term { $$ = f_new_inst(FI_LTE); $$->a1.p = $3; $$->a2.p = $1; }
|
||||
| term '~' term { $$ = f_new_inst(FI_MATCH); $$->a1.p = $1; $$->a2.p = $3; }
|
||||
| term NMA term { $$ = f_new_inst(FI_NOT_MATCH);$$->a1.p = $1; $$->a2.p = $3; }
|
||||
| '!' term { $$ = f_new_inst(FI_NOT); $$->a1.p = $2; }
|
||||
| DEFINED '(' term ')' { $$ = f_new_inst(FI_DEFINED); $$->a1.p = $3; }
|
||||
|
||||
| symbol { $$ = $1; }
|
||||
| constant { $$ = $1; }
|
||||
| constructor { $$ = $1; }
|
||||
|
||||
| PREFERENCE { $$ = f_new_inst(); $$->code = 'P'; }
|
||||
| PREFERENCE { $$ = f_new_inst(FI_PREF_GET); }
|
||||
|
||||
| rtadot static_attr { $$ = $2; $$->code = 'a'; }
|
||||
| rtadot static_attr { $$ = f_new_inst_sa(FI_RTA_GET, $2); }
|
||||
|
||||
| rtadot dynamic_attr { $$ = $2; $$->code = P('e','a'); }
|
||||
| rtadot dynamic_attr { $$ = f_new_inst_da(FI_EA_GET, $2); }
|
||||
|
||||
| term '.' IS_V4 { $$ = f_new_inst(); $$->code = P('I','i'); $$->a1.p = $1; }
|
||||
| term '.' TYPE { $$ = f_new_inst(); $$->code = 'T'; $$->a1.p = $1; }
|
||||
| term '.' IP { $$ = f_new_inst(); $$->code = P('c','p'); $$->a1.p = $1; $$->aux = T_IP; }
|
||||
| term '.' RD { $$ = f_new_inst(); $$->code = P('R','D'); $$->a1.p = $1; $$->aux = T_RD; }
|
||||
| term '.' LEN { $$ = f_new_inst(); $$->code = 'L'; $$->a1.p = $1; }
|
||||
| term '.' MAXLEN { $$ = f_new_inst(); $$->code = P('R','m'); $$->a1.p = $1; }
|
||||
| term '.' ASN { $$ = f_new_inst(); $$->code = P('R','a'); $$->a1.p = $1; }
|
||||
| term '.' MASK '(' term ')' { $$ = f_new_inst(); $$->code = P('i','M'); $$->a1.p = $1; $$->a2.p = $5; }
|
||||
| term '.' FIRST { $$ = f_new_inst(); $$->code = P('a','f'); $$->a1.p = $1; }
|
||||
| term '.' LAST { $$ = f_new_inst(); $$->code = P('a','l'); $$->a1.p = $1; }
|
||||
| term '.' LAST_NONAGGREGATED { $$ = f_new_inst(); $$->code = P('a','L'); $$->a1.p = $1; }
|
||||
| term '.' IS_V4 { $$ = f_new_inst(FI_IS_V4); $$->a1.p = $1; }
|
||||
| term '.' TYPE { $$ = f_new_inst(FI_TYPE); $$->a1.p = $1; }
|
||||
| term '.' IP { $$ = f_new_inst(FI_IP); $$->a1.p = $1; $$->aux = T_IP; }
|
||||
| term '.' RD { $$ = f_new_inst(FI_ROUTE_DISTINGUISHER); $$->a1.p = $1; $$->aux = T_RD; }
|
||||
| term '.' LEN { $$ = f_new_inst(FI_LENGTH); $$->a1.p = $1; }
|
||||
| term '.' MAXLEN { $$ = f_new_inst(FI_ROA_MAXLEN); $$->a1.p = $1; }
|
||||
| term '.' ASN { $$ = f_new_inst(FI_ROA_ASN); $$->a1.p = $1; }
|
||||
| term '.' SRC { $$ = f_new_inst(FI_SADR_SRC); $$->a1.p = $1; }
|
||||
| term '.' MASK '(' term ')' { $$ = f_new_inst(FI_IP_MASK); $$->a1.p = $1; $$->a2.p = $5; }
|
||||
| term '.' FIRST { $$ = f_new_inst(FI_AS_PATH_FIRST); $$->a1.p = $1; }
|
||||
| term '.' LAST { $$ = f_new_inst(FI_AS_PATH_LAST); $$->a1.p = $1; }
|
||||
| term '.' LAST_NONAGGREGATED { $$ = f_new_inst(FI_AS_PATH_LAST_NAG); $$->a1.p = $1; }
|
||||
|
||||
/* Communities */
|
||||
/* This causes one shift/reduce conflict
|
||||
@ -911,19 +903,19 @@ term:
|
||||
| rtadot dynamic_attr '.' RESET{ }
|
||||
*/
|
||||
|
||||
| '+' EMPTY '+' { $$ = f_new_inst(); $$->code = 'E'; $$->aux = T_PATH; }
|
||||
| '-' EMPTY '-' { $$ = f_new_inst(); $$->code = 'E'; $$->aux = T_CLIST; }
|
||||
| '-' '-' EMPTY '-' '-' { $$ = f_new_inst(); $$->code = 'E'; $$->aux = T_ECLIST; }
|
||||
| '-' '-' '-' EMPTY '-' '-' '-' { $$ = f_new_inst(); $$->code = 'E'; $$->aux = T_LCLIST; }
|
||||
| PREPEND '(' term ',' term ')' { $$ = f_new_inst(); $$->code = P('A','p'); $$->a1.p = $3; $$->a2.p = $5; }
|
||||
| ADD '(' term ',' term ')' { $$ = f_new_inst(); $$->code = P('C','a'); $$->a1.p = $3; $$->a2.p = $5; $$->aux = 'a'; }
|
||||
| DELETE '(' term ',' term ')' { $$ = f_new_inst(); $$->code = P('C','a'); $$->a1.p = $3; $$->a2.p = $5; $$->aux = 'd'; }
|
||||
| FILTER '(' term ',' term ')' { $$ = f_new_inst(); $$->code = P('C','a'); $$->a1.p = $3; $$->a2.p = $5; $$->aux = 'f'; }
|
||||
| '+' EMPTY '+' { $$ = f_new_inst(FI_EMPTY); $$->aux = T_PATH; }
|
||||
| '-' EMPTY '-' { $$ = f_new_inst(FI_EMPTY); $$->aux = T_CLIST; }
|
||||
| '-' '-' EMPTY '-' '-' { $$ = f_new_inst(FI_EMPTY); $$->aux = T_ECLIST; }
|
||||
| '-' '-' '-' EMPTY '-' '-' '-' { $$ = f_new_inst(FI_EMPTY); $$->aux = T_LCLIST; }
|
||||
| PREPEND '(' term ',' term ')' { $$ = f_new_inst(FI_PATH_PREPEND); $$->a1.p = $3; $$->a2.p = $5; }
|
||||
| ADD '(' term ',' term ')' { $$ = f_new_inst(FI_CLIST_ADD_DEL); $$->a1.p = $3; $$->a2.p = $5; $$->aux = 'a'; }
|
||||
| DELETE '(' term ',' term ')' { $$ = f_new_inst(FI_CLIST_ADD_DEL); $$->a1.p = $3; $$->a2.p = $5; $$->aux = 'd'; }
|
||||
| FILTER '(' term ',' term ')' { $$ = f_new_inst(FI_CLIST_ADD_DEL); $$->a1.p = $3; $$->a2.p = $5; $$->aux = 'f'; }
|
||||
|
||||
| ROA_CHECK '(' rtable ')' { $$ = f_generate_roa_check($3, NULL, NULL); }
|
||||
| ROA_CHECK '(' rtable ',' term ',' term ')' { $$ = f_generate_roa_check($3, $5, $7); }
|
||||
|
||||
| FORMAT '(' term ')' { $$ = f_new_inst(); $$->code = P('f','m'); $$->a1.p = $3; }
|
||||
| FORMAT '(' term ')' { $$ = f_new_inst(FI_FORMAT); $$->a1.p = $3; }
|
||||
|
||||
/* | term '.' LEN { $$->code = P('P','l'); } */
|
||||
|
||||
@ -934,8 +926,7 @@ term:
|
||||
if ($1->class != SYM_FUNCTION)
|
||||
cf_error("You can't call something which is not a function. Really.");
|
||||
DBG("You are calling function %s\n", $1->name);
|
||||
$$ = f_new_inst();
|
||||
$$->code = P('c','a');
|
||||
$$ = f_new_inst(FI_CALL);
|
||||
$$->a1.p = inst;
|
||||
$$->a2.p = $1->def;
|
||||
sym = $1->aux2;
|
||||
@ -960,7 +951,7 @@ break_command:
|
||||
;
|
||||
|
||||
print_one:
|
||||
term { $$ = f_new_inst(); $$->code = 'p'; $$->a1.p = $1; $$->a2.p = NULL; }
|
||||
term { $$ = f_new_inst(FI_PRINT); $$->a1.p = $1; $$->a2.p = NULL; }
|
||||
;
|
||||
|
||||
print_list: /* EMPTY */ { $$ = NULL; }
|
||||
@ -974,15 +965,13 @@ print_list: /* EMPTY */ { $$ = NULL; }
|
||||
;
|
||||
|
||||
var_listn: term {
|
||||
$$ = f_new_inst();
|
||||
$$->code = 's';
|
||||
$$ = f_new_inst(FI_SET);
|
||||
$$->a1.p = NULL;
|
||||
$$->a2.p = $1;
|
||||
$$->next = NULL;
|
||||
}
|
||||
| term ',' var_listn {
|
||||
$$ = f_new_inst();
|
||||
$$->code = 's';
|
||||
$$ = f_new_inst(FI_SET);
|
||||
$$->a1.p = NULL;
|
||||
$$->a2.p = $1;
|
||||
$$->next = $3;
|
||||
@ -995,73 +984,63 @@ var_list: /* EMPTY */ { $$ = NULL; }
|
||||
|
||||
cmd:
|
||||
IF term THEN block {
|
||||
$$ = f_new_inst();
|
||||
$$->code = '?';
|
||||
$$ = f_new_inst(FI_CONDITION);
|
||||
$$->a1.p = $2;
|
||||
$$->a2.p = $4;
|
||||
}
|
||||
| IF term THEN block ELSE block {
|
||||
struct f_inst *i = f_new_inst();
|
||||
i->code = '?';
|
||||
struct f_inst *i = f_new_inst(FI_CONDITION);
|
||||
i->a1.p = $2;
|
||||
i->a2.p = $4;
|
||||
$$ = f_new_inst();
|
||||
$$->code = '?';
|
||||
$$ = f_new_inst(FI_CONDITION);
|
||||
$$->a1.p = i;
|
||||
$$->a2.p = $6;
|
||||
}
|
||||
| SYM '=' term ';' {
|
||||
$$ = f_new_inst();
|
||||
DBG( "Ook, we'll set value\n" );
|
||||
if (($1->class & ~T_MASK) != SYM_VARIABLE)
|
||||
cf_error( "You may set only variables." );
|
||||
$$->code = 's';
|
||||
$$ = f_new_inst(FI_SET);
|
||||
$$->a1.p = $1;
|
||||
$$->a2.p = $3;
|
||||
}
|
||||
| RETURN term ';' {
|
||||
$$ = f_new_inst();
|
||||
DBG( "Ook, we'll return the value\n" );
|
||||
$$->code = 'r';
|
||||
$$ = f_new_inst(FI_RETURN);
|
||||
$$->a1.p = $2;
|
||||
}
|
||||
| rtadot dynamic_attr '=' term ';' {
|
||||
$$ = $2;
|
||||
$$->code = P('e','S');
|
||||
$$ = f_new_inst_da(FI_EA_SET, $2);
|
||||
$$->a1.p = $4;
|
||||
}
|
||||
| rtadot static_attr '=' term ';' {
|
||||
$$ = $2;
|
||||
$$ = f_new_inst_sa(FI_RTA_SET, $2);
|
||||
if (!$$->a1.i)
|
||||
cf_error( "This static attribute is read-only.");
|
||||
$$->code = P('a','S');
|
||||
$$->a1.p = $4;
|
||||
}
|
||||
| PREFERENCE '=' term ';' {
|
||||
$$ = f_new_inst();
|
||||
$$->code = P('P','S');
|
||||
$$ = f_new_inst(FI_PREF_SET);
|
||||
$$->a1.p = $3;
|
||||
}
|
||||
| UNSET '(' rtadot dynamic_attr ')' ';' {
|
||||
$$ = $4;
|
||||
$$ = f_new_inst_da(FI_EA_SET, $4);
|
||||
$$->aux = EAF_TYPE_UNDEF | EAF_TEMP;
|
||||
$$->code = P('e','S');
|
||||
$$->a1.p = NULL;
|
||||
}
|
||||
| break_command print_list ';' { $$ = f_new_inst(); $$->code = P('p',','); $$->a1.p = $2; $$->a2.i = $1; }
|
||||
| break_command print_list ';' { $$ = f_new_inst(FI_PRINT_AND_DIE); $$->a1.p = $2; $$->a2.i = $1; }
|
||||
| function_call ';' { $$ = $1; }
|
||||
| CASE term '{' switch_body '}' {
|
||||
$$ = f_new_inst();
|
||||
$$->code = P('S','W');
|
||||
$$ = f_new_inst(FI_SWITCH);
|
||||
$$->a1.p = $2;
|
||||
$$->a2.p = build_tree( $4 );
|
||||
}
|
||||
|
||||
| rtadot dynamic_attr '.' EMPTY ';' { $$ = f_generate_empty($2); }
|
||||
| rtadot dynamic_attr '.' PREPEND '(' term ')' ';' { $$ = f_generate_complex( P('A','p'), 'x', $2, $6 ); }
|
||||
| rtadot dynamic_attr '.' ADD '(' term ')' ';' { $$ = f_generate_complex( P('C','a'), 'a', $2, $6 ); }
|
||||
| rtadot dynamic_attr '.' DELETE '(' term ')' ';' { $$ = f_generate_complex( P('C','a'), 'd', $2, $6 ); }
|
||||
| rtadot dynamic_attr '.' FILTER '(' term ')' ';' { $$ = f_generate_complex( P('C','a'), 'f', $2, $6 ); }
|
||||
| rtadot dynamic_attr '.' PREPEND '(' term ')' ';' { $$ = f_generate_complex( FI_PATH_PREPEND, 'x', $2, $6 ); }
|
||||
| rtadot dynamic_attr '.' ADD '(' term ')' ';' { $$ = f_generate_complex( FI_CLIST_ADD_DEL, 'a', $2, $6 ); }
|
||||
| rtadot dynamic_attr '.' DELETE '(' term ')' ';' { $$ = f_generate_complex( FI_CLIST_ADD_DEL, 'd', $2, $6 ); }
|
||||
| rtadot dynamic_attr '.' FILTER '(' term ')' ';' { $$ = f_generate_complex( FI_CLIST_ADD_DEL, 'f', $2, $6 ); }
|
||||
| BT_ASSERT '(' get_cf_position term get_cf_position ')' ';' { $$ = assert_done($4, $3 + 1, $5 - 1); }
|
||||
;
|
||||
|
||||
|
@ -2,6 +2,7 @@
|
||||
* Filters: utility functions
|
||||
*
|
||||
* Copyright 1998 Pavel Machek <pavel@ucw.cz>
|
||||
* 2017 Jan Maria Matejka <mq@ucw.cz>
|
||||
*
|
||||
* Can be freely distributed and used under the terms of the GNU GPL.
|
||||
*/
|
||||
@ -13,43 +14,48 @@
|
||||
#define P(a,b) ((a<<8) | b)
|
||||
|
||||
struct f_inst *
|
||||
f_new_inst(void)
|
||||
f_new_inst(enum f_instruction_code fi_code)
|
||||
{
|
||||
struct f_inst * ret;
|
||||
ret = cfg_alloc(sizeof(struct f_inst));
|
||||
ret->code = ret->aux = 0;
|
||||
ret->arg1 = ret->arg2 = ret->next = NULL;
|
||||
ret = cfg_allocz(sizeof(struct f_inst));
|
||||
ret->fi_code = fi_code;
|
||||
ret->lineno = ifs->lino;
|
||||
return ret;
|
||||
}
|
||||
|
||||
struct f_inst *
|
||||
f_new_dynamic_attr(int type, int f_type UNUSED, int code)
|
||||
f_new_inst_da(enum f_instruction_code fi_code, struct f_dynamic_attr da)
|
||||
{
|
||||
/* FIXME: Remove the f_type parameter? */
|
||||
struct f_inst *f = f_new_inst();
|
||||
f->aux = type;
|
||||
f->a2.i = code;
|
||||
return f;
|
||||
struct f_inst *ret = f_new_inst(fi_code);
|
||||
ret->aux = (da.f_type << 8) | da.type;
|
||||
ret->a2.i = da.ea_code;
|
||||
return ret;
|
||||
}
|
||||
|
||||
struct f_inst *
|
||||
f_new_inst_sa(enum f_instruction_code fi_code, struct f_static_attr sa)
|
||||
{
|
||||
struct f_inst *ret = f_new_inst(fi_code);
|
||||
ret->aux = sa.f_type;
|
||||
ret->a2.i = sa.sa_code;
|
||||
ret->a1.i = sa.readonly;
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
* Generate set_dynamic( operation( get_dynamic(), argument ) )
|
||||
*/
|
||||
struct f_inst *
|
||||
f_generate_complex(int operation, int operation_aux, struct f_inst *dyn, struct f_inst *argument)
|
||||
f_generate_complex(int operation, int operation_aux, struct f_dynamic_attr da, struct f_inst *argument)
|
||||
{
|
||||
struct f_inst *set_dyn = f_new_inst(),
|
||||
*oper = f_new_inst(),
|
||||
*get_dyn = dyn;
|
||||
struct f_inst *set_dyn = f_new_inst_da(FI_EA_SET, da),
|
||||
*oper = f_new_inst(operation),
|
||||
*get_dyn = f_new_inst_da(FI_EA_GET, da);
|
||||
|
||||
*set_dyn = *get_dyn;
|
||||
get_dyn->code = P('e','a');
|
||||
oper->code = operation;
|
||||
oper->aux = operation_aux;
|
||||
oper->a1.p = get_dyn;
|
||||
oper->a2.p = argument;
|
||||
set_dyn->code = P('e','S');
|
||||
|
||||
set_dyn->a1.p = oper;
|
||||
return set_dyn;
|
||||
}
|
||||
@ -58,7 +64,7 @@ struct f_inst *
|
||||
f_generate_roa_check(struct rtable_config *table, struct f_inst *prefix, struct f_inst *asn)
|
||||
{
|
||||
struct f_inst_roa_check *ret = cfg_allocz(sizeof(struct f_inst_roa_check));
|
||||
ret->i.code = P('R','C');
|
||||
ret->i.fi_code = FI_ROA_CHECK;
|
||||
ret->i.lineno = ifs->lino;
|
||||
ret->i.arg1 = prefix;
|
||||
ret->i.arg2 = asn;
|
||||
@ -71,6 +77,22 @@ f_generate_roa_check(struct rtable_config *table, struct f_inst *prefix, struct
|
||||
return &ret->i;
|
||||
}
|
||||
|
||||
static const char * const f_instruction_name_str[] = {
|
||||
#define F(c,a,b) \
|
||||
[c] = #c,
|
||||
FI__LIST
|
||||
#undef F
|
||||
};
|
||||
|
||||
const char *
|
||||
f_instruction_name(enum f_instruction_code fi)
|
||||
{
|
||||
if (fi < FI__MAX)
|
||||
return f_instruction_name_str[fi];
|
||||
else
|
||||
bug("Got unknown instruction code: %d", fi);
|
||||
}
|
||||
|
||||
char *
|
||||
filter_name(struct filter *filter)
|
||||
{
|
||||
|
633
filter/filter.c
633
filter/filter.c
File diff suppressed because it is too large
Load Diff
116
filter/filter.h
116
filter/filter.h
@ -14,9 +14,78 @@
|
||||
#include "nest/route.h"
|
||||
#include "nest/attrs.h"
|
||||
|
||||
/* Filter instruction types */
|
||||
|
||||
#define FI__TWOCHAR(a,b) ((a<<8) | b)
|
||||
#define FI__LIST \
|
||||
F(FI_ADD, 0, '+') \
|
||||
F(FI_SUBTRACT, 0, '-') \
|
||||
F(FI_MULTIPLY, 0, '*') \
|
||||
F(FI_DIVIDE, 0, '/') \
|
||||
F(FI_AND, 0, '&') \
|
||||
F(FI_OR, 0, '|') \
|
||||
F(FI_PAIR_CONSTRUCT, 'm', 'p') \
|
||||
F(FI_EC_CONSTRUCT, 'm', 'c') \
|
||||
F(FI_LC_CONSTRUCT, 'm', 'l') \
|
||||
F(FI_PATHMASK_CONSTRUCT, 'm', 'P') \
|
||||
F(FI_NEQ, '!', '=') \
|
||||
F(FI_EQ, '=', '=') \
|
||||
F(FI_LT, 0, '<') \
|
||||
F(FI_LTE, '<', '=') \
|
||||
F(FI_NOT, 0, '!') \
|
||||
F(FI_MATCH, 0, '~') \
|
||||
F(FI_NOT_MATCH, '!', '~') \
|
||||
F(FI_DEFINED, 'd', 'e') \
|
||||
F(FI_TYPE, 0, 'T') \
|
||||
F(FI_IS_V4, 'I', 'i') \
|
||||
F(FI_SET, 0, 's') \
|
||||
F(FI_CONSTANT, 0, 'c') \
|
||||
F(FI_VARIABLE, 0, 'V') \
|
||||
F(FI_CONSTANT_INDIRECT, 0, 'C') \
|
||||
F(FI_PRINT, 0, 'p') \
|
||||
F(FI_CONDITION, 0, '?') \
|
||||
F(FI_NOP, 0, '0') \
|
||||
F(FI_PRINT_AND_DIE, 'p', ',') \
|
||||
F(FI_RTA_GET, 0, 'a') \
|
||||
F(FI_RTA_SET, 'a', 'S') \
|
||||
F(FI_EA_GET, 'e', 'a') \
|
||||
F(FI_EA_SET, 'e', 'S') \
|
||||
F(FI_PREF_GET, 0, 'P') \
|
||||
F(FI_PREF_SET, 'P', 'S') \
|
||||
F(FI_LENGTH, 0, 'L') \
|
||||
F(FI_ROA_MAXLEN, 'R', 'M') \
|
||||
F(FI_ROA_ASN, 'R', 'A') \
|
||||
F(FI_SADR_SRC, 'n', 's') \
|
||||
F(FI_IP, 'c', 'p') \
|
||||
F(FI_ROUTE_DISTINGUISHER, 'R', 'D') \
|
||||
F(FI_AS_PATH_FIRST, 'a', 'f') \
|
||||
F(FI_AS_PATH_LAST, 'a', 'l') \
|
||||
F(FI_AS_PATH_LAST_NAG, 'a', 'L') \
|
||||
F(FI_RETURN, 0, 'r') \
|
||||
F(FI_CALL, 'c', 'a') \
|
||||
F(FI_CLEAR_LOCAL_VARS, 'c', 'V') \
|
||||
F(FI_SWITCH, 'S', 'W') \
|
||||
F(FI_IP_MASK, 'i', 'M') \
|
||||
F(FI_EMPTY, 0, 'E') \
|
||||
F(FI_PATH_PREPEND, 'A', 'p') \
|
||||
F(FI_CLIST_ADD_DEL, 'C', 'a') \
|
||||
F(FI_ROA_CHECK, 'R', 'C') \
|
||||
F(FI_FORMAT, 0, 'F') \
|
||||
F(FI_ASSERT, 'a', 's')
|
||||
|
||||
enum f_instruction_code {
|
||||
#define F(c,a,b) \
|
||||
c,
|
||||
FI__LIST
|
||||
#undef F
|
||||
FI__MAX,
|
||||
} PACKED;
|
||||
|
||||
const char *f_instruction_name(enum f_instruction_code fi);
|
||||
|
||||
struct f_inst { /* Instruction */
|
||||
struct f_inst *next; /* Structure is 16 bytes, anyway */
|
||||
u16 code; /* Instruction code, see the interpret() function and P() macro */
|
||||
enum f_instruction_code fi_code;
|
||||
u16 aux; /* Extension to instruction code, T_*, EA_*, EAF_* */
|
||||
union {
|
||||
uint i;
|
||||
@ -26,6 +95,10 @@ struct f_inst { /* Instruction */
|
||||
uint i;
|
||||
void *p;
|
||||
} a2; /* The second argument */
|
||||
union {
|
||||
int i;
|
||||
void *p;
|
||||
} a3; /* The third argument */
|
||||
int lineno;
|
||||
};
|
||||
|
||||
@ -38,17 +111,6 @@ struct f_inst_roa_check {
|
||||
struct rtable_config *rtc;
|
||||
};
|
||||
|
||||
struct f_inst3 {
|
||||
struct f_inst i;
|
||||
union {
|
||||
int i;
|
||||
void *p;
|
||||
} a3;
|
||||
};
|
||||
|
||||
#define INST3(x) (((struct f_inst3 *) x)->a3)
|
||||
|
||||
|
||||
struct f_prefix {
|
||||
net_addr net;
|
||||
u8 lo, hi;
|
||||
@ -70,15 +132,32 @@ struct f_val {
|
||||
} val;
|
||||
};
|
||||
|
||||
struct f_dynamic_attr {
|
||||
int type;
|
||||
int f_type;
|
||||
int ea_code;
|
||||
};
|
||||
|
||||
struct f_static_attr {
|
||||
int f_type;
|
||||
int sa_code;
|
||||
int readonly;
|
||||
};
|
||||
|
||||
struct filter {
|
||||
char *name;
|
||||
struct f_inst *root;
|
||||
};
|
||||
|
||||
struct f_inst *f_new_inst(void);
|
||||
struct f_inst *f_new_dynamic_attr(int type, int f_type, int code); /* Type as core knows it, type as filters know it, and code of dynamic attribute */
|
||||
struct f_inst *f_new_inst(enum f_instruction_code fi_code);
|
||||
struct f_inst *f_new_inst_da(enum f_instruction_code fi_code, struct f_dynamic_attr da);
|
||||
struct f_inst *f_new_inst_sa(enum f_instruction_code fi_code, struct f_static_attr sa);
|
||||
static inline struct f_dynamic_attr f_new_dynamic_attr(int type, int f_type, int code) /* Type as core knows it, type as filters know it, and code of dynamic attribute */
|
||||
{ return (struct f_dynamic_attr) { .type = type, .f_type = f_type, .ea_code = code }; } /* f_type currently unused; will be handy for static type checking */
|
||||
static inline struct f_static_attr f_new_static_attr(int f_type, int code, int readonly)
|
||||
{ return (struct f_static_attr) { .f_type = f_type, .sa_code = code, .readonly = readonly }; }
|
||||
struct f_tree *f_new_tree(void);
|
||||
struct f_inst *f_generate_complex(int operation, int operation_aux, struct f_inst *dyn, struct f_inst *argument);
|
||||
struct f_inst *f_generate_complex(int operation, int operation_aux, struct f_dynamic_attr da, struct f_inst *argument);
|
||||
struct f_inst *f_generate_roa_check(struct rtable_config *table, struct f_inst *prefix, struct f_inst *asn);
|
||||
|
||||
|
||||
@ -96,11 +175,10 @@ void trie_format(struct f_trie *t, buffer *buf);
|
||||
struct ea_list;
|
||||
struct rte;
|
||||
|
||||
int f_run(struct filter *filter, struct rte **rte, struct ea_list **tmp_attrs, struct linpool *tmp_pool, int flags);
|
||||
int f_run(struct filter *filter, struct rte **rte, struct linpool *tmp_pool, int flags);
|
||||
struct f_val f_eval_rte(struct f_inst *expr, struct rte **rte, struct linpool *tmp_pool);
|
||||
struct f_val f_eval(struct f_inst *expr, struct linpool *tmp_pool);
|
||||
uint f_eval_int(struct f_inst *expr);
|
||||
u32 f_eval_asn(struct f_inst *expr);
|
||||
|
||||
char *filter_name(struct filter *filter);
|
||||
int filter_same(struct filter *new, struct filter *old);
|
||||
@ -122,6 +200,7 @@ void val_format(struct f_val v, buffer *buf);
|
||||
|
||||
#define FILTER_ACCEPT NULL
|
||||
#define FILTER_REJECT ((void *) 1)
|
||||
#define FILTER_UNDEF ((void *) 2) /* Used in BGP */
|
||||
|
||||
/* Type numbers must be in 0..0xff range */
|
||||
#define T_MASK 0xff
|
||||
@ -147,6 +226,7 @@ void val_format(struct f_val v, buffer *buf);
|
||||
#define T_ENUM_RTD 0x34
|
||||
#define T_ENUM_ROA 0x35
|
||||
#define T_ENUM_NETTYPE 0x36
|
||||
#define T_ENUM_RA_PREFERENCE 0x37
|
||||
|
||||
/* new enums go here */
|
||||
#define T_ENUM_EMPTY 0x3f /* Special hack for atomic_aggr */
|
||||
@ -205,7 +285,7 @@ struct f_trie
|
||||
|
||||
#define NEW_F_VAL struct f_val * val; val = cfg_alloc(sizeof(struct f_val));
|
||||
|
||||
#define FF_FORCE_TMPATTR 1 /* Force all attributes to be temporary */
|
||||
#define FF_SILENT 2 /* Silent filter execution */
|
||||
|
||||
/* Bird Tests */
|
||||
struct f_bt_test_suite {
|
||||
|
@ -589,14 +589,11 @@ function mkpath(int a; int b)
|
||||
|
||||
function t_path()
|
||||
bgpmask pm1;
|
||||
bgpmask pm2;
|
||||
bgppath p2;
|
||||
{
|
||||
pm1 = / 4 3 2 1 /;
|
||||
pm2 = [= 4 3 2 1 =];
|
||||
pm1 = [= 4 3 2 1 =];
|
||||
|
||||
bt_assert(pm1 = pm2);
|
||||
bt_assert(format(pm2) = "[= 4 3 2 1 =]");
|
||||
bt_assert(format(pm1) = "[= 4 3 2 1 =]");
|
||||
|
||||
bt_assert(+empty+ = +empty+);
|
||||
bt_assert(10 !~ +empty+);
|
||||
@ -609,17 +606,14 @@ bgppath p2;
|
||||
bt_assert(format(p2) = "(path 4 3 2 1)");
|
||||
bt_assert(p2.len = 4);
|
||||
bt_assert(p2 ~ pm1);
|
||||
bt_assert(p2 ~ pm2);
|
||||
bt_assert(3 ~ p2);
|
||||
bt_assert(p2 ~ [2, 10..20]);
|
||||
bt_assert(p2 ~ [4, 10..20]);
|
||||
|
||||
p2 = prepend(p2, 5);
|
||||
bt_assert(p2 !~ pm1);
|
||||
bt_assert(p2 !~ pm2);
|
||||
bt_assert(10 !~ p2);
|
||||
bt_assert(p2 !~ [8, ten..(2*ten)]);
|
||||
bt_assert(p2 ~ / ? 4 3 2 1 /);
|
||||
bt_assert(p2 ~ [= * 4 3 * 1 =]);
|
||||
bt_assert(p2 ~ [= (3+2) (2*2) 3 2 1 =]);
|
||||
bt_assert(p2 ~ mkpath(5, 4));
|
||||
|
@ -55,29 +55,28 @@ protocol static {
|
||||
rip_metric = rip_metric + 5;
|
||||
print rip_metric;
|
||||
|
||||
#
|
||||
# TODO: uncomment this part after finishing BGP integration version
|
||||
#
|
||||
# bgp_community = -empty-;
|
||||
# print "hi";
|
||||
# bgp_community = add(bgp_community, (1,2));
|
||||
# print "hello";
|
||||
# bgp_community = add(bgp_community, (2,3));
|
||||
# bgp_community.add((4,5));
|
||||
# print "community = ", bgp_community;
|
||||
# bgp_community.delete((2,3));
|
||||
# print "community = ", bgp_community;
|
||||
# bgp_community.empty;
|
||||
# print "community = ", bgp_community;
|
||||
# print "done";
|
||||
bgp_community = -empty-;
|
||||
print "hi";
|
||||
bgp_community = add(bgp_community, (1,2));
|
||||
print "hello";
|
||||
bgp_community = add(bgp_community, (2,3));
|
||||
bgp_community.add((4,5));
|
||||
print "community = ", bgp_community;
|
||||
bgp_community.delete((2,3));
|
||||
print "community = ", bgp_community;
|
||||
bgp_community.empty;
|
||||
print "community = ", bgp_community;
|
||||
print "done";
|
||||
|
||||
accept;
|
||||
};
|
||||
};
|
||||
route 0.0.0.0/0 via 195.113.31.113;
|
||||
route 62.168.0.0/25 reject;
|
||||
route 1.2.3.4/32 via 195.113.31.124;
|
||||
route 10.0.0.0/8 reject;
|
||||
route 10.1.1.0:255.255.255.0 via 62.168.0.3;
|
||||
route 10.1.2.0:255.255.255.0 via 62.168.0.3;
|
||||
route 10.1.3.0:255.255.255.0 via 62.168.0.4;
|
||||
route 10.1.1.0/24 via 62.168.0.3;
|
||||
route 10.1.2.0/24 via 62.168.0.3;
|
||||
route 10.1.3.0/24 via 62.168.0.4;
|
||||
route 10.2.0.0/24 via "arc0";
|
||||
}
|
||||
|
@ -1,4 +1,4 @@
|
||||
src := bitops.c checksum.c event.c flowspec.c idm.c ip.c lists.c mac.c md5.c mempool.c net.c patmatch.c printf.c resource.c sha1.c sha256.c sha512.c slab.c slists.c tbf.c xmalloc.c
|
||||
src := bitops.c checksum.c event.c flowspec.c idm.c ip.c lists.c mac.c md5.c mempool.c net.c patmatch.c printf.c resource.c sha1.c sha256.c sha512.c slab.c slists.c tbf.c timer.c xmalloc.c
|
||||
obj := $(src-o-files)
|
||||
$(all-daemon)
|
||||
|
||||
|
@ -9,7 +9,6 @@
|
||||
#ifndef _BIRD_BIRDLIB_H_
|
||||
#define _BIRD_BIRDLIB_H_
|
||||
|
||||
#include "sysdep/unix/timer.h"
|
||||
#include "lib/alloca.h"
|
||||
|
||||
/* Ugly structure offset handling macros */
|
||||
@ -70,10 +69,11 @@ static inline int u64_cmp(u64 i1, u64 i2)
|
||||
/* Microsecond time */
|
||||
|
||||
typedef s64 btime;
|
||||
//typedef s64 bird_clock_t;
|
||||
|
||||
#define S_ *1000000
|
||||
#define MS_ *1000
|
||||
#define US_ *1
|
||||
#define S_ * (btime) 1000000
|
||||
#define MS_ * (btime) 1000
|
||||
#define US_ * (btime) 1
|
||||
#define TO_S /1000000
|
||||
#define TO_MS /1000
|
||||
#define TO_US /1
|
||||
@ -82,39 +82,26 @@ typedef s64 btime;
|
||||
#define S S_
|
||||
#define MS MS_
|
||||
#define US US_
|
||||
#define NS /1000
|
||||
#endif
|
||||
|
||||
#define TIME_INFINITY ((s64) 0x7fffffffffffffff)
|
||||
|
||||
|
||||
/* Rate limiting */
|
||||
|
||||
struct tbf {
|
||||
bird_clock_t timestamp; /* Last update */
|
||||
u16 count; /* Available tokens */
|
||||
btime timestamp; /* Last update */
|
||||
u64 count; /* Available micro-tokens */
|
||||
u16 burst; /* Max number of tokens */
|
||||
u16 rate; /* Rate of replenishment */
|
||||
u16 mark; /* Whether last op was limited */
|
||||
u16 rate; /* Rate of replenishment (tokens / sec) */
|
||||
u32 drop; /* Number of failed request since last successful */
|
||||
};
|
||||
|
||||
/* Default TBF values for rate limiting log messages */
|
||||
#define TBF_DEFAULT_LOG_LIMITS { .rate = 1, .burst = 5 }
|
||||
|
||||
void tbf_update(struct tbf *f);
|
||||
|
||||
static inline int
|
||||
tbf_limit(struct tbf *f)
|
||||
{
|
||||
tbf_update(f);
|
||||
|
||||
if (!f->count)
|
||||
{
|
||||
f->mark = 1;
|
||||
return 1;
|
||||
}
|
||||
|
||||
f->count--;
|
||||
f->mark = 0;
|
||||
return 0;
|
||||
}
|
||||
int tbf_limit(struct tbf *f);
|
||||
|
||||
|
||||
/* Logging and dying */
|
||||
@ -165,9 +152,9 @@ void debug(const char *msg, ...); /* Printf to debug output */
|
||||
#endif
|
||||
|
||||
#ifdef DEBUGGING
|
||||
#define ASSERT(x) do { if (!(x)) bug("Assertion `%s' failed at %s:%d", #x, __FILE__, __LINE__); } while(0)
|
||||
#define ASSERT(x) do { if (!(x)) bug("Assertion '%s' failed at %s:%d", #x, __FILE__, __LINE__); } while(0)
|
||||
#else
|
||||
#define ASSERT(x) do { } while(0)
|
||||
#define ASSERT(x) do { if (!(x)) log(L_BUG "Assertion '%s' failed at %s:%d", #x, __FILE__, __LINE__); } while(0)
|
||||
#endif
|
||||
|
||||
/* Pseudorandom numbers */
|
||||
|
@ -13,10 +13,14 @@
|
||||
#include "lib/resource.h"
|
||||
#include "sysdep/config.h"
|
||||
|
||||
#define BUFFER(type) struct { type *data; uint used, size; }
|
||||
#define BUFFER_(type) struct { type *data; uint used, size; }
|
||||
#define BUFFER_TYPE(v) typeof(* (v).data)
|
||||
#define BUFFER_SIZE(v) ((v).size * sizeof(* (v).data))
|
||||
|
||||
#ifndef PARSER
|
||||
#define BUFFER(type) BUFFER_(type)
|
||||
#endif
|
||||
|
||||
#define BUFFER_INIT(v,pool,isize) \
|
||||
({ \
|
||||
(v).used = 0; \
|
||||
|
@ -55,6 +55,7 @@ t_ev_run_list(void)
|
||||
|
||||
resource_init();
|
||||
olock_init();
|
||||
timer_init();
|
||||
io_init();
|
||||
rt_init();
|
||||
if_init();
|
||||
|
@ -91,7 +91,7 @@ const byte *flow6_next_part(const byte *pos, const byte *end);
|
||||
|
||||
/* A data structure for keep a state of flow builder */
|
||||
struct flow_builder {
|
||||
BUFFER(byte) data;
|
||||
BUFFER_(byte) data;
|
||||
enum flow_type this_type;
|
||||
enum flow_type last_type;
|
||||
u16 last_op_offset; /* Position of last operator in data.data */
|
||||
|
@ -70,8 +70,8 @@ t_first_part(void)
|
||||
net_addr_flow4 *f;
|
||||
NET_ADDR_FLOW4_(f, ip4_build(10,0,0,1), 24, ((byte[]) { 0x00, 0x00, 0xab }));
|
||||
|
||||
const byte const *under240 = &f->data[1];
|
||||
const byte const *above240 = &f->data[2];
|
||||
const byte *under240 = &f->data[1];
|
||||
const byte *above240 = &f->data[2];
|
||||
|
||||
/* Case 0x00 0x00 */
|
||||
bt_assert(flow4_first_part(f) == NULL);
|
||||
|
@ -230,4 +230,11 @@ mem_hash(void *p, uint s)
|
||||
return mem_hash_value(&h);
|
||||
}
|
||||
|
||||
static inline uint
|
||||
ptr_hash(void *ptr)
|
||||
{
|
||||
uintptr_t p = (uintptr_t) ptr;
|
||||
return p ^ (p << 8) ^ (p >> 16);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
3
lib/ip.h
3
lib/ip.h
@ -241,6 +241,9 @@ static inline int ip6_is_v4mapped(ip6_addr a)
|
||||
#define ipa_classify(x) ip6_classify(&(x))
|
||||
#define ipa_is_link_local(x) ip6_is_link_local(x)
|
||||
|
||||
static inline int ip4_is_unicast(ip4_addr a)
|
||||
{ return _I(a) < 0xe0000000; }
|
||||
|
||||
/* XXXX remove */
|
||||
static inline int ipa_classify_net(ip_addr a)
|
||||
{ return ipa_zero2(a) ? (IADDR_HOST | SCOPE_UNIVERSE) : ipa_classify(a); }
|
||||
|
98
lib/macro.h
Normal file
98
lib/macro.h
Normal file
@ -0,0 +1,98 @@
|
||||
/*
|
||||
* BIRD Macro Tricks
|
||||
*
|
||||
* (c) 2018 Jan Maria Matejka <mq@jmq.cz>
|
||||
*
|
||||
* Can be freely distributed and used under the terms of the GNU GPL.
|
||||
*
|
||||
* Contains useful but dirty macro tricks:
|
||||
* MACRO_CONCAT(a, b) -> concatenates a##b
|
||||
* MACRO_BOOL(x) -> convert 0 to 0, anything else to 1
|
||||
* MACRO_IFELSE(b)(true-branch)(false-branch)
|
||||
* -> b shall be 0 or 1; expands to the appropriate branch
|
||||
* MACRO_ISEMPTY(...) -> 1 for empty argument list, 0 otherwise
|
||||
* MACRO_FOREACH(func, ...)
|
||||
* -> calling FOREACH(func, a, b, c, d) expands to
|
||||
* func(a) func(b) func(c) func(d)
|
||||
* MACRO_RPACK(func, terminator, ...)
|
||||
* -> packs the list into recursive calls:
|
||||
* func(func(func(func(terminator, a), b), c), d)
|
||||
*/
|
||||
|
||||
#ifndef _BIRD_MACRO_H_
|
||||
#define _BIRD_MACRO_H_
|
||||
|
||||
/* What to do with args */
|
||||
#define MACRO_DROP(...)
|
||||
#define MACRO_UNPAREN(...) __VA_ARGS__
|
||||
#define MACRO_SEP(a, b, sep) a sep b
|
||||
|
||||
/* Aliases for some special chars */
|
||||
#define MACRO_COMMA ,
|
||||
#define MACRO_LPAREN (
|
||||
#define MACRO_RPAREN )
|
||||
#define MACRO_LPAREN_() (
|
||||
#define MACRO_RPAREN_() )
|
||||
|
||||
/* Multiple expansion trick */
|
||||
#define MACRO_EXPAND0(...) __VA_ARGS__
|
||||
#define MACRO_EXPAND1(...) MACRO_EXPAND0(MACRO_EXPAND0(__VA_ARGS__))
|
||||
#define MACRO_EXPAND2(...) MACRO_EXPAND1(MACRO_EXPAND1(__VA_ARGS__))
|
||||
#define MACRO_EXPAND3(...) MACRO_EXPAND2(MACRO_EXPAND2(__VA_ARGS__))
|
||||
#define MACRO_EXPAND(...) MACRO_EXPAND3(MACRO_EXPAND3(__VA_ARGS__))
|
||||
|
||||
/* Deferring expansion in the expansion trick */
|
||||
#define MACRO_EMPTY()
|
||||
#define MACRO_DEFER(t) t MACRO_EMPTY()
|
||||
#define MACRO_DEFER2(t) t MACRO_EMPTY MACRO_EMPTY()()
|
||||
#define MACRO_DEFER3(t) t MACRO_EMPTY MACRO_EMPTY MACRO_EMPTY()()()
|
||||
|
||||
/* Token concatenation */
|
||||
#define MACRO_CONCAT(prefix, ...) prefix##__VA_ARGS__
|
||||
#define MACRO_CONCAT_AFTER(...) MACRO_CONCAT(__VA_ARGS__)
|
||||
|
||||
/* Get first or second argument only */
|
||||
#define MACRO_FIRST(a, ...) a
|
||||
#define MACRO_SECOND(a, b, ...) b
|
||||
#define MACRO_SECOND_OR_ZERO(...) MACRO_SECOND(__VA_ARGS__, 0,)
|
||||
|
||||
/* Macro Boolean auxiliary macros */
|
||||
#define MACRO_BOOL_CHECK_0 ~, 1
|
||||
#define MACRO_BOOL_NEG(x) MACRO_SECOND_OR_ZERO(MACRO_CONCAT(MACRO_BOOL_CHECK_, x))
|
||||
|
||||
#define MACRO_BOOL_NOT_0 1
|
||||
#define MACRO_BOOL_NOT_1 0
|
||||
|
||||
/* Macro Boolean negation */
|
||||
#define MACRO_NOT(x) MACRO_CONCAT(MACRO_BOOL_NOT_, x)
|
||||
|
||||
/* Convert anything to bool (anything -> 1, 0 -> 0) */
|
||||
#define MACRO_BOOL(x) MACRO_NOT(MACRO_BOOL_NEG(x))
|
||||
|
||||
/*
|
||||
* Macro If/Else condition
|
||||
* Usage: MACRO_IFELSE(condition)(true-branch)(false-branch)
|
||||
* Expands to true-branch if condition is true, otherwise to false-branch.
|
||||
*/
|
||||
#define MACRO_IFELSE(b) MACRO_CONCAT(MACRO_IFELSE_, b)
|
||||
#define MACRO_IFELSE_0(...) MACRO_UNPAREN
|
||||
#define MACRO_IFELSE_1(...) __VA_ARGS__ MACRO_DROP
|
||||
|
||||
/* Auxiliary macros for MACRO_FOREACH */
|
||||
#define MACRO_ISLAST(...) MACRO_BOOL_NEG(MACRO_FIRST(MACRO_ISLAST_CHECK __VA_ARGS__)())
|
||||
#define MACRO_ISLAST_CHECK() 0
|
||||
|
||||
#define MACRO_FOREACH_EXPAND(call, a, ...) MACRO_IFELSE(MACRO_ISLAST(__VA_ARGS__))(call(a))(call(a) MACRO_DEFER2(MACRO_FOREACH_PAREN)()(call, __VA_ARGS__))
|
||||
#define MACRO_FOREACH_PAREN() MACRO_FOREACH_EXPAND
|
||||
|
||||
#define MACRO_RPACK_EXPAND(call, terminator, a, ...) MACRO_IFELSE(MACRO_ISLAST(__VA_ARGS__))(call(terminator, a))(call(MACRO_DEFER2(MACRO_RPACK_PAREN)()(call, terminator, __VA_ARGS__), a))
|
||||
#define MACRO_RPACK_PAREN() MACRO_RPACK_EXPAND
|
||||
/*
|
||||
* Call the first argument for each following:
|
||||
* MACRO_FOREACH(func, a, b, c, d) expands to func(a) func(b) func(c) func(d).
|
||||
* It supports also macros as func.
|
||||
*/
|
||||
#define MACRO_FOREACH(call, ...) MACRO_EXPAND(MACRO_FOREACH_EXPAND(call, __VA_ARGS__))
|
||||
#define MACRO_RPACK(call, terminator, ...) MACRO_EXPAND(MACRO_RPACK_EXPAND(call, terminator, __VA_ARGS__))
|
||||
|
||||
#endif
|
@ -11,7 +11,7 @@
|
||||
*
|
||||
* Linear memory pools are collections of memory blocks which
|
||||
* support very fast allocation of new blocks, but are able to free only
|
||||
* the whole collection at once.
|
||||
* the whole collection at once (or in stack order).
|
||||
*
|
||||
* Example: Each configuration is described by a complex system of structures,
|
||||
* linked lists and function trees which are all allocated from a single linear
|
||||
@ -37,7 +37,7 @@ const int lp_chunk_size = sizeof(struct lp_chunk);
|
||||
struct linpool {
|
||||
resource r;
|
||||
byte *ptr, *end;
|
||||
struct lp_chunk *first, *current, **plast; /* Normal (reusable) chunks */
|
||||
struct lp_chunk *first, *current; /* Normal (reusable) chunks */
|
||||
struct lp_chunk *first_large; /* Large chunks */
|
||||
uint chunk_size, threshold, total, total_large;
|
||||
};
|
||||
@ -69,7 +69,6 @@ linpool
|
||||
*lp_new(pool *p, uint blk)
|
||||
{
|
||||
linpool *m = ralloc(p, &lp_class);
|
||||
m->plast = &m->first;
|
||||
m->chunk_size = blk;
|
||||
m->threshold = 3*blk/4;
|
||||
return m;
|
||||
@ -114,22 +113,25 @@ lp_alloc(linpool *m, uint size)
|
||||
}
|
||||
else
|
||||
{
|
||||
if (m->current)
|
||||
if (m->current && m->current->next)
|
||||
{
|
||||
/* Still have free chunks from previous incarnation (before lp_flush()) */
|
||||
c = m->current;
|
||||
m->current = c->next;
|
||||
c = m->current->next;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Need to allocate a new chunk */
|
||||
c = xmalloc(sizeof(struct lp_chunk) + m->chunk_size);
|
||||
m->total += m->chunk_size;
|
||||
*m->plast = c;
|
||||
m->plast = &c->next;
|
||||
c->next = NULL;
|
||||
c->size = m->chunk_size;
|
||||
|
||||
if (m->current)
|
||||
m->current->next = c;
|
||||
else
|
||||
m->first = c;
|
||||
}
|
||||
m->current = c;
|
||||
m->ptr = c->data + size;
|
||||
m->end = c->data + m->chunk_size;
|
||||
}
|
||||
@ -190,9 +192,11 @@ lp_flush(linpool *m)
|
||||
{
|
||||
struct lp_chunk *c;
|
||||
|
||||
/* Relink all normal chunks to free list and free all large chunks */
|
||||
m->ptr = m->end = NULL;
|
||||
m->current = m->first;
|
||||
/* Move ptr to the first chunk and free all large chunks */
|
||||
m->current = c = m->first;
|
||||
m->ptr = c ? c->data : NULL;
|
||||
m->end = c ? c->data + m->chunk_size : NULL;
|
||||
|
||||
while (c = m->first_large)
|
||||
{
|
||||
m->first_large = c->next;
|
||||
@ -201,6 +205,50 @@ lp_flush(linpool *m)
|
||||
m->total_large = 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* lp_save - save the state of a linear memory pool
|
||||
* @m: linear memory pool
|
||||
* @p: state buffer
|
||||
*
|
||||
* This function saves the state of a linear memory pool. Saved state can be
|
||||
* used later to restore the pool (to free memory allocated since).
|
||||
*/
|
||||
void
|
||||
lp_save(linpool *m, lp_state *p)
|
||||
{
|
||||
p->current = m->current;
|
||||
p->large = m->first_large;
|
||||
p->ptr = m->ptr;
|
||||
}
|
||||
|
||||
/**
|
||||
* lp_restore - restore the state of a linear memory pool
|
||||
* @m: linear memory pool
|
||||
* @p: saved state
|
||||
*
|
||||
* This function restores the state of a linear memory pool, freeing all memory
|
||||
* allocated since the state was saved. Note that the function cannot un-free
|
||||
* the memory, therefore the function also invalidates other states that were
|
||||
* saved between (on the same pool).
|
||||
*/
|
||||
void
|
||||
lp_restore(linpool *m, lp_state *p)
|
||||
{
|
||||
struct lp_chunk *c;
|
||||
|
||||
/* Move ptr to the saved pos and free all newer large chunks */
|
||||
m->current = c = p->current;
|
||||
m->ptr = p->ptr;
|
||||
m->end = c ? c->data + m->chunk_size : NULL;
|
||||
|
||||
while ((c = m->first_large) && (c != p->large))
|
||||
{
|
||||
m->first_large = c->next;
|
||||
m->total_large -= c->size;
|
||||
xfree(c);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
lp_free(resource *r)
|
||||
{
|
||||
|
105
lib/net.c
105
lib/net.c
@ -6,50 +6,54 @@
|
||||
|
||||
|
||||
const char * const net_label[] = {
|
||||
[NET_IP4] = "ipv4",
|
||||
[NET_IP6] = "ipv6",
|
||||
[NET_VPN4] = "vpn4",
|
||||
[NET_VPN6] = "vpn6",
|
||||
[NET_ROA4] = "roa4",
|
||||
[NET_ROA6] = "roa6",
|
||||
[NET_FLOW4] = "flow4",
|
||||
[NET_FLOW6] = "flow6",
|
||||
[NET_IP4] = "ipv4",
|
||||
[NET_IP6] = "ipv6",
|
||||
[NET_VPN4] = "vpn4",
|
||||
[NET_VPN6] = "vpn6",
|
||||
[NET_ROA4] = "roa4",
|
||||
[NET_ROA6] = "roa6",
|
||||
[NET_FLOW4] = "flow4",
|
||||
[NET_FLOW6] = "flow6",
|
||||
[NET_IP6_SADR]= "ipv6-sadr",
|
||||
[NET_MPLS] = "mpls",
|
||||
};
|
||||
|
||||
const u16 net_addr_length[] = {
|
||||
[NET_IP4] = sizeof(net_addr_ip4),
|
||||
[NET_IP6] = sizeof(net_addr_ip6),
|
||||
[NET_VPN4] = sizeof(net_addr_vpn4),
|
||||
[NET_VPN6] = sizeof(net_addr_vpn6),
|
||||
[NET_ROA4] = sizeof(net_addr_roa4),
|
||||
[NET_ROA6] = sizeof(net_addr_roa6),
|
||||
[NET_FLOW4] = 0,
|
||||
[NET_FLOW6] = 0,
|
||||
[NET_IP4] = sizeof(net_addr_ip4),
|
||||
[NET_IP6] = sizeof(net_addr_ip6),
|
||||
[NET_VPN4] = sizeof(net_addr_vpn4),
|
||||
[NET_VPN6] = sizeof(net_addr_vpn6),
|
||||
[NET_ROA4] = sizeof(net_addr_roa4),
|
||||
[NET_ROA6] = sizeof(net_addr_roa6),
|
||||
[NET_FLOW4] = 0,
|
||||
[NET_FLOW6] = 0,
|
||||
[NET_IP6_SADR]= sizeof(net_addr_ip6_sadr),
|
||||
[NET_MPLS] = sizeof(net_addr_mpls),
|
||||
};
|
||||
|
||||
const u8 net_max_prefix_length[] = {
|
||||
[NET_IP4] = IP4_MAX_PREFIX_LENGTH,
|
||||
[NET_IP6] = IP6_MAX_PREFIX_LENGTH,
|
||||
[NET_VPN4] = IP4_MAX_PREFIX_LENGTH,
|
||||
[NET_VPN6] = IP6_MAX_PREFIX_LENGTH,
|
||||
[NET_ROA4] = IP4_MAX_PREFIX_LENGTH,
|
||||
[NET_ROA6] = IP6_MAX_PREFIX_LENGTH,
|
||||
[NET_FLOW4] = IP4_MAX_PREFIX_LENGTH,
|
||||
[NET_FLOW6] = IP6_MAX_PREFIX_LENGTH,
|
||||
[NET_IP4] = IP4_MAX_PREFIX_LENGTH,
|
||||
[NET_IP6] = IP6_MAX_PREFIX_LENGTH,
|
||||
[NET_VPN4] = IP4_MAX_PREFIX_LENGTH,
|
||||
[NET_VPN6] = IP6_MAX_PREFIX_LENGTH,
|
||||
[NET_ROA4] = IP4_MAX_PREFIX_LENGTH,
|
||||
[NET_ROA6] = IP6_MAX_PREFIX_LENGTH,
|
||||
[NET_FLOW4] = IP4_MAX_PREFIX_LENGTH,
|
||||
[NET_FLOW6] = IP6_MAX_PREFIX_LENGTH,
|
||||
[NET_IP6_SADR]= IP6_MAX_PREFIX_LENGTH,
|
||||
[NET_MPLS] = 0,
|
||||
};
|
||||
|
||||
const u16 net_max_text_length[] = {
|
||||
[NET_IP4] = 18, /* "255.255.255.255/32" */
|
||||
[NET_IP6] = 43, /* "ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff/128" */
|
||||
[NET_VPN4] = 40, /* "4294967296:4294967296 255.255.255.255/32" */
|
||||
[NET_VPN6] = 65, /* "4294967296:4294967296 ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff/128" */
|
||||
[NET_ROA4] = 34, /* "255.255.255.255/32-32 AS4294967295" */
|
||||
[NET_ROA6] = 60, /* "ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff/128-128 AS4294967295" */
|
||||
[NET_FLOW4] = 0, /* "flow4 { ... }" */
|
||||
[NET_FLOW6] = 0, /* "flow6 { ... }" */
|
||||
[NET_IP4] = 18, /* "255.255.255.255/32" */
|
||||
[NET_IP6] = 43, /* "ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff/128" */
|
||||
[NET_VPN4] = 40, /* "4294967296:4294967296 255.255.255.255/32" */
|
||||
[NET_VPN6] = 65, /* "4294967296:4294967296 ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff/128" */
|
||||
[NET_ROA4] = 34, /* "255.255.255.255/32-32 AS4294967295" */
|
||||
[NET_ROA6] = 60, /* "ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff/128-128 AS4294967295" */
|
||||
[NET_FLOW4] = 0, /* "flow4 { ... }" */
|
||||
[NET_FLOW6] = 0, /* "flow6 { ... }" */
|
||||
[NET_IP6_SADR]= 92, /* "ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff/128 from ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff/128" */
|
||||
[NET_MPLS] = 7, /* "1048575" */
|
||||
};
|
||||
|
||||
@ -102,6 +106,8 @@ net_format(const net_addr *N, char *buf, int buflen)
|
||||
return flow4_net_format(buf, buflen, &n->flow4);
|
||||
case NET_FLOW6:
|
||||
return flow6_net_format(buf, buflen, &n->flow6);
|
||||
case NET_IP6_SADR:
|
||||
return bsnprintf(buf, buflen, "%I6/%d from %I6/%d", n->ip6_sadr.dst_prefix, n->ip6_sadr.dst_pxlen, n->ip6_sadr.src_prefix, n->ip6_sadr.src_pxlen);
|
||||
case NET_MPLS:
|
||||
return bsnprintf(buf, buflen, "%u", n->mpls.label);
|
||||
}
|
||||
@ -124,6 +130,7 @@ net_pxmask(const net_addr *a)
|
||||
case NET_VPN6:
|
||||
case NET_ROA6:
|
||||
case NET_FLOW6:
|
||||
case NET_IP6_SADR:
|
||||
return ipa_from_ip6(ip6_mkmask(net6_pxlen(a)));
|
||||
|
||||
case NET_MPLS:
|
||||
@ -156,6 +163,8 @@ net_compare(const net_addr *a, const net_addr *b)
|
||||
return net_compare_flow4((const net_addr_flow4 *) a, (const net_addr_flow4 *) b);
|
||||
case NET_FLOW6:
|
||||
return net_compare_flow6((const net_addr_flow6 *) a, (const net_addr_flow6 *) b);
|
||||
case NET_IP6_SADR:
|
||||
return net_compare_ip6_sadr((const net_addr_ip6_sadr *) a, (const net_addr_ip6_sadr *) b);
|
||||
case NET_MPLS:
|
||||
return net_compare_mpls((const net_addr_mpls *) a, (const net_addr_mpls *) b);
|
||||
}
|
||||
@ -177,6 +186,7 @@ net_hash(const net_addr *n)
|
||||
case NET_ROA6: return NET_HASH(n, roa6);
|
||||
case NET_FLOW4: return NET_HASH(n, flow4);
|
||||
case NET_FLOW6: return NET_HASH(n, flow6);
|
||||
case NET_IP6_SADR: return NET_HASH(n, ip6_sadr);
|
||||
case NET_MPLS: return NET_HASH(n, mpls);
|
||||
default: bug("invalid type");
|
||||
}
|
||||
@ -198,6 +208,7 @@ net_validate(const net_addr *n)
|
||||
case NET_ROA6: return NET_VALIDATE(n, roa6);
|
||||
case NET_FLOW4: return NET_VALIDATE(n, flow4);
|
||||
case NET_FLOW6: return NET_VALIDATE(n, flow6);
|
||||
case NET_IP6_SADR: return NET_VALIDATE(n, ip6_sadr);
|
||||
case NET_MPLS: return NET_VALIDATE(n, mpls);
|
||||
default: return 0;
|
||||
}
|
||||
@ -222,6 +233,9 @@ net_normalize(net_addr *N)
|
||||
case NET_FLOW6:
|
||||
return net_normalize_ip6(&n->ip6);
|
||||
|
||||
case NET_IP6_SADR:
|
||||
return net_normalize_ip6_sadr(&n->ip6_sadr);
|
||||
|
||||
case NET_MPLS:
|
||||
return;
|
||||
}
|
||||
@ -246,6 +260,9 @@ net_classify(const net_addr *N)
|
||||
case NET_FLOW6:
|
||||
return ip6_zero(n->ip6.prefix) ? (IADDR_HOST | SCOPE_UNIVERSE) : ip6_classify(&n->ip6.prefix);
|
||||
|
||||
case NET_IP6_SADR:
|
||||
return ip6_zero(n->ip6_sadr.dst_prefix) ? (IADDR_HOST | SCOPE_UNIVERSE) : ip6_classify(&n->ip6_sadr.dst_prefix);
|
||||
|
||||
case NET_MPLS:
|
||||
return IADDR_HOST | SCOPE_UNIVERSE;
|
||||
}
|
||||
@ -274,6 +291,11 @@ ipa_in_netX(const ip_addr a, const net_addr *n)
|
||||
return ip6_zero(ip6_and(ip6_xor(ipa_to_ip6(a), net6_prefix(n)),
|
||||
ip6_mkmask(net6_pxlen(n))));
|
||||
|
||||
case NET_IP6_SADR:
|
||||
if (ipa_is_ip4(a)) return 0;
|
||||
return ip6_zero(ip6_and(ip6_xor(ipa_to_ip6(a), net6_prefix(n)),
|
||||
ip6_mkmask(net6_pxlen(n))));
|
||||
|
||||
case NET_MPLS:
|
||||
default:
|
||||
return 0;
|
||||
@ -288,3 +310,22 @@ net_in_netX(const net_addr *a, const net_addr *n)
|
||||
|
||||
return (net_pxlen(n) <= net_pxlen(a)) && ipa_in_netX(net_prefix(a), n);
|
||||
}
|
||||
|
||||
#define CHECK_NET(T,S) \
|
||||
({ if (sizeof(T) != S) die("sizeof %s is %d/%d", #T, (int) sizeof(T), S); })
|
||||
|
||||
void
|
||||
net_init(void)
|
||||
{
|
||||
CHECK_NET(net_addr, 24);
|
||||
CHECK_NET(net_addr_ip4, 8);
|
||||
CHECK_NET(net_addr_ip6, 20);
|
||||
CHECK_NET(net_addr_vpn4, 16);
|
||||
CHECK_NET(net_addr_vpn6, 32);
|
||||
CHECK_NET(net_addr_roa4, 16);
|
||||
CHECK_NET(net_addr_roa6, 28);
|
||||
CHECK_NET(net_addr_flow4, 8);
|
||||
CHECK_NET(net_addr_flow6, 20);
|
||||
CHECK_NET(net_addr_ip6_sadr, 40);
|
||||
CHECK_NET(net_addr_mpls, 8);
|
||||
}
|
||||
|
111
lib/net.h
111
lib/net.h
@ -21,8 +21,9 @@
|
||||
#define NET_ROA6 6
|
||||
#define NET_FLOW4 7
|
||||
#define NET_FLOW6 8
|
||||
#define NET_MPLS 9
|
||||
#define NET_MAX 10
|
||||
#define NET_IP6_SADR 9
|
||||
#define NET_MPLS 10
|
||||
#define NET_MAX 11
|
||||
|
||||
#define NB_IP4 (1 << NET_IP4)
|
||||
#define NB_IP6 (1 << NET_IP6)
|
||||
@ -32,12 +33,13 @@
|
||||
#define NB_ROA6 (1 << NET_ROA6)
|
||||
#define NB_FLOW4 (1 << NET_FLOW4)
|
||||
#define NB_FLOW6 (1 << NET_FLOW6)
|
||||
#define NB_IP6_SADR (1 << NET_IP6_SADR)
|
||||
#define NB_MPLS (1 << NET_MPLS)
|
||||
|
||||
#define NB_IP (NB_IP4 | NB_IP6)
|
||||
#define NB_VPN (NB_VPN4 | NB_VPN6)
|
||||
#define NB_FLOW (NB_FLOW4 | NB_FLOW6)
|
||||
#define NB_DEST (NB_IP | NB_VPN | NB_MPLS)
|
||||
#define NB_DEST (NB_IP | NB_IP6_SADR | NB_VPN | NB_MPLS)
|
||||
#define NB_ANY 0xffffffff
|
||||
|
||||
|
||||
@ -45,7 +47,7 @@ typedef struct net_addr {
|
||||
u8 type;
|
||||
u8 pxlen;
|
||||
u16 length;
|
||||
u8 data[16];
|
||||
u8 data[20];
|
||||
u64 align[0];
|
||||
} net_addr;
|
||||
|
||||
@ -76,6 +78,7 @@ typedef struct net_addr_vpn6 {
|
||||
u8 pxlen;
|
||||
u16 length;
|
||||
ip6_addr prefix;
|
||||
u32 padding;
|
||||
u64 rd;
|
||||
} net_addr_vpn6;
|
||||
|
||||
@ -120,6 +123,15 @@ typedef struct net_addr_mpls {
|
||||
u32 label;
|
||||
} net_addr_mpls;
|
||||
|
||||
typedef struct net_addr_ip6_sadr {
|
||||
u8 type;
|
||||
u8 dst_pxlen;
|
||||
u16 length;
|
||||
ip6_addr dst_prefix;
|
||||
s32 src_pxlen; /* s32 to avoid padding */
|
||||
ip6_addr src_prefix;
|
||||
} net_addr_ip6_sadr;
|
||||
|
||||
typedef union net_addr_union {
|
||||
net_addr n;
|
||||
net_addr_ip4 ip4;
|
||||
@ -130,6 +142,7 @@ typedef union net_addr_union {
|
||||
net_addr_roa6 roa6;
|
||||
net_addr_flow4 flow4;
|
||||
net_addr_flow6 flow6;
|
||||
net_addr_ip6_sadr ip6_sadr;
|
||||
net_addr_mpls mpls;
|
||||
} net_addr_union;
|
||||
|
||||
@ -152,7 +165,7 @@ extern const u16 net_max_text_length[];
|
||||
((net_addr_vpn4) { NET_VPN4, pxlen, sizeof(net_addr_vpn4), prefix, rd })
|
||||
|
||||
#define NET_ADDR_VPN6(prefix,pxlen,rd) \
|
||||
((net_addr_vpn6) { NET_VPN6, pxlen, sizeof(net_addr_vpn6), prefix, rd })
|
||||
((net_addr_vpn6) { NET_VPN6, pxlen, sizeof(net_addr_vpn6), prefix, 0, rd })
|
||||
|
||||
#define NET_ADDR_ROA4(prefix,pxlen,max_pxlen,asn) \
|
||||
((net_addr_roa4) { NET_ROA4, pxlen, sizeof(net_addr_roa4), prefix, max_pxlen, asn })
|
||||
@ -166,6 +179,9 @@ extern const u16 net_max_text_length[];
|
||||
#define NET_ADDR_FLOW6(prefix,pxlen,dlen) \
|
||||
((net_addr_flow6) { NET_FLOW6, pxlen, sizeof(net_addr_ip6) + dlen, prefix })
|
||||
|
||||
#define NET_ADDR_IP6_SADR(dst_prefix,dst_pxlen,src_prefix,src_pxlen) \
|
||||
((net_addr_ip6_sadr) { NET_IP6_SADR, dst_pxlen, sizeof(net_addr_ip6_sadr), dst_prefix, src_pxlen, src_prefix })
|
||||
|
||||
#define NET_ADDR_MPLS(label) \
|
||||
((net_addr_mpls) { NET_MPLS, 20, sizeof(net_addr_mpls), label })
|
||||
|
||||
@ -188,6 +204,9 @@ static inline void net_fill_roa4(net_addr *a, ip4_addr prefix, uint pxlen, uint
|
||||
static inline void net_fill_roa6(net_addr *a, ip6_addr prefix, uint pxlen, uint max_pxlen, u32 asn)
|
||||
{ *(net_addr_roa6 *)a = NET_ADDR_ROA6(prefix, pxlen, max_pxlen, asn); }
|
||||
|
||||
static inline void net_fill_ip6_sadr(net_addr *a, ip6_addr dst_prefix, uint dst_pxlen, ip6_addr src_prefix, uint src_pxlen)
|
||||
{ *(net_addr_ip6_sadr *)a = NET_ADDR_IP6_SADR(dst_prefix, dst_pxlen, src_prefix, src_pxlen); }
|
||||
|
||||
static inline void net_fill_mpls(net_addr *a, u32 label)
|
||||
{ *(net_addr_mpls *)a = NET_ADDR_MPLS(label); }
|
||||
|
||||
@ -221,6 +240,16 @@ static inline void net_fill_flow6(net_addr *a, ip6_addr prefix, uint pxlen, byte
|
||||
memcpy(f->data, data, dlen);
|
||||
}
|
||||
|
||||
/* Make NET_IP6_SADR from NET_IP6, assuming there is enough space */
|
||||
static inline void net_make_ip6_sadr(net_addr *a)
|
||||
{
|
||||
net_addr_ip6_sadr *n = (void *) a;
|
||||
n->type = NET_IP6_SADR;
|
||||
n->length = sizeof(net_addr_ip6_sadr);
|
||||
n->src_pxlen = 0;
|
||||
n->src_prefix = IP6_NONE;
|
||||
}
|
||||
|
||||
static inline int net_val_match(u8 type, u32 mask)
|
||||
{ return !!((1 << type) & mask); }
|
||||
|
||||
@ -230,12 +259,17 @@ static inline int net_type_match(const net_addr *a, u32 mask)
|
||||
static inline int net_is_ip(const net_addr *a)
|
||||
{ return (a->type == NET_IP4) || (a->type == NET_IP6); }
|
||||
|
||||
static inline int net_is_roa(const net_addr *a)
|
||||
{ return (a->type == NET_ROA4) || (a->type == NET_ROA6); }
|
||||
|
||||
static inline int net_is_vpn(const net_addr *a)
|
||||
{ return (a->type == NET_VPN4) || (a->type == NET_VPN6); }
|
||||
|
||||
static inline int net_is_roa(const net_addr *a)
|
||||
{ return (a->type == NET_ROA4) || (a->type == NET_ROA6); }
|
||||
|
||||
static inline int net_is_flow(const net_addr *a)
|
||||
{ return (a->type == NET_FLOW4) || (a->type == NET_FLOW6); }
|
||||
|
||||
static inline int net_is_sadr(const net_addr *a)
|
||||
{ return (a->type == NET_IP6_SADR); }
|
||||
|
||||
static inline ip4_addr net4_prefix(const net_addr *a)
|
||||
{ return ((net_addr_ip4 *) a)->prefix; }
|
||||
@ -257,6 +291,7 @@ static inline ip_addr net_prefix(const net_addr *a)
|
||||
case NET_VPN6:
|
||||
case NET_ROA6:
|
||||
case NET_FLOW6:
|
||||
case NET_IP6_SADR:
|
||||
return ipa_from_ip6(net6_prefix(a));
|
||||
|
||||
case NET_MPLS:
|
||||
@ -324,6 +359,9 @@ static inline int net_equal_flow4(const net_addr_flow4 *a, const net_addr_flow4
|
||||
static inline int net_equal_flow6(const net_addr_flow6 *a, const net_addr_flow6 *b)
|
||||
{ return net_equal((const net_addr *) a, (const net_addr *) b); }
|
||||
|
||||
static inline int net_equal_ip6_sadr(const net_addr_ip6_sadr *a, const net_addr_ip6_sadr *b)
|
||||
{ return !memcmp(a, b, sizeof(net_addr_ip6_sadr)); }
|
||||
|
||||
static inline int net_equal_mpls(const net_addr_mpls *a, const net_addr_mpls *b)
|
||||
{ return !memcmp(a, b, sizeof(net_addr_mpls)); }
|
||||
|
||||
@ -334,6 +372,12 @@ static inline int net_equal_prefix_roa4(const net_addr_roa4 *a, const net_addr_r
|
||||
static inline int net_equal_prefix_roa6(const net_addr_roa6 *a, const net_addr_roa6 *b)
|
||||
{ return ip6_equal(a->prefix, b->prefix) && (a->pxlen == b->pxlen); }
|
||||
|
||||
static inline int net_equal_dst_ip6_sadr(const net_addr_ip6_sadr *a, const net_addr_ip6_sadr *b)
|
||||
{ return ip6_equal(a->dst_prefix, b->dst_prefix) && (a->dst_pxlen == b->dst_pxlen); }
|
||||
|
||||
static inline int net_equal_src_ip6_sadr(const net_addr_ip6_sadr *a, const net_addr_ip6_sadr *b)
|
||||
{ return ip6_equal(a->src_prefix, b->src_prefix) && (a->src_pxlen == b->src_pxlen); }
|
||||
|
||||
|
||||
static inline int net_zero_ip4(const net_addr_ip4 *a)
|
||||
{ return !a->pxlen && ip4_zero(a->prefix); }
|
||||
@ -354,10 +398,10 @@ static inline int net_zero_roa6(const net_addr_roa6 *a)
|
||||
{ return !a->pxlen && ip6_zero(a->prefix) && !a->max_pxlen && !a->asn; }
|
||||
|
||||
static inline int net_zero_flow4(const net_addr_flow4 *a)
|
||||
{ return !a->pxlen && ip4_zero(a->prefix) && !a->data; }
|
||||
{ return !a->pxlen && ip4_zero(a->prefix) && (a->length == sizeof(net_addr_flow4)); }
|
||||
|
||||
static inline int net_zero_flow6(const net_addr_flow6 *a)
|
||||
{ return !a->pxlen && ip6_zero(a->prefix) && !a->data; }
|
||||
{ return !a->pxlen && ip6_zero(a->prefix) && (a->length == sizeof(net_addr_flow6)); }
|
||||
|
||||
static inline int net_zero_mpls(const net_addr_mpls *a)
|
||||
{ return !a->label; }
|
||||
@ -387,6 +431,13 @@ static inline int net_compare_flow4(const net_addr_flow4 *a, const net_addr_flow
|
||||
static inline int net_compare_flow6(const net_addr_flow6 *a, const net_addr_flow6 *b)
|
||||
{ return ip6_compare(a->prefix, b->prefix) ?: uint_cmp(a->pxlen, b->pxlen) ?: uint_cmp(a->length, b->length) ?: memcmp(a->data, b->data, a->length - sizeof(net_addr_flow6)); }
|
||||
|
||||
static inline int net_compare_ip6_sadr(const net_addr_ip6_sadr *a, const net_addr_ip6_sadr *b)
|
||||
{
|
||||
return
|
||||
ip6_compare(a->dst_prefix, b->dst_prefix) ?: uint_cmp(a->dst_pxlen, b->dst_pxlen) ?:
|
||||
ip6_compare(a->src_prefix, b->src_prefix) ?: uint_cmp(a->src_pxlen, b->src_pxlen);
|
||||
}
|
||||
|
||||
static inline int net_compare_mpls(const net_addr_mpls *a, const net_addr_mpls *b)
|
||||
{ return uint_cmp(a->label, b->label); }
|
||||
|
||||
@ -420,6 +471,9 @@ static inline void net_copy_flow4(net_addr_flow4 *dst, const net_addr_flow4 *src
|
||||
static inline void net_copy_flow6(net_addr_flow6 *dst, const net_addr_flow6 *src)
|
||||
{ memcpy(dst, src, src->length); }
|
||||
|
||||
static inline void net_copy_ip6_sadr(net_addr_ip6_sadr *dst, const net_addr_ip6_sadr *src)
|
||||
{ memcpy(dst, src, sizeof(net_addr_ip6_sadr)); }
|
||||
|
||||
static inline void net_copy_mpls(net_addr_mpls *dst, const net_addr_mpls *src)
|
||||
{ memcpy(dst, src, sizeof(net_addr_mpls)); }
|
||||
|
||||
@ -452,6 +506,9 @@ static inline u32 net_hash_flow4(const net_addr_flow4 *n)
|
||||
static inline u32 net_hash_flow6(const net_addr_flow6 *n)
|
||||
{ return ip6_hash(n->prefix) ^ ((u32) n->pxlen << 26); }
|
||||
|
||||
static inline u32 net_hash_ip6_sadr(const net_addr_ip6_sadr *n)
|
||||
{ return net_hash_ip6((net_addr_ip6 *) n); }
|
||||
|
||||
static inline u32 net_hash_mpls(const net_addr_mpls *n)
|
||||
{ return n->label; }
|
||||
|
||||
@ -504,6 +561,9 @@ static inline int net_validate_flow6(const net_addr_flow6 *n)
|
||||
static inline int net_validate_mpls(const net_addr_mpls *n)
|
||||
{ return n->label < (1 << 20); }
|
||||
|
||||
static inline int net_validate_ip6_sadr(const net_addr_ip6_sadr *n)
|
||||
{ return net_validate_px6(n->dst_prefix, n->dst_pxlen) && net_validate_px6(n->src_prefix, n->src_pxlen); }
|
||||
|
||||
int net_validate(const net_addr *N);
|
||||
|
||||
|
||||
@ -519,6 +579,12 @@ static inline void net_normalize_vpn4(net_addr_vpn4 *n)
|
||||
static inline void net_normalize_vpn6(net_addr_vpn6 *n)
|
||||
{ net_normalize_ip6((net_addr_ip6 *) n); }
|
||||
|
||||
static inline void net_normalize_ip6_sadr(net_addr_ip6_sadr *n)
|
||||
{
|
||||
n->dst_prefix = ip6_and(n->dst_prefix, ip6_mkmask(n->dst_pxlen));
|
||||
n->src_prefix = ip6_and(n->src_prefix, ip6_mkmask(n->src_pxlen));
|
||||
}
|
||||
|
||||
void net_normalize(net_addr *N);
|
||||
|
||||
|
||||
@ -526,8 +592,33 @@ int net_classify(const net_addr *N);
|
||||
int net_format(const net_addr *N, char *buf, int buflen);
|
||||
int rd_format(const u64 rd, char *buf, int buflen);
|
||||
|
||||
static inline int ipa_in_px4(ip4_addr a, ip4_addr prefix, uint pxlen)
|
||||
{ return ip4_zero(ip4_and(ip4_xor(a, prefix), ip4_mkmask(pxlen))); }
|
||||
|
||||
static inline int ipa_in_px6(ip6_addr a, ip6_addr prefix, uint pxlen)
|
||||
{ return ip6_zero(ip6_and(ip6_xor(a, prefix), ip6_mkmask(pxlen))); }
|
||||
|
||||
static inline int ipa_in_net_ip4(ip4_addr a, const net_addr_ip4 *n)
|
||||
{ return ipa_in_px4(a, n->prefix, n->pxlen); }
|
||||
|
||||
static inline int ipa_in_net_ip6(ip6_addr a, const net_addr_ip6 *n)
|
||||
{ return ipa_in_px6(a, n->prefix, n->pxlen); }
|
||||
|
||||
static inline int net_in_net_ip4(const net_addr_ip4 *a, const net_addr_ip4 *b)
|
||||
{ return (a->pxlen >= b->pxlen) && ipa_in_px4(a->prefix, b->prefix, b->pxlen); }
|
||||
|
||||
static inline int net_in_net_ip6(const net_addr_ip6 *a, const net_addr_ip6 *b)
|
||||
{ return (a->pxlen >= b->pxlen) && ipa_in_px6(a->prefix, b->prefix, b->pxlen); }
|
||||
|
||||
static inline int net_in_net_dst_ip6_sadr(const net_addr_ip6_sadr *a, const net_addr_ip6_sadr *b)
|
||||
{ return (a->dst_pxlen >= b->dst_pxlen) && ipa_in_px6(a->dst_prefix, b->dst_prefix, b->dst_pxlen); }
|
||||
|
||||
static inline int net_in_net_src_ip6_sadr(const net_addr_ip6_sadr *a, const net_addr_ip6_sadr *b)
|
||||
{ return (a->src_pxlen >= b->src_pxlen) && ipa_in_px6(a->src_prefix, b->src_prefix, b->src_pxlen); }
|
||||
|
||||
int ipa_in_netX(const ip_addr A, const net_addr *N);
|
||||
int net_in_netX(const net_addr *A, const net_addr *N);
|
||||
|
||||
void net_init(void);
|
||||
|
||||
#endif
|
||||
|
66
lib/printf.c
66
lib/printf.c
@ -125,9 +125,10 @@ static char * number(char * str, long num, int base, int size, int precision,
|
||||
* or |%I6| can be used for explicit ip4_addr / ip6_addr arguments, |%N| for
|
||||
* generic network addresses (net_addr *), |%R| for Router / Network ID (u32
|
||||
* value printed as IPv4 address), |%lR| for 64bit Router / Network ID (u64
|
||||
* value printed as eight :-separated octets) and |%m| resp. |%M| for error
|
||||
* messages (uses strerror() to translate @errno code to message text). On the
|
||||
* other hand, it doesn't support floating point numbers.
|
||||
* value printed as eight :-separated octets), |%t| for time values (btime) with
|
||||
* specified subsecond precision, and |%m| resp. |%M| for error messages (uses
|
||||
* strerror() to translate @errno code to message text). On the other hand, it
|
||||
* doesn't support floating point numbers.
|
||||
*
|
||||
* Result: number of characters of the output string or -1 if
|
||||
* the buffer space was insufficient.
|
||||
@ -139,6 +140,8 @@ int bvsnprintf(char *buf, int size, const char *fmt, va_list args)
|
||||
int i, base;
|
||||
u32 x;
|
||||
u64 X;
|
||||
btime t;
|
||||
s64 t1, t2;
|
||||
char *str, *start;
|
||||
const char *s;
|
||||
char ipbuf[NET_MAX_TEXT_LENGTH+1];
|
||||
@ -267,6 +270,17 @@ int bvsnprintf(char *buf, int size, const char *fmt, va_list args)
|
||||
*str++ = ' ';
|
||||
continue;
|
||||
|
||||
case 'V': {
|
||||
const char *vfmt = va_arg(args, const char *);
|
||||
va_list *vargs = va_arg(args, va_list *);
|
||||
int res = bvsnprintf(str, size, vfmt, *vargs);
|
||||
if (res < 0)
|
||||
return -1;
|
||||
str += res;
|
||||
size -= res;
|
||||
continue;
|
||||
}
|
||||
|
||||
case 'p':
|
||||
if (field_width == -1) {
|
||||
field_width = 2*sizeof(void *);
|
||||
@ -279,7 +293,6 @@ int bvsnprintf(char *buf, int size, const char *fmt, va_list args)
|
||||
return -1;
|
||||
continue;
|
||||
|
||||
|
||||
case 'n':
|
||||
if (qualifier == 'l') {
|
||||
long * ip = va_arg(args, long *);
|
||||
@ -360,6 +373,50 @@ int bvsnprintf(char *buf, int size, const char *fmt, va_list args)
|
||||
s = ipbuf;
|
||||
goto str;
|
||||
|
||||
case 't':
|
||||
t = va_arg(args, btime);
|
||||
t1 = t TO_S;
|
||||
t2 = t - t1 S;
|
||||
|
||||
if (precision < 0)
|
||||
precision = 3;
|
||||
|
||||
if (precision > 6)
|
||||
precision = 6;
|
||||
|
||||
/* Compute field_width for second part */
|
||||
if ((precision > 0) && (field_width > 0))
|
||||
field_width -= (1 + precision);
|
||||
|
||||
if (field_width < 0)
|
||||
field_width = 0;
|
||||
|
||||
/* Print seconds */
|
||||
flags |= SIGN;
|
||||
str = number(str, t1, 10, field_width, 0, flags, size);
|
||||
if (!str)
|
||||
return -1;
|
||||
|
||||
if (precision > 0)
|
||||
{
|
||||
size -= (str-start);
|
||||
start = str;
|
||||
|
||||
if ((1 + precision) > size)
|
||||
return -1;
|
||||
|
||||
/* Convert microseconds to requested precision */
|
||||
for (i = precision; i < 6; i++)
|
||||
t2 /= 10;
|
||||
|
||||
/* Print sub-seconds */
|
||||
*str++ = '.';
|
||||
str = number(str, t2, 10, precision, 0, ZEROPAD, size - 1);
|
||||
if (!str)
|
||||
return -1;
|
||||
}
|
||||
goto done;
|
||||
|
||||
/* integer number formats - set up the flags and "break" */
|
||||
case 'o':
|
||||
base = 8;
|
||||
@ -401,6 +458,7 @@ int bvsnprintf(char *buf, int size, const char *fmt, va_list args)
|
||||
str = number(str, num, base, field_width, precision, flags, size);
|
||||
if (!str)
|
||||
return -1;
|
||||
done: ;
|
||||
}
|
||||
if (!size)
|
||||
return -1;
|
||||
|
@ -56,6 +56,15 @@ t_simple(void)
|
||||
BSPRINTF(2, "-1", buf, "%d", -1);
|
||||
BSPRINTF(11, "-2147483648", buf, "%d", -2147483648);
|
||||
|
||||
BSPRINTF(7, "123.456", buf, "%t", (btime) 123456789);
|
||||
BSPRINTF(7, "123.456", buf, "%2t", (btime) 123456789);
|
||||
BSPRINTF(8, " 123.456", buf, "%8t", (btime) 123456789);
|
||||
BSPRINTF(4, " 123", buf, "%4.0t", (btime) 123456789);
|
||||
BSPRINTF(8, "123.4567", buf, "%8.4t", (btime) 123456789);
|
||||
BSPRINTF(9, "0123.4567", buf, "%09.4t", (btime) 123456789);
|
||||
BSPRINTF(12, " 123.456789", buf, "%12.10t", (btime) 123456789);
|
||||
BSPRINTF(8, " 123.004", buf, "%8t", (btime) 123004 MS);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
@ -59,11 +59,18 @@ void mb_free(void *);
|
||||
|
||||
typedef struct linpool linpool;
|
||||
|
||||
typedef struct lp_state {
|
||||
void *current, *large;
|
||||
byte *ptr;
|
||||
} lp_state;
|
||||
|
||||
linpool *lp_new(pool *, unsigned blk);
|
||||
void *lp_alloc(linpool *, unsigned size); /* Aligned */
|
||||
void *lp_allocu(linpool *, unsigned size); /* Unaligned */
|
||||
void *lp_allocz(linpool *, unsigned size); /* With clear */
|
||||
void lp_flush(linpool *); /* Free everything, but leave linpool */
|
||||
void lp_save(linpool *m, lp_state *p); /* Save state */
|
||||
void lp_restore(linpool *m, lp_state *p); /* Restore state */
|
||||
|
||||
extern const int lp_chunk_size;
|
||||
#define LP_GAS 1024
|
||||
|
@ -52,6 +52,7 @@ typedef struct birdsock {
|
||||
int ttl; /* Time To Live, -1 = default */
|
||||
u32 flags;
|
||||
struct iface *iface; /* Interface; specify this for broad/multicast sockets */
|
||||
struct iface *vrf; /* Related VRF instance, NULL if global */
|
||||
|
||||
byte *rbuf, *rpos; /* NULL=allocate automatically */
|
||||
uint fast_rx; /* RX has higher priority in event loop */
|
||||
|
32
lib/tbf.c
32
lib/tbf.c
@ -8,22 +8,30 @@
|
||||
*/
|
||||
|
||||
#include "nest/bird.h"
|
||||
#include "lib/timer.h"
|
||||
|
||||
void
|
||||
tbf_update(struct tbf *f)
|
||||
int
|
||||
tbf_limit(struct tbf *f)
|
||||
{
|
||||
bird_clock_t delta = now - f->timestamp;
|
||||
btime delta = current_time() - f->timestamp;
|
||||
|
||||
if (delta == 0)
|
||||
return;
|
||||
|
||||
f->timestamp = now;
|
||||
|
||||
if ((0 < delta) && (delta < f->burst))
|
||||
if (delta > 0)
|
||||
{
|
||||
u32 next = f->count + delta * f->rate;
|
||||
f->count = MIN(next, f->burst);
|
||||
u64 next = f->count + delta * f->rate;
|
||||
u64 burst = (u64) f->burst << 20;
|
||||
f->count = MIN(next, burst);
|
||||
f->timestamp += delta;
|
||||
}
|
||||
|
||||
if (f->count < 1000000)
|
||||
{
|
||||
f->drop++;
|
||||
return 1;
|
||||
}
|
||||
else
|
||||
f->count = f->burst;
|
||||
{
|
||||
f->count -= 1000000;
|
||||
f->drop = 0;
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
378
lib/timer.c
Normal file
378
lib/timer.c
Normal file
@ -0,0 +1,378 @@
|
||||
/*
|
||||
* BIRD -- Timers
|
||||
*
|
||||
* (c) 2013--2017 Ondrej Zajicek <santiago@crfreenet.org>
|
||||
* (c) 2013--2017 CZ.NIC z.s.p.o.
|
||||
*
|
||||
* Can be freely distributed and used under the terms of the GNU GPL.
|
||||
*/
|
||||
|
||||
/**
|
||||
* DOC: Timers
|
||||
*
|
||||
* Timers are resources which represent a wish of a module to call a function at
|
||||
* the specified time. The timer code does not guarantee exact timing, only that
|
||||
* a timer function will not be called before the requested time.
|
||||
*
|
||||
* In BIRD, time is represented by values of the &btime type which is signed
|
||||
* 64-bit integer interpreted as a relative number of microseconds since some
|
||||
* fixed time point in past. The current time can be obtained by current_time()
|
||||
* function with reasonable accuracy and is monotonic. There is also a current
|
||||
* 'wall-clock' real time obtainable by current_real_time() reported by OS.
|
||||
*
|
||||
* Each timer is described by a &timer structure containing a pointer to the
|
||||
* handler function (@hook), data private to this function (@data), time the
|
||||
* function should be called at (@expires, 0 for inactive timers), for the other
|
||||
* fields see |timer.h|.
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <time.h>
|
||||
|
||||
#include "nest/bird.h"
|
||||
|
||||
#include "lib/heap.h"
|
||||
#include "lib/resource.h"
|
||||
#include "lib/timer.h"
|
||||
|
||||
|
||||
struct timeloop main_timeloop;
|
||||
|
||||
|
||||
#ifdef USE_PTHREADS
|
||||
|
||||
#include <pthread.h>
|
||||
|
||||
/* Data accessed and modified from proto/bfd/io.c */
|
||||
pthread_key_t current_time_key;
|
||||
|
||||
static inline struct timeloop *
|
||||
timeloop_current(void)
|
||||
{
|
||||
return pthread_getspecific(current_time_key);
|
||||
}
|
||||
|
||||
static inline void
|
||||
timeloop_init_current(void)
|
||||
{
|
||||
pthread_key_create(¤t_time_key, NULL);
|
||||
pthread_setspecific(current_time_key, &main_timeloop);
|
||||
}
|
||||
|
||||
void wakeup_kick_current(void);
|
||||
|
||||
#else
|
||||
|
||||
/* Just use main timelooop */
|
||||
static inline struct timeloop * timeloop_current(void) { return &main_timeloop; }
|
||||
static inline void timeloop_init_current(void) { }
|
||||
|
||||
#endif
|
||||
|
||||
btime
|
||||
current_time(void)
|
||||
{
|
||||
return timeloop_current()->last_time;
|
||||
}
|
||||
|
||||
btime
|
||||
current_real_time(void)
|
||||
{
|
||||
struct timeloop *loop = timeloop_current();
|
||||
|
||||
if (!loop->real_time)
|
||||
times_update_real_time(loop);
|
||||
|
||||
return loop->real_time;
|
||||
}
|
||||
|
||||
|
||||
#define TIMER_LESS(a,b) ((a)->expires < (b)->expires)
|
||||
#define TIMER_SWAP(heap,a,b,t) (t = heap[a], heap[a] = heap[b], heap[b] = t, \
|
||||
heap[a]->index = (a), heap[b]->index = (b))
|
||||
|
||||
|
||||
static void
|
||||
tm_free(resource *r)
|
||||
{
|
||||
timer *t = (void *) r;
|
||||
|
||||
tm_stop(t);
|
||||
}
|
||||
|
||||
static void
|
||||
tm_dump(resource *r)
|
||||
{
|
||||
timer *t = (void *) r;
|
||||
|
||||
debug("(code %p, data %p, ", t->hook, t->data);
|
||||
if (t->randomize)
|
||||
debug("rand %d, ", t->randomize);
|
||||
if (t->recurrent)
|
||||
debug("recur %d, ", t->recurrent);
|
||||
if (t->expires)
|
||||
debug("expires in %d ms)\n", (t->expires - current_time()) TO_MS);
|
||||
else
|
||||
debug("inactive)\n");
|
||||
}
|
||||
|
||||
|
||||
static struct resclass tm_class = {
|
||||
"Timer",
|
||||
sizeof(timer),
|
||||
tm_free,
|
||||
tm_dump,
|
||||
NULL,
|
||||
NULL
|
||||
};
|
||||
|
||||
timer *
|
||||
tm_new(pool *p)
|
||||
{
|
||||
timer *t = ralloc(p, &tm_class);
|
||||
t->index = -1;
|
||||
return t;
|
||||
}
|
||||
|
||||
void
|
||||
tm_set(timer *t, btime when)
|
||||
{
|
||||
struct timeloop *loop = timeloop_current();
|
||||
uint tc = timers_count(loop);
|
||||
|
||||
if (!t->expires)
|
||||
{
|
||||
t->index = ++tc;
|
||||
t->expires = when;
|
||||
BUFFER_PUSH(loop->timers) = t;
|
||||
HEAP_INSERT(loop->timers.data, tc, timer *, TIMER_LESS, TIMER_SWAP);
|
||||
}
|
||||
else if (t->expires < when)
|
||||
{
|
||||
t->expires = when;
|
||||
HEAP_INCREASE(loop->timers.data, tc, timer *, TIMER_LESS, TIMER_SWAP, t->index);
|
||||
}
|
||||
else if (t->expires > when)
|
||||
{
|
||||
t->expires = when;
|
||||
HEAP_DECREASE(loop->timers.data, tc, timer *, TIMER_LESS, TIMER_SWAP, t->index);
|
||||
}
|
||||
|
||||
#ifdef CONFIG_BFD
|
||||
/* Hack to notify BFD loops */
|
||||
if ((loop != &main_timeloop) && (t->index == 1))
|
||||
wakeup_kick_current();
|
||||
#endif
|
||||
}
|
||||
|
||||
void
|
||||
tm_start(timer *t, btime after)
|
||||
{
|
||||
tm_set(t, current_time() + MAX(after, 0));
|
||||
}
|
||||
|
||||
void
|
||||
tm_stop(timer *t)
|
||||
{
|
||||
if (!t->expires)
|
||||
return;
|
||||
|
||||
struct timeloop *loop = timeloop_current();
|
||||
uint tc = timers_count(loop);
|
||||
|
||||
HEAP_DELETE(loop->timers.data, tc, timer *, TIMER_LESS, TIMER_SWAP, t->index);
|
||||
BUFFER_POP(loop->timers);
|
||||
|
||||
t->index = -1;
|
||||
t->expires = 0;
|
||||
}
|
||||
|
||||
void
|
||||
timers_init(struct timeloop *loop, pool *p)
|
||||
{
|
||||
times_init(loop);
|
||||
|
||||
BUFFER_INIT(loop->timers, p, 4);
|
||||
BUFFER_PUSH(loop->timers) = NULL;
|
||||
}
|
||||
|
||||
void io_log_event(void *hook, void *data);
|
||||
|
||||
void
|
||||
timers_fire(struct timeloop *loop)
|
||||
{
|
||||
btime base_time;
|
||||
timer *t;
|
||||
|
||||
times_update(loop);
|
||||
base_time = loop->last_time;
|
||||
|
||||
while (t = timers_first(loop))
|
||||
{
|
||||
if (t->expires > base_time)
|
||||
return;
|
||||
|
||||
if (t->recurrent)
|
||||
{
|
||||
btime when = t->expires + t->recurrent;
|
||||
|
||||
if (when <= loop->last_time)
|
||||
when = loop->last_time + t->recurrent;
|
||||
|
||||
if (t->randomize)
|
||||
when += random() % (t->randomize + 1);
|
||||
|
||||
tm_set(t, when);
|
||||
}
|
||||
else
|
||||
tm_stop(t);
|
||||
|
||||
/* This is ugly hack, we want to log just timers executed from the main I/O loop */
|
||||
if (loop == &main_timeloop)
|
||||
io_log_event(t->hook, t->data);
|
||||
|
||||
t->hook(t);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
timer_init(void)
|
||||
{
|
||||
timers_init(&main_timeloop, &root_pool);
|
||||
timeloop_init_current();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* tm_parse_time - parse a date and time
|
||||
* @x: time string
|
||||
*
|
||||
* tm_parse_time() takes a textual representation of a date and time
|
||||
* (yyyy-mm-dd[ hh:mm:ss[.sss]]) and converts it to the corresponding value of
|
||||
* type &btime.
|
||||
*/
|
||||
btime
|
||||
tm_parse_time(char *x)
|
||||
{
|
||||
struct tm tm;
|
||||
int usec, n1, n2, n3, r;
|
||||
|
||||
r = sscanf(x, "%d-%d-%d%n %d:%d:%d%n.%d%n",
|
||||
&tm.tm_year, &tm.tm_mon, &tm.tm_mday, &n1,
|
||||
&tm.tm_hour, &tm.tm_min, &tm.tm_sec, &n2,
|
||||
&usec, &n3);
|
||||
|
||||
if ((r == 3) && !x[n1])
|
||||
tm.tm_hour = tm.tm_min = tm.tm_sec = usec = 0;
|
||||
else if ((r == 6) && !x[n2])
|
||||
usec = 0;
|
||||
else if ((r == 7) && !x[n3])
|
||||
{
|
||||
/* Convert subsecond digits to proper precision */
|
||||
int digits = n3 - n2 - 1;
|
||||
if ((usec < 0) || (usec > 999999) || (digits < 1) || (digits > 6))
|
||||
return 0;
|
||||
|
||||
while (digits++ < 6)
|
||||
usec *= 10;
|
||||
}
|
||||
else
|
||||
return 0;
|
||||
|
||||
tm.tm_mon--;
|
||||
tm.tm_year -= 1900;
|
||||
s64 ts = mktime(&tm);
|
||||
if ((ts == (s64) (time_t) -1) || (ts < 0) || (ts > ((s64) 1 << 40)))
|
||||
return 0;
|
||||
|
||||
return ts S + usec;
|
||||
}
|
||||
|
||||
/**
|
||||
* tm_format_time - convert date and time to textual representation
|
||||
* @x: destination buffer of size %TM_DATETIME_BUFFER_SIZE
|
||||
* @fmt: specification of resulting textual representation of the time
|
||||
* @t: time
|
||||
*
|
||||
* This function formats the given relative time value @t to a textual
|
||||
* date/time representation (dd-mm-yyyy hh:mm:ss) in real time.
|
||||
*/
|
||||
void
|
||||
tm_format_time(char *x, struct timeformat *fmt, btime t)
|
||||
{
|
||||
btime dt = current_time() - t;
|
||||
btime rt = current_real_time() - dt;
|
||||
int v1 = !fmt->limit || (dt < fmt->limit);
|
||||
|
||||
tm_format_real_time(x, v1 ? fmt->fmt1 : fmt->fmt2, rt);
|
||||
}
|
||||
|
||||
/* Replace %f in format string with usec scaled to requested precision */
|
||||
static int
|
||||
strfusec(char *buf, int size, const char *fmt, uint usec)
|
||||
{
|
||||
char *str = buf;
|
||||
int parity = 0;
|
||||
|
||||
while (*fmt)
|
||||
{
|
||||
if (!size)
|
||||
return 0;
|
||||
|
||||
if ((fmt[0] == '%') && (!parity) &&
|
||||
((fmt[1] == 'f') || (fmt[1] >= '1') && (fmt[1] <= '6') && (fmt[2] == 'f')))
|
||||
{
|
||||
int digits = (fmt[1] == 'f') ? 6 : (fmt[1] - '0');
|
||||
uint d = digits, u = usec;
|
||||
|
||||
/* Convert microseconds to requested precision */
|
||||
while (d++ < 6)
|
||||
u /= 10;
|
||||
|
||||
int num = bsnprintf(str, size, "%0*u", digits, u);
|
||||
if (num < 0)
|
||||
return 0;
|
||||
|
||||
fmt += (fmt[1] == 'f') ? 2 : 3;
|
||||
ADVANCE(str, size, num);
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Handle '%%' expression */
|
||||
parity = (*fmt == '%') ? !parity : 0;
|
||||
*str++ = *fmt++;
|
||||
size--;
|
||||
}
|
||||
}
|
||||
|
||||
if (!size)
|
||||
return 0;
|
||||
|
||||
*str = 0;
|
||||
return str - buf;
|
||||
}
|
||||
|
||||
void
|
||||
tm_format_real_time(char *x, const char *fmt, btime t)
|
||||
{
|
||||
s64 t1 = t TO_S;
|
||||
s64 t2 = t - t1 S;
|
||||
|
||||
time_t ts = t1;
|
||||
struct tm tm;
|
||||
if (!localtime_r(&ts, &tm))
|
||||
goto err;
|
||||
|
||||
byte tbuf[TM_DATETIME_BUFFER_SIZE];
|
||||
if (!strfusec(tbuf, TM_DATETIME_BUFFER_SIZE, fmt, t2))
|
||||
goto err;
|
||||
|
||||
if (!strftime(x, TM_DATETIME_BUFFER_SIZE, tbuf, &tm))
|
||||
goto err;
|
||||
|
||||
return;
|
||||
|
||||
err:
|
||||
strcpy(x, "<error>");
|
||||
}
|
127
lib/timer.h
Normal file
127
lib/timer.h
Normal file
@ -0,0 +1,127 @@
|
||||
/*
|
||||
* BIRD -- Timers
|
||||
*
|
||||
* (c) 2013--2017 Ondrej Zajicek <santiago@crfreenet.org>
|
||||
* (c) 2013--2017 CZ.NIC z.s.p.o.
|
||||
*
|
||||
* Can be freely distributed and used under the terms of the GNU GPL.
|
||||
*/
|
||||
|
||||
#ifndef _BIRD_TIMER_H_
|
||||
#define _BIRD_TIMER_H_
|
||||
|
||||
#include "nest/bird.h"
|
||||
#include "lib/buffer.h"
|
||||
#include "lib/resource.h"
|
||||
|
||||
|
||||
typedef struct timer
|
||||
{
|
||||
resource r;
|
||||
void (*hook)(struct timer *);
|
||||
void *data;
|
||||
|
||||
btime expires; /* 0=inactive */
|
||||
uint randomize; /* Amount of randomization */
|
||||
uint recurrent; /* Timer recurrence */
|
||||
|
||||
int index;
|
||||
} timer;
|
||||
|
||||
struct timeloop
|
||||
{
|
||||
BUFFER_(timer *) timers;
|
||||
btime last_time;
|
||||
btime real_time;
|
||||
};
|
||||
|
||||
static inline uint timers_count(struct timeloop *loop)
|
||||
{ return loop->timers.used - 1; }
|
||||
|
||||
static inline timer *timers_first(struct timeloop *loop)
|
||||
{ return (loop->timers.used > 1) ? loop->timers.data[1] : NULL; }
|
||||
|
||||
extern struct timeloop main_timeloop;
|
||||
|
||||
btime current_time(void);
|
||||
btime current_real_time(void);
|
||||
|
||||
//#define now (current_time() TO_S)
|
||||
//#define now_real (current_real_time() TO_S)
|
||||
extern btime boot_time;
|
||||
|
||||
timer *tm_new(pool *p);
|
||||
void tm_set(timer *t, btime when);
|
||||
void tm_start(timer *t, btime after);
|
||||
void tm_stop(timer *t);
|
||||
|
||||
static inline int
|
||||
tm_active(timer *t)
|
||||
{
|
||||
return t->expires != 0;
|
||||
}
|
||||
|
||||
static inline btime
|
||||
tm_remains(timer *t)
|
||||
{
|
||||
btime now_ = current_time();
|
||||
return (t->expires > now_) ? (t->expires - now_) : 0;
|
||||
}
|
||||
|
||||
static inline timer *
|
||||
tm_new_init(pool *p, void (*hook)(struct timer *), void *data, uint rec, uint rand)
|
||||
{
|
||||
timer *t = tm_new(p);
|
||||
t->hook = hook;
|
||||
t->data = data;
|
||||
t->recurrent = rec;
|
||||
t->randomize = rand;
|
||||
return t;
|
||||
}
|
||||
|
||||
static inline void
|
||||
tm_set_max(timer *t, btime when)
|
||||
{
|
||||
if (when > t->expires)
|
||||
tm_set(t, when);
|
||||
}
|
||||
|
||||
static inline void
|
||||
tm_start_max(timer *t, btime after)
|
||||
{
|
||||
btime rem = tm_remains(t);
|
||||
tm_start(t, MAX_(rem, after));
|
||||
}
|
||||
|
||||
/* In sysdep code */
|
||||
void times_init(struct timeloop *loop);
|
||||
void times_update(struct timeloop *loop);
|
||||
void times_update_real_time(struct timeloop *loop);
|
||||
|
||||
/* For I/O loop */
|
||||
void timers_init(struct timeloop *loop, pool *p);
|
||||
void timers_fire(struct timeloop *loop);
|
||||
|
||||
void timer_init(void);
|
||||
|
||||
|
||||
struct timeformat {
|
||||
char *fmt1, *fmt2;
|
||||
btime limit;
|
||||
};
|
||||
|
||||
#define TM_ISO_SHORT_S (struct timeformat){"%T", "%F", (s64) (20*3600) S_}
|
||||
#define TM_ISO_SHORT_MS (struct timeformat){"%T.%3f", "%F", (s64) (20*3600) S_}
|
||||
#define TM_ISO_SHORT_US (struct timeformat){"%T.%6f", "%F", (s64) (20*3600) S_}
|
||||
|
||||
#define TM_ISO_LONG_S (struct timeformat){"%F %T", NULL, 0}
|
||||
#define TM_ISO_LONG_MS (struct timeformat){"%F %T.%3f", NULL, 0}
|
||||
#define TM_ISO_LONG_US (struct timeformat){"%F %T.%6f", NULL, 0}
|
||||
|
||||
#define TM_DATETIME_BUFFER_SIZE 32 /* Buffer size required by tm_format_time() */
|
||||
|
||||
btime tm_parse_time(char *x);
|
||||
void tm_format_time(char *x, struct timeformat *fmt, btime t);
|
||||
void tm_format_real_time(char *x, const char *fmt, btime t);
|
||||
|
||||
#endif
|
@ -1,6 +1,6 @@
|
||||
Summary: BIRD Internet Routing Daemon
|
||||
Name: bird
|
||||
Version: 2.0.0
|
||||
Version: 2.0.2
|
||||
Release: 1
|
||||
Copyright: GPL
|
||||
Group: Networking/Daemons
|
||||
|
11
misc/docker/centos-6-amd64/Dockerfile
Normal file
11
misc/docker/centos-6-amd64/Dockerfile
Normal file
@ -0,0 +1,11 @@
|
||||
FROM centos:6
|
||||
RUN yum -y upgrade
|
||||
RUN yum -y install \
|
||||
autoconf \
|
||||
flex \
|
||||
bison \
|
||||
pkgconfig \
|
||||
'readline-devel' \
|
||||
'pkgconfig(ncurses)' \
|
||||
gcc \
|
||||
make
|
11
misc/docker/centos-7-amd64/Dockerfile
Normal file
11
misc/docker/centos-7-amd64/Dockerfile
Normal file
@ -0,0 +1,11 @@
|
||||
FROM centos:7
|
||||
RUN yum -y upgrade
|
||||
RUN yum -y install \
|
||||
autoconf \
|
||||
flex \
|
||||
bison \
|
||||
pkgconfig \
|
||||
'readline-devel' \
|
||||
'pkgconfig(ncurses)' \
|
||||
gcc \
|
||||
make
|
12
misc/docker/debian-7-amd64/Dockerfile
Normal file
12
misc/docker/debian-7-amd64/Dockerfile
Normal file
@ -0,0 +1,12 @@
|
||||
FROM debian:wheezy-slim
|
||||
ENV DEBIAN_FRONTEND noninteractive
|
||||
RUN sed -i 's/deb.debian.org/ftp.cz.debian.org/' /etc/apt/sources.list
|
||||
RUN apt-get -y update
|
||||
RUN apt-get -y upgrade
|
||||
RUN apt-get -y install \
|
||||
autoconf \
|
||||
build-essential \
|
||||
flex \
|
||||
bison \
|
||||
ncurses-dev \
|
||||
libreadline-dev
|
12
misc/docker/debian-7-i386/Dockerfile
Normal file
12
misc/docker/debian-7-i386/Dockerfile
Normal file
@ -0,0 +1,12 @@
|
||||
FROM i386/debian:wheezy-slim
|
||||
ENV DEBIAN_FRONTEND noninteractive
|
||||
RUN sed -i 's/deb.debian.org/ftp.cz.debian.org/' /etc/apt/sources.list
|
||||
RUN apt-get -y update
|
||||
RUN apt-get -y upgrade
|
||||
RUN apt-get -y install \
|
||||
autoconf \
|
||||
build-essential \
|
||||
flex \
|
||||
bison \
|
||||
ncurses-dev \
|
||||
libreadline-dev
|
12
misc/docker/debian-8-amd64/Dockerfile
Normal file
12
misc/docker/debian-8-amd64/Dockerfile
Normal file
@ -0,0 +1,12 @@
|
||||
FROM debian:jessie-slim
|
||||
ENV DEBIAN_FRONTEND noninteractive
|
||||
RUN sed -i 's/deb.debian.org/ftp.cz.debian.org/' /etc/apt/sources.list
|
||||
RUN apt-get -y update
|
||||
RUN apt-get -y upgrade
|
||||
RUN apt-get -y install \
|
||||
autoconf \
|
||||
build-essential \
|
||||
flex \
|
||||
bison \
|
||||
ncurses-dev \
|
||||
libreadline-dev
|
12
misc/docker/debian-8-i386/Dockerfile
Normal file
12
misc/docker/debian-8-i386/Dockerfile
Normal file
@ -0,0 +1,12 @@
|
||||
FROM i386/debian:jessie-slim
|
||||
ENV DEBIAN_FRONTEND noninteractive
|
||||
RUN sed -i 's/deb.debian.org/ftp.cz.debian.org/' /etc/apt/sources.list
|
||||
RUN apt-get -y update
|
||||
RUN apt-get -y upgrade
|
||||
RUN apt-get -y install \
|
||||
autoconf \
|
||||
build-essential \
|
||||
flex \
|
||||
bison \
|
||||
ncurses-dev \
|
||||
libreadline-dev
|
12
misc/docker/debian-9-amd64/Dockerfile
Normal file
12
misc/docker/debian-9-amd64/Dockerfile
Normal file
@ -0,0 +1,12 @@
|
||||
FROM debian:stretch-slim
|
||||
ENV DEBIAN_FRONTEND noninteractive
|
||||
RUN sed -i 's/deb.debian.org/ftp.cz.debian.org/' /etc/apt/sources.list
|
||||
RUN apt-get -y update
|
||||
RUN apt-get -y upgrade
|
||||
RUN apt-get -y install \
|
||||
autoconf \
|
||||
build-essential \
|
||||
flex \
|
||||
bison \
|
||||
ncurses-dev \
|
||||
libreadline-dev
|
12
misc/docker/debian-9-i386/Dockerfile
Normal file
12
misc/docker/debian-9-i386/Dockerfile
Normal file
@ -0,0 +1,12 @@
|
||||
FROM i386/debian:stretch-slim
|
||||
ENV DEBIAN_FRONTEND noninteractive
|
||||
RUN sed -i 's/deb.debian.org/ftp.cz.debian.org/' /etc/apt/sources.list
|
||||
RUN apt-get -y update
|
||||
RUN apt-get -y upgrade
|
||||
RUN apt-get -y install \
|
||||
autoconf \
|
||||
build-essential \
|
||||
flex \
|
||||
bison \
|
||||
ncurses-dev \
|
||||
libreadline-dev
|
12
misc/docker/debian-testing-amd64/Dockerfile
Normal file
12
misc/docker/debian-testing-amd64/Dockerfile
Normal file
@ -0,0 +1,12 @@
|
||||
FROM debian:testing-slim
|
||||
ENV DEBIAN_FRONTEND noninteractive
|
||||
RUN sed -i 's/deb.debian.org/ftp.cz.debian.org/' /etc/apt/sources.list
|
||||
RUN apt-get -y update
|
||||
RUN apt-get -y upgrade
|
||||
RUN apt-get -y install \
|
||||
autoconf \
|
||||
build-essential \
|
||||
flex \
|
||||
bison \
|
||||
ncurses-dev \
|
||||
libreadline-dev
|
12
misc/docker/debian-testing-i386/Dockerfile
Normal file
12
misc/docker/debian-testing-i386/Dockerfile
Normal file
@ -0,0 +1,12 @@
|
||||
FROM i386/debian:testing-slim
|
||||
ENV DEBIAN_FRONTEND noninteractive
|
||||
RUN sed -i 's/deb.debian.org/ftp.cz.debian.org/' /etc/apt/sources.list
|
||||
RUN apt-get -y update
|
||||
RUN apt-get -y upgrade
|
||||
RUN apt-get -y install \
|
||||
autoconf \
|
||||
build-essential \
|
||||
flex \
|
||||
bison \
|
||||
ncurses-dev \
|
||||
libreadline-dev
|
10
misc/docker/fedora-25-amd64/Dockerfile
Normal file
10
misc/docker/fedora-25-amd64/Dockerfile
Normal file
@ -0,0 +1,10 @@
|
||||
FROM fedora:25
|
||||
RUN dnf -y upgrade
|
||||
RUN dnf -y install \
|
||||
autoconf \
|
||||
flex \
|
||||
bison \
|
||||
pkgconfig \
|
||||
'readline-devel' \
|
||||
'pkgconfig(ncurses)' \
|
||||
gcc
|
10
misc/docker/fedora-26-amd64/Dockerfile
Normal file
10
misc/docker/fedora-26-amd64/Dockerfile
Normal file
@ -0,0 +1,10 @@
|
||||
FROM fedora:26
|
||||
RUN dnf -y upgrade
|
||||
RUN dnf -y install \
|
||||
autoconf \
|
||||
flex \
|
||||
bison \
|
||||
pkgconfig \
|
||||
'readline-devel' \
|
||||
'pkgconfig(ncurses)' \
|
||||
gcc
|
11
misc/docker/opensuse-42.3-amd64/Dockerfile
Normal file
11
misc/docker/opensuse-42.3-amd64/Dockerfile
Normal file
@ -0,0 +1,11 @@
|
||||
FROM opensuse:42.3
|
||||
RUN zypper -n up
|
||||
RUN zypper -n install \
|
||||
autoconf \
|
||||
flex \
|
||||
bison \
|
||||
pkgconfig \
|
||||
readline-devel \
|
||||
ncurses-devel \
|
||||
gcc \
|
||||
gmake
|
12
misc/docker/ubuntu-14.04-amd64/Dockerfile
Normal file
12
misc/docker/ubuntu-14.04-amd64/Dockerfile
Normal file
@ -0,0 +1,12 @@
|
||||
FROM ubuntu:14.04
|
||||
ENV DEBIAN_FRONTEND noninteractive
|
||||
RUN sed -i 's/deb.debian.org/ftp.cz.debian.org/' /etc/apt/sources.list
|
||||
RUN apt-get -y update
|
||||
RUN apt-get -y upgrade
|
||||
RUN apt-get -y install \
|
||||
autoconf \
|
||||
build-essential \
|
||||
flex \
|
||||
bison \
|
||||
ncurses-dev \
|
||||
libreadline-dev
|
12
misc/docker/ubuntu-16.04-amd64/Dockerfile
Normal file
12
misc/docker/ubuntu-16.04-amd64/Dockerfile
Normal file
@ -0,0 +1,12 @@
|
||||
FROM ubuntu:16.04
|
||||
ENV DEBIAN_FRONTEND noninteractive
|
||||
RUN sed -i 's/deb.debian.org/ftp.cz.debian.org/' /etc/apt/sources.list
|
||||
RUN apt-get -y update
|
||||
RUN apt-get -y upgrade
|
||||
RUN apt-get -y install \
|
||||
autoconf \
|
||||
build-essential \
|
||||
flex \
|
||||
bison \
|
||||
ncurses-dev \
|
||||
libreadline-dev
|
@ -805,12 +805,11 @@ as_path_match(const struct adata *path, struct f_path_mask *mask)
|
||||
val2 = val = mask->val;
|
||||
goto step;
|
||||
case PM_ASN_EXPR:
|
||||
val2 = val = f_eval_asn((struct f_inst *) mask->val);
|
||||
goto step;
|
||||
ASSERT(0);
|
||||
case PM_ASN_RANGE:
|
||||
val = mask->val;
|
||||
val2 = mask->val2;
|
||||
goto step;
|
||||
goto step;
|
||||
case PM_QUESTION:
|
||||
step:
|
||||
nh = nl = -1;
|
||||
|
@ -43,14 +43,14 @@ t_as_path_match(void)
|
||||
bt_debug("Prepending ASN: %10u \n", val);
|
||||
|
||||
if (i == 0)
|
||||
first_prepended = val;
|
||||
first_prepended = val;
|
||||
if (i == AS_PATH_LENGTH-1)
|
||||
last_prepended = val;
|
||||
last_prepended = val;
|
||||
|
||||
mask[i].kind = PM_ASN;
|
||||
mask[i].val = val;
|
||||
if (i)
|
||||
mask[i].next = &mask[i-1];
|
||||
mask[i].next = &mask[i-1];
|
||||
}
|
||||
|
||||
bt_assert_msg(as_path_match(as_path, &mask[AS_PATH_LENGTH-1]), "Mask should match with AS path");
|
||||
|
@ -536,6 +536,13 @@ ec_set_sort(struct linpool *pool, struct adata *src)
|
||||
return dst;
|
||||
}
|
||||
|
||||
void
|
||||
ec_set_sort_x(struct adata *set)
|
||||
{
|
||||
/* Sort in place */
|
||||
qsort(set->data, set->length / 8, 8, ec_set_cmp);
|
||||
}
|
||||
|
||||
|
||||
static int
|
||||
lc_set_cmp(const void *X, const void *Y)
|
||||
|
@ -197,4 +197,6 @@ struct adata *int_set_sort(struct linpool *pool, struct adata *src);
|
||||
struct adata *ec_set_sort(struct linpool *pool, struct adata *src);
|
||||
struct adata *lc_set_sort(struct linpool *pool, struct adata *src);
|
||||
|
||||
void ec_set_sort_x(struct adata *set); /* Sort in place */
|
||||
|
||||
#endif
|
||||
|
@ -46,7 +46,7 @@ static inline void cf_check_bfd(int use UNUSED) { }
|
||||
|
||||
#else
|
||||
|
||||
static inline struct bfd_request * bfd_request_session(pool *p, ip_addr addr, ip_addr local, struct iface *iface, void (*hook)(struct bfd_request *), void *data) { return NULL; }
|
||||
static inline struct bfd_request * bfd_request_session(pool *p UNUSED, ip_addr addr UNUSED, ip_addr local UNUSED, struct iface *iface UNUSED, void (*hook)(struct bfd_request *) UNUSED, void *data UNUSED) { return NULL; }
|
||||
|
||||
static inline void cf_check_bfd(int use) { if (use) cf_error("BFD not available"); }
|
||||
|
||||
|
@ -25,12 +25,12 @@ cmd_show_status(void)
|
||||
byte tim[TM_DATETIME_BUFFER_SIZE];
|
||||
|
||||
cli_msg(-1000, "BIRD " BIRD_VERSION);
|
||||
tm_format_datetime(tim, &config->tf_base, now);
|
||||
tm_format_time(tim, &config->tf_base, current_time());
|
||||
cli_msg(-1011, "Router ID is %R", config->router_id);
|
||||
cli_msg(-1011, "Current server time is %s", tim);
|
||||
tm_format_datetime(tim, &config->tf_base, boot_time);
|
||||
tm_format_time(tim, &config->tf_base, boot_time);
|
||||
cli_msg(-1011, "Last reboot on %s", tim);
|
||||
tm_format_datetime(tim, &config->tf_base, config->load_time);
|
||||
tm_format_time(tim, &config->tf_base, config->load_time);
|
||||
cli_msg(-1011, "Last reconfiguration on %s", tim);
|
||||
|
||||
graceful_restart_show_status();
|
||||
|
113
nest/config.Y
113
nest/config.Y
@ -65,18 +65,19 @@ proto_postconfig(void)
|
||||
CF_DECLS
|
||||
|
||||
CF_KEYWORDS(ROUTER, ID, PROTOCOL, TEMPLATE, PREFERENCE, DISABLED, DEBUG, ALL, OFF, DIRECT)
|
||||
CF_KEYWORDS(INTERFACE, IMPORT, EXPORT, FILTER, NONE, TABLE, STATES, ROUTES, FILTERS)
|
||||
CF_KEYWORDS(IPV4, IPV6, VPN4, VPN6, ROA4, ROA6)
|
||||
CF_KEYWORDS(INTERFACE, IMPORT, EXPORT, FILTER, NONE, VRF, TABLE, STATES, ROUTES, FILTERS)
|
||||
CF_KEYWORDS(IPV4, IPV6, VPN4, VPN6, ROA4, ROA6, FLOW4, FLOW6, SADR, MPLS)
|
||||
CF_KEYWORDS(RECEIVE, LIMIT, ACTION, WARN, BLOCK, RESTART, DISABLE, KEEP, FILTERED)
|
||||
CF_KEYWORDS(PASSWORD, FROM, PASSIVE, TO, ID, EVENTS, PACKETS, PROTOCOLS, INTERFACES)
|
||||
CF_KEYWORDS(ALGORITHM, KEYED, HMAC, MD5, SHA1, SHA256, SHA384, SHA512)
|
||||
CF_KEYWORDS(PRIMARY, STATS, COUNT, BY, FOR, COMMANDS, PREEXPORT, NOEXPORT, GENERATE)
|
||||
CF_KEYWORDS(LISTEN, BGP, V6ONLY, DUAL, ADDRESS, PORT, PASSWORDS, DESCRIPTION, SORTED)
|
||||
CF_KEYWORDS(BGP, PASSWORDS, DESCRIPTION, SORTED)
|
||||
CF_KEYWORDS(RELOAD, IN, OUT, MRTDUMP, MESSAGES, RESTRICT, MEMORY, IGP_METRIC, CLASS, DSCP)
|
||||
CF_KEYWORDS(TIMEFORMAT, ISO, SHORT, LONG, ROUTE, PROTOCOL, BASE, LOG, S, MS, US)
|
||||
CF_KEYWORDS(GRACEFUL, RESTART, WAIT, MAX, FLUSH, AS)
|
||||
|
||||
/* For r_args_channel */
|
||||
CF_KEYWORDS(IPV4, IPV4_MC, IPV4_MPLS, IPV6, IPV6_MC, IPV6_MPLS, VPN4, VPN4_MC, VPN4_MPLS, VPN6, VPN6_MC, VPN6_MPLS, ROA4, ROA6, FLOW4, FLOW6, MPLS, PRI, SEC)
|
||||
CF_KEYWORDS(IPV4, IPV4_MC, IPV4_MPLS, IPV6, IPV6_MC, IPV6_MPLS, IPV6_SADR, VPN4, VPN4_MC, VPN4_MPLS, VPN6, VPN6_MC, VPN6_MPLS, ROA4, ROA6, FLOW4, FLOW6, MPLS, PRI, SEC)
|
||||
|
||||
CF_ENUM(T_ENUM_RTS, RTS_, DUMMY, STATIC, INHERIT, DEVICE, STATIC_DEVICE, REDIRECT,
|
||||
RIP, OSPF, OSPF_IA, OSPF_EXT1, OSPF_EXT2, BGP, PIPE, BABEL)
|
||||
@ -102,7 +103,7 @@ CF_GRAMMAR
|
||||
|
||||
/* Setting of router ID */
|
||||
|
||||
CF_ADDTO(conf, rtrid)
|
||||
conf: rtrid ;
|
||||
|
||||
rtrid:
|
||||
ROUTER ID idval ';' { new_config->router_id = $3; }
|
||||
@ -123,25 +124,7 @@ idval:
|
||||
}
|
||||
;
|
||||
|
||||
|
||||
CF_ADDTO(conf, listen)
|
||||
|
||||
listen: LISTEN BGP listen_opts ';' ;
|
||||
|
||||
listen_opts:
|
||||
/* Nothing */
|
||||
| listen_opts listen_opt
|
||||
;
|
||||
|
||||
listen_opt:
|
||||
ADDRESS ipa { new_config->listen_bgp_addr = $2; }
|
||||
| PORT expr { new_config->listen_bgp_port = $2; }
|
||||
| V6ONLY { new_config->listen_bgp_flags = 0; }
|
||||
| DUAL { new_config->listen_bgp_flags = 1; }
|
||||
;
|
||||
|
||||
|
||||
CF_ADDTO(conf, gr_opts)
|
||||
conf: gr_opts ;
|
||||
|
||||
gr_opts: GRACEFUL RESTART WAIT expr ';' { new_config->gr_wait = $4; } ;
|
||||
|
||||
@ -151,23 +134,25 @@ gr_opts: GRACEFUL RESTART WAIT expr ';' { new_config->gr_wait = $4; } ;
|
||||
net_type:
|
||||
IPV4 { $$ = NET_IP4; }
|
||||
| IPV6 { $$ = NET_IP6; }
|
||||
| IPV6 SADR { $$ = NET_IP6_SADR; }
|
||||
| VPN4 { $$ = NET_VPN4; }
|
||||
| VPN6 { $$ = NET_VPN6; }
|
||||
| ROA4 { $$ = NET_ROA4; }
|
||||
| ROA6 { $$ = NET_ROA6; }
|
||||
| FLOW4{ $$ = NET_FLOW4; }
|
||||
| FLOW6{ $$ = NET_FLOW6; }
|
||||
| MPLS { $$ = NET_MPLS; }
|
||||
;
|
||||
|
||||
CF_ENUM(T_ENUM_NETTYPE, NET_, IP4, IP6, VPN4, VPN6, ROA4, ROA6, FLOW4, FLOW6)
|
||||
CF_ENUM(T_ENUM_NETTYPE, NET_, IP4, IP6, VPN4, VPN6, ROA4, ROA6, FLOW4, FLOW6, IP6_SADR)
|
||||
|
||||
|
||||
/* Creation of routing tables */
|
||||
|
||||
CF_ADDTO(conf, table)
|
||||
conf: table ;
|
||||
|
||||
table_sorted:
|
||||
{ $$ = 0; }
|
||||
{ $$ = 0; }
|
||||
| SORTED { $$ = 1; }
|
||||
;
|
||||
|
||||
@ -181,7 +166,7 @@ table: net_type TABLE SYM table_sorted {
|
||||
|
||||
/* Definition of protocols */
|
||||
|
||||
CF_ADDTO(conf, proto { proto_postconfig(); })
|
||||
conf: proto { proto_postconfig(); } ;
|
||||
|
||||
proto_start:
|
||||
PROTOCOL { $$ = SYM_PROTO; }
|
||||
@ -224,12 +209,13 @@ proto_item:
|
||||
| MRTDUMP mrtdump_mask { this_proto->mrtdump = $2; }
|
||||
| ROUTER ID idval { this_proto->router_id = $3; }
|
||||
| DESCRIPTION text { this_proto->dsc = $2; }
|
||||
| VRF text { this_proto->vrf = if_get_by_name($2); }
|
||||
;
|
||||
|
||||
|
||||
channel_start: net_type
|
||||
{
|
||||
$$ = this_channel = channel_config_new(NULL, $1, this_proto);
|
||||
$$ = this_channel = channel_config_get(NULL, net_label[$1], $1, this_proto);
|
||||
};
|
||||
|
||||
channel_item:
|
||||
@ -295,7 +281,8 @@ limit_spec:
|
||||
| OFF { $$ = (struct channel_limit){}; }
|
||||
;
|
||||
|
||||
CF_ADDTO(conf, debug_default)
|
||||
|
||||
conf: debug_default ;
|
||||
|
||||
debug_default:
|
||||
DEBUG PROTOCOLS debug_mask { new_config->proto_default_debug = $3; }
|
||||
@ -304,6 +291,31 @@ debug_default:
|
||||
|
||||
/* MRTDUMP PROTOCOLS is in systep/unix/config.Y */
|
||||
|
||||
conf: timeformat_base ;
|
||||
|
||||
timeformat_which:
|
||||
ROUTE { $$ = &new_config->tf_route; }
|
||||
| PROTOCOL { $$ = &new_config->tf_proto; }
|
||||
| BASE { $$ = &new_config->tf_base; }
|
||||
| LOG { $$ = &new_config->tf_log; }
|
||||
;
|
||||
|
||||
timeformat_spec:
|
||||
timeformat_which TEXT { *$1 = (struct timeformat){$2, NULL, 0}; }
|
||||
| timeformat_which TEXT expr TEXT { *$1 = (struct timeformat){$2, $4, (s64) $3 S_}; }
|
||||
| timeformat_which ISO SHORT { *$1 = TM_ISO_SHORT_S; }
|
||||
| timeformat_which ISO SHORT MS { *$1 = TM_ISO_SHORT_MS; }
|
||||
| timeformat_which ISO SHORT US { *$1 = TM_ISO_SHORT_US; }
|
||||
| timeformat_which ISO LONG { *$1 = TM_ISO_LONG_S; }
|
||||
| timeformat_which ISO LONG MS { *$1 = TM_ISO_LONG_MS; }
|
||||
| timeformat_which ISO LONG US { *$1 = TM_ISO_LONG_US; }
|
||||
;
|
||||
|
||||
timeformat_base:
|
||||
TIMEFORMAT timeformat_spec ';'
|
||||
;
|
||||
|
||||
|
||||
/* Interface patterns */
|
||||
|
||||
iface_patt_node_init:
|
||||
@ -355,7 +367,7 @@ tos:
|
||||
|
||||
/* Direct device route protocol */
|
||||
|
||||
CF_ADDTO(proto, dev_proto '}')
|
||||
proto: dev_proto '}' ;
|
||||
|
||||
dev_proto_start: proto_start DIRECT {
|
||||
this_proto = proto_config_new(&proto_device, $1);
|
||||
@ -443,9 +455,9 @@ password_item:
|
||||
password_item_begin:
|
||||
PASSWORD text {
|
||||
if (!this_p_list) {
|
||||
this_p_list = cfg_alloc(sizeof(list));
|
||||
init_list(this_p_list);
|
||||
password_id = 1;
|
||||
this_p_list = cfg_alloc(sizeof(list));
|
||||
init_list(this_p_list);
|
||||
password_id = 1;
|
||||
}
|
||||
this_p_item = cfg_alloc(sizeof (struct password_item));
|
||||
this_p_item->password = $2;
|
||||
@ -462,12 +474,12 @@ password_item_begin:
|
||||
|
||||
password_item_params:
|
||||
/* empty */ { }
|
||||
| GENERATE FROM datetime ';' password_item_params { this_p_item->genfrom = $3; }
|
||||
| GENERATE TO datetime ';' password_item_params { this_p_item->gento = $3; }
|
||||
| ACCEPT FROM datetime ';' password_item_params { this_p_item->accfrom = $3; }
|
||||
| ACCEPT TO datetime ';' password_item_params { this_p_item->accto = $3; }
|
||||
| FROM datetime ';' password_item_params { this_p_item->genfrom = this_p_item->accfrom = $2; }
|
||||
| TO datetime ';' password_item_params { this_p_item->gento = this_p_item->accto = $2; }
|
||||
| GENERATE FROM time ';' password_item_params { this_p_item->genfrom = $3; }
|
||||
| GENERATE TO time ';' password_item_params { this_p_item->gento = $3; }
|
||||
| ACCEPT FROM time ';' password_item_params { this_p_item->accfrom = $3; }
|
||||
| ACCEPT TO time ';' password_item_params { this_p_item->accto = $3; }
|
||||
| FROM time ';' password_item_params { this_p_item->genfrom = this_p_item->accfrom = $2; }
|
||||
| TO time ';' password_item_params { this_p_item->gento = this_p_item->accto = $2; }
|
||||
| ID expr ';' password_item_params { this_p_item->id = $2; if ($2 <= 0) cf_error("Password ID has to be greated than zero."); }
|
||||
| ALGORITHM password_algorithm ';' password_item_params { this_p_item->alg = $2; }
|
||||
;
|
||||
@ -614,6 +626,7 @@ r_args_for:
|
||||
}
|
||||
| net_vpn4_
|
||||
| net_vpn6_
|
||||
| net_ip6_sadr_
|
||||
| VPN_RD IP4 {
|
||||
$$ = cfg_alloc(sizeof(net_addr_vpn4));
|
||||
net_fill_vpn4($$, $2, IP4_MAX_PREFIX_LENGTH, $1);
|
||||
@ -622,6 +635,10 @@ r_args_for:
|
||||
$$ = cfg_alloc(sizeof(net_addr_vpn6));
|
||||
net_fill_vpn6($$, $2, IP6_MAX_PREFIX_LENGTH, $1);
|
||||
}
|
||||
| IP6 FROM IP6 {
|
||||
$$ = cfg_alloc(sizeof(net_addr_ip6_sadr));
|
||||
net_fill_ip6_sadr($$, $1, IP6_MAX_PREFIX_LENGTH, $3, IP6_MAX_PREFIX_LENGTH);
|
||||
}
|
||||
| SYM {
|
||||
if ($1->class == (SYM_CONSTANT | T_IP))
|
||||
{
|
||||
@ -655,6 +672,7 @@ r_args_channel:
|
||||
| IPV6 { $$ = "ipv6"; }
|
||||
| IPV6_MC { $$ = "ipv6-mc"; }
|
||||
| IPV6_MPLS { $$ = "ipv6-mpls"; }
|
||||
| IPV6_SADR { $$ = "ipv6-sadr"; }
|
||||
| VPN4 { $$ = "vpn4"; }
|
||||
| VPN4_MC { $$ = "vpn4-mc"; }
|
||||
| VPN4_MPLS { $$ = "vpn4-mpls"; }
|
||||
@ -728,12 +746,12 @@ echo_size:
|
||||
}
|
||||
;
|
||||
|
||||
CF_CLI(DISABLE, proto_patt, <protocol> | \"<pattern>\" | all, [[Disable protocol]])
|
||||
{ proto_apply_cmd($2, proto_cmd_disable, 1, 0); } ;
|
||||
CF_CLI(ENABLE, proto_patt, <protocol> | \"<pattern>\" | all, [[Enable protocol]])
|
||||
{ proto_apply_cmd($2, proto_cmd_enable, 1, 0); } ;
|
||||
CF_CLI(RESTART, proto_patt, <protocol> | \"<pattern>\" | all, [[Restart protocol]])
|
||||
{ proto_apply_cmd($2, proto_cmd_restart, 1, 0); } ;
|
||||
CF_CLI(DISABLE, proto_patt opttext, (<protocol> | \"<pattern>\" | all) [message], [[Disable protocol]])
|
||||
{ proto_apply_cmd($2, proto_cmd_disable, 1, (uintptr_t) $3); } ;
|
||||
CF_CLI(ENABLE, proto_patt opttext, (<protocol> | \"<pattern>\" | all) [message], [[Enable protocol]])
|
||||
{ proto_apply_cmd($2, proto_cmd_enable, 1, (uintptr_t) $3); } ;
|
||||
CF_CLI(RESTART, proto_patt opttext, (<protocol> | \"<pattern>\" | all) [message], [[Restart protocol]])
|
||||
{ proto_apply_cmd($2, proto_cmd_restart, 1, (uintptr_t) $3); } ;
|
||||
CF_CLI(RELOAD, proto_patt, <protocol> | \"<pattern>\" | all, [[Reload protocol]])
|
||||
{ proto_apply_cmd($2, proto_cmd_reload, 1, CMD_RELOAD); } ;
|
||||
CF_CLI(RELOAD IN, proto_patt, <protocol> | \"<pattern>\" | all, [[Reload protocol (just imported routes)]])
|
||||
@ -764,8 +782,7 @@ proto_patt2:
|
||||
| TEXT { $$.ptr = $1; $$.patt = 1; }
|
||||
;
|
||||
|
||||
CF_ADDTO(dynamic_attr, IGP_METRIC
|
||||
{ $$ = f_new_dynamic_attr(EAF_TYPE_INT, T_INT, EA_GEN_IGP_METRIC); })
|
||||
dynamic_attr: IGP_METRIC { $$ = f_new_dynamic_attr(EAF_TYPE_INT, T_INT, EA_GEN_IGP_METRIC); } ;
|
||||
|
||||
|
||||
CF_CODE
|
||||
|
264
nest/iface.c
264
nest/iface.c
@ -32,11 +32,14 @@
|
||||
#include "lib/resource.h"
|
||||
#include "lib/string.h"
|
||||
#include "conf/conf.h"
|
||||
#include "sysdep/unix/krt.h"
|
||||
|
||||
static pool *if_pool;
|
||||
|
||||
list iface_list;
|
||||
|
||||
static void if_recalc_preferred(struct iface *i);
|
||||
|
||||
/**
|
||||
* ifa_dump - dump interface address
|
||||
* @a: interface address descriptor
|
||||
@ -46,10 +49,11 @@ list iface_list;
|
||||
void
|
||||
ifa_dump(struct ifa *a)
|
||||
{
|
||||
debug("\t%I, net %N bc %I -> %I%s%s%s\n", a->ip, &a->prefix, a->brd, a->opposite,
|
||||
(a->flags & IF_UP) ? "" : " DOWN",
|
||||
(a->flags & IA_PRIMARY) ? "" : " SEC",
|
||||
(a->flags & IA_PEER) ? "PEER" : "");
|
||||
debug("\t%I, net %N bc %I -> %I%s%s%s%s\n", a->ip, &a->prefix, a->brd, a->opposite,
|
||||
(a->flags & IA_PRIMARY) ? " PRIMARY" : "",
|
||||
(a->flags & IA_SECONDARY) ? " SEC" : "",
|
||||
(a->flags & IA_HOST) ? " HOST" : "",
|
||||
(a->flags & IA_PEER) ? " PEER" : "");
|
||||
}
|
||||
|
||||
/**
|
||||
@ -89,7 +93,8 @@ if_dump(struct iface *i)
|
||||
WALK_LIST(a, i->addrs)
|
||||
{
|
||||
ifa_dump(a);
|
||||
ASSERT((a != i->addr) == !(a->flags & IA_PRIMARY));
|
||||
ASSERT(!!(a->flags & IA_PRIMARY) ==
|
||||
((a == i->addr4) || (a == i->addr6) || (a == i->llv6)));
|
||||
}
|
||||
}
|
||||
|
||||
@ -116,7 +121,7 @@ if_what_changed(struct iface *i, struct iface *j)
|
||||
unsigned c;
|
||||
|
||||
if (((i->flags ^ j->flags) & ~(IF_UP | IF_SHUTDOWN | IF_UPDATED | IF_ADMIN_UP | IF_LINK_UP | IF_TMP_DOWN | IF_JUST_CREATED))
|
||||
|| i->index != j->index)
|
||||
|| (i->index != j->index) || (i->master != j->master))
|
||||
return IF_CHANGE_TOO_MUCH;
|
||||
c = 0;
|
||||
if ((i->flags ^ j->flags) & IF_UP)
|
||||
@ -133,17 +138,21 @@ if_copy(struct iface *to, struct iface *from)
|
||||
{
|
||||
to->flags = from->flags | (to->flags & IF_TMP_DOWN);
|
||||
to->mtu = from->mtu;
|
||||
to->master_index = from->master_index;
|
||||
to->master = from->master;
|
||||
}
|
||||
|
||||
static inline void
|
||||
ifa_send_notify(struct proto *p, unsigned c, struct ifa *a)
|
||||
{
|
||||
if (p->ifa_notify && (p->proto_state != PS_DOWN))
|
||||
if (p->ifa_notify &&
|
||||
(p->proto_state != PS_DOWN) &&
|
||||
(!p->vrf || p->vrf == a->iface->master))
|
||||
{
|
||||
if (p->debug & D_IFACES)
|
||||
log(L_TRACE "%s < %s address %N on interface %s %s",
|
||||
p->name, (a->flags & IA_PRIMARY) ? "primary" : "secondary",
|
||||
&a->prefix, a->iface->name, (c & IF_CHANGE_UP) ? "added" : "removed");
|
||||
log(L_TRACE "%s < address %N on interface %s %s",
|
||||
p->name, &a->prefix, a->iface->name,
|
||||
(c & IF_CHANGE_UP) ? "added" : "removed");
|
||||
p->ifa_notify(p, c, a);
|
||||
}
|
||||
}
|
||||
@ -174,7 +183,9 @@ ifa_notify_change(unsigned c, struct ifa *a)
|
||||
static inline void
|
||||
if_send_notify(struct proto *p, unsigned c, struct iface *i)
|
||||
{
|
||||
if (p->if_notify && (p->proto_state != PS_DOWN))
|
||||
if (p->if_notify &&
|
||||
(p->proto_state != PS_DOWN) &&
|
||||
(!p->vrf || p->vrf == i->master))
|
||||
{
|
||||
if (p->debug & D_IFACES)
|
||||
log(L_TRACE "%s < interface %s %s", p->name, i->name,
|
||||
@ -182,6 +193,7 @@ if_send_notify(struct proto *p, unsigned c, struct iface *i)
|
||||
(c & IF_CHANGE_DOWN) ? "goes down" :
|
||||
(c & IF_CHANGE_MTU) ? "changes MTU" :
|
||||
(c & IF_CHANGE_LINK) ? "changes link" :
|
||||
(c & IF_CHANGE_PREFERRED) ? "changes preferred address" :
|
||||
(c & IF_CHANGE_CREATE) ? "created" :
|
||||
"sends unknown event");
|
||||
p->if_notify(p, c, i);
|
||||
@ -210,20 +222,14 @@ if_notify_change(unsigned c, struct iface *i)
|
||||
|
||||
if (c & IF_CHANGE_DOWN)
|
||||
WALK_LIST(a, i->addrs)
|
||||
{
|
||||
a->flags = (i->flags & ~IA_FLAGS) | (a->flags & IA_FLAGS);
|
||||
ifa_notify_change_(IF_CHANGE_DOWN, a);
|
||||
}
|
||||
ifa_notify_change_(IF_CHANGE_DOWN, a);
|
||||
|
||||
WALK_LIST(p, proto_list)
|
||||
if_send_notify(p, c, i);
|
||||
|
||||
if (c & IF_CHANGE_UP)
|
||||
WALK_LIST(a, i->addrs)
|
||||
{
|
||||
a->flags = (i->flags & ~IA_FLAGS) | (a->flags & IA_FLAGS);
|
||||
ifa_notify_change_(IF_CHANGE_UP, a);
|
||||
}
|
||||
ifa_notify_change_(IF_CHANGE_UP, a);
|
||||
|
||||
if (c & IF_CHANGE_UP)
|
||||
neigh_if_up(i);
|
||||
@ -232,24 +238,25 @@ if_notify_change(unsigned c, struct iface *i)
|
||||
neigh_if_link(i);
|
||||
}
|
||||
|
||||
static unsigned
|
||||
if_recalc_flags(struct iface *i, unsigned flags)
|
||||
static uint
|
||||
if_recalc_flags(struct iface *i UNUSED, uint flags)
|
||||
{
|
||||
if ((flags & (IF_SHUTDOWN | IF_TMP_DOWN)) ||
|
||||
!(flags & IF_ADMIN_UP) ||
|
||||
!i->addr)
|
||||
flags &= ~IF_UP;
|
||||
else
|
||||
if ((flags & IF_ADMIN_UP) &&
|
||||
!(flags & (IF_SHUTDOWN | IF_TMP_DOWN)) &&
|
||||
!(i->master_index && !i->master))
|
||||
flags |= IF_UP;
|
||||
else
|
||||
flags &= ~IF_UP;
|
||||
|
||||
return flags;
|
||||
}
|
||||
|
||||
static void
|
||||
if_change_flags(struct iface *i, unsigned flags)
|
||||
if_change_flags(struct iface *i, uint flags)
|
||||
{
|
||||
unsigned of = i->flags;
|
||||
|
||||
uint of = i->flags;
|
||||
i->flags = if_recalc_flags(i, flags);
|
||||
|
||||
if ((i->flags ^ of) & IF_UP)
|
||||
if_notify_change((i->flags & IF_UP) ? IF_CHANGE_UP : IF_CHANGE_DOWN, i);
|
||||
}
|
||||
@ -297,7 +304,6 @@ if_update(struct iface *new)
|
||||
WALK_LIST(i, iface_list)
|
||||
if (!strcmp(new->name, i->name))
|
||||
{
|
||||
new->addr = i->addr;
|
||||
new->flags = if_recalc_flags(new, new->flags);
|
||||
c = if_what_changed(i, new);
|
||||
if (c & IF_CHANGE_TOO_MUCH) /* Changed a lot, convert it to down/up */
|
||||
@ -305,10 +311,13 @@ if_update(struct iface *new)
|
||||
DBG("Interface %s changed too much -- forcing down/up transition\n", i->name);
|
||||
if_change_flags(i, i->flags | IF_TMP_DOWN);
|
||||
rem_node(&i->n);
|
||||
new->addr = i->addr;
|
||||
new->addr4 = i->addr4;
|
||||
new->addr6 = i->addr6;
|
||||
new->llv6 = i->llv6;
|
||||
new->sysdep = i->sysdep;
|
||||
memcpy(&new->addrs, &i->addrs, sizeof(i->addrs));
|
||||
memcpy(i, new, sizeof(*i));
|
||||
i->flags &= ~IF_UP; /* IF_TMP_DOWN will be added later */
|
||||
i->flags &= ~IF_UP; /* IF_TMP_DOWN will be added later */
|
||||
goto newif;
|
||||
}
|
||||
|
||||
@ -339,13 +348,16 @@ if_start_update(void)
|
||||
{
|
||||
i->flags &= ~IF_UPDATED;
|
||||
WALK_LIST(a, i->addrs)
|
||||
a->flags &= ~IF_UPDATED;
|
||||
a->flags &= ~IA_UPDATED;
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
if_end_partial_update(struct iface *i)
|
||||
{
|
||||
if (i->flags & IF_NEEDS_RECALC)
|
||||
if_recalc_preferred(i);
|
||||
|
||||
if (i->flags & IF_TMP_DOWN)
|
||||
if_change_flags(i, i->flags & ~IF_TMP_DOWN);
|
||||
}
|
||||
@ -363,7 +375,7 @@ if_end_update(void)
|
||||
else
|
||||
{
|
||||
WALK_LIST_DELSAFE(a, b, i->addrs)
|
||||
if (!(a->flags & IF_UPDATED))
|
||||
if (!(a->flags & IA_UPDATED))
|
||||
ifa_delete(a);
|
||||
if_end_partial_update(i);
|
||||
}
|
||||
@ -460,40 +472,99 @@ if_get_by_name(char *name)
|
||||
return i;
|
||||
}
|
||||
|
||||
struct ifa *kif_choose_primary(struct iface *i);
|
||||
|
||||
static int
|
||||
ifa_recalc_primary(struct iface *i)
|
||||
static inline void
|
||||
if_set_preferred(struct ifa **pos, struct ifa *new)
|
||||
{
|
||||
struct ifa *a = kif_choose_primary(i);
|
||||
if (*pos)
|
||||
(*pos)->flags &= ~IA_PRIMARY;
|
||||
if (new)
|
||||
new->flags |= IA_PRIMARY;
|
||||
|
||||
if (a == i->addr)
|
||||
return 0;
|
||||
*pos = new;
|
||||
}
|
||||
|
||||
if (i->addr)
|
||||
i->addr->flags &= ~IA_PRIMARY;
|
||||
static void
|
||||
if_recalc_preferred(struct iface *i)
|
||||
{
|
||||
/*
|
||||
* Preferred address selection priority:
|
||||
* 1) Address configured in Device protocol
|
||||
* 2) Sysdep IPv4 address (BSD)
|
||||
* 3) Old preferred address
|
||||
* 4) First address in list
|
||||
*/
|
||||
|
||||
if (a)
|
||||
struct kif_iface_config *ic = kif_get_iface_config(i);
|
||||
struct ifa *a4 = i->addr4, *a6 = i->addr6, *ll = i->llv6;
|
||||
ip_addr pref_v4 = ic->pref_v4;
|
||||
uint change = 0;
|
||||
|
||||
if (kif_update_sysdep_addr(i))
|
||||
change |= IF_CHANGE_SYSDEP;
|
||||
|
||||
/* BSD sysdep address */
|
||||
if (ipa_zero(pref_v4) && ip4_nonzero(i->sysdep))
|
||||
pref_v4 = ipa_from_ip4(i->sysdep);
|
||||
|
||||
struct ifa *a;
|
||||
WALK_LIST(a, i->addrs)
|
||||
{
|
||||
a->flags |= IA_PRIMARY;
|
||||
rem_node(&a->n);
|
||||
add_head(&i->addrs, &a->n);
|
||||
/* Secondary address is never selected */
|
||||
if (a->flags & IA_SECONDARY)
|
||||
continue;
|
||||
|
||||
if (ipa_is_ip4(a->ip)) {
|
||||
if (!a4 || ipa_equal(a->ip, pref_v4))
|
||||
a4 = a;
|
||||
} else if (!ipa_is_link_local(a->ip)) {
|
||||
if (!a6 || ipa_equal(a->ip, ic->pref_v6))
|
||||
a6 = a;
|
||||
} else {
|
||||
if (!ll || ipa_equal(a->ip, ic->pref_ll))
|
||||
ll = a;
|
||||
}
|
||||
}
|
||||
|
||||
i->addr = a;
|
||||
return 1;
|
||||
if (a4 != i->addr4)
|
||||
{
|
||||
if_set_preferred(&i->addr4, a4);
|
||||
change |= IF_CHANGE_ADDR4;
|
||||
}
|
||||
|
||||
if (a6 != i->addr6)
|
||||
{
|
||||
if_set_preferred(&i->addr6, a6);
|
||||
change |= IF_CHANGE_ADDR6;
|
||||
}
|
||||
|
||||
if (ll != i->llv6)
|
||||
{
|
||||
if_set_preferred(&i->llv6, ll);
|
||||
change |= IF_CHANGE_LLV6;
|
||||
}
|
||||
|
||||
i->flags &= ~IF_NEEDS_RECALC;
|
||||
|
||||
/*
|
||||
* FIXME: There should be proper notification instead of iface restart:
|
||||
* if_notify_change(change, i)
|
||||
*/
|
||||
if (change)
|
||||
if_change_flags(i, i->flags | IF_TMP_DOWN);
|
||||
}
|
||||
|
||||
void
|
||||
ifa_recalc_all_primary_addresses(void)
|
||||
if_recalc_all_preferred_addresses(void)
|
||||
{
|
||||
struct iface *i;
|
||||
|
||||
WALK_LIST(i, iface_list)
|
||||
{
|
||||
if (ifa_recalc_primary(i))
|
||||
if_change_flags(i, i->flags | IF_TMP_DOWN);
|
||||
}
|
||||
{
|
||||
if_recalc_preferred(i);
|
||||
|
||||
if (i->flags & IF_TMP_DOWN)
|
||||
if_change_flags(i, i->flags & ~IF_TMP_DOWN);
|
||||
}
|
||||
}
|
||||
|
||||
static inline int
|
||||
@ -525,7 +596,7 @@ ifa_update(struct ifa *a)
|
||||
b->scope == a->scope &&
|
||||
!((b->flags ^ a->flags) & IA_PEER))
|
||||
{
|
||||
b->flags |= IF_UPDATED;
|
||||
b->flags |= IA_UPDATED;
|
||||
return b;
|
||||
}
|
||||
ifa_delete(b);
|
||||
@ -533,15 +604,15 @@ ifa_update(struct ifa *a)
|
||||
}
|
||||
|
||||
if ((a->prefix.type == NET_IP4) && (i->flags & IF_BROADCAST) && ipa_zero(a->brd))
|
||||
log(L_ERR "Missing broadcast address for interface %s", i->name);
|
||||
log(L_WARN "Missing broadcast address for interface %s", i->name);
|
||||
|
||||
b = mb_alloc(if_pool, sizeof(struct ifa));
|
||||
memcpy(b, a, sizeof(struct ifa));
|
||||
add_tail(&i->addrs, &b->n);
|
||||
b->flags = (i->flags & ~IA_FLAGS) | (a->flags & IA_FLAGS);
|
||||
if (ifa_recalc_primary(i))
|
||||
if_change_flags(i, i->flags | IF_TMP_DOWN);
|
||||
if (b->flags & IF_UP)
|
||||
b->flags |= IA_UPDATED;
|
||||
|
||||
i->flags |= IF_NEEDS_RECALC;
|
||||
if (i->flags & IF_UP)
|
||||
ifa_notify_change(IF_CHANGE_CREATE | IF_CHANGE_UP, b);
|
||||
return b;
|
||||
}
|
||||
@ -564,16 +635,24 @@ ifa_delete(struct ifa *a)
|
||||
if (ifa_same(b, a))
|
||||
{
|
||||
rem_node(&b->n);
|
||||
if (b->flags & IF_UP)
|
||||
{
|
||||
b->flags &= ~IF_UP;
|
||||
ifa_notify_change(IF_CHANGE_DOWN, b);
|
||||
}
|
||||
|
||||
if (b->flags & IA_PRIMARY)
|
||||
{
|
||||
if_change_flags(i, i->flags | IF_TMP_DOWN);
|
||||
ifa_recalc_primary(i);
|
||||
/*
|
||||
* We unlink deleted preferred address and mark for recalculation.
|
||||
* FIXME: This could break if we make iface scan non-atomic, as
|
||||
* protocols still could use the freed address until they get
|
||||
* if_notify from preferred route recalculation.
|
||||
*/
|
||||
if (b == i->addr4) i->addr4 = NULL;
|
||||
if (b == i->addr6) i->addr6 = NULL;
|
||||
if (b == i->llv6) i->llv6 = NULL;
|
||||
i->flags |= IF_NEEDS_RECALC;
|
||||
}
|
||||
|
||||
if (i->flags & IF_UP)
|
||||
ifa_notify_change(IF_CHANGE_DOWN, b);
|
||||
|
||||
mb_free(b);
|
||||
return;
|
||||
}
|
||||
@ -740,16 +819,17 @@ iface_patts_equal(list *a, list *b, int (*comp)(struct iface_patt *, struct ifac
|
||||
static void
|
||||
if_show_addr(struct ifa *a)
|
||||
{
|
||||
byte opp[IPA_MAX_TEXT_LENGTH + 16];
|
||||
byte *flg, opp[IPA_MAX_TEXT_LENGTH + 16];
|
||||
|
||||
flg = (a->flags & IA_PRIMARY) ? "Preferred, " : (a->flags & IA_SECONDARY) ? "Secondary, " : "";
|
||||
|
||||
if (ipa_nonzero(a->opposite))
|
||||
bsprintf(opp, ", opposite %I", a->opposite);
|
||||
bsprintf(opp, "opposite %I, ", a->opposite);
|
||||
else
|
||||
opp[0] = 0;
|
||||
cli_msg(-1003, "\t%I/%d (%s%s, scope %s)",
|
||||
a->ip, a->prefix.pxlen,
|
||||
(a->flags & IA_PRIMARY) ? "Primary" : (a->flags & IA_SECONDARY) ? "Secondary" : "Unselected",
|
||||
opp, ip_scope_text(a->scope));
|
||||
|
||||
cli_msg(-1003, "\t%I/%d (%s%sscope %s)",
|
||||
a->ip, a->prefix.pxlen, flg, opp, ip_scope_text(a->scope));
|
||||
}
|
||||
|
||||
void
|
||||
@ -764,7 +844,13 @@ if_show(void)
|
||||
if (i->flags & IF_SHUTDOWN)
|
||||
continue;
|
||||
|
||||
cli_msg(-1001, "%s %s (index=%d)", i->name, (i->flags & IF_UP) ? "up" : "DOWN", i->index);
|
||||
char mbuf[16 + sizeof(i->name)] = {};
|
||||
if (i->master)
|
||||
bsprintf(mbuf, " master=%s", i->master->name);
|
||||
else if (i->master_index)
|
||||
bsprintf(mbuf, " master=#%u", i->master_index);
|
||||
|
||||
cli_msg(-1001, "%s %s (index=%d%s)", i->name, (i->flags & IF_UP) ? "up" : "down", i->index, mbuf);
|
||||
if (!(i->flags & IF_MULTIACCESS))
|
||||
type = "PtP";
|
||||
else
|
||||
@ -778,10 +864,13 @@ if_show(void)
|
||||
(i->flags & IF_LOOPBACK) ? " Loopback" : "",
|
||||
(i->flags & IF_IGNORE) ? " Ignored" : "",
|
||||
i->mtu);
|
||||
if (i->addr)
|
||||
if_show_addr(i->addr);
|
||||
|
||||
WALK_LIST(a, i->addrs)
|
||||
if (a != i->addr)
|
||||
if (a->prefix.type == NET_IP4)
|
||||
if_show_addr(a);
|
||||
|
||||
WALK_LIST(a, i->addrs)
|
||||
if (a->prefix.type == NET_IP6)
|
||||
if_show_addr(a);
|
||||
}
|
||||
cli_msg(0, "");
|
||||
@ -791,16 +880,25 @@ void
|
||||
if_show_summary(void)
|
||||
{
|
||||
struct iface *i;
|
||||
byte addr[IPA_MAX_TEXT_LENGTH + 16];
|
||||
|
||||
cli_msg(-2005, "interface state address");
|
||||
cli_msg(-2005, "%-10s %-6s %-18s %s", "Interface", "State", "IPv4 address", "IPv6 address");
|
||||
WALK_LIST(i, iface_list)
|
||||
{
|
||||
if (i->addr)
|
||||
bsprintf(addr, "%I/%d", i->addr->ip, i->addr->prefix.pxlen);
|
||||
byte a4[IPA_MAX_TEXT_LENGTH + 17];
|
||||
byte a6[IPA_MAX_TEXT_LENGTH + 17];
|
||||
|
||||
if (i->addr4)
|
||||
bsprintf(a4, "%I/%d", i->addr4->ip, i->addr4->prefix.pxlen);
|
||||
else
|
||||
addr[0] = 0;
|
||||
cli_msg(-1005, "%-9s %-5s %s", i->name, (i->flags & IF_UP) ? "up" : "DOWN", addr);
|
||||
a4[0] = 0;
|
||||
|
||||
if (i->addr6)
|
||||
bsprintf(a6, "%I/%d", i->addr6->ip, i->addr6->prefix.pxlen);
|
||||
else
|
||||
a6[0] = 0;
|
||||
|
||||
cli_msg(-1005, "%-10s %-6s %-18s %s",
|
||||
i->name, (i->flags & IF_UP) ? "up" : "down", a4, a6);
|
||||
}
|
||||
cli_msg(0, "");
|
||||
}
|
||||
|
42
nest/iface.h
42
nest/iface.h
@ -34,12 +34,17 @@ struct iface {
|
||||
unsigned flags;
|
||||
unsigned mtu;
|
||||
unsigned index; /* OS-dependent interface index */
|
||||
unsigned master_index; /* Interface index of master iface */
|
||||
struct iface *master; /* Master iface (e.g. for VRF) */
|
||||
list addrs; /* Addresses assigned to this interface */
|
||||
struct ifa *addr; /* Primary address */
|
||||
struct ifa *addr4; /* Primary address for IPv4 */
|
||||
struct ifa *addr6; /* Primary address for IPv6 */
|
||||
struct ifa *llv6; /* Primary link-local address for IPv6 */
|
||||
ip4_addr sysdep; /* Arbitrary IPv4 address for internal sysdep use */
|
||||
list neighbors; /* All neighbors on this interface */
|
||||
};
|
||||
|
||||
#define IF_UP 1 /* IF_ADMIN_UP and IP address known */
|
||||
#define IF_UP 1 /* Currently just IF_ADMIN_UP */
|
||||
#define IF_MULTIACCESS 2
|
||||
#define IF_BROADCAST 4
|
||||
#define IF_MULTICAST 8
|
||||
@ -70,7 +75,10 @@ struct iface {
|
||||
|
||||
#define IF_JUST_CREATED 0x10000000 /* Send creation event as soon as possible */
|
||||
#define IF_TMP_DOWN 0x20000000 /* Temporary shutdown due to interface reconfiguration */
|
||||
#define IF_UPDATED 0x40000000 /* Touched in last scan */
|
||||
#define IF_UPDATED 0x40000000 /* Iface touched in last scan */
|
||||
#define IF_NEEDS_RECALC 0x80000000 /* Preferred address recalculation is needed */
|
||||
|
||||
#define IA_UPDATED IF_UPDATED /* Address touched in last scan */
|
||||
|
||||
/* Interface change events */
|
||||
|
||||
@ -79,8 +87,14 @@ struct iface {
|
||||
#define IF_CHANGE_MTU 4
|
||||
#define IF_CHANGE_CREATE 8 /* Seen this interface for the first time */
|
||||
#define IF_CHANGE_LINK 0x10
|
||||
#define IF_CHANGE_ADDR4 0x100 /* Change of iface->addr4 */
|
||||
#define IF_CHANGE_ADDR6 0x200 /* ... */
|
||||
#define IF_CHANGE_LLV6 0x400
|
||||
#define IF_CHANGE_SYSDEP 0x800
|
||||
#define IF_CHANGE_TOO_MUCH 0x40000000 /* Used internally */
|
||||
|
||||
#define IF_CHANGE_PREFERRED (IF_CHANGE_ADDR4 | IF_CHANGE_ADDR6 | IF_CHANGE_LLV6)
|
||||
|
||||
void if_init(void);
|
||||
void if_dump(struct iface *);
|
||||
void if_dump_all(void);
|
||||
@ -99,7 +113,7 @@ void if_feed_baby(struct proto *);
|
||||
struct iface *if_find_by_index(unsigned);
|
||||
struct iface *if_find_by_name(char *);
|
||||
struct iface *if_get_by_name(char *);
|
||||
void ifa_recalc_all_primary_addresses(void);
|
||||
void if_recalc_all_preferred_addresses(void);
|
||||
|
||||
|
||||
/* The Neighbor Cache */
|
||||
@ -110,29 +124,21 @@ typedef struct neighbor {
|
||||
ip_addr addr; /* Address of the neighbor */
|
||||
struct ifa *ifa; /* Ifa on related iface */
|
||||
struct iface *iface; /* Interface it's connected to */
|
||||
struct iface *ifreq; /* Requested iface, NULL for any */
|
||||
struct proto *proto; /* Protocol this belongs to */
|
||||
void *data; /* Protocol-specific data */
|
||||
unsigned aux; /* Protocol-specific data */
|
||||
unsigned flags;
|
||||
int scope; /* Address scope, -1 for unreachable sticky neighbors,
|
||||
uint aux; /* Protocol-specific data */
|
||||
u16 flags; /* NEF_* flags */
|
||||
s16 scope; /* Address scope, -1 for unreachable neighbors,
|
||||
SCOPE_HOST when it's our own address */
|
||||
} neighbor;
|
||||
|
||||
#define NEF_STICKY 1
|
||||
#define NEF_ONLINK 2
|
||||
#define NEF_BIND 4 /* Used internally for neighbors bound to an iface */
|
||||
#define NEF_IFACE 8 /* Neighbors bound to iface */
|
||||
#define NEF_IFACE 4 /* Entry for whole iface */
|
||||
|
||||
|
||||
neighbor *neigh_find(struct proto *, ip_addr *, unsigned flags);
|
||||
neighbor *neigh_find2(struct proto *p, ip_addr *a, struct iface *ifa, unsigned flags);
|
||||
neighbor *neigh_find_iface(struct proto *p, struct iface *ifa);
|
||||
|
||||
static inline int neigh_connected_to(struct proto *p, ip_addr *a, struct iface *i)
|
||||
{
|
||||
neighbor *n = neigh_find(p, a, 0);
|
||||
return n && n->iface == i;
|
||||
}
|
||||
neighbor *neigh_find(struct proto *p, ip_addr a, struct iface *ifa, uint flags);
|
||||
|
||||
void neigh_dump(neighbor *);
|
||||
void neigh_dump_all(void);
|
||||
|
@ -45,6 +45,7 @@ olock_same(struct object_lock *x, struct object_lock *y)
|
||||
return
|
||||
x->type == y->type &&
|
||||
x->iface == y->iface &&
|
||||
x->vrf == y->vrf &&
|
||||
x->port == y->port &&
|
||||
x->inst == y->inst &&
|
||||
ipa_equal(x->addr, y->addr);
|
||||
|
@ -30,6 +30,7 @@ struct object_lock {
|
||||
uint port; /* ... port number */
|
||||
uint inst; /* ... instance ID */
|
||||
struct iface *iface; /* ... interface */
|
||||
struct iface *vrf; /* ... or VRF (if iface is unknown) */
|
||||
void (*hook)(struct object_lock *); /* Called when the lock succeeds */
|
||||
void *data; /* User data */
|
||||
/* ... internal to lock manager, don't touch ... */
|
||||
|
@ -28,4 +28,3 @@
|
||||
void mrt_dump_message(struct proto *p, u16 type, u16 subtype, byte *buf, u32 len);
|
||||
|
||||
#endif
|
||||
|
||||
|
469
nest/neighbor.c
469
nest/neighbor.c
@ -2,6 +2,8 @@
|
||||
* BIRD -- Neighbor Cache
|
||||
*
|
||||
* (c) 1998--2000 Martin Mares <mj@ucw.cz>
|
||||
* (c) 2008--2018 Ondrej Zajicek <santiago@crfreenet.org>
|
||||
* (c) 2008--2018 CZ.NIC z.s.p.o.
|
||||
*
|
||||
* Can be freely distributed and used under the terms of the GNU GPL.
|
||||
*/
|
||||
@ -10,31 +12,38 @@
|
||||
* DOC: Neighbor cache
|
||||
*
|
||||
* Most routing protocols need to associate their internal state data with
|
||||
* neighboring routers, check whether an address given as the next hop
|
||||
* attribute of a route is really an address of a directly connected host
|
||||
* and which interface is it connected through. Also, they often need to
|
||||
* be notified when a neighbor ceases to exist or when their long awaited
|
||||
* neighbor becomes connected. The neighbor cache is there to solve all
|
||||
* these problems.
|
||||
* neighboring routers, check whether an address given as the next hop attribute
|
||||
* of a route is really an address of a directly connected host and which
|
||||
* interface is it connected through. Also, they often need to be notified when
|
||||
* a neighbor ceases to exist or when their long awaited neighbor becomes
|
||||
* connected. The neighbor cache is there to solve all these problems.
|
||||
*
|
||||
* The neighbor cache maintains a collection of neighbor entries. Each
|
||||
* entry represents one IP address corresponding to either our directly
|
||||
* connected neighbor or our own end of the link (when the scope of the
|
||||
* address is set to %SCOPE_HOST) together with per-neighbor data belonging to a
|
||||
* single protocol.
|
||||
* The neighbor cache maintains a collection of neighbor entries. Each entry
|
||||
* represents one IP address corresponding to either our directly connected
|
||||
* neighbor or our own end of the link (when the scope of the address is set to
|
||||
* %SCOPE_HOST) together with per-neighbor data belonging to a single protocol.
|
||||
* A neighbor entry may be bound to a specific interface, which is required for
|
||||
* link-local IP addresses and optional for global IP addresses.
|
||||
*
|
||||
* Active entries represent known neighbors and are stored in a hash
|
||||
* table (to allow fast retrieval based on the IP address of the node) and
|
||||
* two linked lists: one global and one per-interface (allowing quick
|
||||
* processing of interface change events). Inactive entries exist only
|
||||
* when the protocol has explicitly requested it via the %NEF_STICKY
|
||||
* flag because it wishes to be notified when the node will again become
|
||||
* a neighbor. Such entries are enqueued in a special list which is walked
|
||||
* whenever an interface changes its state to up.
|
||||
* Neighbor cache entries are stored in a hash table, which is indexed by triple
|
||||
* (protocol, IP, requested-iface), so if both regular and iface-bound neighbors
|
||||
* are requested, they are represented by two neighbor cache entries. Active
|
||||
* entries are also linked in per-interface list (allowing quick processing of
|
||||
* interface change events). Inactive entries exist only when the protocol has
|
||||
* explicitly requested it via the %NEF_STICKY flag because it wishes to be
|
||||
* notified when the node will again become a neighbor. Such entries are instead
|
||||
* linked in a special list, which is walked whenever an interface changes its
|
||||
* state to up. Neighbor entry VRF association is implied by respective
|
||||
* protocol.
|
||||
*
|
||||
* Besides the already mentioned %NEF_STICKY flag, there is also %NEF_ONLINK,
|
||||
* which specifies that neighbor should be considered reachable on given iface
|
||||
* regardless of associated address ranges, and %NEF_IFACE, which represents
|
||||
* pseudo-neighbor entry for whole interface (and uses %IPA_NONE IP address).
|
||||
*
|
||||
* When a neighbor event occurs (a neighbor gets disconnected or a sticky
|
||||
* inactive neighbor becomes connected), the protocol hook neigh_notify()
|
||||
* is called to advertise the change.
|
||||
* inactive neighbor becomes connected), the protocol hook neigh_notify() is
|
||||
* called to advertise the change.
|
||||
*/
|
||||
|
||||
#undef LOCAL_DEBUG
|
||||
@ -42,125 +51,151 @@
|
||||
#include "nest/bird.h"
|
||||
#include "nest/iface.h"
|
||||
#include "nest/protocol.h"
|
||||
#include "lib/hash.h"
|
||||
#include "lib/resource.h"
|
||||
|
||||
#define NEIGH_HASH_SIZE 256
|
||||
#define NEIGH_HASH_OFFSET 24
|
||||
|
||||
static slab *neigh_slab;
|
||||
static list sticky_neigh_list, iface_neigh_list, neigh_hash_table[NEIGH_HASH_SIZE];
|
||||
static list neigh_hash_table[NEIGH_HASH_SIZE], sticky_neigh_list;
|
||||
|
||||
static inline uint
|
||||
neigh_hash(struct proto *p, ip_addr *a)
|
||||
neigh_hash(struct proto *p, ip_addr a, struct iface *i)
|
||||
{
|
||||
return (p->hash_key ^ ipa_hash(*a)) >> NEIGH_HASH_OFFSET;
|
||||
return (p->hash_key ^ ipa_hash(a) ^ ptr_hash(i)) >> NEIGH_HASH_OFFSET;
|
||||
}
|
||||
|
||||
static int
|
||||
if_connected(ip_addr *a, struct iface *i, struct ifa **ap)
|
||||
if_connected(ip_addr a, struct iface *i, struct ifa **ap, uint flags)
|
||||
{
|
||||
struct ifa *b;
|
||||
|
||||
/* Handle iface pseudo-neighbors */
|
||||
if (flags & NEF_IFACE)
|
||||
return *ap = NULL, (i->flags & IF_UP) ? SCOPE_HOST : -1;
|
||||
|
||||
/* Host addresses match even if iface is down */
|
||||
WALK_LIST(b, i->addrs)
|
||||
if (ipa_equal(a, b->ip))
|
||||
return *ap = b, SCOPE_HOST;
|
||||
|
||||
/* Rest do not match if iface is down */
|
||||
if (!(i->flags & IF_UP))
|
||||
return *ap = NULL, -1;
|
||||
|
||||
/* Regular neighbors */
|
||||
WALK_LIST(b, i->addrs)
|
||||
{
|
||||
*ap = NULL;
|
||||
return -1;
|
||||
if (b->flags & IA_PEER)
|
||||
{
|
||||
if (ipa_equal(a, b->opposite))
|
||||
return *ap = b, b->scope;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (ipa_in_netX(a, &b->prefix))
|
||||
{
|
||||
/* Do not allow IPv4 network and broadcast addresses */
|
||||
if (ipa_is_ip4(a) &&
|
||||
(net_pxlen(&b->prefix) < (IP4_MAX_PREFIX_LENGTH - 1)) &&
|
||||
(ipa_equal(a, net_prefix(&b->prefix)) || /* Network address */
|
||||
ipa_equal(a, b->brd))) /* Broadcast */
|
||||
return *ap = NULL, -1;
|
||||
|
||||
return *ap = b, b->scope;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
WALK_LIST(b, i->addrs)
|
||||
{
|
||||
*ap = b;
|
||||
/* Handle ONLINK flag */
|
||||
if (flags & NEF_ONLINK)
|
||||
return *ap = NULL, ipa_classify(a) & IADDR_SCOPE_MASK;
|
||||
|
||||
if (ipa_equal(*a, b->ip))
|
||||
return SCOPE_HOST;
|
||||
if (b->flags & IA_PEER)
|
||||
{
|
||||
if (ipa_equal(*a, b->opposite))
|
||||
return b->scope;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (ipa_in_netX(*a, &b->prefix))
|
||||
{
|
||||
/* Do not allow IPv4 network and broadcast addresses */
|
||||
if (ipa_is_ip4(*a) &&
|
||||
(net_pxlen(&b->prefix) < (IP4_MAX_PREFIX_LENGTH - 1)) &&
|
||||
(ipa_equal(*a, net_prefix(&b->prefix)) || /* Network address */
|
||||
ipa_equal(*a, b->brd))) /* Broadcast */
|
||||
{
|
||||
*ap = NULL;
|
||||
return -1;
|
||||
}
|
||||
return *ap = NULL, -1;
|
||||
}
|
||||
|
||||
return b->scope;
|
||||
}
|
||||
}
|
||||
static inline int
|
||||
if_connected_any(ip_addr a, struct iface *vrf, struct iface **iface, struct ifa **addr, uint flags)
|
||||
{
|
||||
struct iface *i;
|
||||
struct ifa *b;
|
||||
int s, scope = -1;
|
||||
|
||||
*iface = NULL;
|
||||
*addr = NULL;
|
||||
|
||||
/* Get first match, but prefer SCOPE_HOST to other matches */
|
||||
WALK_LIST(i, iface_list)
|
||||
if ((!vrf || vrf == i->master) && ((s = if_connected(a, i, &b, flags)) >= 0))
|
||||
if ((scope < 0) || ((scope > SCOPE_HOST) && (s == SCOPE_HOST)))
|
||||
{
|
||||
*iface = i;
|
||||
*addr = b;
|
||||
scope = s;
|
||||
}
|
||||
|
||||
*ap = NULL;
|
||||
return -1;
|
||||
return scope;
|
||||
}
|
||||
|
||||
/**
|
||||
* neigh_find - find or create a neighbor entry.
|
||||
* @p: protocol which asks for the entry.
|
||||
* @a: pointer to IP address of the node to be searched for.
|
||||
* @flags: 0 or %NEF_STICKY if you want to create a sticky entry.
|
||||
* neigh_find - find or create a neighbor entry
|
||||
* @p: protocol which asks for the entry
|
||||
* @a: IP address of the node to be searched for
|
||||
* @iface: optionally bound neighbor to this iface (may be NULL)
|
||||
* @flags: %NEF_STICKY for sticky entry, %NEF_ONLINK for onlink entry
|
||||
*
|
||||
* Search the neighbor cache for a node with given IP address. If
|
||||
* it's found, a pointer to the neighbor entry is returned. If no
|
||||
* such entry exists and the node is directly connected on
|
||||
* one of our active interfaces, a new entry is created and returned
|
||||
* to the caller with protocol-dependent fields initialized to zero.
|
||||
* If the node is not connected directly or *@a is not a valid unicast
|
||||
* IP address, neigh_find() returns %NULL.
|
||||
* Search the neighbor cache for a node with given IP address. Iface can be
|
||||
* specified for link-local addresses or for cases, where neighbor is expected
|
||||
* on given interface. If it is found, a pointer to the neighbor entry is
|
||||
* returned. If no such entry exists and the node is directly connected on one
|
||||
* of our active interfaces, a new entry is created and returned to the caller
|
||||
* with protocol-dependent fields initialized to zero. If the node is not
|
||||
* connected directly or *@a is not a valid unicast IP address, neigh_find()
|
||||
* returns %NULL.
|
||||
*/
|
||||
neighbor *
|
||||
neigh_find(struct proto *p, ip_addr *a, unsigned flags)
|
||||
{
|
||||
return neigh_find2(p, a, NULL, flags);
|
||||
}
|
||||
|
||||
|
||||
neighbor *
|
||||
neigh_find2(struct proto *p, ip_addr *a, struct iface *ifa, unsigned flags)
|
||||
neigh_find(struct proto *p, ip_addr a, struct iface *iface, uint flags)
|
||||
{
|
||||
neighbor *n;
|
||||
int class, scope = -1;
|
||||
uint h = neigh_hash(p, a);
|
||||
struct iface *i;
|
||||
struct ifa *addr;
|
||||
uint h = neigh_hash(p, a, iface);
|
||||
struct iface *ifreq = iface;
|
||||
struct ifa *addr = NULL;
|
||||
|
||||
WALK_LIST(n, neigh_hash_table[h]) /* Search the cache */
|
||||
if (n->proto == p && ipa_equal(*a, n->addr) && (!ifa || (ifa == n->iface)))
|
||||
if ((n->proto == p) && ipa_equal(n->addr, a) && (n->ifreq == iface))
|
||||
return n;
|
||||
|
||||
class = ipa_classify(*a);
|
||||
if (class < 0) /* Invalid address */
|
||||
return NULL;
|
||||
if (((class & IADDR_SCOPE_MASK) == SCOPE_HOST) ||
|
||||
(((class & IADDR_SCOPE_MASK) == SCOPE_LINK) && (ifa == NULL)) ||
|
||||
!(class & IADDR_HOST))
|
||||
return NULL; /* Bad scope or a somecast */
|
||||
|
||||
if (ifa)
|
||||
{
|
||||
scope = if_connected(a, ifa, &addr);
|
||||
flags |= NEF_BIND;
|
||||
|
||||
if ((scope < 0) && (flags & NEF_ONLINK))
|
||||
scope = class & IADDR_SCOPE_MASK;
|
||||
}
|
||||
if (flags & NEF_IFACE)
|
||||
{
|
||||
if (ipa_nonzero(a) || !iface)
|
||||
return NULL;
|
||||
}
|
||||
else
|
||||
WALK_LIST(i, iface_list)
|
||||
if ((scope = if_connected(a, i, &addr)) >= 0)
|
||||
{
|
||||
ifa = i;
|
||||
break;
|
||||
}
|
||||
{
|
||||
class = ipa_classify(a);
|
||||
if (class < 0) /* Invalid address */
|
||||
return NULL;
|
||||
if (((class & IADDR_SCOPE_MASK) == SCOPE_HOST) ||
|
||||
(((class & IADDR_SCOPE_MASK) == SCOPE_LINK) && !iface) ||
|
||||
!(class & IADDR_HOST))
|
||||
return NULL; /* Bad scope or a somecast */
|
||||
}
|
||||
|
||||
if ((flags & NEF_ONLINK) && !iface)
|
||||
return NULL;
|
||||
|
||||
if (iface)
|
||||
{
|
||||
scope = if_connected(a, iface, &addr, flags);
|
||||
iface = (scope < 0) ? NULL : iface;
|
||||
}
|
||||
else
|
||||
scope = if_connected_any(a, p->vrf, &iface, &addr, flags);
|
||||
|
||||
/* scope < 0 means i don't know neighbor */
|
||||
/* scope >= 0 implies ifa != NULL */
|
||||
/* scope >= 0 <=> iface != NULL */
|
||||
|
||||
if ((scope < 0) && !(flags & NEF_STICKY))
|
||||
return NULL;
|
||||
@ -168,52 +203,15 @@ neigh_find2(struct proto *p, ip_addr *a, struct iface *ifa, unsigned flags)
|
||||
n = sl_alloc(neigh_slab);
|
||||
memset(n, 0, sizeof(neighbor));
|
||||
|
||||
n->addr = *a;
|
||||
if (scope >= 0)
|
||||
{
|
||||
add_tail(&neigh_hash_table[h], &n->n);
|
||||
add_tail(&ifa->neighbors, &n->if_n);
|
||||
}
|
||||
else
|
||||
{
|
||||
add_tail(&sticky_neigh_list, &n->n);
|
||||
scope = -1;
|
||||
}
|
||||
n->iface = ifa;
|
||||
add_tail(&neigh_hash_table[h], &n->n);
|
||||
add_tail((scope >= 0) ? &iface->neighbors : &sticky_neigh_list, &n->if_n);
|
||||
n->addr = a;
|
||||
n->ifa = addr;
|
||||
n->iface = iface;
|
||||
n->ifreq = ifreq;
|
||||
n->proto = p;
|
||||
n->data = NULL;
|
||||
n->aux = 0;
|
||||
n->flags = flags;
|
||||
n->scope = scope;
|
||||
return n;
|
||||
}
|
||||
|
||||
neighbor *
|
||||
neigh_find_iface(struct proto *p, struct iface *ifa)
|
||||
{
|
||||
neighbor *n;
|
||||
node *nn;
|
||||
|
||||
/* We keep neighbors with NEF_IFACE foremost in ifa->neighbors list */
|
||||
WALK_LIST2(n, nn, ifa->neighbors, if_n)
|
||||
{
|
||||
if (! (n->flags & NEF_IFACE))
|
||||
break;
|
||||
|
||||
if (n->proto == p)
|
||||
return n;
|
||||
}
|
||||
|
||||
n = sl_alloc(neigh_slab);
|
||||
memset(n, 0, sizeof(neighbor));
|
||||
|
||||
add_tail(&iface_neigh_list, &n->n);
|
||||
add_head(&ifa->neighbors, &n->if_n);
|
||||
n->iface = ifa;
|
||||
n->proto = p;
|
||||
n->flags = NEF_IFACE;
|
||||
n->scope = (ifa->flags & IF_UP) ? SCOPE_HOST : -1;
|
||||
|
||||
return n;
|
||||
}
|
||||
@ -222,30 +220,26 @@ neigh_find_iface(struct proto *p, struct iface *ifa)
|
||||
* neigh_dump - dump specified neighbor entry.
|
||||
* @n: the entry to dump
|
||||
*
|
||||
* This functions dumps the contents of a given neighbor entry
|
||||
* to debug output.
|
||||
* This functions dumps the contents of a given neighbor entry to debug output.
|
||||
*/
|
||||
void
|
||||
neigh_dump(neighbor *n)
|
||||
{
|
||||
debug("%p %I ", n, n->addr);
|
||||
if (n->iface)
|
||||
debug("%s ", n->iface->name);
|
||||
else
|
||||
debug("[] ");
|
||||
debug("%p %I %s %s ", n, n->addr,
|
||||
n->iface ? n->iface->name : "[]",
|
||||
n->ifreq ? n->ifreq->name : "[]");
|
||||
debug("%s %p %08x scope %s", n->proto->name, n->data, n->aux, ip_scope_text(n->scope));
|
||||
if (n->flags & NEF_STICKY)
|
||||
debug(" STICKY");
|
||||
if (n->flags & NEF_IFACE)
|
||||
debug(" IFACE");
|
||||
if (n->flags & NEF_ONLINK)
|
||||
debug(" ONLINK");
|
||||
debug("\n");
|
||||
}
|
||||
|
||||
/**
|
||||
* neigh_dump_all - dump all neighbor entries.
|
||||
*
|
||||
* This function dumps the contents of the neighbor cache to
|
||||
* debug output.
|
||||
* This function dumps the contents of the neighbor cache to debug output.
|
||||
*/
|
||||
void
|
||||
neigh_dump_all(void)
|
||||
@ -254,73 +248,109 @@ neigh_dump_all(void)
|
||||
int i;
|
||||
|
||||
debug("Known neighbors:\n");
|
||||
WALK_LIST(n, sticky_neigh_list)
|
||||
neigh_dump(n);
|
||||
WALK_LIST(n, iface_neigh_list)
|
||||
neigh_dump(n);
|
||||
for(i=0; i<NEIGH_HASH_SIZE; i++)
|
||||
WALK_LIST(n, neigh_hash_table[i])
|
||||
neigh_dump(n);
|
||||
debug("\n");
|
||||
}
|
||||
|
||||
static inline void
|
||||
neigh_notify(neighbor *n)
|
||||
{
|
||||
if (n->proto->neigh_notify && (n->proto->proto_state != PS_STOP))
|
||||
n->proto->neigh_notify(n);
|
||||
}
|
||||
|
||||
static void
|
||||
neigh_up(neighbor *n, struct iface *i, int scope, struct ifa *a)
|
||||
neigh_up(neighbor *n, struct iface *i, struct ifa *a, int scope)
|
||||
{
|
||||
DBG("Waking up sticky neighbor %I\n", n->addr);
|
||||
n->iface = i;
|
||||
n->ifa = a;
|
||||
n->scope = scope;
|
||||
|
||||
if (! (n->flags & NEF_IFACE))
|
||||
{
|
||||
add_tail(&i->neighbors, &n->if_n);
|
||||
rem_node(&n->n);
|
||||
add_tail(&neigh_hash_table[neigh_hash(n->proto, &n->addr)], &n->n);
|
||||
}
|
||||
rem_node(&n->if_n);
|
||||
add_tail(&i->neighbors, &n->if_n);
|
||||
|
||||
if (n->proto->neigh_notify && (n->proto->proto_state != PS_STOP))
|
||||
n->proto->neigh_notify(n);
|
||||
neigh_notify(n);
|
||||
}
|
||||
|
||||
static void
|
||||
neigh_down(neighbor *n)
|
||||
{
|
||||
DBG("Flushing neighbor %I on %s\n", n->addr, n->iface->name);
|
||||
if (! (n->flags & (NEF_BIND | NEF_IFACE)))
|
||||
n->iface = NULL;
|
||||
n->iface = NULL;
|
||||
n->ifa = NULL;
|
||||
n->scope = -1;
|
||||
|
||||
if (! (n->flags & NEF_IFACE))
|
||||
rem_node(&n->if_n);
|
||||
add_tail(&sticky_neigh_list, &n->if_n);
|
||||
|
||||
neigh_notify(n);
|
||||
}
|
||||
|
||||
static inline void
|
||||
neigh_free(neighbor *n)
|
||||
{
|
||||
rem_node(&n->n);
|
||||
rem_node(&n->if_n);
|
||||
sl_free(neigh_slab, n);
|
||||
}
|
||||
|
||||
/**
|
||||
* neigh_update: update neighbor entry w.r.t. change on specific iface
|
||||
* @n: neighbor to update
|
||||
* @iface: changed iface
|
||||
*
|
||||
* The function recalculates state of the neighbor entry @n assuming that only
|
||||
* the interface @iface may changed its state or addresses. Then, appropriate
|
||||
* actions are executed (the neighbor goes up, down, up-down, or just notified).
|
||||
*/
|
||||
void
|
||||
neigh_update(neighbor *n, struct iface *iface)
|
||||
{
|
||||
struct ifa *ifa = NULL;
|
||||
int scope = -1;
|
||||
|
||||
/* Iface-bound neighbors ignore other ifaces */
|
||||
if (n->ifreq && (n->ifreq != iface))
|
||||
return;
|
||||
|
||||
/* VRF-bound neighbors ignore changes in other VRFs */
|
||||
if (n->proto->vrf && (n->proto->vrf != iface->master))
|
||||
return;
|
||||
|
||||
scope = if_connected(n->addr, iface, &ifa, n->flags);
|
||||
|
||||
/* When neighbor is going down, try to respawn it on other ifaces */
|
||||
if ((scope < 0) && (n->scope >= 0) && !n->ifreq && (n->flags & NEF_STICKY))
|
||||
scope = if_connected_any(n->addr, n->proto->vrf, &iface, &ifa, n->flags);
|
||||
|
||||
/* No change or minor change - ignore or notify */
|
||||
if ((scope == n->scope) && (iface == n->iface))
|
||||
{
|
||||
if (ifa != n->ifa)
|
||||
{
|
||||
rem_node(&n->if_n);
|
||||
rem_node(&n->n);
|
||||
n->ifa = ifa;
|
||||
neigh_notify(n);
|
||||
}
|
||||
|
||||
if (n->proto->neigh_notify && (n->proto->proto_state != PS_STOP))
|
||||
n->proto->neigh_notify(n);
|
||||
return;
|
||||
}
|
||||
|
||||
if (n->flags & NEF_STICKY)
|
||||
{
|
||||
add_tail(&sticky_neigh_list, &n->n);
|
||||
/* Major change - going down and/or going up */
|
||||
|
||||
/* Respawn neighbor if there is another matching prefix */
|
||||
struct iface *i;
|
||||
struct ifa *a;
|
||||
int scope;
|
||||
if (n->scope >= 0)
|
||||
neigh_down(n);
|
||||
|
||||
if (!n->iface)
|
||||
WALK_LIST(i, iface_list)
|
||||
if ((scope = if_connected(&n->addr, i, &a)) >= 0)
|
||||
{
|
||||
neigh_up(n, i, scope, a);
|
||||
return;
|
||||
}
|
||||
}
|
||||
if ((n->scope < 0) && !(n->flags & NEF_STICKY))
|
||||
{
|
||||
neigh_free(n);
|
||||
return;
|
||||
}
|
||||
|
||||
if (! (n->flags & (NEF_STICKY | NEF_IFACE)))
|
||||
sl_free(neigh_slab, n);
|
||||
if (scope >= 0)
|
||||
neigh_up(n, iface, ifa, scope);
|
||||
}
|
||||
|
||||
|
||||
@ -336,21 +366,11 @@ neigh_down(neighbor *n)
|
||||
void
|
||||
neigh_if_up(struct iface *i)
|
||||
{
|
||||
struct ifa *a;
|
||||
neighbor *n;
|
||||
node *x, *y;
|
||||
int scope;
|
||||
|
||||
/* Wake up all iface neighbors */
|
||||
WALK_LIST2_DELSAFE(n, x, y, i->neighbors, if_n)
|
||||
if ((n->scope < 0) && (n->flags & NEF_IFACE))
|
||||
neigh_up(n, i, SCOPE_HOST, NULL);
|
||||
|
||||
/* Wake up appropriate sticky neighbors */
|
||||
WALK_LIST_DELSAFE(n, x, sticky_neigh_list)
|
||||
if ((!n->iface || n->iface == i) &&
|
||||
((scope = if_connected(&n->addr, i, &a)) >= 0))
|
||||
neigh_up(n, i, scope, a);
|
||||
WALK_LIST2_DELSAFE(n, x, y, sticky_neigh_list, if_n)
|
||||
neigh_update(n, i);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -359,8 +379,7 @@ neigh_if_up(struct iface *i)
|
||||
*
|
||||
* Notify the neighbor cache that an interface has ceased to exist.
|
||||
*
|
||||
* It causes all entries belonging to neighbors connected to this interface
|
||||
* to be flushed.
|
||||
* It causes all neighbors connected to this interface to be updated or removed.
|
||||
*/
|
||||
void
|
||||
neigh_if_down(struct iface *i)
|
||||
@ -369,16 +388,15 @@ neigh_if_down(struct iface *i)
|
||||
node *x, *y;
|
||||
|
||||
WALK_LIST2_DELSAFE(n, x, y, i->neighbors, if_n)
|
||||
neigh_down(n);
|
||||
neigh_update(n, i);
|
||||
}
|
||||
|
||||
/**
|
||||
* neigh_if_link - notify neighbor cache about interface link change
|
||||
* @i: the interface in question
|
||||
*
|
||||
* Notify the neighbor cache that an interface changed link state.
|
||||
* All owners of neighbor entries connected to this interface are
|
||||
* notified.
|
||||
* Notify the neighbor cache that an interface changed link state. All owners of
|
||||
* neighbor entries connected to this interface are notified.
|
||||
*/
|
||||
void
|
||||
neigh_if_link(struct iface *i)
|
||||
@ -387,8 +405,7 @@ neigh_if_link(struct iface *i)
|
||||
node *x, *y;
|
||||
|
||||
WALK_LIST2_DELSAFE(n, x, y, i->neighbors, if_n)
|
||||
if (n->proto->neigh_notify && (n->proto->proto_state != PS_STOP))
|
||||
n->proto->neigh_notify(n);
|
||||
neigh_notify(n);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -405,21 +422,16 @@ void
|
||||
neigh_ifa_update(struct ifa *a)
|
||||
{
|
||||
struct iface *i = a->iface;
|
||||
struct ifa *aa;
|
||||
node *x, *y;
|
||||
neighbor *n;
|
||||
int scope;
|
||||
node *x, *y;
|
||||
|
||||
/* Remove all neighbors whose scope has changed */
|
||||
/* Update all neighbors whose scope has changed */
|
||||
WALK_LIST2_DELSAFE(n, x, y, i->neighbors, if_n)
|
||||
if (n->ifa && (if_connected(&n->addr, i, &aa) != n->scope))
|
||||
neigh_down(n);
|
||||
neigh_update(n, i);
|
||||
|
||||
/* Wake up all sticky neighbors that are reachable now */
|
||||
WALK_LIST_DELSAFE(n, x, sticky_neigh_list)
|
||||
if ((!n->iface || n->iface == i) &&
|
||||
((scope = if_connected(&n->addr, i, &aa)) >= 0))
|
||||
neigh_up(n, i, scope, aa);
|
||||
WALK_LIST2_DELSAFE(n, x, y, sticky_neigh_list, if_n)
|
||||
neigh_update(n, i);
|
||||
}
|
||||
|
||||
static inline void
|
||||
@ -427,10 +439,8 @@ neigh_prune_one(neighbor *n)
|
||||
{
|
||||
if (n->proto->proto_state != PS_DOWN)
|
||||
return;
|
||||
rem_node(&n->n);
|
||||
if (n->if_n.next)
|
||||
rem_node(&n->if_n);
|
||||
sl_free(neigh_slab, n);
|
||||
|
||||
neigh_free(n);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -451,10 +461,6 @@ neigh_prune(void)
|
||||
for(i=0; i<NEIGH_HASH_SIZE; i++)
|
||||
WALK_LIST_DELSAFE(n, m, neigh_hash_table[i])
|
||||
neigh_prune_one(n);
|
||||
WALK_LIST_DELSAFE(n, m, sticky_neigh_list)
|
||||
neigh_prune_one(n);
|
||||
WALK_LIST_DELSAFE(n, m, iface_neigh_list)
|
||||
neigh_prune_one(n);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -469,9 +475,8 @@ neigh_init(pool *if_pool)
|
||||
{
|
||||
neigh_slab = sl_new(if_pool, sizeof(neighbor));
|
||||
|
||||
init_list(&sticky_neigh_list);
|
||||
init_list(&iface_neigh_list);
|
||||
|
||||
for(int i = 0; i < NEIGH_HASH_SIZE; i++)
|
||||
init_list(&neigh_hash_table[i]);
|
||||
|
||||
init_list(&sticky_neigh_list);
|
||||
}
|
||||
|
@ -10,6 +10,7 @@
|
||||
#include "nest/bird.h"
|
||||
#include "nest/password.h"
|
||||
#include "lib/string.h"
|
||||
#include "lib/timer.h"
|
||||
#include "lib/mac.h"
|
||||
|
||||
struct password_item *last_password_item = NULL;
|
||||
@ -19,12 +20,13 @@ password_find(list *l, int first_fit)
|
||||
{
|
||||
struct password_item *pi;
|
||||
struct password_item *pf = NULL;
|
||||
btime now_ = current_real_time();
|
||||
|
||||
if (l)
|
||||
{
|
||||
WALK_LIST(pi, *l)
|
||||
{
|
||||
if ((pi->genfrom < now_real) && (pi->gento > now_real))
|
||||
if ((pi->genfrom < now_) && (pi->gento > now_))
|
||||
{
|
||||
if (first_fit)
|
||||
return pi;
|
||||
@ -41,12 +43,13 @@ struct password_item *
|
||||
password_find_by_id(list *l, uint id)
|
||||
{
|
||||
struct password_item *pi;
|
||||
btime now_ = current_real_time();
|
||||
|
||||
if (!l)
|
||||
return NULL;
|
||||
|
||||
WALK_LIST(pi, *l)
|
||||
if ((pi->id == id) && (pi->accfrom <= now_real) && (now_real < pi->accto))
|
||||
if ((pi->id == id) && (pi->accfrom <= now_) && (now_ < pi->accto))
|
||||
return pi;
|
||||
|
||||
return NULL;
|
||||
@ -56,12 +59,13 @@ struct password_item *
|
||||
password_find_by_value(list *l, char *pass, uint size)
|
||||
{
|
||||
struct password_item *pi;
|
||||
btime now_ = current_real_time();
|
||||
|
||||
if (!l)
|
||||
return NULL;
|
||||
|
||||
WALK_LIST(pi, *l)
|
||||
if (password_verify(pi, pass, size) && (pi->accfrom <= now_real) && (now_real < pi->accto))
|
||||
if (password_verify(pi, pass, size) && (pi->accfrom <= now_) && (now_ < pi->accto))
|
||||
return pi;
|
||||
|
||||
return NULL;
|
||||
|
@ -10,15 +10,13 @@
|
||||
#ifndef PASSWORD_H
|
||||
#define PASSWORD_H
|
||||
|
||||
#include "sysdep/unix/timer.h"
|
||||
|
||||
struct password_item {
|
||||
node n;
|
||||
char *password; /* Key data, null terminated */
|
||||
uint length; /* Key length, without null */
|
||||
uint id; /* Key ID */
|
||||
uint alg; /* MAC algorithm */
|
||||
bird_clock_t accfrom, accto, genfrom, gento;
|
||||
btime accfrom, accto, genfrom, gento;
|
||||
};
|
||||
|
||||
extern struct password_item *last_password_item;
|
||||
|
145
nest/proto.c
145
nest/proto.c
@ -13,6 +13,7 @@
|
||||
#include "lib/resource.h"
|
||||
#include "lib/lists.h"
|
||||
#include "lib/event.h"
|
||||
#include "lib/timer.h"
|
||||
#include "lib/string.h"
|
||||
#include "conf/conf.h"
|
||||
#include "nest/route.h"
|
||||
@ -24,6 +25,7 @@ pool *proto_pool;
|
||||
list proto_list;
|
||||
|
||||
static list protocol_list;
|
||||
struct protocol *class_to_protocol[PROTOCOL__MAX];
|
||||
|
||||
#define PD(pr, msg, args...) do { if (pr->debug & D_STATES) { log(L_TRACE "%s: " msg, pr->name , ## args); } } while(0)
|
||||
|
||||
@ -43,7 +45,7 @@ static char *c_states[] = { "DOWN", "START", "UP", "FLUSHING" };
|
||||
|
||||
extern struct protocol proto_unix_iface;
|
||||
|
||||
static void proto_shutdown_loop(struct timer *);
|
||||
static void proto_shutdown_loop(timer *);
|
||||
static void proto_rethink_goal(struct proto *p);
|
||||
static char *proto_state_name(struct proto *p);
|
||||
static void channel_verify_limits(struct channel *c);
|
||||
@ -162,7 +164,8 @@ proto_add_channel(struct proto *p, struct channel_config *cf)
|
||||
|
||||
c->channel_state = CS_DOWN;
|
||||
c->export_state = ES_DOWN;
|
||||
c->last_state_change = now;
|
||||
c->last_state_change = current_time();
|
||||
c->last_tx_filter_change = current_time();
|
||||
c->reloadable = 1;
|
||||
|
||||
CALL(c->channel->init, c, cf);
|
||||
@ -341,7 +344,7 @@ channel_set_state(struct channel *c, uint state)
|
||||
return;
|
||||
|
||||
c->channel_state = state;
|
||||
c->last_state_change = now;
|
||||
c->last_state_change = current_time();
|
||||
|
||||
switch (state)
|
||||
{
|
||||
@ -436,7 +439,7 @@ static void
|
||||
channel_request_reload(struct channel *c)
|
||||
{
|
||||
ASSERT(c->channel_state == CS_UP);
|
||||
// ASSERT(channel_reloadable(c));
|
||||
ASSERT(channel_reloadable(c));
|
||||
|
||||
c->proto->reload_routes(c);
|
||||
|
||||
@ -454,11 +457,10 @@ const struct channel_class channel_basic = {
|
||||
};
|
||||
|
||||
void *
|
||||
channel_config_new(const struct channel_class *cc, uint net_type, struct proto_config *proto)
|
||||
channel_config_new(const struct channel_class *cc, const char *name, uint net_type, struct proto_config *proto)
|
||||
{
|
||||
struct channel_config *cf = NULL;
|
||||
struct rtable_config *tab = NULL;
|
||||
const char *name = NULL;
|
||||
|
||||
if (net_type)
|
||||
{
|
||||
@ -469,7 +471,6 @@ channel_config_new(const struct channel_class *cc, uint net_type, struct proto_c
|
||||
cf_error("Different channel type");
|
||||
|
||||
tab = new_config->def_tables[net_type];
|
||||
name = net_label[net_type];
|
||||
}
|
||||
|
||||
if (!cc)
|
||||
@ -478,6 +479,7 @@ channel_config_new(const struct channel_class *cc, uint net_type, struct proto_c
|
||||
cf = cfg_allocz(cc->config_size);
|
||||
cf->name = name;
|
||||
cf->channel = cc;
|
||||
cf->parent = proto;
|
||||
cf->table = tab;
|
||||
cf->out_filter = FILTER_REJECT;
|
||||
|
||||
@ -490,6 +492,26 @@ channel_config_new(const struct channel_class *cc, uint net_type, struct proto_c
|
||||
return cf;
|
||||
}
|
||||
|
||||
void *
|
||||
channel_config_get(const struct channel_class *cc, const char *name, uint net_type, struct proto_config *proto)
|
||||
{
|
||||
struct channel_config *cf;
|
||||
|
||||
/* We are using name as token, so no strcmp() */
|
||||
WALK_LIST(cf, proto->channels)
|
||||
if (cf->name == name)
|
||||
{
|
||||
/* Allow to redefine channel only if inherited from template */
|
||||
if (cf->parent == proto)
|
||||
cf_error("Multiple %s channels", name);
|
||||
|
||||
cf->parent = proto;
|
||||
return cf;
|
||||
}
|
||||
|
||||
return channel_config_new(cc, name, net_type, proto);
|
||||
}
|
||||
|
||||
struct channel_config *
|
||||
channel_copy_config(struct channel_config *src, struct proto_config *proto)
|
||||
{
|
||||
@ -512,8 +534,9 @@ channel_reconfigure(struct channel *c, struct channel_config *cf)
|
||||
if ((c->table != cf->table->table) || (cf->ra_mode && (c->ra_mode != cf->ra_mode)))
|
||||
return 0;
|
||||
|
||||
int import_changed = !filter_same(c->in_filter, cf->in_filter);
|
||||
int export_changed = !filter_same(c->out_filter, cf->out_filter);
|
||||
/* Note that filter_same() requires arguments in (new, old) order */
|
||||
int import_changed = !filter_same(cf->in_filter, c->in_filter);
|
||||
int export_changed = !filter_same(cf->out_filter, c->out_filter);
|
||||
|
||||
if (c->preference != cf->preference)
|
||||
import_changed = 1;
|
||||
@ -535,6 +558,9 @@ channel_reconfigure(struct channel *c, struct channel_config *cf)
|
||||
|
||||
channel_verify_limits(c);
|
||||
|
||||
if (export_changed)
|
||||
c->last_tx_filter_change = current_time();
|
||||
|
||||
/* Execute channel-specific reconfigure hook */
|
||||
if (c->channel->reconfigure && !c->channel->reconfigure(c, cf))
|
||||
return 0;
|
||||
@ -672,7 +698,8 @@ proto_init(struct proto_config *c, node *n)
|
||||
struct proto *p = pr->init(c);
|
||||
|
||||
p->proto_state = PS_DOWN;
|
||||
p->last_state_change = now;
|
||||
p->last_state_change = current_time();
|
||||
p->vrf = c->vrf;
|
||||
insert_node(&p->n, n);
|
||||
|
||||
p->event = ev_new(proto_pool);
|
||||
@ -819,7 +846,8 @@ proto_reconfigure(struct proto *p, struct proto_config *oc, struct proto_config
|
||||
/* If there is a too big change in core attributes, ... */
|
||||
if ((nc->protocol != oc->protocol) ||
|
||||
(nc->net_type != oc->net_type) ||
|
||||
(nc->disabled != p->disabled))
|
||||
(nc->disabled != p->disabled) ||
|
||||
(nc->vrf != oc->vrf))
|
||||
return 0;
|
||||
|
||||
p->name = nc->name;
|
||||
@ -977,6 +1005,7 @@ proto_rethink_goal(struct proto *p)
|
||||
proto_remove_channels(p);
|
||||
rem_node(&p->n);
|
||||
rfree(p->event);
|
||||
mb_free(p->message);
|
||||
mb_free(p);
|
||||
if (!nc)
|
||||
return;
|
||||
@ -1046,7 +1075,7 @@ proto_rethink_goal(struct proto *p)
|
||||
*
|
||||
*/
|
||||
|
||||
static void graceful_restart_done(struct timer *t);
|
||||
static void graceful_restart_done(timer *t);
|
||||
|
||||
/**
|
||||
* graceful_restart_recovery - request initial graceful restart recovery
|
||||
@ -1083,9 +1112,8 @@ graceful_restart_init(void)
|
||||
}
|
||||
|
||||
graceful_restart_state = GRS_ACTIVE;
|
||||
gr_wait_timer = tm_new(proto_pool);
|
||||
gr_wait_timer->hook = graceful_restart_done;
|
||||
tm_start(gr_wait_timer, config->gr_wait);
|
||||
gr_wait_timer = tm_new_init(proto_pool, graceful_restart_done, NULL, 0, 0);
|
||||
tm_start(gr_wait_timer, config->gr_wait S);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -1099,7 +1127,7 @@ graceful_restart_init(void)
|
||||
* restart wait timer fires (but there are still some locks).
|
||||
*/
|
||||
static void
|
||||
graceful_restart_done(struct timer *t UNUSED)
|
||||
graceful_restart_done(timer *t UNUSED)
|
||||
{
|
||||
log(L_INFO "Graceful restart done");
|
||||
graceful_restart_state = GRS_DONE;
|
||||
@ -1136,7 +1164,7 @@ graceful_restart_show_status(void)
|
||||
|
||||
cli_msg(-24, "Graceful restart recovery in progress");
|
||||
cli_msg(-24, " Waiting for %d channels to recover", graceful_restart_locks);
|
||||
cli_msg(-24, " Wait timer is %d/%d", tm_remains(gr_wait_timer), config->gr_wait);
|
||||
cli_msg(-24, " Wait timer is %t/%u", tm_remains(gr_wait_timer), config->gr_wait);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -1233,11 +1261,9 @@ void
|
||||
proto_build(struct protocol *p)
|
||||
{
|
||||
add_tail(&protocol_list, &p->n);
|
||||
if (p->attr_class)
|
||||
{
|
||||
ASSERT(!attr_class_to_protocol[p->attr_class]);
|
||||
attr_class_to_protocol[p->attr_class] = p;
|
||||
}
|
||||
ASSERT(p->class);
|
||||
ASSERT(!class_to_protocol[p->class]);
|
||||
class_to_protocol[p->class] = p;
|
||||
}
|
||||
|
||||
/* FIXME: convert this call to some protocol hook */
|
||||
@ -1298,7 +1324,7 @@ protos_build(void)
|
||||
int proto_restart;
|
||||
|
||||
static void
|
||||
proto_shutdown_loop(struct timer *t UNUSED)
|
||||
proto_shutdown_loop(timer *t UNUSED)
|
||||
{
|
||||
struct proto *p, *p_next;
|
||||
|
||||
@ -1329,7 +1355,40 @@ proto_schedule_down(struct proto *p, byte restart, byte code)
|
||||
|
||||
p->down_sched = restart ? PDS_RESTART : PDS_DISABLE;
|
||||
p->down_code = code;
|
||||
tm_start_max(proto_shutdown_timer, restart ? 2 : 0);
|
||||
tm_start_max(proto_shutdown_timer, restart ? 250 MS : 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* proto_set_message - set administrative message to protocol
|
||||
* @p: protocol
|
||||
* @msg: message
|
||||
* @len: message length (-1 for NULL-terminated string)
|
||||
*
|
||||
* The function sets administrative message (string) related to protocol state
|
||||
* change. It is called by the nest code for manual enable/disable/restart
|
||||
* commands all routes to the protocol, and by protocol-specific code when the
|
||||
* protocol state change is initiated by the protocol. Using NULL message clears
|
||||
* the last message. The message string may be either NULL-terminated or with an
|
||||
* explicit length.
|
||||
*/
|
||||
void
|
||||
proto_set_message(struct proto *p, char *msg, int len)
|
||||
{
|
||||
mb_free(p->message);
|
||||
p->message = NULL;
|
||||
|
||||
if (!msg || !len)
|
||||
return;
|
||||
|
||||
if (len < 0)
|
||||
len = strlen(msg);
|
||||
|
||||
if (!len)
|
||||
return;
|
||||
|
||||
p->message = mb_alloc(proto_pool, len + 1);
|
||||
memcpy(p->message, msg, len);
|
||||
p->message[len] = 0;
|
||||
}
|
||||
|
||||
|
||||
@ -1500,7 +1559,7 @@ proto_notify_state(struct proto *p, uint state)
|
||||
return;
|
||||
|
||||
p->proto_state = state;
|
||||
p->last_state_change = now;
|
||||
p->last_state_change = current_time();
|
||||
|
||||
switch (state)
|
||||
{
|
||||
@ -1620,19 +1679,20 @@ channel_show_info(struct channel *c)
|
||||
}
|
||||
|
||||
void
|
||||
proto_cmd_show(struct proto *p, uint verbose, int cnt)
|
||||
proto_cmd_show(struct proto *p, uintptr_t verbose, int cnt)
|
||||
{
|
||||
byte buf[256], tbuf[TM_DATETIME_BUFFER_SIZE];
|
||||
|
||||
/* First protocol - show header */
|
||||
if (!cnt)
|
||||
cli_msg(-2002, "name proto table state since info");
|
||||
cli_msg(-2002, "%-10s %-10s %-10s %-6s %-12s %s",
|
||||
"Name", "Proto", "Table", "State", "Since", "Info");
|
||||
|
||||
buf[0] = 0;
|
||||
if (p->proto->get_status)
|
||||
p->proto->get_status(p, buf);
|
||||
tm_format_datetime(tbuf, &config->tf_proto, p->last_state_change);
|
||||
cli_msg(-1002, "%-8s %-8s %-8s %-5s %-10s %s",
|
||||
tm_format_time(tbuf, &config->tf_proto, p->last_state_change);
|
||||
cli_msg(-1002, "%-10s %-10s %-10s %-6s %-12s %s",
|
||||
p->name,
|
||||
p->proto->name,
|
||||
p->main_channel ? p->main_channel->table->name : "---",
|
||||
@ -1644,8 +1704,12 @@ proto_cmd_show(struct proto *p, uint verbose, int cnt)
|
||||
{
|
||||
if (p->cf->dsc)
|
||||
cli_msg(-1006, " Description: %s", p->cf->dsc);
|
||||
if (p->message)
|
||||
cli_msg(-1006, " Message: %s", p->message);
|
||||
if (p->cf->router_id)
|
||||
cli_msg(-1006, " Router ID: %R", p->cf->router_id);
|
||||
if (p->vrf)
|
||||
cli_msg(-1006, " VRF: %s", p->vrf->name);
|
||||
|
||||
if (p->proto->show_proto_info)
|
||||
p->proto->show_proto_info(p);
|
||||
@ -1661,7 +1725,7 @@ proto_cmd_show(struct proto *p, uint verbose, int cnt)
|
||||
}
|
||||
|
||||
void
|
||||
proto_cmd_disable(struct proto *p, uint arg UNUSED, int cnt UNUSED)
|
||||
proto_cmd_disable(struct proto *p, uintptr_t arg, int cnt UNUSED)
|
||||
{
|
||||
if (p->disabled)
|
||||
{
|
||||
@ -1672,12 +1736,13 @@ proto_cmd_disable(struct proto *p, uint arg UNUSED, int cnt UNUSED)
|
||||
log(L_INFO "Disabling protocol %s", p->name);
|
||||
p->disabled = 1;
|
||||
p->down_code = PDC_CMD_DISABLE;
|
||||
proto_set_message(p, (char *) arg, -1);
|
||||
proto_rethink_goal(p);
|
||||
cli_msg(-9, "%s: disabled", p->name);
|
||||
}
|
||||
|
||||
void
|
||||
proto_cmd_enable(struct proto *p, uint arg UNUSED, int cnt UNUSED)
|
||||
proto_cmd_enable(struct proto *p, uintptr_t arg, int cnt UNUSED)
|
||||
{
|
||||
if (!p->disabled)
|
||||
{
|
||||
@ -1687,12 +1752,13 @@ proto_cmd_enable(struct proto *p, uint arg UNUSED, int cnt UNUSED)
|
||||
|
||||
log(L_INFO "Enabling protocol %s", p->name);
|
||||
p->disabled = 0;
|
||||
proto_set_message(p, (char *) arg, -1);
|
||||
proto_rethink_goal(p);
|
||||
cli_msg(-11, "%s: enabled", p->name);
|
||||
}
|
||||
|
||||
void
|
||||
proto_cmd_restart(struct proto *p, uint arg UNUSED, int cnt UNUSED)
|
||||
proto_cmd_restart(struct proto *p, uintptr_t arg, int cnt UNUSED)
|
||||
{
|
||||
if (p->disabled)
|
||||
{
|
||||
@ -1703,6 +1769,7 @@ proto_cmd_restart(struct proto *p, uint arg UNUSED, int cnt UNUSED)
|
||||
log(L_INFO "Restarting protocol %s", p->name);
|
||||
p->disabled = 1;
|
||||
p->down_code = PDC_CMD_RESTART;
|
||||
proto_set_message(p, (char *) arg, -1);
|
||||
proto_rethink_goal(p);
|
||||
p->disabled = 0;
|
||||
proto_rethink_goal(p);
|
||||
@ -1710,7 +1777,7 @@ proto_cmd_restart(struct proto *p, uint arg UNUSED, int cnt UNUSED)
|
||||
}
|
||||
|
||||
void
|
||||
proto_cmd_reload(struct proto *p, uint dir, int cnt UNUSED)
|
||||
proto_cmd_reload(struct proto *p, uintptr_t dir, int cnt UNUSED)
|
||||
{
|
||||
struct channel *c;
|
||||
|
||||
@ -1749,19 +1816,19 @@ proto_cmd_reload(struct proto *p, uint dir, int cnt UNUSED)
|
||||
}
|
||||
|
||||
void
|
||||
proto_cmd_debug(struct proto *p, uint mask, int cnt UNUSED)
|
||||
proto_cmd_debug(struct proto *p, uintptr_t mask, int cnt UNUSED)
|
||||
{
|
||||
p->debug = mask;
|
||||
}
|
||||
|
||||
void
|
||||
proto_cmd_mrtdump(struct proto *p, uint mask, int cnt UNUSED)
|
||||
proto_cmd_mrtdump(struct proto *p, uintptr_t mask, int cnt UNUSED)
|
||||
{
|
||||
p->mrtdump = mask;
|
||||
}
|
||||
|
||||
static void
|
||||
proto_apply_cmd_symbol(struct symbol *s, void (* cmd)(struct proto *, uint, int), uint arg)
|
||||
proto_apply_cmd_symbol(struct symbol *s, void (* cmd)(struct proto *, uintptr_t, int), uintptr_t arg)
|
||||
{
|
||||
if (s->class != SYM_PROTO)
|
||||
{
|
||||
@ -1774,7 +1841,7 @@ proto_apply_cmd_symbol(struct symbol *s, void (* cmd)(struct proto *, uint, int)
|
||||
}
|
||||
|
||||
static void
|
||||
proto_apply_cmd_patt(char *patt, void (* cmd)(struct proto *, uint, int), uint arg)
|
||||
proto_apply_cmd_patt(char *patt, void (* cmd)(struct proto *, uintptr_t, int), uintptr_t arg)
|
||||
{
|
||||
struct proto *p;
|
||||
int cnt = 0;
|
||||
@ -1790,8 +1857,8 @@ proto_apply_cmd_patt(char *patt, void (* cmd)(struct proto *, uint, int), uint a
|
||||
}
|
||||
|
||||
void
|
||||
proto_apply_cmd(struct proto_spec ps, void (* cmd)(struct proto *, uint, int),
|
||||
int restricted, uint arg)
|
||||
proto_apply_cmd(struct proto_spec ps, void (* cmd)(struct proto *, uintptr_t, int),
|
||||
int restricted, uintptr_t arg)
|
||||
{
|
||||
if (restricted && cli_access_restricted())
|
||||
return;
|
||||
|
@ -12,7 +12,6 @@
|
||||
#include "lib/lists.h"
|
||||
#include "lib/resource.h"
|
||||
#include "lib/event.h"
|
||||
#include "sysdep/unix/timer.h"
|
||||
#include "nest/route.h"
|
||||
#include "conf/conf.h"
|
||||
|
||||
@ -38,12 +37,31 @@ struct symbol;
|
||||
* Routing Protocol
|
||||
*/
|
||||
|
||||
enum protocol_class {
|
||||
PROTOCOL_NONE,
|
||||
PROTOCOL_BABEL,
|
||||
PROTOCOL_BFD,
|
||||
PROTOCOL_BGP,
|
||||
PROTOCOL_DEVICE,
|
||||
PROTOCOL_DIRECT,
|
||||
PROTOCOL_KERNEL,
|
||||
PROTOCOL_OSPF,
|
||||
PROTOCOL_PIPE,
|
||||
PROTOCOL_RADV,
|
||||
PROTOCOL_RIP,
|
||||
PROTOCOL_RPKI,
|
||||
PROTOCOL_STATIC,
|
||||
PROTOCOL__MAX
|
||||
};
|
||||
|
||||
extern struct protocol *class_to_protocol[PROTOCOL__MAX];
|
||||
|
||||
struct protocol {
|
||||
node n;
|
||||
char *name;
|
||||
char *template; /* Template for automatic generation of names */
|
||||
int name_counter; /* Counter for automatic name generation */
|
||||
int attr_class; /* Attribute class known to this protocol */
|
||||
enum protocol_class class; /* Machine readable protocol class */
|
||||
uint preference; /* Default protocol preference */
|
||||
uint channel_mask; /* Mask of accepted channel types (NB_*) */
|
||||
uint proto_size; /* Size of protocol data structure */
|
||||
@ -59,7 +77,7 @@ struct protocol {
|
||||
int (*shutdown)(struct proto *); /* Stop the instance */
|
||||
void (*cleanup)(struct proto *); /* Called after shutdown when protocol became hungry/down */
|
||||
void (*get_status)(struct proto *, byte *buf); /* Get instance status (for `show protocols' command) */
|
||||
void (*get_route_info)(struct rte *, byte *buf, struct ea_list *attrs); /* Get route information (for `show route' command) */
|
||||
void (*get_route_info)(struct rte *, byte *buf); /* Get route information (for `show route' command) */
|
||||
int (*get_attr)(struct eattr *, byte *buf, int buflen); /* ASCIIfy dynamic attribute (returns GA_*) */
|
||||
void (*show_proto_info)(struct proto *); /* Show protocol info (for `show protocols all' command) */
|
||||
void (*copy_config)(struct proto_config *, struct proto_config *); /* Copy config from given protocol instance */
|
||||
@ -101,6 +119,7 @@ struct proto_config {
|
||||
u32 router_id; /* Protocol specific router ID */
|
||||
|
||||
list channels; /* List of channel configs (struct channel_config) */
|
||||
struct iface *vrf; /* Related VRF instance, NULL if global */
|
||||
|
||||
/* Check proto_reconfigure() and proto_copy_config() after changing struct proto_config */
|
||||
|
||||
@ -143,6 +162,7 @@ struct proto {
|
||||
list channels; /* List of channels to rtables (struct channel) */
|
||||
struct channel *main_channel; /* Primary channel */
|
||||
struct rte_src *main_source; /* Primary route source */
|
||||
struct iface *vrf; /* Related VRF instance, NULL if global */
|
||||
|
||||
char *name; /* Name of this instance (== cf->name) */
|
||||
u32 debug; /* Debugging flags */
|
||||
@ -159,8 +179,9 @@ struct proto {
|
||||
byte down_sched; /* Shutdown is scheduled for later (PDS_*) */
|
||||
byte down_code; /* Reason for shutdown (PDC_* codes) */
|
||||
u32 hash_key; /* Random key used for hashing of neighbors */
|
||||
bird_clock_t last_state_change; /* Time of last state transition */
|
||||
btime last_state_change; /* Time of last state transition */
|
||||
char *last_state_name_announced; /* Last state name we've announced to the user */
|
||||
char *message; /* State-change message, allocated from proto_pool */
|
||||
|
||||
/*
|
||||
* General protocol hooks:
|
||||
@ -170,7 +191,7 @@ struct proto {
|
||||
* rt_notify Notify protocol about routing table updates.
|
||||
* neigh_notify Notify protocol about neighbor cache events.
|
||||
* make_tmp_attrs Construct ea_list from private attrs stored in rte.
|
||||
* store_tmp_attrs Store private attrs back to the rte.
|
||||
* store_tmp_attrs Store private attrs back to rta. The route MUST NOT be cached.
|
||||
* import_control Called as the first step of the route importing process.
|
||||
* It can construct a new rte, add private attributes and
|
||||
* decide whether the route shall be imported: 1=yes, -1=no,
|
||||
@ -184,11 +205,11 @@ struct proto {
|
||||
|
||||
void (*if_notify)(struct proto *, unsigned flags, struct iface *i);
|
||||
void (*ifa_notify)(struct proto *, unsigned flags, struct ifa *a);
|
||||
void (*rt_notify)(struct proto *, struct channel *, struct network *net, struct rte *new, struct rte *old, struct ea_list *attrs);
|
||||
void (*rt_notify)(struct proto *, struct channel *, struct network *net, struct rte *new, struct rte *old);
|
||||
void (*neigh_notify)(struct neighbor *neigh);
|
||||
struct ea_list *(*make_tmp_attrs)(struct rte *rt, struct linpool *pool);
|
||||
void (*store_tmp_attrs)(struct rte *rt, struct ea_list *attrs);
|
||||
int (*import_control)(struct proto *, struct rte **rt, struct ea_list **attrs, struct linpool *pool);
|
||||
void (*store_tmp_attrs)(struct rte *rt);
|
||||
int (*import_control)(struct proto *, struct rte **rt, struct linpool *pool);
|
||||
void (*reload_routes)(struct channel *);
|
||||
void (*feed_begin)(struct channel *, int initial);
|
||||
void (*feed_end)(struct channel *);
|
||||
@ -208,6 +229,7 @@ struct proto {
|
||||
int (*rte_better)(struct rte *, struct rte *);
|
||||
int (*rte_same)(struct rte *, struct rte *);
|
||||
int (*rte_mergable)(struct rte *, struct rte *);
|
||||
struct rte * (*rte_modify)(struct rte *, struct linpool *);
|
||||
void (*rte_insert)(struct network *, struct rte *);
|
||||
void (*rte_remove)(struct network *, struct rte *);
|
||||
|
||||
@ -237,6 +259,7 @@ struct proto_spec {
|
||||
void *proto_new(struct proto_config *);
|
||||
void *proto_config_new(struct protocol *, int class);
|
||||
void proto_copy_config(struct proto_config *dest, struct proto_config *src);
|
||||
void proto_set_message(struct proto *p, char *msg, int len);
|
||||
|
||||
void graceful_restart_recovery(void);
|
||||
void graceful_restart_init(void);
|
||||
@ -249,15 +272,15 @@ void channel_graceful_restart_unlock(struct channel *c);
|
||||
void channel_show_limit(struct channel_limit *l, const char *dsc);
|
||||
void channel_show_info(struct channel *c);
|
||||
|
||||
void proto_cmd_show(struct proto *, uint, int);
|
||||
void proto_cmd_disable(struct proto *, uint, int);
|
||||
void proto_cmd_enable(struct proto *, uint, int);
|
||||
void proto_cmd_restart(struct proto *, uint, int);
|
||||
void proto_cmd_reload(struct proto *, uint, int);
|
||||
void proto_cmd_debug(struct proto *, uint, int);
|
||||
void proto_cmd_mrtdump(struct proto *, uint, int);
|
||||
void proto_cmd_show(struct proto *, uintptr_t, int);
|
||||
void proto_cmd_disable(struct proto *, uintptr_t, int);
|
||||
void proto_cmd_enable(struct proto *, uintptr_t, int);
|
||||
void proto_cmd_restart(struct proto *, uintptr_t, int);
|
||||
void proto_cmd_reload(struct proto *, uintptr_t, int);
|
||||
void proto_cmd_debug(struct proto *, uintptr_t, int);
|
||||
void proto_cmd_mrtdump(struct proto *, uintptr_t, int);
|
||||
|
||||
void proto_apply_cmd(struct proto_spec ps, void (* cmd)(struct proto *, uint, int), int restricted, uint arg);
|
||||
void proto_apply_cmd(struct proto_spec ps, void (* cmd)(struct proto *, uintptr_t, int), int restricted, uintptr_t arg);
|
||||
struct proto *proto_get_named(struct symbol *, struct protocol *);
|
||||
|
||||
#define CMD_RELOAD 0
|
||||
@ -270,12 +293,16 @@ proto_get_router_id(struct proto_config *pc)
|
||||
return pc->router_id ? pc->router_id : pc->global->router_id;
|
||||
}
|
||||
|
||||
static inline struct ea_list *
|
||||
rte_make_tmp_attrs(struct rte *rt, struct linpool *pool)
|
||||
static inline void
|
||||
rte_make_tmp_attrs(struct rte **rt, struct linpool *pool)
|
||||
{
|
||||
struct ea_list *(*mta)(struct rte *rt, struct linpool *pool);
|
||||
mta = rt->attrs->src->proto->make_tmp_attrs;
|
||||
return mta ? mta(rt, pool) : NULL;
|
||||
mta = (*rt)->attrs->src->proto->make_tmp_attrs;
|
||||
if (!mta) return;
|
||||
*rt = rte_cow_rta(*rt, pool);
|
||||
struct ea_list *ea = mta(*rt, pool);
|
||||
ea->next = (*rt)->attrs->eattrs;
|
||||
(*rt)->attrs->eattrs = ea;
|
||||
}
|
||||
|
||||
/* Moved from route.h to avoid dependency conflicts */
|
||||
@ -334,7 +361,7 @@ void proto_notify_state(struct proto *p, unsigned state);
|
||||
*
|
||||
* HUNGRY ----> FEEDING
|
||||
* ^ |
|
||||
* | V
|
||||
* | V
|
||||
* FLUSHING <---- HAPPY
|
||||
*
|
||||
* States: HUNGRY Protocol either administratively down (i.e.,
|
||||
@ -444,7 +471,7 @@ struct channel_class {
|
||||
void (*dump_attrs)(struct rte *); /* Dump protocol-dependent attributes */
|
||||
|
||||
void (*get_status)(struct proto *, byte *buf); /* Get instance status (for `show protocols' command) */
|
||||
void (*get_route_info)(struct rte *, byte *buf, struct ea_list *attrs); /* Get route information (for `show route' command) */
|
||||
void (*get_route_info)(struct rte *, byte *buf); /* Get route information (for `show route' command) */
|
||||
int (*get_attr)(struct eattr *, byte *buf, int buflen); /* ASCIIfy dynamic attribute (returns GA_*) */
|
||||
void (*show_proto_info)(struct proto *); /* Show protocol info (for `show protocols all' command) */
|
||||
|
||||
@ -458,6 +485,7 @@ struct channel_config {
|
||||
const char *name;
|
||||
const struct channel_class *channel;
|
||||
|
||||
struct proto_config *parent; /* Where channel is defined (proto or template) */
|
||||
struct rtable_config *table; /* Table we're attached to */
|
||||
struct filter *in_filter, *out_filter; /* Attached filters */
|
||||
struct channel_limit rx_limit; /* Limit for receiving routes from protocol
|
||||
@ -508,7 +536,8 @@ struct channel {
|
||||
u8 gr_lock; /* Graceful restart mechanism should wait for this channel */
|
||||
u8 gr_wait; /* Route export to channel is postponed until graceful restart */
|
||||
|
||||
bird_clock_t last_state_change; /* Time of last state transition */
|
||||
btime last_state_change; /* Time of last state transition */
|
||||
btime last_tx_filter_change;
|
||||
};
|
||||
|
||||
|
||||
@ -582,7 +611,8 @@ static inline void channel_open(struct channel *c) { channel_set_state(c, CS_UP)
|
||||
static inline void channel_close(struct channel *c) { channel_set_state(c, CS_FLUSHING); }
|
||||
|
||||
void channel_request_feeding(struct channel *c);
|
||||
void *channel_config_new(const struct channel_class *cc, uint net_type, struct proto_config *proto);
|
||||
void *channel_config_new(const struct channel_class *cc, const char *name, uint net_type, struct proto_config *proto);
|
||||
void *channel_config_get(const struct channel_class *cc, const char *name, uint net_type, struct proto_config *proto);
|
||||
int channel_reconfigure(struct channel *c, struct channel_config *cf);
|
||||
|
||||
|
||||
|
87
nest/route.h
87
nest/route.h
@ -11,7 +11,6 @@
|
||||
|
||||
#include "lib/lists.h"
|
||||
#include "lib/resource.h"
|
||||
#include "sysdep/unix/timer.h"
|
||||
#include "lib/net.h"
|
||||
|
||||
struct ea_list;
|
||||
@ -75,7 +74,7 @@ static inline struct fib_node * fib_user_to_node(struct fib *f, void *e)
|
||||
void fib_init(struct fib *f, pool *p, uint addr_type, uint node_size, uint node_offset, uint hash_order, fib_init_fn init);
|
||||
void *fib_find(struct fib *, const net_addr *); /* Find or return NULL if doesn't exist */
|
||||
void *fib_get_chain(struct fib *f, const net_addr *a); /* Find first node in linked list from hash table */
|
||||
void *fib_get(struct fib *, const net_addr *); /* Find or create new if nonexistent */
|
||||
void *fib_get(struct fib *, const net_addr *); /* Find or create new if nonexistent */
|
||||
void *fib_route(struct fib *, const net_addr *); /* Longest-match routing lookup */
|
||||
void fib_delete(struct fib *, void *); /* Remove fib entry */
|
||||
void fib_free(struct fib *); /* Destroy the fib */
|
||||
@ -105,7 +104,7 @@ void fit_put_next(struct fib *f, struct fib_iterator *i, struct fib_node *n, uin
|
||||
type *z; \
|
||||
for(;;) { \
|
||||
if (!fn_) \
|
||||
{ \
|
||||
{ \
|
||||
if (++hpos_ >= count_) \
|
||||
break; \
|
||||
fn_ = (fib)->hash_table[hpos_]; \
|
||||
@ -159,8 +158,8 @@ typedef struct rtable {
|
||||
* obstacle from this routing table.
|
||||
*/
|
||||
struct event *rt_event; /* Routing table event */
|
||||
btime gc_time; /* Time of last GC */
|
||||
int gc_counter; /* Number of operations since last GC */
|
||||
bird_clock_t gc_time; /* Time of last GC */
|
||||
byte prune_state; /* Table prune state, 1 -> scheduled, 2-> running */
|
||||
byte hcu_scheduled; /* Hostcache update is scheduled */
|
||||
byte nhu_state; /* Next Hop Update state */
|
||||
@ -213,7 +212,7 @@ typedef struct rte {
|
||||
byte flags; /* Flags (REF_...) */
|
||||
byte pflags; /* Protocol-specific flags */
|
||||
word pref; /* Route preference */
|
||||
bird_clock_t lastmod; /* Last modified */
|
||||
btime lastmod; /* Last modified */
|
||||
union { /* Protocol-dependent data (metrics etc.) */
|
||||
#ifdef CONFIG_RIP
|
||||
struct {
|
||||
@ -232,10 +231,12 @@ typedef struct rte {
|
||||
#ifdef CONFIG_BGP
|
||||
struct {
|
||||
u8 suppressed; /* Used for deterministic MED comparison */
|
||||
s8 stale; /* Route is LLGR_STALE, -1 if unknown */
|
||||
} bgp;
|
||||
#endif
|
||||
#ifdef CONFIG_BABEL
|
||||
struct {
|
||||
u16 seqno; /* Babel seqno */
|
||||
u16 metric; /* Babel metric */
|
||||
u64 router_id; /* Babel router id */
|
||||
} babel;
|
||||
@ -254,6 +255,7 @@ typedef struct rte {
|
||||
#define REF_FILTERED 2 /* Route is rejected by import filter */
|
||||
#define REF_STALE 4 /* Route is stale in a refresh cycle */
|
||||
#define REF_DISCARD 8 /* Route is scheduled for discard */
|
||||
#define REF_MODIFY 16 /* Route is scheduled for modify */
|
||||
|
||||
/* Route is valid for propagation (may depend on other flags in the future), accepts NULL */
|
||||
static inline int rte_is_valid(rte *r) { return r && !(r->flags & REF_FILTERED); }
|
||||
@ -282,7 +284,7 @@ void rt_preconfig(struct config *);
|
||||
void rt_commit(struct config *new, struct config *old);
|
||||
void rt_lock_table(rtable *);
|
||||
void rt_unlock_table(rtable *);
|
||||
void rt_setup(pool *, rtable *, char *, struct rtable_config *);
|
||||
void rt_setup(pool *, rtable *, struct rtable_config *);
|
||||
static inline net *net_find(rtable *tab, const net_addr *addr) { return (net *) fib_find(&tab->fib, addr); }
|
||||
static inline net *net_find_valid(rtable *tab, const net_addr *addr)
|
||||
{ net *n = net_find(tab, addr); return (n && rte_is_valid(n->routes)) ? n : NULL; }
|
||||
@ -294,9 +296,10 @@ rte *rte_get_temp(struct rta *);
|
||||
void rte_update2(struct channel *c, const net_addr *n, rte *new, struct rte_src *src);
|
||||
/* rte_update() moved to protocol.h to avoid dependency conflicts */
|
||||
int rt_examine(rtable *t, net_addr *a, struct proto *p, struct filter *filter);
|
||||
rte *rt_export_merged(struct channel *c, net *net, rte **rt_free, struct ea_list **tmpa, linpool *pool, int silent);
|
||||
rte *rt_export_merged(struct channel *c, net *net, rte **rt_free, linpool *pool, int silent);
|
||||
void rt_refresh_begin(rtable *t, struct channel *c);
|
||||
void rt_refresh_end(rtable *t, struct channel *c);
|
||||
void rt_modify_stale(rtable *t, struct channel *c);
|
||||
void rt_schedule_prune(rtable *t);
|
||||
void rte_dump(rte *);
|
||||
void rte_free(rte *);
|
||||
@ -309,6 +312,8 @@ int rt_feed_channel(struct channel *c);
|
||||
void rt_feed_channel_abort(struct channel *c);
|
||||
struct rtable_config *rt_new_table(struct symbol *s, uint addr_type);
|
||||
|
||||
/* Default limit for ECMP next hops, defined in sysdep code */
|
||||
extern const int rt_default_ecmp;
|
||||
|
||||
struct rt_show_data_rtable {
|
||||
node n;
|
||||
@ -455,7 +460,7 @@ static inline int rte_is_reachable(rte *r)
|
||||
*/
|
||||
|
||||
typedef struct eattr {
|
||||
word id; /* EA_CODE(EAP_..., protocol-dependent ID) */
|
||||
word id; /* EA_CODE(PROTOCOL_..., protocol-dependent ID) */
|
||||
byte flags; /* Protocol-dependent flags */
|
||||
byte type; /* Attribute type and several flags (EAF_...) */
|
||||
union {
|
||||
@ -464,19 +469,11 @@ typedef struct eattr {
|
||||
} u;
|
||||
} eattr;
|
||||
|
||||
#define EAP_GENERIC 0 /* Generic attributes */
|
||||
#define EAP_BGP 1 /* BGP attributes */
|
||||
#define EAP_RIP 2 /* RIP */
|
||||
#define EAP_OSPF 3 /* OSPF */
|
||||
#define EAP_KRT 4 /* Kernel route attributes */
|
||||
#define EAP_BABEL 5 /* Babel attributes */
|
||||
#define EAP_MAX 6
|
||||
|
||||
#define EA_CODE(proto,id) (((proto) << 8) | (id))
|
||||
#define EA_PROTO(ea) ((ea) >> 8)
|
||||
#define EA_ID(ea) ((ea) & 0xff)
|
||||
|
||||
#define EA_GEN_IGP_METRIC EA_CODE(EAP_GENERIC, 0)
|
||||
#define EA_GEN_IGP_METRIC EA_CODE(PROTOCOL_NONE, 0)
|
||||
|
||||
#define EA_CODE_MASK 0xffff
|
||||
#define EA_ALLOW_UNDEF 0x10000 /* ea_find: allow EAF_TYPE_UNDEF */
|
||||
@ -552,6 +549,55 @@ uint ea_hash(ea_list *e); /* Calculate 16-bit hash value */
|
||||
ea_list *ea_append(ea_list *to, ea_list *what);
|
||||
void ea_format_bitfield(struct eattr *a, byte *buf, int bufsize, const char **names, int min, int max);
|
||||
|
||||
#define ea_normalize(ea) do { \
|
||||
if (ea->next) { \
|
||||
ea_list *t = alloca(ea_scan(ea)); \
|
||||
ea_merge(ea, t); \
|
||||
ea = t; \
|
||||
} \
|
||||
ea_sort(ea); \
|
||||
} while(0) \
|
||||
|
||||
static inline eattr *
|
||||
ea_set_attr(ea_list **to, struct linpool *pool, uint id, uint flags, uint type, uintptr_t val)
|
||||
{
|
||||
ea_list *a = lp_alloc(pool, sizeof(ea_list) + sizeof(eattr));
|
||||
eattr *e = &a->attrs[0];
|
||||
|
||||
a->flags = EALF_SORTED;
|
||||
a->count = 1;
|
||||
a->next = *to;
|
||||
*to = a;
|
||||
|
||||
e->id = id;
|
||||
e->type = type;
|
||||
e->flags = flags;
|
||||
|
||||
if (type & EAF_EMBEDDED)
|
||||
e->u.data = (u32) val;
|
||||
else
|
||||
e->u.ptr = (struct adata *) val;
|
||||
|
||||
return e;
|
||||
}
|
||||
|
||||
static inline void
|
||||
ea_set_attr_u32(ea_list **to, struct linpool *pool, uint id, uint flags, uint type, u32 val)
|
||||
{ ea_set_attr(to, pool, id, flags, type, (uintptr_t) val); }
|
||||
|
||||
static inline void
|
||||
ea_set_attr_ptr(ea_list **to, struct linpool *pool, uint id, uint flags, uint type, struct adata *val)
|
||||
{ ea_set_attr(to, pool, id, flags, type, (uintptr_t) val); }
|
||||
|
||||
static inline void
|
||||
ea_set_attr_data(ea_list **to, struct linpool *pool, uint id, uint flags, uint type, void *data, uint len)
|
||||
{
|
||||
struct adata *a = lp_alloc_adata(pool, len);
|
||||
memcpy(a->data, data, len);
|
||||
ea_set_attr(to, pool, id, flags, type, (uintptr_t) a);
|
||||
}
|
||||
|
||||
|
||||
#define NEXTHOP_MAX_SIZE (sizeof(struct nexthop) + sizeof(u32)*MPLS_MAX_LABEL_STACK)
|
||||
|
||||
static inline size_t nexthop_size(const struct nexthop *nh)
|
||||
@ -577,7 +623,7 @@ rta *rta_do_cow(rta *o, linpool *lp);
|
||||
static inline rta * rta_cow(rta *r, linpool *lp) { return rta_is_cached(r) ? rta_do_cow(r, lp) : r; }
|
||||
void rta_dump(rta *);
|
||||
void rta_dump_all(void);
|
||||
void rta_show(struct cli *, rta *, ea_list *);
|
||||
void rta_show(struct cli *, rta *);
|
||||
|
||||
struct hostentry * rt_get_hostentry(rtable *tab, ip_addr a, ip_addr ll, rtable *dep);
|
||||
void rta_apply_hostentry(rta *a, struct hostentry *he, mpls_label_stack *mls);
|
||||
@ -613,14 +659,11 @@ rta_set_recursive_next_hop(rtable *dep, rta *a, rtable *tab, ip_addr gw, ip_addr
|
||||
static inline void rt_lock_hostentry(struct hostentry *he) { if (he) he->uc++; }
|
||||
static inline void rt_unlock_hostentry(struct hostentry *he) { if (he) he->uc--; }
|
||||
|
||||
|
||||
extern struct protocol *attr_class_to_protocol[EAP_MAX];
|
||||
|
||||
/*
|
||||
* Default protocol preferences
|
||||
*/
|
||||
|
||||
#define DEF_PREF_DIRECT 240 /* Directly connected */
|
||||
#define DEF_PREF_DIRECT 240 /* Directly connected */
|
||||
#define DEF_PREF_STATIC 200 /* Static route */
|
||||
#define DEF_PREF_OSPF 150 /* OSPF intra-area, inter-area and type 1 external routes */
|
||||
#define DEF_PREF_BABEL 130 /* Babel */
|
||||
|
@ -88,9 +88,6 @@ static struct idm src_ids;
|
||||
|
||||
static HASH(struct rte_src) src_hash;
|
||||
|
||||
struct protocol *attr_class_to_protocol[EAP_MAX];
|
||||
|
||||
|
||||
static void
|
||||
rte_src_init(void)
|
||||
{
|
||||
@ -553,29 +550,47 @@ ea_do_sort(ea_list *e)
|
||||
while (ss);
|
||||
}
|
||||
|
||||
/**
|
||||
* In place discard duplicates, undefs and temporary attributes in sorted
|
||||
* ea_list. We use stable sort for this reason.
|
||||
**/
|
||||
static inline void
|
||||
ea_do_prune(ea_list *e)
|
||||
{
|
||||
eattr *s, *d, *l, *s0;
|
||||
int i = 0;
|
||||
|
||||
/* Discard duplicates and undefs. Do you remember sorting was stable? */
|
||||
s = d = e->attrs;
|
||||
l = e->attrs + e->count;
|
||||
s = d = e->attrs; /* Beginning of the list. @s is source, @d is destination. */
|
||||
l = e->attrs + e->count; /* End of the list */
|
||||
|
||||
/* Walk from begin to end. */
|
||||
while (s < l)
|
||||
{
|
||||
s0 = s++;
|
||||
/* Find a consecutive block of the same attribute */
|
||||
while (s < l && s->id == s[-1].id)
|
||||
s++;
|
||||
/* s0 is the most recent version, s[-1] the oldest one */
|
||||
if ((s0->type & EAF_TYPE_MASK) != EAF_TYPE_UNDEF)
|
||||
{
|
||||
*d = *s0;
|
||||
d->type = (d->type & ~(EAF_ORIGINATED|EAF_FRESH)) | (s[-1].type & EAF_ORIGINATED);
|
||||
d++;
|
||||
i++;
|
||||
}
|
||||
|
||||
/* Now s0 is the most recent version, s[-1] the oldest one */
|
||||
/* Drop undefs */
|
||||
if ((s0->type & EAF_TYPE_MASK) == EAF_TYPE_UNDEF)
|
||||
continue;
|
||||
|
||||
/* Drop temporary attributes */
|
||||
if (s0->type & EAF_TEMP)
|
||||
continue;
|
||||
|
||||
/* Copy the newest version to destination */
|
||||
*d = *s0;
|
||||
|
||||
/* Preserve info whether it originated locally */
|
||||
d->type = (d->type & ~(EAF_ORIGINATED|EAF_FRESH)) | (s[-1].type & EAF_ORIGINATED);
|
||||
|
||||
/* Next destination */
|
||||
d++;
|
||||
i++;
|
||||
}
|
||||
|
||||
e->count = i;
|
||||
}
|
||||
|
||||
@ -851,7 +866,7 @@ ea_show(struct cli *c, eattr *e)
|
||||
byte buf[CLI_MSG_SIZE];
|
||||
byte *pos = buf, *end = buf + sizeof(buf);
|
||||
|
||||
if (p = attr_class_to_protocol[EA_PROTO(e->id)])
|
||||
if (p = class_to_protocol[EA_PROTO(e->id)])
|
||||
{
|
||||
pos += bsprintf(pos, "%s.", p->name);
|
||||
if (p->get_attr)
|
||||
@ -1131,15 +1146,7 @@ rta_lookup(rta *o)
|
||||
|
||||
ASSERT(!(o->aflags & RTAF_CACHED));
|
||||
if (o->eattrs)
|
||||
{
|
||||
if (o->eattrs->next) /* Multiple ea_list's, need to merge them */
|
||||
{
|
||||
ea_list *ml = alloca(ea_scan(o->eattrs));
|
||||
ea_merge(o->eattrs, ml);
|
||||
o->eattrs = ml;
|
||||
}
|
||||
ea_sort(o->eattrs);
|
||||
}
|
||||
ea_normalize(o->eattrs);
|
||||
|
||||
h = rta_hash(o);
|
||||
for(r=rta_hash_table[h & rta_cache_mask]; r; r=r->next)
|
||||
@ -1204,7 +1211,7 @@ rta_dump(rta *a)
|
||||
static char *rts[] = { "RTS_DUMMY", "RTS_STATIC", "RTS_INHERIT", "RTS_DEVICE",
|
||||
"RTS_STAT_DEV", "RTS_REDIR", "RTS_RIP",
|
||||
"RTS_OSPF", "RTS_OSPF_IA", "RTS_OSPF_EXT1",
|
||||
"RTS_OSPF_EXT2", "RTS_BGP", "RTS_PIPE", "RTS_BABEL" };
|
||||
"RTS_OSPF_EXT2", "RTS_BGP", "RTS_PIPE", "RTS_BABEL" };
|
||||
static char *rtd[] = { "", " DEV", " HOLE", " UNREACH", " PROHIBIT" };
|
||||
|
||||
debug("p=%s uc=%d %s %s%s h=%04x",
|
||||
@ -1253,17 +1260,15 @@ rta_dump_all(void)
|
||||
}
|
||||
|
||||
void
|
||||
rta_show(struct cli *c, rta *a, ea_list *eal)
|
||||
rta_show(struct cli *c, rta *a)
|
||||
{
|
||||
static char *src_names[] = { "dummy", "static", "inherit", "device", "static-device", "redirect",
|
||||
"RIP", "OSPF", "OSPF-IA", "OSPF-E1", "OSPF-E2", "BGP", "pipe" };
|
||||
int i;
|
||||
|
||||
cli_printf(c, -1008, "\tType: %s %s", src_names[a->source], ip_scope_text(a->scope));
|
||||
if (!eal)
|
||||
eal = a->eattrs;
|
||||
for(; eal; eal=eal->next)
|
||||
for(i=0; i<eal->count; i++)
|
||||
|
||||
for(ea_list *eal = a->eattrs; eal; eal=eal->next)
|
||||
for(int i=0; i<eal->count; i++)
|
||||
ea_show(c, &eal->attrs[i]);
|
||||
}
|
||||
|
||||
|
@ -31,9 +31,10 @@ dev_ifa_notify(struct proto *P, uint flags, struct ifa *ad)
|
||||
struct rt_dev_proto *p = (void *) P;
|
||||
struct rt_dev_config *cf = (void *) P->cf;
|
||||
struct channel *c;
|
||||
net_addr *net = &ad->prefix;
|
||||
|
||||
if (!EMPTY_LIST(cf->iface_list) &&
|
||||
!iface_patt_find(&cf->iface_list, ad->iface, ad->iface->addr))
|
||||
!iface_patt_find(&cf->iface_list, ad->iface, ad))
|
||||
/* Empty list is automatically treated as "*" */
|
||||
return;
|
||||
|
||||
@ -53,13 +54,20 @@ dev_ifa_notify(struct proto *P, uint flags, struct ifa *ad)
|
||||
if (!c)
|
||||
return;
|
||||
|
||||
/* For IPv6 SADR, replace regular prefix with SADR prefix */
|
||||
if (c->net_type == NET_IP6_SADR)
|
||||
{
|
||||
net = alloca(sizeof(net_addr_ip6_sadr));
|
||||
net_fill_ip6_sadr(net, net6_prefix(&ad->prefix), net6_pxlen(&ad->prefix), IP6_NONE, 0);
|
||||
}
|
||||
|
||||
if (flags & IF_CHANGE_DOWN)
|
||||
{
|
||||
DBG("dev_if_notify: %s:%I going down\n", ad->iface->name, ad->ip);
|
||||
|
||||
/* Use iface ID as local source ID */
|
||||
struct rte_src *src = rt_get_source(P, ad->iface->index);
|
||||
rte_update2(c, &ad->prefix, NULL, src);
|
||||
rte_update2(c, net, NULL, src);
|
||||
}
|
||||
else if (flags & IF_CHANGE_UP)
|
||||
{
|
||||
@ -85,7 +93,7 @@ dev_ifa_notify(struct proto *P, uint flags, struct ifa *ad)
|
||||
a = rta_lookup(&a0);
|
||||
e = rte_get_temp(a);
|
||||
e->pflags = 0;
|
||||
rte_update2(c, &ad->prefix, e, src);
|
||||
rte_update2(c, net, e, src);
|
||||
}
|
||||
}
|
||||
|
||||
@ -107,16 +115,32 @@ dev_if_notify(struct proto *p, uint c, struct iface *iface)
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
dev_postconfig(struct proto_config *CF)
|
||||
{
|
||||
struct rt_dev_config *cf = (void *) CF;
|
||||
struct channel_config *ip4, *ip6, *ip6_sadr;
|
||||
|
||||
ip4 = proto_cf_find_channel(CF, NET_IP4);
|
||||
ip6 = proto_cf_find_channel(CF, NET_IP6);
|
||||
ip6_sadr = proto_cf_find_channel(CF, NET_IP6_SADR);
|
||||
|
||||
if (ip6 && ip6_sadr)
|
||||
cf_error("Both ipv6 and ipv6-sadr channels");
|
||||
|
||||
cf->ip4_channel = ip4;
|
||||
cf->ip6_channel = ip6 ?: ip6_sadr;
|
||||
}
|
||||
|
||||
static struct proto *
|
||||
dev_init(struct proto_config *CF)
|
||||
{
|
||||
struct proto *P = proto_new(CF);
|
||||
struct rt_dev_proto *p = (void *) P;
|
||||
// struct rt_dev_config *cf = (void *) CF;
|
||||
struct rt_dev_config *cf = (void *) CF;
|
||||
|
||||
proto_configure_channel(P, &p->ip4_channel, proto_cf_find_channel(CF, NET_IP4));
|
||||
proto_configure_channel(P, &p->ip6_channel, proto_cf_find_channel(CF, NET_IP6));
|
||||
proto_configure_channel(P, &p->ip4_channel, cf->ip4_channel);
|
||||
proto_configure_channel(P, &p->ip6_channel, cf->ip6_channel);
|
||||
|
||||
P->if_notify = dev_if_notify;
|
||||
P->ifa_notify = dev_ifa_notify;
|
||||
@ -136,8 +160,8 @@ dev_reconfigure(struct proto *P, struct proto_config *CF)
|
||||
return 0;
|
||||
|
||||
return
|
||||
proto_configure_channel(P, &p->ip4_channel, proto_cf_find_channel(CF, NET_IP4)) &&
|
||||
proto_configure_channel(P, &p->ip6_channel, proto_cf_find_channel(CF, NET_IP6));
|
||||
proto_configure_channel(P, &p->ip4_channel, n->ip4_channel) &&
|
||||
proto_configure_channel(P, &p->ip6_channel, n->ip6_channel);
|
||||
|
||||
return 1;
|
||||
}
|
||||
@ -159,13 +183,15 @@ dev_copy_config(struct proto_config *dest, struct proto_config *src)
|
||||
}
|
||||
|
||||
struct protocol proto_device = {
|
||||
.name = "Direct",
|
||||
.template = "direct%d",
|
||||
.name = "Direct",
|
||||
.template = "direct%d",
|
||||
.class = PROTOCOL_DIRECT,
|
||||
.preference = DEF_PREF_DIRECT,
|
||||
.channel_mask = NB_IP,
|
||||
.channel_mask = NB_IP | NB_IP6_SADR,
|
||||
.proto_size = sizeof(struct rt_dev_proto),
|
||||
.config_size = sizeof(struct rt_dev_config),
|
||||
.init = dev_init,
|
||||
.reconfigure = dev_reconfigure,
|
||||
.copy_config = dev_copy_config
|
||||
.postconfig = dev_postconfig,
|
||||
.init = dev_init,
|
||||
.reconfigure = dev_reconfigure,
|
||||
.copy_config = dev_copy_config
|
||||
};
|
||||
|
@ -13,6 +13,9 @@ struct rt_dev_config {
|
||||
struct proto_config c;
|
||||
list iface_list; /* list of struct iface_patt */
|
||||
int check_link;
|
||||
|
||||
struct channel_config *ip4_channel;
|
||||
struct channel_config *ip6_channel;
|
||||
};
|
||||
|
||||
struct rt_dev_proto {
|
||||
|
@ -32,6 +32,24 @@
|
||||
* Basic FIB operations are performed by functions defined by this module,
|
||||
* enumerating of FIB contents is accomplished by using the FIB_WALK() macro
|
||||
* or FIB_ITERATE_START() if you want to do it asynchronously.
|
||||
*
|
||||
* For simple iteration just place the body of the loop between FIB_WALK() and
|
||||
* FIB_WALK_END(). You can't modify the FIB during the iteration (you can modify
|
||||
* data in the node, but not add or remove nodes).
|
||||
*
|
||||
* If you need more freedom, you can use the FIB_ITERATE_*() group of macros.
|
||||
* First, you initialize an iterator with FIB_ITERATE_INIT(). Then you can put
|
||||
* the loop body in between FIB_ITERATE_START() and FIB_ITERATE_END(). In
|
||||
* addition, the iteration can be suspended by calling FIB_ITERATE_PUT().
|
||||
* This'll link the iterator inside the FIB. While suspended, you may modify the
|
||||
* FIB, exit the current function, etc. To resume the iteration, enter the loop
|
||||
* again. You can use FIB_ITERATE_UNLINK() to unlink the iterator (while
|
||||
* iteration is suspended) in cases like premature end of FIB iteration.
|
||||
*
|
||||
* Note that the iterator must not be destroyed when the iteration is suspended,
|
||||
* the FIB would then contain a pointer to invalid memory. Therefore, after each
|
||||
* FIB_ITERATE_INIT() or FIB_ITERATE_PUT() there must be either
|
||||
* FIB_ITERATE_START() or FIB_ITERATE_UNLINK() before the iterator is destroyed.
|
||||
*/
|
||||
|
||||
#undef LOCAL_DEBUG
|
||||
@ -74,8 +92,7 @@ fib_ht_free(struct fib_node **h)
|
||||
}
|
||||
|
||||
|
||||
static u32
|
||||
fib_hash(struct fib *f, const net_addr *a);
|
||||
static inline u32 fib_hash(struct fib *f, const net_addr *a);
|
||||
|
||||
/**
|
||||
* fib_init - initialize a new FIB
|
||||
@ -180,23 +197,11 @@ fib_rehash(struct fib *f, int step)
|
||||
})
|
||||
|
||||
|
||||
static u32
|
||||
static inline u32
|
||||
fib_hash(struct fib *f, const net_addr *a)
|
||||
{
|
||||
ASSERT(f->addr_type == a->type);
|
||||
|
||||
switch (f->addr_type)
|
||||
{
|
||||
case NET_IP4: return FIB_HASH(f, a, ip4);
|
||||
case NET_IP6: return FIB_HASH(f, a, ip6);
|
||||
case NET_VPN4: return FIB_HASH(f, a, vpn4);
|
||||
case NET_VPN6: return FIB_HASH(f, a, vpn6);
|
||||
case NET_ROA4: return FIB_HASH(f, a, roa4);
|
||||
case NET_ROA6: return FIB_HASH(f, a, roa6);
|
||||
case NET_FLOW4: return FIB_HASH(f, a, flow4);
|
||||
case NET_FLOW6: return FIB_HASH(f, a, flow6);
|
||||
default: bug("invalid type");
|
||||
}
|
||||
/* Same as FIB_HASH() */
|
||||
return net_hash(a) >> f->hash_shift;
|
||||
}
|
||||
|
||||
void *
|
||||
@ -231,6 +236,8 @@ fib_find(struct fib *f, const net_addr *a)
|
||||
case NET_ROA6: return FIB_FIND(f, a, roa6);
|
||||
case NET_FLOW4: return FIB_FIND(f, a, flow4);
|
||||
case NET_FLOW6: return FIB_FIND(f, a, flow6);
|
||||
case NET_IP6_SADR: return FIB_FIND(f, a, ip6_sadr);
|
||||
case NET_MPLS: return FIB_FIND(f, a, mpls);
|
||||
default: bug("invalid type");
|
||||
}
|
||||
}
|
||||
@ -250,6 +257,8 @@ fib_insert(struct fib *f, const net_addr *a, struct fib_node *e)
|
||||
case NET_ROA6: FIB_INSERT(f, a, e, roa6); return;
|
||||
case NET_FLOW4: FIB_INSERT(f, a, e, flow4); return;
|
||||
case NET_FLOW6: FIB_INSERT(f, a, e, flow6); return;
|
||||
case NET_IP6_SADR: FIB_INSERT(f, a, e, ip6_sadr); return;
|
||||
case NET_MPLS: FIB_INSERT(f, a, e, mpls); return;
|
||||
default: bug("invalid type");
|
||||
}
|
||||
}
|
||||
@ -547,22 +556,17 @@ found:
|
||||
void
|
||||
fib_check(struct fib *f)
|
||||
{
|
||||
#if 0
|
||||
uint i, ec, lo, nulls;
|
||||
uint i, ec, nulls;
|
||||
|
||||
ec = 0;
|
||||
for(i=0; i<f->hash_size; i++)
|
||||
{
|
||||
struct fib_node *n;
|
||||
lo = 0;
|
||||
for(n=f->hash_table[i]; n; n=n->next)
|
||||
{
|
||||
struct fib_iterator *j, *j0;
|
||||
uint h0 = ipa_hash(n->prefix);
|
||||
if (h0 < lo)
|
||||
bug("fib_check: discord in hash chains");
|
||||
lo = h0;
|
||||
if ((h0 >> f->hash_shift) != i)
|
||||
uint h0 = fib_hash(f, n->addr);
|
||||
if (h0 != i)
|
||||
bug("fib_check: mishashed %x->%x (order %d)", h0, i, f->hash_order);
|
||||
j0 = (struct fib_iterator *) n;
|
||||
nulls = 0;
|
||||
@ -583,7 +587,6 @@ fib_check(struct fib *f)
|
||||
}
|
||||
if (ec != f->entries)
|
||||
bug("fib_check: invalid entry count (%d != %d)", ec, f->entries);
|
||||
#endif
|
||||
return;
|
||||
}
|
||||
|
||||
@ -602,7 +605,7 @@ fib_histogram(struct fib *f)
|
||||
for (e = f->hash_table[i]; e != NULL; e = e->next)
|
||||
j++;
|
||||
if (j > 0)
|
||||
log(L_WARN "Histogram line %d: %d", i, j);
|
||||
log(L_WARN "Histogram line %d: %d", i, j);
|
||||
}
|
||||
|
||||
log(L_WARN "Histogram dump end");
|
||||
|
@ -29,41 +29,35 @@ rt_show_table(struct cli *c, struct rt_show_data *d)
|
||||
}
|
||||
|
||||
static void
|
||||
rt_show_rte(struct cli *c, byte *ia, rte *e, struct rt_show_data *d, ea_list *tmpa)
|
||||
rt_show_rte(struct cli *c, byte *ia, rte *e, struct rt_show_data *d)
|
||||
{
|
||||
byte from[IPA_MAX_TEXT_LENGTH+8];
|
||||
byte tm[TM_DATETIME_BUFFER_SIZE], info[256];
|
||||
rta *a = e->attrs;
|
||||
int primary = (e->net->routes == e);
|
||||
int sync_error = (e->net->n.flags & KRF_SYNC_ERROR);
|
||||
void (*get_route_info)(struct rte *, byte *buf, struct ea_list *attrs);
|
||||
void (*get_route_info)(struct rte *, byte *buf);
|
||||
struct nexthop *nh;
|
||||
|
||||
tm_format_datetime(tm, &config->tf_route, e->lastmod);
|
||||
tm_format_time(tm, &config->tf_route, e->lastmod);
|
||||
if (ipa_nonzero(a->from) && !ipa_equal(a->from, a->nh.gw))
|
||||
bsprintf(from, " from %I", a->from);
|
||||
else
|
||||
from[0] = 0;
|
||||
|
||||
get_route_info = a->src->proto->proto->get_route_info;
|
||||
if (get_route_info || d->verbose)
|
||||
{
|
||||
/* Need to normalize the extended attributes */
|
||||
ea_list *t = tmpa;
|
||||
t = ea_append(t, a->eattrs);
|
||||
tmpa = alloca(ea_scan(t));
|
||||
ea_merge(t, tmpa);
|
||||
ea_sort(tmpa);
|
||||
}
|
||||
/* Need to normalize the extended attributes */
|
||||
if ((get_route_info || d->verbose) && !rta_is_cached(a))
|
||||
ea_normalize(a->eattrs);
|
||||
if (get_route_info)
|
||||
get_route_info(e, info, tmpa);
|
||||
get_route_info(e, info);
|
||||
else
|
||||
bsprintf(info, " (%d)", e->pref);
|
||||
|
||||
if (d->last_table != d->tab)
|
||||
rt_show_table(c, d);
|
||||
|
||||
cli_printf(c, -1007, "%-18s %s [%s %s%s]%s%s", ia, rta_dest_name(a->dest),
|
||||
cli_printf(c, -1007, "%-20s %s [%s %s%s]%s%s", ia, rta_dest_name(a->dest),
|
||||
a->src->proto->name, tm, from, primary ? (sync_error ? " !" : " *") : "", info);
|
||||
|
||||
if (a->dest == RTD_UNICAST)
|
||||
@ -71,9 +65,10 @@ rt_show_rte(struct cli *c, byte *ia, rte *e, struct rt_show_data *d, ea_list *tm
|
||||
{
|
||||
char mpls[MPLS_MAX_LABEL_STACK*12 + 5], *lsp = mpls;
|
||||
char *onlink = (nh->flags & RNF_ONLINK) ? " onlink" : "";
|
||||
char weight[16] = "";
|
||||
|
||||
if (nh->labels)
|
||||
{
|
||||
{
|
||||
lsp += bsprintf(lsp, " mpls %d", nh->label[0]);
|
||||
for (int i=1;i<nh->labels; i++)
|
||||
lsp += bsprintf(lsp, "/%d", nh->label[i]);
|
||||
@ -81,15 +76,18 @@ rt_show_rte(struct cli *c, byte *ia, rte *e, struct rt_show_data *d, ea_list *tm
|
||||
*lsp = '\0';
|
||||
|
||||
if (a->nh.next)
|
||||
cli_printf(c, -1007, "\tvia %I%s on %s%s weight %d",
|
||||
nh->gw, mpls, nh->iface->name, onlink, nh->weight + 1);
|
||||
bsprintf(weight, " weight %d", nh->weight + 1);
|
||||
|
||||
if (ipa_nonzero(nh->gw))
|
||||
cli_printf(c, -1007, "\tvia %I on %s%s%s%s",
|
||||
nh->gw, nh->iface->name, mpls, onlink, weight);
|
||||
else
|
||||
cli_printf(c, -1007, "\tvia %I%s on %s%s",
|
||||
nh->gw, mpls, nh->iface->name, onlink);
|
||||
cli_printf(c, -1007, "\tdev %s%s%s",
|
||||
nh->iface->name, mpls, onlink, weight);
|
||||
}
|
||||
|
||||
if (d->verbose)
|
||||
rta_show(c, a, tmpa);
|
||||
rta_show(c, a);
|
||||
}
|
||||
|
||||
static void
|
||||
@ -97,7 +95,6 @@ rt_show_net(struct cli *c, net *n, struct rt_show_data *d)
|
||||
{
|
||||
rte *e, *ee;
|
||||
byte ia[NET_MAX_TEXT_LENGTH+1];
|
||||
struct ea_list *tmpa;
|
||||
struct channel *ec = d->tab->export_channel;
|
||||
int first = 1;
|
||||
int pass = 0;
|
||||
@ -117,7 +114,7 @@ rt_show_net(struct cli *c, net *n, struct rt_show_data *d)
|
||||
continue;
|
||||
|
||||
ee = e;
|
||||
tmpa = rte_make_tmp_attrs(e, c->show_pool);
|
||||
rte_make_tmp_attrs(&e, c->show_pool);
|
||||
|
||||
/* Export channel is down, do not try to export routes to it */
|
||||
if (ec && (ec->export_state == ES_DOWN))
|
||||
@ -125,9 +122,9 @@ rt_show_net(struct cli *c, net *n, struct rt_show_data *d)
|
||||
|
||||
/* Special case for merged export */
|
||||
if ((d->export_mode == RSEM_EXPORT) && (ec->ra_mode == RA_MERGED))
|
||||
{
|
||||
{
|
||||
rte *rt_free;
|
||||
e = rt_export_merged(ec, n, &rt_free, &tmpa, c->show_pool, 1);
|
||||
e = rt_export_merged(ec, n, &rt_free, c->show_pool, 1);
|
||||
pass = 1;
|
||||
|
||||
if (!e)
|
||||
@ -136,7 +133,7 @@ rt_show_net(struct cli *c, net *n, struct rt_show_data *d)
|
||||
else if (d->export_mode)
|
||||
{
|
||||
struct proto *ep = ec->proto;
|
||||
int ic = ep->import_control ? ep->import_control(ep, &e, &tmpa, c->show_pool) : 0;
|
||||
int ic = ep->import_control ? ep->import_control(ep, &e, c->show_pool) : 0;
|
||||
|
||||
if (ec->ra_mode == RA_OPTIMAL || ec->ra_mode == RA_MERGED)
|
||||
pass = 1;
|
||||
@ -152,7 +149,7 @@ rt_show_net(struct cli *c, net *n, struct rt_show_data *d)
|
||||
* command may change the export filter and do not update routes.
|
||||
*/
|
||||
int do_export = (ic > 0) ||
|
||||
(f_run(ec->out_filter, &e, &tmpa, c->show_pool, FF_FORCE_TMPATTR) <= F_ACCEPT);
|
||||
(f_run(ec->out_filter, &e, c->show_pool, FF_SILENT) <= F_ACCEPT);
|
||||
|
||||
if (do_export != (d->export_mode == RSEM_EXPORT))
|
||||
goto skip;
|
||||
@ -165,11 +162,11 @@ rt_show_net(struct cli *c, net *n, struct rt_show_data *d)
|
||||
if (d->show_protocol && (d->show_protocol != e->attrs->src->proto))
|
||||
goto skip;
|
||||
|
||||
if (f_run(d->filter, &e, &tmpa, c->show_pool, FF_FORCE_TMPATTR) > F_ACCEPT)
|
||||
if (f_run(d->filter, &e, c->show_pool, 0) > F_ACCEPT)
|
||||
goto skip;
|
||||
|
||||
if (d->stats < 2)
|
||||
rt_show_rte(c, ia, e, d, tmpa);
|
||||
rt_show_rte(c, ia, e, d);
|
||||
|
||||
d->show_counter++;
|
||||
ia[0] = 0;
|
||||
@ -414,4 +411,3 @@ rt_show(struct rt_show_data *d)
|
||||
cli_msg(8001, "Network not found");
|
||||
}
|
||||
}
|
||||
|
||||
|
301
nest/rt-table.c
301
nest/rt-table.c
@ -39,6 +39,7 @@
|
||||
#include "lib/string.h"
|
||||
#include "conf/conf.h"
|
||||
#include "filter/filter.h"
|
||||
#include "lib/hash.h"
|
||||
#include "lib/string.h"
|
||||
#include "lib/alloca.h"
|
||||
|
||||
@ -85,6 +86,45 @@ net_route_ip6(rtable *t, net_addr_ip6 *n)
|
||||
return r;
|
||||
}
|
||||
|
||||
static inline void *
|
||||
net_route_ip6_sadr(rtable *t, net_addr_ip6_sadr *n)
|
||||
{
|
||||
struct fib_node *fn;
|
||||
|
||||
while (1)
|
||||
{
|
||||
net *best = NULL;
|
||||
int best_pxlen = 0;
|
||||
|
||||
/* We need to do dst first matching. Since sadr addresses are hashed on dst
|
||||
prefix only, find the hash table chain and go through it to find the
|
||||
match with the smallest matching src prefix. */
|
||||
for (fn = fib_get_chain(&t->fib, (net_addr *) n); fn; fn = fn->next)
|
||||
{
|
||||
net_addr_ip6_sadr *a = (void *) fn->addr;
|
||||
|
||||
if (net_equal_dst_ip6_sadr(n, a) &&
|
||||
net_in_net_src_ip6_sadr(n, a) &&
|
||||
(a->src_pxlen >= best_pxlen))
|
||||
{
|
||||
best = fib_node_to_user(&t->fib, fn);
|
||||
best_pxlen = a->src_pxlen;
|
||||
}
|
||||
}
|
||||
|
||||
if (best)
|
||||
return best;
|
||||
|
||||
if (!n->dst_pxlen)
|
||||
break;
|
||||
|
||||
n->dst_pxlen--;
|
||||
ip6_clrbit(&n->dst_prefix, n->dst_pxlen);
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void *
|
||||
net_route(rtable *tab, const net_addr *n)
|
||||
{
|
||||
@ -105,6 +145,9 @@ net_route(rtable *tab, const net_addr *n)
|
||||
case NET_ROA6:
|
||||
return net_route_ip6(tab, (net_addr_ip6 *) n0);
|
||||
|
||||
case NET_IP6_SADR:
|
||||
return net_route_ip6_sadr(tab, (net_addr_ip6_sadr *) n0);
|
||||
|
||||
default:
|
||||
return NULL;
|
||||
}
|
||||
@ -275,11 +318,11 @@ rte_cow_rta(rte *r, linpool *lp)
|
||||
if (!rta_is_cached(r->attrs))
|
||||
return r;
|
||||
|
||||
rte *e = rte_cow(r);
|
||||
r = rte_cow(r);
|
||||
rta *a = rta_do_cow(r->attrs, lp);
|
||||
rta_free(e->attrs);
|
||||
e->attrs = a;
|
||||
return e;
|
||||
rta_free(r->attrs);
|
||||
r->attrs = a;
|
||||
return r;
|
||||
}
|
||||
|
||||
static int /* Actually better or at least as good as */
|
||||
@ -351,24 +394,20 @@ rte_trace_out(uint flag, struct proto *p, rte *e, char *msg)
|
||||
}
|
||||
|
||||
static rte *
|
||||
export_filter_(struct channel *c, rte *rt0, rte **rt_free, ea_list **tmpa, linpool *pool, int silent)
|
||||
export_filter_(struct channel *c, rte *rt0, rte **rt_free, linpool *pool, int silent)
|
||||
{
|
||||
struct proto *p = c->proto;
|
||||
struct filter *filter = c->out_filter;
|
||||
struct proto_stats *stats = &c->stats;
|
||||
ea_list *tmpb = NULL;
|
||||
rte *rt;
|
||||
int v;
|
||||
|
||||
rt = rt0;
|
||||
*rt_free = NULL;
|
||||
|
||||
if (!tmpa)
|
||||
tmpa = &tmpb;
|
||||
rte_make_tmp_attrs(&rt, pool);
|
||||
|
||||
*tmpa = rte_make_tmp_attrs(rt, pool);
|
||||
|
||||
v = p->import_control ? p->import_control(p, &rt, tmpa, pool) : 0;
|
||||
v = p->import_control ? p->import_control(p, &rt, pool) : 0;
|
||||
if (v < 0)
|
||||
{
|
||||
if (silent)
|
||||
@ -387,7 +426,8 @@ export_filter_(struct channel *c, rte *rt0, rte **rt_free, ea_list **tmpa, linpo
|
||||
}
|
||||
|
||||
v = filter && ((filter == FILTER_REJECT) ||
|
||||
(f_run(filter, &rt, tmpa, pool, FF_FORCE_TMPATTR) > F_ACCEPT));
|
||||
(f_run(filter, &rt, pool,
|
||||
(silent ? FF_SILENT : 0)) > F_ACCEPT));
|
||||
if (v)
|
||||
{
|
||||
if (silent)
|
||||
@ -411,13 +451,13 @@ export_filter_(struct channel *c, rte *rt0, rte **rt_free, ea_list **tmpa, linpo
|
||||
}
|
||||
|
||||
static inline rte *
|
||||
export_filter(struct channel *c, rte *rt0, rte **rt_free, ea_list **tmpa, int silent)
|
||||
export_filter(struct channel *c, rte *rt0, rte **rt_free, int silent)
|
||||
{
|
||||
return export_filter_(c, rt0, rt_free, tmpa, rte_update_pool, silent);
|
||||
return export_filter_(c, rt0, rt_free, rte_update_pool, silent);
|
||||
}
|
||||
|
||||
static void
|
||||
do_rt_notify(struct channel *c, net *net, rte *new, rte *old, ea_list *tmpa, int refeed)
|
||||
do_rt_notify(struct channel *c, net *net, rte *new, rte *old, int refeed)
|
||||
{
|
||||
struct proto *p = c->proto;
|
||||
struct proto_stats *stats = &c->stats;
|
||||
@ -490,19 +530,7 @@ do_rt_notify(struct channel *c, net *net, rte *new, rte *old, ea_list *tmpa, int
|
||||
else if (old)
|
||||
rte_trace_out(D_ROUTES, p, old, "removed");
|
||||
}
|
||||
if (!new)
|
||||
p->rt_notify(p, c, net, NULL, old, NULL);
|
||||
else if (tmpa)
|
||||
{
|
||||
ea_list *t = tmpa;
|
||||
while (t->next)
|
||||
t = t->next;
|
||||
t->next = new->attrs->eattrs;
|
||||
p->rt_notify(p, c, net, new, old, tmpa);
|
||||
t->next = NULL;
|
||||
}
|
||||
else
|
||||
p->rt_notify(p, c, net, new, old, new->attrs->eattrs);
|
||||
p->rt_notify(p, c, net, new, old);
|
||||
}
|
||||
|
||||
static void
|
||||
@ -514,7 +542,6 @@ rt_notify_basic(struct channel *c, net *net, rte *new0, rte *old0, int refeed)
|
||||
rte *old = old0;
|
||||
rte *new_free = NULL;
|
||||
rte *old_free = NULL;
|
||||
ea_list *tmpa = NULL;
|
||||
|
||||
if (new)
|
||||
c->stats.exp_updates_received++;
|
||||
@ -522,30 +549,30 @@ rt_notify_basic(struct channel *c, net *net, rte *new0, rte *old0, int refeed)
|
||||
c->stats.exp_withdraws_received++;
|
||||
|
||||
/*
|
||||
* This is a tricky part - we don't know whether route 'old' was
|
||||
* exported to protocol 'p' or was filtered by the export filter.
|
||||
* We try to run the export filter to know this to have a correct
|
||||
* value in 'old' argument of rte_update (and proper filter value)
|
||||
* This is a tricky part - we don't know whether route 'old' was exported to
|
||||
* protocol 'p' or was filtered by the export filter. We try to run the export
|
||||
* filter to know this to have a correct value in 'old' argument of rte_update
|
||||
* (and proper filter value).
|
||||
*
|
||||
* FIXME - this is broken because 'configure soft' may change
|
||||
* filters but keep routes. Refeed is expected to be called after
|
||||
* change of the filters and with old == new, therefore we do not
|
||||
* even try to run the filter on an old route, This may lead to
|
||||
* 'spurious withdraws' but ensure that there are no 'missing
|
||||
* This is broken because 'configure soft' may change filters but keep routes.
|
||||
* Refeed cycle is expected to be called after change of the filters and with
|
||||
* old == new, therefore we do not even try to run the filter on an old route.
|
||||
* This may lead to 'spurious withdraws' but ensure that there are no 'missing
|
||||
* withdraws'.
|
||||
*
|
||||
* This is not completely safe as there is a window between
|
||||
* reconfiguration and the end of refeed - if a newly filtered
|
||||
* route disappears during this period, proper withdraw is not
|
||||
* sent (because old would be also filtered) and the route is
|
||||
* not refeeded (because it disappeared before that).
|
||||
* This is not completely safe as there is a window between reconfiguration
|
||||
* and the end of refeed - if a newly filtered route disappears during this
|
||||
* period, proper withdraw is not sent (because old would be also filtered)
|
||||
* and the route is not refeeded (because it disappeared before that).
|
||||
* Therefore, we also do not try to run the filter on old routes that are
|
||||
* older than the last filter change.
|
||||
*/
|
||||
|
||||
if (new)
|
||||
new = export_filter(c, new, &new_free, &tmpa, 0);
|
||||
new = export_filter(c, new, &new_free, 0);
|
||||
|
||||
if (old && !refeed)
|
||||
old = export_filter(c, old, &old_free, NULL, 1);
|
||||
if (old && !(refeed || (old->lastmod <= c->last_tx_filter_change)))
|
||||
old = export_filter(c, old, &old_free, 1);
|
||||
|
||||
if (!new && !old)
|
||||
{
|
||||
@ -562,13 +589,13 @@ rt_notify_basic(struct channel *c, net *net, rte *new0, rte *old0, int refeed)
|
||||
|
||||
#ifdef CONFIG_PIPE
|
||||
if ((p->proto == &proto_pipe) && !new0 && (p != old0->sender->proto))
|
||||
p->rt_notify(p, c, net, NULL, old0, NULL);
|
||||
p->rt_notify(p, c, net, NULL, old0);
|
||||
#endif
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
do_rt_notify(c, net, new, old, tmpa, refeed);
|
||||
do_rt_notify(c, net, new, old, refeed);
|
||||
|
||||
/* Discard temporary rte's */
|
||||
if (new_free)
|
||||
@ -587,7 +614,6 @@ rt_notify_accepted(struct channel *c, net *net, rte *new_changed, rte *old_chang
|
||||
rte *old_best = NULL;
|
||||
rte *new_free = NULL;
|
||||
rte *old_free = NULL;
|
||||
ea_list *tmpa = NULL;
|
||||
|
||||
/* Used to track whether we met old_changed position. If before_old is NULL
|
||||
old_changed was the first and we met it implicitly before current best route. */
|
||||
@ -605,7 +631,7 @@ rt_notify_accepted(struct channel *c, net *net, rte *new_changed, rte *old_chang
|
||||
/* First, find the new_best route - first accepted by filters */
|
||||
for (r=net->routes; rte_is_valid(r); r=r->next)
|
||||
{
|
||||
if (new_best = export_filter(c, r, &new_free, &tmpa, 0))
|
||||
if (new_best = export_filter(c, r, &new_free, 0))
|
||||
break;
|
||||
|
||||
/* Note if we walked around the position of old_changed route */
|
||||
@ -652,11 +678,21 @@ rt_notify_accepted(struct channel *c, net *net, rte *new_changed, rte *old_chang
|
||||
*
|
||||
* - We found new_best the same as new_changed, therefore it cannot
|
||||
* be old_best and we have to continue search for old_best.
|
||||
*
|
||||
* There is also a hack to ensure consistency in case of changed filters.
|
||||
* It does not find the proper old_best, just selects a non-NULL route.
|
||||
*/
|
||||
|
||||
/* Hack for changed filters */
|
||||
if (old_changed && (old_changed->lastmod <= c->last_tx_filter_change))
|
||||
{
|
||||
old_best = old_changed;
|
||||
goto found;
|
||||
}
|
||||
|
||||
/* First case */
|
||||
if (old_meet)
|
||||
if (old_best = export_filter(c, old_changed, &old_free, NULL, 1))
|
||||
if (old_best = export_filter(c, old_changed, &old_free, 1))
|
||||
goto found;
|
||||
|
||||
/* Second case */
|
||||
@ -674,18 +710,18 @@ rt_notify_accepted(struct channel *c, net *net, rte *new_changed, rte *old_chang
|
||||
/* Fourth case */
|
||||
for (r=r->next; rte_is_valid(r); r=r->next)
|
||||
{
|
||||
if (old_best = export_filter(c, r, &old_free, NULL, 1))
|
||||
if (old_best = export_filter(c, r, &old_free, 1))
|
||||
goto found;
|
||||
|
||||
if (r == before_old)
|
||||
if (old_best = export_filter(c, old_changed, &old_free, NULL, 1))
|
||||
if (old_best = export_filter(c, old_changed, &old_free, 1))
|
||||
goto found;
|
||||
}
|
||||
|
||||
/* Implicitly, old_best is NULL and new_best is non-NULL */
|
||||
|
||||
found:
|
||||
do_rt_notify(c, net, new_best, old_best, tmpa, (feed == 2));
|
||||
do_rt_notify(c, net, new_best, old_best, (feed == 2));
|
||||
|
||||
/* Discard temporary rte's */
|
||||
if (new_free)
|
||||
@ -702,7 +738,7 @@ nexthop_merge_rta(struct nexthop *nhs, rta *a, linpool *pool, int max)
|
||||
}
|
||||
|
||||
rte *
|
||||
rt_export_merged(struct channel *c, net *net, rte **rt_free, ea_list **tmpa, linpool *pool, int silent)
|
||||
rt_export_merged(struct channel *c, net *net, rte **rt_free, linpool *pool, int silent)
|
||||
{
|
||||
// struct proto *p = c->proto;
|
||||
struct nexthop *nhs = NULL;
|
||||
@ -714,7 +750,7 @@ rt_export_merged(struct channel *c, net *net, rte **rt_free, ea_list **tmpa, lin
|
||||
if (!rte_is_valid(best0))
|
||||
return NULL;
|
||||
|
||||
best = export_filter_(c, best0, rt_free, tmpa, pool, silent);
|
||||
best = export_filter_(c, best0, rt_free, pool, silent);
|
||||
|
||||
if (!best || !rte_is_reachable(best))
|
||||
return best;
|
||||
@ -724,7 +760,7 @@ rt_export_merged(struct channel *c, net *net, rte **rt_free, ea_list **tmpa, lin
|
||||
if (!rte_mergable(best0, rt0))
|
||||
continue;
|
||||
|
||||
rt = export_filter_(c, rt0, &tmp, NULL, pool, 1);
|
||||
rt = export_filter_(c, rt0, &tmp, pool, 1);
|
||||
|
||||
if (!rt)
|
||||
continue;
|
||||
@ -764,7 +800,6 @@ rt_notify_merged(struct channel *c, net *net, rte *new_changed, rte *old_changed
|
||||
rte *old_best_free = NULL;
|
||||
rte *new_changed_free = NULL;
|
||||
rte *old_changed_free = NULL;
|
||||
ea_list *tmpa = NULL;
|
||||
|
||||
/* We assume that all rte arguments are either NULL or rte_is_valid() */
|
||||
|
||||
@ -776,10 +811,10 @@ rt_notify_merged(struct channel *c, net *net, rte *new_changed, rte *old_changed
|
||||
if ((new_best == old_best) && !refeed)
|
||||
{
|
||||
new_changed = rte_mergable(new_best, new_changed) ?
|
||||
export_filter(c, new_changed, &new_changed_free, NULL, 1) : NULL;
|
||||
export_filter(c, new_changed, &new_changed_free, 1) : NULL;
|
||||
|
||||
old_changed = rte_mergable(old_best, old_changed) ?
|
||||
export_filter(c, old_changed, &old_changed_free, NULL, 1) : NULL;
|
||||
export_filter(c, old_changed, &old_changed_free, 1) : NULL;
|
||||
|
||||
if (!new_changed && !old_changed)
|
||||
return;
|
||||
@ -792,15 +827,15 @@ rt_notify_merged(struct channel *c, net *net, rte *new_changed, rte *old_changed
|
||||
|
||||
/* Prepare new merged route */
|
||||
if (new_best)
|
||||
new_best = rt_export_merged(c, net, &new_best_free, &tmpa, rte_update_pool, 0);
|
||||
new_best = rt_export_merged(c, net, &new_best_free, rte_update_pool, 0);
|
||||
|
||||
/* Prepare old merged route (without proper merged next hops) */
|
||||
/* There are some issues with running filter on old route - see rt_notify_basic() */
|
||||
if (old_best && !refeed)
|
||||
old_best = export_filter(c, old_best, &old_best_free, NULL, 1);
|
||||
old_best = export_filter(c, old_best, &old_best_free, 1);
|
||||
|
||||
if (new_best || old_best)
|
||||
do_rt_notify(c, net, new_best, old_best, tmpa, refeed);
|
||||
do_rt_notify(c, net, new_best, old_best, refeed);
|
||||
|
||||
/* Discard temporary rte's */
|
||||
if (new_best_free)
|
||||
@ -824,7 +859,7 @@ rt_notify_merged(struct channel *c, net *net, rte *new_changed, rte *old_changed
|
||||
* @new_best: the new best route for the same network
|
||||
* @old_best: the previous best route for the same network
|
||||
* @before_old: The previous route before @old for the same network.
|
||||
* If @before_old is NULL @old was the first.
|
||||
* If @before_old is NULL @old was the first.
|
||||
*
|
||||
* This function gets a routing table update and announces it
|
||||
* to all protocols that acccepts given type of route announcement
|
||||
@ -897,7 +932,9 @@ rte_validate(rte *e)
|
||||
return 0;
|
||||
}
|
||||
|
||||
c = net_classify(n->n.addr);
|
||||
/* FIXME: better handling different nettypes */
|
||||
c = !net_is_flow(n->n.addr) ?
|
||||
net_classify(n->n.addr): (IADDR_HOST | SCOPE_UNIVERSE);
|
||||
if ((c < 0) || !(c & IADDR_HOST) || ((c & IADDR_SCOPE_MASK) <= SCOPE_LINK))
|
||||
{
|
||||
log(L_WARN "Ignoring bogus route %N received via %s",
|
||||
@ -1173,7 +1210,7 @@ rte_recalculate(struct channel *c, net *net, rte *new, struct rte_src *src)
|
||||
}
|
||||
|
||||
if (new)
|
||||
new->lastmod = now;
|
||||
new->lastmod = current_time();
|
||||
|
||||
/* Log the route change */
|
||||
if (p->debug & D_ROUTES)
|
||||
@ -1201,7 +1238,7 @@ rte_recalculate(struct channel *c, net *net, rte *new, struct rte_src *src)
|
||||
|
||||
if (!net->routes &&
|
||||
(table->gc_counter++ >= table->config->gc_max_ops) &&
|
||||
(table->gc_time + table->config->gc_min_time <= now))
|
||||
(table->gc_time + table->config->gc_min_time <= current_time()))
|
||||
rt_schedule_prune(table);
|
||||
|
||||
if (old_ok && p->rte_remove)
|
||||
@ -1296,7 +1333,6 @@ rte_update2(struct channel *c, const net_addr *n, rte *new, struct rte_src *src)
|
||||
struct proto *p = c->proto;
|
||||
struct proto_stats *stats = &c->stats;
|
||||
struct filter *filter = c->in_filter;
|
||||
ea_list *tmpa = NULL;
|
||||
rte *dummy = NULL;
|
||||
net *nn;
|
||||
|
||||
@ -1334,11 +1370,11 @@ rte_update2(struct channel *c, const net_addr *n, rte *new, struct rte_src *src)
|
||||
}
|
||||
else
|
||||
{
|
||||
tmpa = rte_make_tmp_attrs(new, rte_update_pool);
|
||||
rte_make_tmp_attrs(&new, rte_update_pool);
|
||||
if (filter && (filter != FILTER_REJECT))
|
||||
{
|
||||
ea_list *old_tmpa = tmpa;
|
||||
int fr = f_run(filter, &new, &tmpa, rte_update_pool, 0);
|
||||
ea_list *oldea = new->attrs->eattrs;
|
||||
int fr = f_run(filter, &new, rte_update_pool, 0);
|
||||
if (fr > F_ACCEPT)
|
||||
{
|
||||
stats->imp_updates_filtered++;
|
||||
@ -1349,8 +1385,8 @@ rte_update2(struct channel *c, const net_addr *n, rte *new, struct rte_src *src)
|
||||
|
||||
new->flags |= REF_FILTERED;
|
||||
}
|
||||
if (tmpa != old_tmpa && src->proto->store_tmp_attrs)
|
||||
src->proto->store_tmp_attrs(new, tmpa);
|
||||
if (new->attrs->eattrs != oldea && src->proto->store_tmp_attrs)
|
||||
src->proto->store_tmp_attrs(new);
|
||||
}
|
||||
}
|
||||
if (!rta_is_cached(new->attrs)) /* Need to copy attributes */
|
||||
@ -1401,6 +1437,28 @@ rte_discard(rte *old) /* Non-filtered route deletion, used during garbage collec
|
||||
rte_update_unlock();
|
||||
}
|
||||
|
||||
/* Modify existing route by protocol hook, used for long-lived graceful restart */
|
||||
static inline void
|
||||
rte_modify(rte *old)
|
||||
{
|
||||
rte_update_lock();
|
||||
|
||||
rte *new = old->sender->proto->rte_modify(old, rte_update_pool);
|
||||
if (new != old)
|
||||
{
|
||||
if (new)
|
||||
{
|
||||
if (!rta_is_cached(new->attrs))
|
||||
new->attrs = rta_lookup(new->attrs);
|
||||
new->flags = (old->flags & ~REF_MODIFY) | REF_COW;
|
||||
}
|
||||
|
||||
rte_recalculate(old->sender, old->net, new, old->attrs->src);
|
||||
}
|
||||
|
||||
rte_update_unlock();
|
||||
}
|
||||
|
||||
/* Check rtable for best route to given net whether it would be exported do p */
|
||||
int
|
||||
rt_examine(rtable *t, net_addr *a, struct proto *p, struct filter *filter)
|
||||
@ -1414,10 +1472,10 @@ rt_examine(rtable *t, net_addr *a, struct proto *p, struct filter *filter)
|
||||
rte_update_lock();
|
||||
|
||||
/* Rest is stripped down export_filter() */
|
||||
ea_list *tmpa = rte_make_tmp_attrs(rt, rte_update_pool);
|
||||
int v = p->import_control ? p->import_control(p, &rt, &tmpa, rte_update_pool) : 0;
|
||||
rte_make_tmp_attrs(&rt, rte_update_pool);
|
||||
int v = p->import_control ? p->import_control(p, &rt, rte_update_pool) : 0;
|
||||
if (v == RIC_PROCESS)
|
||||
v = (f_run(filter, &rt, &tmpa, rte_update_pool, FF_FORCE_TMPATTR) <= F_ACCEPT);
|
||||
v = (f_run(filter, &rt, rte_update_pool, FF_SILENT) <= F_ACCEPT);
|
||||
|
||||
/* Discard temporary rte */
|
||||
if (rt != n->routes)
|
||||
@ -1485,6 +1543,26 @@ rt_refresh_end(rtable *t, struct channel *c)
|
||||
rt_schedule_prune(t);
|
||||
}
|
||||
|
||||
void
|
||||
rt_modify_stale(rtable *t, struct channel *c)
|
||||
{
|
||||
int prune = 0;
|
||||
|
||||
FIB_WALK(&t->fib, net, n)
|
||||
{
|
||||
rte *e;
|
||||
for (e = n->routes; e; e = e->next)
|
||||
if ((e->sender == c) && (e->flags & REF_STALE) && !(e->flags & REF_FILTERED))
|
||||
{
|
||||
e->flags |= REF_MODIFY;
|
||||
prune = 1;
|
||||
}
|
||||
}
|
||||
FIB_WALK_END;
|
||||
|
||||
if (prune)
|
||||
rt_schedule_prune(t);
|
||||
}
|
||||
|
||||
/**
|
||||
* rte_dump - dump a route
|
||||
@ -1497,7 +1575,7 @@ rte_dump(rte *e)
|
||||
{
|
||||
net *n = e->net;
|
||||
debug("%-1N ", n->n.addr);
|
||||
debug("KF=%02x PF=%02x pref=%d lm=%d ", n->n.flags, e->pflags, e->pref, now-e->lastmod);
|
||||
debug("KF=%02x PF=%02x pref=%d ", n->n.flags, e->pflags, e->pref);
|
||||
rta_dump(e->attrs);
|
||||
if (e->attrs->src->proto->proto->dump_attrs)
|
||||
e->attrs->src->proto->proto->dump_attrs(e);
|
||||
@ -1595,22 +1673,19 @@ rt_event(void *ptr)
|
||||
}
|
||||
|
||||
void
|
||||
rt_setup(pool *p, rtable *t, char *name, struct rtable_config *cf)
|
||||
rt_setup(pool *p, rtable *t, struct rtable_config *cf)
|
||||
{
|
||||
bzero(t, sizeof(*t));
|
||||
t->name = name;
|
||||
t->name = cf->name;
|
||||
t->config = cf;
|
||||
t->addr_type = cf ? cf->addr_type : NET_IP4;
|
||||
t->addr_type = cf->addr_type;
|
||||
fib_init(&t->fib, p, t->addr_type, sizeof(net), OFFSETOF(net, n), 0, NULL);
|
||||
init_list(&t->channels);
|
||||
|
||||
if (cf)
|
||||
{
|
||||
t->rt_event = ev_new(p);
|
||||
t->rt_event->hook = rt_event;
|
||||
t->rt_event->data = t;
|
||||
t->gc_time = now;
|
||||
}
|
||||
t->rt_event = ev_new(p);
|
||||
t->rt_event->hook = rt_event;
|
||||
t->rt_event->data = t;
|
||||
t->gc_time = current_time();
|
||||
}
|
||||
|
||||
/**
|
||||
@ -1679,6 +1754,7 @@ again:
|
||||
|
||||
rescan:
|
||||
for (e=n->routes; e; e=e->next)
|
||||
{
|
||||
if (e->sender->flush_active || (e->flags & REF_DISCARD))
|
||||
{
|
||||
if (limit <= 0)
|
||||
@ -1694,6 +1770,22 @@ again:
|
||||
goto rescan;
|
||||
}
|
||||
|
||||
if (e->flags & REF_MODIFY)
|
||||
{
|
||||
if (limit <= 0)
|
||||
{
|
||||
FIB_ITERATE_PUT(fit);
|
||||
ev_schedule(tab->rt_event);
|
||||
return;
|
||||
}
|
||||
|
||||
rte_modify(e);
|
||||
limit--;
|
||||
|
||||
goto rescan;
|
||||
}
|
||||
}
|
||||
|
||||
if (!n->routes) /* Orphaned FIB entry */
|
||||
{
|
||||
FIB_ITERATE_PUT(fit);
|
||||
@ -1708,7 +1800,7 @@ again:
|
||||
#endif
|
||||
|
||||
tab->gc_counter = 0;
|
||||
tab->gc_time = now;
|
||||
tab->gc_time = current_time();
|
||||
|
||||
/* state change 2->0, 3->1 */
|
||||
tab->prune_state &= 1;
|
||||
@ -2033,6 +2125,13 @@ rt_unlock_table(rtable *r)
|
||||
}
|
||||
}
|
||||
|
||||
static struct rtable_config *
|
||||
rt_find_table_config(struct config *cf, char *name)
|
||||
{
|
||||
struct symbol *sym = cf_find_symbol(cf, name);
|
||||
return (sym && (sym->class == SYM_TABLE)) ? sym->def : NULL;
|
||||
}
|
||||
|
||||
/**
|
||||
* rt_commit - commit new routing table configuration
|
||||
* @new: new configuration
|
||||
@ -2058,11 +2157,10 @@ rt_commit(struct config *new, struct config *old)
|
||||
rtable *ot = o->table;
|
||||
if (!ot->deleted)
|
||||
{
|
||||
struct symbol *sym = cf_find_symbol(new, o->name);
|
||||
if (sym && sym->class == SYM_TABLE && !new->shutdown)
|
||||
r = rt_find_table_config(new, o->name);
|
||||
if (r && (r->addr_type == o->addr_type) && !new->shutdown)
|
||||
{
|
||||
DBG("\t%s: same\n", o->name);
|
||||
r = sym->def;
|
||||
r->table = ot;
|
||||
ot->name = r->name;
|
||||
ot->config = r;
|
||||
@ -2086,7 +2184,7 @@ rt_commit(struct config *new, struct config *old)
|
||||
{
|
||||
rtable *t = mb_alloc(rt_table_pool, sizeof(struct rtable));
|
||||
DBG("\t%s: created\n", r->name);
|
||||
rt_setup(rt_table_pool, t, r->name, r);
|
||||
rt_setup(rt_table_pool, t, r);
|
||||
add_tail(&routing_tables, &t->n);
|
||||
r->table = t;
|
||||
}
|
||||
@ -2192,13 +2290,6 @@ rt_feed_channel_abort(struct channel *c)
|
||||
}
|
||||
}
|
||||
|
||||
static inline unsigned
|
||||
ptr_hash(void *ptr)
|
||||
{
|
||||
uintptr_t p = (uintptr_t) ptr;
|
||||
return p ^ (p << 8) ^ (p >> 16);
|
||||
}
|
||||
|
||||
static inline u32
|
||||
hc_hash(ip_addr a, rtable *dep)
|
||||
{
|
||||
@ -2388,12 +2479,13 @@ static int
|
||||
rt_update_hostentry(rtable *tab, struct hostentry *he)
|
||||
{
|
||||
rta *old_src = he->src;
|
||||
int direct = 0;
|
||||
int pxlen = 0;
|
||||
|
||||
/* Reset the hostentry */
|
||||
he->src = NULL;
|
||||
he->nexthop_linkable = 0;
|
||||
he->dest = RTD_UNREACHABLE;
|
||||
he->nexthop_linkable = 0;
|
||||
he->igp_metric = 0;
|
||||
|
||||
net_addr he_addr;
|
||||
@ -2413,9 +2505,7 @@ rt_update_hostentry(rtable *tab, struct hostentry *he)
|
||||
goto done;
|
||||
}
|
||||
|
||||
he->dest = a->dest;
|
||||
he->nexthop_linkable = 1;
|
||||
if (he->dest == RTD_UNICAST)
|
||||
if (a->dest == RTD_UNICAST)
|
||||
{
|
||||
for (struct nexthop *nh = &(a->nh); nh; nh = nh->next)
|
||||
if (ipa_zero(nh->gw))
|
||||
@ -2428,12 +2518,13 @@ rt_update_hostentry(rtable *tab, struct hostentry *he)
|
||||
goto done;
|
||||
}
|
||||
|
||||
he->nexthop_linkable = 0;
|
||||
break;
|
||||
direct++;
|
||||
}
|
||||
}
|
||||
|
||||
he->src = rta_clone(a);
|
||||
he->dest = a->dest;
|
||||
he->nexthop_linkable = !direct;
|
||||
he->igp_metric = rt_get_igp_metric(e);
|
||||
}
|
||||
|
||||
|
1386
proto/babel/babel.c
1386
proto/babel/babel.c
File diff suppressed because it is too large
Load Diff
@ -2,6 +2,8 @@
|
||||
* BIRD -- The Babel protocol
|
||||
*
|
||||
* Copyright (c) 2015--2016 Toke Hoiland-Jorgensen
|
||||
* (c) 2016--2017 Ondrej Zajicek <santiago@crfreenet.org>
|
||||
* (c) 2016--2017 CZ.NIC z.s.p.o.
|
||||
*
|
||||
* Can be freely distributed and used under the terms of the GNU GPL.
|
||||
*
|
||||
@ -21,10 +23,10 @@
|
||||
#include "lib/lists.h"
|
||||
#include "lib/socket.h"
|
||||
#include "lib/string.h"
|
||||
#include "sysdep/unix/timer.h"
|
||||
#include "lib/timer.h"
|
||||
|
||||
#define EA_BABEL_METRIC EA_CODE(EAP_BABEL, 0)
|
||||
#define EA_BABEL_ROUTER_ID EA_CODE(EAP_BABEL, 1)
|
||||
#define EA_BABEL_METRIC EA_CODE(PROTOCOL_BABEL, 0)
|
||||
#define EA_BABEL_ROUTER_ID EA_CODE(PROTOCOL_BABEL, 1)
|
||||
|
||||
#define BABEL_MAGIC 42
|
||||
#define BABEL_VERSION 2
|
||||
@ -32,25 +34,28 @@
|
||||
#define BABEL_INFINITY 0xFFFF
|
||||
|
||||
|
||||
#define BABEL_HELLO_INTERVAL_WIRED 4 /* Default hello intervals in seconds */
|
||||
#define BABEL_HELLO_INTERVAL_WIRELESS 4
|
||||
#define BABEL_HELLO_INTERVAL_WIRED (4 S_) /* Default hello intervals in seconds */
|
||||
#define BABEL_HELLO_INTERVAL_WIRELESS (4 S_)
|
||||
#define BABEL_HELLO_LIMIT 12
|
||||
#define BABEL_UPDATE_INTERVAL_FACTOR 4
|
||||
#define BABEL_IHU_INTERVAL_FACTOR 3
|
||||
#define BABEL_IHU_EXPIRY_FACTOR(X) ((X)*3/2) /* 1.5 */
|
||||
#define BABEL_HELLO_EXPIRY_FACTOR(X) ((X)*3/2) /* 1.5 */
|
||||
#define BABEL_ROUTE_EXPIRY_FACTOR(X) ((X)*7/2) /* 3.5 */
|
||||
#define BABEL_ROUTE_REFRESH_INTERVAL 2 /* Seconds before route expiry to send route request */
|
||||
#define BABEL_HOLD_TIME 10 /* Expiry time for our own routes */
|
||||
#define BABEL_HOLD_TIME_FACTOR 4 /* How long we keep unreachable route relative to update interval */
|
||||
#define BABEL_IHU_EXPIRY_FACTOR(X) ((btime)(X)*7/2) /* 3.5 */
|
||||
#define BABEL_HELLO_EXPIRY_FACTOR(X) ((btime)(X)*3/2) /* 1.5 */
|
||||
#define BABEL_ROUTE_EXPIRY_FACTOR(X) ((btime)(X)*7/2) /* 3.5 */
|
||||
#define BABEL_ROUTE_REFRESH_FACTOR(X) ((btime)(X)*5/2) /* 2.5 */
|
||||
#define BABEL_SEQNO_REQUEST_RETRY 4
|
||||
#define BABEL_SEQNO_REQUEST_EXPIRY (2 S_)
|
||||
#define BABEL_GARBAGE_INTERVAL (300 S_)
|
||||
#define BABEL_RXCOST_WIRED 96
|
||||
#define BABEL_RXCOST_WIRELESS 256
|
||||
#define BABEL_INITIAL_HOP_COUNT 255
|
||||
#define BABEL_MAX_SEND_INTERVAL 5
|
||||
#define BABEL_TIME_UNITS 100 /* On-wire times are counted in centiseconds */
|
||||
#define BABEL_SEQNO_REQUEST_EXPIRY 60
|
||||
#define BABEL_GARBAGE_INTERVAL 300
|
||||
#define BABEL_MAX_SEND_INTERVAL 5 /* Unused ? */
|
||||
|
||||
/* Max interval that will not overflow when carried as 16-bit centiseconds */
|
||||
#define BABEL_MAX_INTERVAL (0xFFFF/BABEL_TIME_UNITS)
|
||||
#define BABEL_TIME_UNITS 10000 /* On-wire times are counted in centiseconds */
|
||||
#define BABEL_MIN_INTERVAL (0x0001 * BABEL_TIME_UNITS)
|
||||
#define BABEL_MAX_INTERVAL (0xFFFF * BABEL_TIME_UNITS)
|
||||
|
||||
#define BABEL_OVERHEAD (IP6_HEADER_LENGTH+UDP_HEADER_LENGTH)
|
||||
#define BABEL_MIN_MTU (512 + BABEL_OVERHEAD)
|
||||
@ -80,7 +85,10 @@ enum babel_tlv_type {
|
||||
|
||||
enum babel_subtlv_type {
|
||||
BABEL_SUBTLV_PAD1 = 0,
|
||||
BABEL_SUBTLV_PADN = 1
|
||||
BABEL_SUBTLV_PADN = 1,
|
||||
|
||||
/* Mandatory subtlvs */
|
||||
BABEL_SUBTLV_SOURCE_PREFIX = 128,
|
||||
};
|
||||
|
||||
enum babel_iface_type {
|
||||
@ -102,8 +110,12 @@ enum babel_ae_type {
|
||||
|
||||
struct babel_config {
|
||||
struct proto_config c;
|
||||
list iface_list; /* List of iface configs (struct babel_iface_config) */
|
||||
uint hold_time; /* Time to hold stale entries and unreachable routes */
|
||||
u8 randomize_router_id;
|
||||
|
||||
list iface_list; /* Patterns configured -- keep it first; see babel_reconfigure why */
|
||||
struct channel_config *ip4_channel;
|
||||
struct channel_config *ip6_channel;
|
||||
};
|
||||
|
||||
struct babel_iface_config {
|
||||
@ -111,11 +123,12 @@ struct babel_iface_config {
|
||||
|
||||
u16 rxcost;
|
||||
u8 type;
|
||||
u8 limit; /* Minimum number of Hellos to keep link up */
|
||||
u8 check_link;
|
||||
uint port;
|
||||
u16 hello_interval;
|
||||
u16 ihu_interval;
|
||||
u16 update_interval;
|
||||
uint hello_interval; /* Hello interval, in us */
|
||||
uint ihu_interval; /* IHU interval, in us */
|
||||
uint update_interval; /* Update interval, in us */
|
||||
|
||||
u16 rx_buffer; /* RX buffer size, 0 for MTU */
|
||||
u16 tx_length; /* TX packet length limit (including headers), 0 for MTU */
|
||||
@ -138,14 +151,13 @@ struct babel_proto {
|
||||
list interfaces; /* Interfaces we really know about (struct babel_iface) */
|
||||
u64 router_id;
|
||||
u16 update_seqno; /* To be increased on request */
|
||||
u8 update_seqno_inc; /* Request for update_seqno increase */
|
||||
u8 triggered; /* For triggering global updates */
|
||||
|
||||
slab *route_slab;
|
||||
slab *source_slab;
|
||||
slab *msg_slab;
|
||||
|
||||
slab *seqno_slab;
|
||||
list seqno_cache; /* Seqno requests in the cache (struct babel_seqno_request) */
|
||||
|
||||
struct tbf log_pkt_tbf; /* TBF for packet messages */
|
||||
};
|
||||
@ -172,10 +184,10 @@ struct babel_iface {
|
||||
|
||||
u16 hello_seqno; /* To be increased on each hello */
|
||||
|
||||
bird_clock_t next_hello;
|
||||
bird_clock_t next_regular;
|
||||
bird_clock_t next_triggered;
|
||||
bird_clock_t want_triggered;
|
||||
btime next_hello;
|
||||
btime next_regular;
|
||||
btime next_triggered;
|
||||
btime want_triggered;
|
||||
|
||||
timer *timer;
|
||||
event *send_event;
|
||||
@ -186,13 +198,18 @@ struct babel_neighbor {
|
||||
struct babel_iface *ifa;
|
||||
|
||||
ip_addr addr;
|
||||
u16 txcost;
|
||||
uint uc; /* Reference counter for seqno requests */
|
||||
u16 rxcost; /* Sent in last IHU */
|
||||
u16 txcost; /* Received in last IHU */
|
||||
u16 cost; /* Computed neighbor cost */
|
||||
s8 ihu_cnt; /* IHU countdown, 0 to send it */
|
||||
u8 hello_cnt;
|
||||
u16 hello_map;
|
||||
u16 next_hello_seqno;
|
||||
uint last_hello_int;
|
||||
/* expiry timers */
|
||||
bird_clock_t hello_expiry;
|
||||
bird_clock_t ihu_expiry;
|
||||
btime hello_expiry;
|
||||
btime ihu_expiry;
|
||||
|
||||
list routes; /* Routes this neighbour has sent us (struct babel_route) */
|
||||
};
|
||||
@ -203,7 +220,7 @@ struct babel_source {
|
||||
u64 router_id;
|
||||
u16 seqno;
|
||||
u16 metric;
|
||||
bird_clock_t expires;
|
||||
btime expires;
|
||||
};
|
||||
|
||||
struct babel_route {
|
||||
@ -212,37 +229,46 @@ struct babel_route {
|
||||
struct babel_entry *e;
|
||||
struct babel_neighbor *neigh;
|
||||
|
||||
u8 feasible;
|
||||
u16 seqno;
|
||||
u16 advert_metric;
|
||||
u16 metric;
|
||||
u16 advert_metric;
|
||||
u64 router_id;
|
||||
ip_addr next_hop;
|
||||
bird_clock_t refresh_time;
|
||||
bird_clock_t expires;
|
||||
u16 expiry_interval;
|
||||
btime refresh_time;
|
||||
btime expires;
|
||||
};
|
||||
|
||||
struct babel_seqno_request {
|
||||
node n;
|
||||
u64 router_id;
|
||||
u16 seqno;
|
||||
u8 hop_count;
|
||||
u8 count;
|
||||
btime expires;
|
||||
struct babel_neighbor *nbr;
|
||||
};
|
||||
|
||||
struct babel_entry {
|
||||
struct babel_proto *proto;
|
||||
struct babel_route *selected_in;
|
||||
struct babel_route *selected_out;
|
||||
struct babel_route *selected;
|
||||
|
||||
bird_clock_t updated;
|
||||
|
||||
list sources; /* Source entries for this prefix (struct babel_source). */
|
||||
list routes; /* Routes for this prefix (struct babel_route) */
|
||||
list sources; /* Source entries for this prefix (struct babel_source). */
|
||||
list requests;
|
||||
|
||||
u8 valid; /* Entry validity state (BABEL_ENTRY_*) */
|
||||
u8 unreachable; /* Unreachable route is announced */
|
||||
u16 seqno; /* Outgoing seqno */
|
||||
u16 metric; /* Outgoing metric */
|
||||
u64 router_id; /* Outgoing router ID */
|
||||
btime updated; /* Last change of outgoing rte, for triggered updates */
|
||||
|
||||
struct fib_node n;
|
||||
};
|
||||
|
||||
/* Stores forwarded seqno requests for duplicate suppression. */
|
||||
struct babel_seqno_request {
|
||||
node n;
|
||||
net_addr net;
|
||||
u64 router_id;
|
||||
u16 seqno;
|
||||
bird_clock_t updated;
|
||||
};
|
||||
#define BABEL_ENTRY_DUMMY 0 /* No outgoing route */
|
||||
#define BABEL_ENTRY_VALID 1 /* Valid outgoing route */
|
||||
#define BABEL_ENTRY_STALE 2 /* Stale outgoing route, waiting for GC */
|
||||
|
||||
|
||||
/*
|
||||
@ -252,7 +278,7 @@ struct babel_seqno_request {
|
||||
struct babel_msg_ack_req {
|
||||
u8 type;
|
||||
u16 nonce;
|
||||
u16 interval;
|
||||
uint interval;
|
||||
ip_addr sender;
|
||||
};
|
||||
|
||||
@ -264,7 +290,7 @@ struct babel_msg_ack {
|
||||
struct babel_msg_hello {
|
||||
u8 type;
|
||||
u16 seqno;
|
||||
u16 interval;
|
||||
uint interval;
|
||||
ip_addr sender;
|
||||
};
|
||||
|
||||
@ -272,7 +298,7 @@ struct babel_msg_ihu {
|
||||
u8 type;
|
||||
u8 ae;
|
||||
u16 rxcost;
|
||||
u16 interval;
|
||||
uint interval;
|
||||
ip_addr addr;
|
||||
ip_addr sender;
|
||||
};
|
||||
@ -280,11 +306,14 @@ struct babel_msg_ihu {
|
||||
struct babel_msg_update {
|
||||
u8 type;
|
||||
u8 wildcard;
|
||||
u16 interval;
|
||||
uint interval;
|
||||
u16 seqno;
|
||||
u16 metric;
|
||||
u64 router_id;
|
||||
net_addr net;
|
||||
union {
|
||||
net_addr net;
|
||||
net_addr_ip6_sadr net_sadr;
|
||||
};
|
||||
ip_addr next_hop;
|
||||
ip_addr sender;
|
||||
};
|
||||
@ -292,7 +321,10 @@ struct babel_msg_update {
|
||||
struct babel_msg_route_request {
|
||||
u8 type;
|
||||
u8 full;
|
||||
net_addr net;
|
||||
union {
|
||||
net_addr net;
|
||||
net_addr_ip6_sadr net_sadr;
|
||||
};
|
||||
};
|
||||
|
||||
struct babel_msg_seqno_request {
|
||||
@ -300,7 +332,10 @@ struct babel_msg_seqno_request {
|
||||
u8 hop_count;
|
||||
u16 seqno;
|
||||
u64 router_id;
|
||||
net_addr net;
|
||||
union {
|
||||
net_addr net;
|
||||
net_addr_ip6_sadr net_sadr;
|
||||
};
|
||||
ip_addr sender;
|
||||
};
|
||||
|
||||
@ -320,6 +355,8 @@ struct babel_msg_node {
|
||||
union babel_msg msg;
|
||||
};
|
||||
|
||||
static inline int babel_sadr_enabled(struct babel_proto *p)
|
||||
{ return p->ip6_rtable.addr_type == NET_IP6_SADR; }
|
||||
|
||||
/* babel.c */
|
||||
void babel_handle_ack_req(union babel_msg *msg, struct babel_iface *ifa);
|
||||
@ -334,6 +371,7 @@ void babel_handle_seqno_request(union babel_msg *msg, struct babel_iface *ifa);
|
||||
void babel_show_interfaces(struct proto *P, char *iff);
|
||||
void babel_show_neighbors(struct proto *P, char *iff);
|
||||
void babel_show_entries(struct proto *P);
|
||||
void babel_show_routes(struct proto *P);
|
||||
|
||||
/* packets.c */
|
||||
void babel_enqueue(union babel_msg *msg, struct babel_iface *ifa);
|
||||
|
@ -2,6 +2,8 @@
|
||||
* BIRD -- Babel Configuration
|
||||
*
|
||||
* Copyright (c) 2015-2016 Toke Hoiland-Jorgensen
|
||||
* (c) 2016--2017 Ondrej Zajicek <santiago@crfreenet.org>
|
||||
* (c) 2016--2017 CZ.NIC z.s.p.o.
|
||||
*
|
||||
* Can be freely distributed and used under the terms of the GNU GPL.
|
||||
*/
|
||||
@ -20,24 +22,27 @@ CF_DEFINES
|
||||
|
||||
CF_DECLS
|
||||
|
||||
CF_KEYWORDS(BABEL, METRIC, RXCOST, HELLO, UPDATE, INTERVAL, PORT, WIRED,
|
||||
WIRELESS, RX, TX, BUFFER, LENGTH, CHECK, LINK, BABEL_METRIC, NEXT, HOP,
|
||||
IPV4, IPV6)
|
||||
CF_KEYWORDS(BABEL, INTERFACE, METRIC, RXCOST, HELLO, UPDATE, INTERVAL, PORT,
|
||||
TYPE, WIRED, WIRELESS, RX, TX, BUFFER, PRIORITY, LENGTH, CHECK, LINK,
|
||||
NEXT, HOP, IPV4, IPV6, BABEL_METRIC, SHOW, INTERFACES, NEIGHBORS,
|
||||
ENTRIES, RANDOMIZE, ROUTER, ID)
|
||||
|
||||
CF_GRAMMAR
|
||||
|
||||
CF_ADDTO(proto, babel_proto)
|
||||
proto: babel_proto ;
|
||||
|
||||
babel_proto_start: proto_start BABEL
|
||||
{
|
||||
this_proto = proto_config_new(&proto_babel, $1);
|
||||
init_list(&BABEL_CFG->iface_list);
|
||||
BABEL_CFG->hold_time = 1 S_;
|
||||
};
|
||||
|
||||
babel_proto_item:
|
||||
proto_item
|
||||
| proto_channel
|
||||
| INTERFACE babel_iface
|
||||
| RANDOMIZE ROUTER ID bool { BABEL_CFG->randomize_router_id = $4; }
|
||||
;
|
||||
|
||||
babel_proto_opts:
|
||||
@ -56,6 +61,7 @@ babel_iface_start:
|
||||
init_list(&this_ipatt->ipn_list);
|
||||
BABEL_IFACE->port = BABEL_PORT;
|
||||
BABEL_IFACE->type = BABEL_IFACE_TYPE_WIRED;
|
||||
BABEL_IFACE->limit = BABEL_HELLO_LIMIT;
|
||||
BABEL_IFACE->tx_tos = IP_PREC_INTERNET_CONTROL;
|
||||
BABEL_IFACE->tx_priority = sk_priority_control;
|
||||
BABEL_IFACE->check_link = 1;
|
||||
@ -83,16 +89,19 @@ babel_iface_finish:
|
||||
if (!BABEL_IFACE->update_interval)
|
||||
BABEL_IFACE->update_interval = MIN_(BABEL_IFACE->hello_interval*BABEL_UPDATE_INTERVAL_FACTOR, BABEL_MAX_INTERVAL);
|
||||
BABEL_IFACE->ihu_interval = MIN_(BABEL_IFACE->hello_interval*BABEL_IHU_INTERVAL_FACTOR, BABEL_MAX_INTERVAL);
|
||||
|
||||
BABEL_CFG->hold_time = MAX_(BABEL_CFG->hold_time, BABEL_IFACE->update_interval*BABEL_HOLD_TIME_FACTOR);
|
||||
};
|
||||
|
||||
|
||||
babel_iface_item:
|
||||
| PORT expr { BABEL_IFACE->port = $2; if (($2<1) || ($2>65535)) cf_error("Invalid port number"); }
|
||||
| RXCOST expr { BABEL_IFACE->rxcost = $2; if (($2<1) || ($2>65535)) cf_error("Invalid rxcost"); }
|
||||
| HELLO INTERVAL expr { BABEL_IFACE->hello_interval = $3; if (($3<1) || ($3>BABEL_MAX_INTERVAL)) cf_error("Invalid hello interval"); }
|
||||
| UPDATE INTERVAL expr { BABEL_IFACE->update_interval = $3; if (($3<1) || ($3>BABEL_MAX_INTERVAL)) cf_error("Invalid update interval"); }
|
||||
| LIMIT expr { BABEL_IFACE->limit = $2; if (($2<1) || ($2>16)) cf_error("Limit must be in range 1-16"); }
|
||||
| TYPE WIRED { BABEL_IFACE->type = BABEL_IFACE_TYPE_WIRED; }
|
||||
| TYPE WIRELESS { BABEL_IFACE->type = BABEL_IFACE_TYPE_WIRELESS; }
|
||||
| HELLO INTERVAL expr_us { BABEL_IFACE->hello_interval = $3; if (($3<BABEL_MIN_INTERVAL) || ($3>BABEL_MAX_INTERVAL)) cf_error("Hello interval must be in range 10 ms - 655 s"); }
|
||||
| UPDATE INTERVAL expr_us { BABEL_IFACE->update_interval = $3; if (($3<BABEL_MIN_INTERVAL) || ($3>BABEL_MAX_INTERVAL)) cf_error("Update interval must be in range 10 ms - 655 s"); }
|
||||
| RX BUFFER expr { BABEL_IFACE->rx_buffer = $3; if (($3<256) || ($3>65535)) cf_error("RX buffer must be in range 256-65535"); }
|
||||
| TX LENGTH expr { BABEL_IFACE->tx_length = $3; if (($3<256) || ($3>65535)) cf_error("TX length must be in range 256-65535"); }
|
||||
| TX tos { BABEL_IFACE->tx_tos = $2; }
|
||||
@ -116,7 +125,7 @@ babel_iface_opt_list:
|
||||
babel_iface:
|
||||
babel_iface_start iface_patt_list_nopx babel_iface_opt_list babel_iface_finish;
|
||||
|
||||
CF_ADDTO(dynamic_attr, BABEL_METRIC { $$ = f_new_dynamic_attr(EAF_TYPE_INT | EAF_TEMP, T_INT, EA_BABEL_METRIC); })
|
||||
dynamic_attr: BABEL_METRIC { $$ = f_new_dynamic_attr(EAF_TYPE_INT, T_INT, EA_BABEL_METRIC); } ;
|
||||
|
||||
CF_CLI_HELP(SHOW BABEL, ..., [[Show information about Babel protocol]]);
|
||||
|
||||
@ -129,6 +138,9 @@ CF_CLI(SHOW BABEL NEIGHBORS, optsym opttext, [<name>] [\"<interface>\"], [[Show
|
||||
CF_CLI(SHOW BABEL ENTRIES, optsym opttext, [<name>], [[Show information about Babel prefix entries]])
|
||||
{ babel_show_entries(proto_get_named($4, &proto_babel)); };
|
||||
|
||||
CF_CLI(SHOW BABEL ROUTES, optsym opttext, [<name>], [[Show information about Babel route entries]])
|
||||
{ babel_show_routes(proto_get_named($4, &proto_babel)); };
|
||||
|
||||
CF_CODE
|
||||
|
||||
CF_END
|
||||
|
@ -2,6 +2,8 @@
|
||||
* BIRD -- The Babel protocol
|
||||
*
|
||||
* Copyright (c) 2015--2016 Toke Hoiland-Jorgensen
|
||||
* (c) 2016--2017 Ondrej Zajicek <santiago@crfreenet.org>
|
||||
* (c) 2016--2017 CZ.NIC z.s.p.o.
|
||||
*
|
||||
* Can be freely distributed and used under the terms of the GNU GPL.
|
||||
*
|
||||
@ -40,7 +42,7 @@ struct babel_tlv_ack {
|
||||
struct babel_tlv_hello {
|
||||
u8 type;
|
||||
u8 length;
|
||||
u16 reserved;
|
||||
u16 flags;
|
||||
u16 seqno;
|
||||
u16 interval;
|
||||
} PACKED;
|
||||
@ -103,9 +105,20 @@ struct babel_tlv_seqno_request {
|
||||
u8 addr[0];
|
||||
} PACKED;
|
||||
|
||||
struct babel_subtlv_source_prefix {
|
||||
u8 type;
|
||||
u8 length;
|
||||
u8 plen;
|
||||
u8 addr[0];
|
||||
} PACKED;
|
||||
|
||||
#define BABEL_FLAG_DEF_PREFIX 0x80
|
||||
#define BABEL_FLAG_ROUTER_ID 0x40
|
||||
|
||||
/* Hello flags */
|
||||
#define BABEL_HF_UNICAST 0x8000
|
||||
|
||||
/* Update flags */
|
||||
#define BABEL_UF_DEF_PREFIX 0x80
|
||||
#define BABEL_UF_ROUTER_ID 0x40
|
||||
|
||||
|
||||
struct babel_parse_state {
|
||||
@ -121,6 +134,7 @@ struct babel_parse_state {
|
||||
u8 def_ip6_prefix_seen; /* def_ip6_prefix is valid */
|
||||
u8 def_ip4_prefix_seen; /* def_ip4_prefix is valid */
|
||||
u8 current_tlv_endpos; /* End of self-terminating TLVs (offset from start) */
|
||||
u8 sadr_enabled;
|
||||
};
|
||||
|
||||
enum parse_result {
|
||||
@ -162,17 +176,17 @@ bytes_equal(u8 *b1, u8 *b2, uint maxlen)
|
||||
return i;
|
||||
}
|
||||
|
||||
static inline u16
|
||||
static inline uint
|
||||
get_time16(const void *p)
|
||||
{
|
||||
u16 v = get_u16(p) / BABEL_TIME_UNITS;
|
||||
return MAX(1, v);
|
||||
uint v = get_u16(p) * BABEL_TIME_UNITS;
|
||||
return MAX(BABEL_MIN_INTERVAL, v);
|
||||
}
|
||||
|
||||
static inline void
|
||||
put_time16(void *p, u16 v)
|
||||
put_time16(void *p, uint v)
|
||||
{
|
||||
put_u16(p, v * BABEL_TIME_UNITS);
|
||||
put_u16(p, v / BABEL_TIME_UNITS);
|
||||
}
|
||||
|
||||
static inline void
|
||||
@ -231,6 +245,7 @@ static int babel_read_next_hop(struct babel_tlv *hdr, union babel_msg *msg, stru
|
||||
static int babel_read_update(struct babel_tlv *hdr, union babel_msg *msg, struct babel_parse_state *state);
|
||||
static int babel_read_route_request(struct babel_tlv *hdr, union babel_msg *msg, struct babel_parse_state *state);
|
||||
static int babel_read_seqno_request(struct babel_tlv *hdr, union babel_msg *msg, struct babel_parse_state *state);
|
||||
static int babel_read_source_prefix(struct babel_tlv *hdr, union babel_msg *msg, struct babel_parse_state *state);
|
||||
|
||||
static uint babel_write_ack(struct babel_tlv *hdr, union babel_msg *msg, struct babel_write_state *state, uint max_len);
|
||||
static uint babel_write_hello(struct babel_tlv *hdr, union babel_msg *msg, struct babel_write_state *state, uint max_len);
|
||||
@ -238,6 +253,7 @@ static uint babel_write_ihu(struct babel_tlv *hdr, union babel_msg *msg, struct
|
||||
static uint babel_write_update(struct babel_tlv *hdr, union babel_msg *msg, struct babel_write_state *state, uint max_len);
|
||||
static uint babel_write_route_request(struct babel_tlv *hdr, union babel_msg *msg, struct babel_write_state *state, uint max_len);
|
||||
static uint babel_write_seqno_request(struct babel_tlv *hdr, union babel_msg *msg, struct babel_write_state *state, uint max_len);
|
||||
static int babel_write_source_prefix(struct babel_tlv *hdr, net_addr *net, uint max_len);
|
||||
|
||||
struct babel_tlv_data {
|
||||
u8 min_length;
|
||||
@ -341,6 +357,11 @@ babel_read_hello(struct babel_tlv *hdr, union babel_msg *m,
|
||||
struct babel_tlv_hello *tlv = (void *) hdr;
|
||||
struct babel_msg_hello *msg = &m->hello;
|
||||
|
||||
/* We currently don't support unicast Hello */
|
||||
u16 flags = get_u16(&tlv->flags);
|
||||
if (flags & BABEL_HF_UNICAST)
|
||||
return PARSE_IGNORE;
|
||||
|
||||
msg->type = BABEL_TLV_HELLO;
|
||||
msg->seqno = get_u16(&tlv->seqno);
|
||||
msg->interval = get_time16(&tlv->interval);
|
||||
@ -593,9 +614,9 @@ babel_read_update(struct babel_tlv *hdr, union babel_msg *m,
|
||||
if (tlv->omitted && !state->def_ip4_prefix_seen)
|
||||
return PARSE_ERROR;
|
||||
|
||||
/* Need next hop for v4 routes */
|
||||
if (ipa_zero(state->next_hop_ip4))
|
||||
return PARSE_ERROR;
|
||||
/* Update must have next hop, unless it is retraction */
|
||||
if (ipa_zero(state->next_hop_ip4) && (msg->metric != BABEL_INFINITY))
|
||||
return PARSE_IGNORE;
|
||||
|
||||
/* Merge saved prefix and received prefix parts */
|
||||
memcpy(buf, state->def_ip4_prefix, tlv->omitted);
|
||||
@ -604,7 +625,7 @@ babel_read_update(struct babel_tlv *hdr, union babel_msg *m,
|
||||
ip4_addr prefix4 = get_ip4(buf);
|
||||
net_fill_ip4(&msg->net, prefix4, tlv->plen);
|
||||
|
||||
if (tlv->flags & BABEL_FLAG_DEF_PREFIX)
|
||||
if (tlv->flags & BABEL_UF_DEF_PREFIX)
|
||||
{
|
||||
put_ip4(state->def_ip4_prefix, prefix4);
|
||||
state->def_ip4_prefix_seen = 1;
|
||||
@ -629,13 +650,16 @@ babel_read_update(struct babel_tlv *hdr, union babel_msg *m,
|
||||
ip6_addr prefix6 = get_ip6(buf);
|
||||
net_fill_ip6(&msg->net, prefix6, tlv->plen);
|
||||
|
||||
if (tlv->flags & BABEL_FLAG_DEF_PREFIX)
|
||||
if (state->sadr_enabled)
|
||||
net_make_ip6_sadr(&msg->net);
|
||||
|
||||
if (tlv->flags & BABEL_UF_DEF_PREFIX)
|
||||
{
|
||||
put_ip6(state->def_ip6_prefix, prefix6);
|
||||
state->def_ip6_prefix_seen = 1;
|
||||
}
|
||||
|
||||
if (tlv->flags & BABEL_FLAG_ROUTER_ID)
|
||||
if (tlv->flags & BABEL_UF_ROUTER_ID)
|
||||
{
|
||||
state->router_id = ((u64) _I2(prefix6)) << 32 | _I3(prefix6);
|
||||
state->router_id_seen = 1;
|
||||
@ -748,7 +772,7 @@ babel_write_update(struct babel_tlv *hdr, union babel_msg *m,
|
||||
else
|
||||
{
|
||||
put_ip6_px(tlv->addr, &msg->net);
|
||||
tlv->flags |= BABEL_FLAG_DEF_PREFIX;
|
||||
tlv->flags |= BABEL_UF_DEF_PREFIX;
|
||||
|
||||
put_ip6(state->def_ip6_prefix, net6_prefix(&msg->net));
|
||||
state->def_ip6_pxlen = tlv->plen;
|
||||
@ -759,12 +783,21 @@ babel_write_update(struct babel_tlv *hdr, union babel_msg *m,
|
||||
put_u16(&tlv->seqno, msg->seqno);
|
||||
put_u16(&tlv->metric, msg->metric);
|
||||
|
||||
if (msg->net.type == NET_IP6_SADR)
|
||||
{
|
||||
int l = babel_write_source_prefix(hdr, &msg->net, max_len - (len0 + len));
|
||||
if (l < 0)
|
||||
return 0;
|
||||
|
||||
len += l;
|
||||
}
|
||||
|
||||
return len0 + len;
|
||||
}
|
||||
|
||||
static int
|
||||
babel_read_route_request(struct babel_tlv *hdr, union babel_msg *m,
|
||||
struct babel_parse_state *state UNUSED)
|
||||
struct babel_parse_state *state)
|
||||
{
|
||||
struct babel_tlv_route_request *tlv = (void *) hdr;
|
||||
struct babel_msg_route_request *msg = &m->route_request;
|
||||
@ -801,6 +834,10 @@ babel_read_route_request(struct babel_tlv *hdr, union babel_msg *m,
|
||||
|
||||
read_ip6_px(&msg->net, tlv->addr, tlv->plen);
|
||||
state->current_tlv_endpos += BYTES(tlv->plen);
|
||||
|
||||
if (state->sadr_enabled)
|
||||
net_make_ip6_sadr(&msg->net);
|
||||
|
||||
return PARSE_SUCCESS;
|
||||
|
||||
case BABEL_AE_IP6_LL:
|
||||
@ -845,6 +882,15 @@ babel_write_route_request(struct babel_tlv *hdr, union babel_msg *m,
|
||||
put_ip6_px(tlv->addr, &msg->net);
|
||||
}
|
||||
|
||||
if (msg->net.type == NET_IP6_SADR)
|
||||
{
|
||||
int l = babel_write_source_prefix(hdr, &msg->net, max_len - len);
|
||||
if (l < 0)
|
||||
return 0;
|
||||
|
||||
len += l;
|
||||
}
|
||||
|
||||
return len;
|
||||
}
|
||||
|
||||
@ -889,6 +935,10 @@ babel_read_seqno_request(struct babel_tlv *hdr, union babel_msg *m,
|
||||
|
||||
read_ip6_px(&msg->net, tlv->addr, tlv->plen);
|
||||
state->current_tlv_endpos += BYTES(tlv->plen);
|
||||
|
||||
if (state->sadr_enabled)
|
||||
net_make_ip6_sadr(&msg->net);
|
||||
|
||||
return PARSE_SUCCESS;
|
||||
|
||||
case BABEL_AE_IP6_LL:
|
||||
@ -932,38 +982,147 @@ babel_write_seqno_request(struct babel_tlv *hdr, union babel_msg *m,
|
||||
tlv->hop_count = msg->hop_count;
|
||||
put_u64(&tlv->router_id, msg->router_id);
|
||||
|
||||
if (msg->net.type == NET_IP6_SADR)
|
||||
{
|
||||
int l = babel_write_source_prefix(hdr, &msg->net, max_len - len);
|
||||
if (l < 0)
|
||||
return 0;
|
||||
|
||||
len += l;
|
||||
}
|
||||
|
||||
return len;
|
||||
}
|
||||
|
||||
static int
|
||||
babel_read_source_prefix(struct babel_tlv *hdr, union babel_msg *msg,
|
||||
struct babel_parse_state *state UNUSED)
|
||||
{
|
||||
struct babel_subtlv_source_prefix *tlv = (void *) hdr;
|
||||
net_addr_ip6_sadr *net;
|
||||
|
||||
/*
|
||||
* We would like to skip the sub-TLV if SADR is not enabled, but we do not
|
||||
* know AF of the enclosing TLV yet. We will do that later.
|
||||
*/
|
||||
|
||||
/* Check internal consistency */
|
||||
if ((tlv->length < 1) ||
|
||||
(tlv->plen > IP6_MAX_PREFIX_LENGTH) ||
|
||||
(tlv->length < (1 + BYTES(tlv->plen))))
|
||||
return PARSE_ERROR;
|
||||
|
||||
/* Plen MUST NOT be 0 */
|
||||
if (tlv->plen == 0)
|
||||
return PARSE_ERROR;
|
||||
|
||||
switch(msg->type)
|
||||
{
|
||||
case BABEL_TLV_UPDATE:
|
||||
/* Wildcard updates with source prefix MUST be silently ignored */
|
||||
if (msg->update.wildcard)
|
||||
return PARSE_IGNORE;
|
||||
|
||||
net = (void *) &msg->update.net;
|
||||
break;
|
||||
|
||||
case BABEL_TLV_ROUTE_REQUEST:
|
||||
/* Wildcard requests with source addresses MUST be silently ignored */
|
||||
if (msg->route_request.full)
|
||||
return PARSE_IGNORE;
|
||||
|
||||
net = (void *) &msg->route_request.net;
|
||||
break;
|
||||
|
||||
case BABEL_TLV_SEQNO_REQUEST:
|
||||
net = (void *) &msg->seqno_request.net;
|
||||
break;
|
||||
|
||||
default:
|
||||
return PARSE_ERROR;
|
||||
}
|
||||
|
||||
/* If SADR is active, the net has appropriate type */
|
||||
if (net->type != NET_IP6_SADR)
|
||||
return PARSE_IGNORE;
|
||||
|
||||
/* Duplicate Source Prefix sub-TLV; SHOULD ignore whole TLV */
|
||||
if (net->src_pxlen > 0)
|
||||
return PARSE_IGNORE;
|
||||
|
||||
net_addr_ip6 src;
|
||||
read_ip6_px((void *) &src, tlv->addr, tlv->plen);
|
||||
net->src_prefix = src.prefix;
|
||||
net->src_pxlen = src.pxlen;
|
||||
|
||||
return PARSE_SUCCESS;
|
||||
}
|
||||
|
||||
static int
|
||||
babel_write_source_prefix(struct babel_tlv *hdr, net_addr *n, uint max_len)
|
||||
{
|
||||
struct babel_subtlv_source_prefix *tlv = (void *) NEXT_TLV(hdr);
|
||||
net_addr_ip6_sadr *net = (void *) n;
|
||||
|
||||
/* Do not use this sub-TLV for default prefix */
|
||||
if (net->src_pxlen == 0)
|
||||
return 0;
|
||||
|
||||
uint len = sizeof(*tlv) + BYTES(net->src_pxlen);
|
||||
|
||||
if (len > max_len)
|
||||
return -1;
|
||||
|
||||
TLV_HDR(tlv, BABEL_SUBTLV_SOURCE_PREFIX, len);
|
||||
hdr->length += len;
|
||||
|
||||
net_addr_ip6 src = NET_ADDR_IP6(net->src_prefix, net->src_pxlen);
|
||||
tlv->plen = src.pxlen;
|
||||
put_ip6_px(tlv->addr, (void *) &src);
|
||||
|
||||
return len;
|
||||
}
|
||||
|
||||
|
||||
static inline int
|
||||
babel_read_subtlvs(struct babel_tlv *hdr,
|
||||
union babel_msg *msg UNUSED,
|
||||
union babel_msg *msg,
|
||||
struct babel_parse_state *state)
|
||||
{
|
||||
struct babel_tlv *tlv;
|
||||
byte *pos, *end = (byte *) hdr + TLV_LENGTH(hdr);
|
||||
int res;
|
||||
|
||||
for (tlv = (void *) hdr + state->current_tlv_endpos;
|
||||
(void *) tlv < (void *) hdr + TLV_LENGTH(hdr);
|
||||
(byte *) tlv < end;
|
||||
tlv = NEXT_TLV(tlv))
|
||||
{
|
||||
/* Ugly special case */
|
||||
if (tlv->type == BABEL_TLV_PAD1)
|
||||
continue;
|
||||
|
||||
/* The end of the common TLV header */
|
||||
pos = (byte *)tlv + sizeof(struct babel_tlv);
|
||||
if ((pos > end) || (pos + tlv->length > end))
|
||||
return PARSE_ERROR;
|
||||
|
||||
/*
|
||||
* The subtlv type space is non-contiguous (due to the mandatory bit), so
|
||||
* use a switch for dispatch instead of the mapping array we use for TLVs
|
||||
*/
|
||||
switch (tlv->type)
|
||||
{
|
||||
case BABEL_SUBTLV_PAD1:
|
||||
case BABEL_SUBTLV_PADN:
|
||||
/* FIXME: Framing errors in PADN are silently ignored, see babel_process_packet() */
|
||||
case BABEL_SUBTLV_SOURCE_PREFIX:
|
||||
res = babel_read_source_prefix(tlv, msg, state);
|
||||
if (res != PARSE_SUCCESS)
|
||||
return res;
|
||||
break;
|
||||
|
||||
case BABEL_SUBTLV_PADN:
|
||||
default:
|
||||
/* Unknown mandatory subtlv; PARSE_IGNORE ignores the whole TLV */
|
||||
if (tlv->type > 128)
|
||||
{
|
||||
DBG("Babel: Mandatory subtlv %d found; skipping TLV\n", tlv->type);
|
||||
if (tlv->type >= 128)
|
||||
return PARSE_IGNORE;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
@ -1186,6 +1345,7 @@ babel_process_packet(struct babel_pkt_header *pkt, int len,
|
||||
.ifa = ifa,
|
||||
.saddr = saddr,
|
||||
.next_hop_ip6 = saddr,
|
||||
.sadr_enabled = babel_sadr_enabled(p),
|
||||
};
|
||||
|
||||
if ((pkt->magic != BABEL_MAGIC) || (pkt->version != BABEL_VERSION))
|
||||
@ -1294,7 +1454,7 @@ babel_rx_hook(sock *sk, uint len)
|
||||
sk->iface->name, sk->faddr, sk->laddr);
|
||||
|
||||
/* Silently ignore my own packets */
|
||||
if (ipa_equal(ifa->iface->addr->ip, sk->faddr))
|
||||
if (ipa_equal(sk->faddr, sk->saddr))
|
||||
return 1;
|
||||
|
||||
if (!ipa_is_link_local(sk->faddr))
|
||||
@ -1329,6 +1489,8 @@ babel_open_socket(struct babel_iface *ifa)
|
||||
sk->sport = ifa->cf->port;
|
||||
sk->dport = ifa->cf->port;
|
||||
sk->iface = ifa->iface;
|
||||
sk->saddr = ifa->addr;
|
||||
sk->vrf = p->p.vrf;
|
||||
|
||||
sk->rx_hook = babel_rx_hook;
|
||||
sk->tx_hook = babel_tx_hook;
|
||||
|
@ -64,16 +64,15 @@
|
||||
* ready, the protocol just creates a BFD request like any other protocol.
|
||||
*
|
||||
* The protocol uses a new generic event loop (structure &birdloop) from |io.c|,
|
||||
* which supports sockets, timers and events like the main loop. Timers
|
||||
* (structure &timer2) are new microsecond based timers, while sockets and
|
||||
* events are the same. A birdloop is associated with a thread (field @thread)
|
||||
* in which event hooks are executed. Most functions for setting event sources
|
||||
* (like sk_start() or tm2_start()) must be called from the context of that
|
||||
* thread. Birdloop allows to temporarily acquire the context of that thread for
|
||||
* the main thread by calling birdloop_enter() and then birdloop_leave(), which
|
||||
* also ensures mutual exclusion with all event hooks. Note that resources
|
||||
* associated with a birdloop (like timers) should be attached to the
|
||||
* independent resource pool, detached from the main resource tree.
|
||||
* which supports sockets, timers and events like the main loop. A birdloop is
|
||||
* associated with a thread (field @thread) in which event hooks are executed.
|
||||
* Most functions for setting event sources (like sk_start() or tm_start()) must
|
||||
* be called from the context of that thread. Birdloop allows to temporarily
|
||||
* acquire the context of that thread for the main thread by calling
|
||||
* birdloop_enter() and then birdloop_leave(), which also ensures mutual
|
||||
* exclusion with all event hooks. Note that resources associated with a
|
||||
* birdloop (like timers) should be attached to the independent resource pool,
|
||||
* detached from the main resource tree.
|
||||
*
|
||||
* There are two kinds of interaction between the BFD core (running in the BFD
|
||||
* thread) and the rest of BFD (running in the main thread). The first kind are
|
||||
@ -145,6 +144,7 @@ bfd_session_update_state(struct bfd_session *s, uint state, uint diag)
|
||||
bfd_lock_sessions(p);
|
||||
s->loc_state = state;
|
||||
s->loc_diag = diag;
|
||||
s->last_state_change = current_time();
|
||||
|
||||
notify = !NODE_VALID(&s->n);
|
||||
if (notify)
|
||||
@ -176,7 +176,7 @@ bfd_session_update_tx_interval(struct bfd_session *s)
|
||||
return;
|
||||
|
||||
/* Set timer relative to last tx_timer event */
|
||||
tm2_set(s->tx_timer, s->last_tx + tx_int_l);
|
||||
tm_set(s->tx_timer, s->last_tx + tx_int_l);
|
||||
}
|
||||
|
||||
static void
|
||||
@ -190,7 +190,7 @@ bfd_session_update_detection_time(struct bfd_session *s, int kick)
|
||||
if (!s->last_rx)
|
||||
return;
|
||||
|
||||
tm2_set(s->hold_timer, s->last_rx + timeout);
|
||||
tm_set(s->hold_timer, s->last_rx + timeout);
|
||||
}
|
||||
|
||||
static void
|
||||
@ -211,16 +211,16 @@ bfd_session_control_tx_timer(struct bfd_session *s, int reset)
|
||||
goto stop;
|
||||
|
||||
/* So TX timer should run */
|
||||
if (reset || !tm2_active(s->tx_timer))
|
||||
if (reset || !tm_active(s->tx_timer))
|
||||
{
|
||||
s->last_tx = 0;
|
||||
tm2_start(s->tx_timer, 0);
|
||||
tm_start(s->tx_timer, 0);
|
||||
}
|
||||
|
||||
return;
|
||||
|
||||
stop:
|
||||
tm2_stop(s->tx_timer);
|
||||
tm_stop(s->tx_timer);
|
||||
s->last_tx = 0;
|
||||
}
|
||||
|
||||
@ -379,7 +379,7 @@ bfd_find_session_by_addr(struct bfd_proto *p, ip_addr addr)
|
||||
}
|
||||
|
||||
static void
|
||||
bfd_tx_timer_hook(timer2 *t)
|
||||
bfd_tx_timer_hook(timer *t)
|
||||
{
|
||||
struct bfd_session *s = t->data;
|
||||
|
||||
@ -388,7 +388,7 @@ bfd_tx_timer_hook(timer2 *t)
|
||||
}
|
||||
|
||||
static void
|
||||
bfd_hold_timer_hook(timer2 *t)
|
||||
bfd_hold_timer_hook(timer *t)
|
||||
{
|
||||
bfd_session_timeout(t->data);
|
||||
}
|
||||
@ -432,13 +432,13 @@ bfd_add_session(struct bfd_proto *p, ip_addr addr, ip_addr local, struct iface *
|
||||
s->passive = ifa->cf->passive;
|
||||
s->tx_csn = random_u32();
|
||||
|
||||
s->tx_timer = tm2_new_init(p->tpool, bfd_tx_timer_hook, s, 0, 0);
|
||||
s->hold_timer = tm2_new_init(p->tpool, bfd_hold_timer_hook, s, 0, 0);
|
||||
s->tx_timer = tm_new_init(p->tpool, bfd_tx_timer_hook, s, 0, 0);
|
||||
s->hold_timer = tm_new_init(p->tpool, bfd_hold_timer_hook, s, 0, 0);
|
||||
bfd_session_update_tx_interval(s);
|
||||
bfd_session_control_tx_timer(s, 1);
|
||||
|
||||
init_list(&s->request_list);
|
||||
s->last_state_change = now;
|
||||
s->last_state_change = current_time();
|
||||
|
||||
TRACE(D_EVENTS, "Session to %I added", s->addr);
|
||||
|
||||
@ -775,7 +775,7 @@ bfd_start_neighbor(struct bfd_proto *p, struct bfd_neighbor *n)
|
||||
return;
|
||||
}
|
||||
|
||||
struct neighbor *nb = neigh_find2(&p->p, &n->addr, n->iface, NEF_STICKY);
|
||||
struct neighbor *nb = neigh_find(&p->p, n->addr, n->iface, NEF_STICKY);
|
||||
if (!nb)
|
||||
{
|
||||
log(L_ERR "%s: Invalid remote address %I%J", p->p.name, n->addr, n->iface);
|
||||
@ -879,9 +879,6 @@ bfd_notify_hook(sock *sk, uint len UNUSED)
|
||||
diag = s->loc_diag;
|
||||
bfd_unlock_sessions(p);
|
||||
|
||||
/* FIXME: convert to btime and move to bfd_session_update_state() */
|
||||
s->last_state_change = now;
|
||||
|
||||
s->notify_running = 1;
|
||||
WALK_LIST_DELSAFE(n, nn, s->request_list)
|
||||
bfd_request_notify(SKIP_BACK(struct bfd_request, n, n), state, diag);
|
||||
@ -1080,7 +1077,7 @@ bfd_show_sessions(struct proto *P)
|
||||
byte tbuf[TM_DATETIME_BUFFER_SIZE];
|
||||
struct bfd_proto *p = (struct bfd_proto *) P;
|
||||
uint state, diag UNUSED;
|
||||
u32 tx_int, timeout;
|
||||
btime tx_int, timeout;
|
||||
const char *ifname;
|
||||
|
||||
if (p->p.proto_state != PS_UP)
|
||||
@ -1101,15 +1098,14 @@ bfd_show_sessions(struct proto *P)
|
||||
state = s->loc_state;
|
||||
diag = s->loc_diag;
|
||||
ifname = (s->ifa && s->ifa->iface) ? s->ifa->iface->name : "---";
|
||||
tx_int = s->last_tx ? (MAX(s->des_min_tx_int, s->rem_min_rx_int) TO_MS) : 0;
|
||||
timeout = (MAX(s->req_min_rx_int, s->rem_min_tx_int) TO_MS) * s->rem_detect_mult;
|
||||
tx_int = s->last_tx ? MAX(s->des_min_tx_int, s->rem_min_rx_int) : 0;
|
||||
timeout = (btime) MAX(s->req_min_rx_int, s->rem_min_tx_int) * s->rem_detect_mult;
|
||||
|
||||
state = (state < 4) ? state : 0;
|
||||
tm_format_datetime(tbuf, &config->tf_proto, s->last_state_change);
|
||||
tm_format_time(tbuf, &config->tf_proto, s->last_state_change);
|
||||
|
||||
cli_msg(-1020, "%-25I %-10s %-10s %-10s %3u.%03u %3u.%03u",
|
||||
s->addr, ifname, bfd_state_names[state], tbuf,
|
||||
tx_int / 1000, tx_int % 1000, timeout / 1000, timeout % 1000);
|
||||
cli_msg(-1020, "%-25I %-10s %-10s %-10s %7t %7t",
|
||||
s->addr, ifname, bfd_state_names[state], tbuf, tx_int, timeout);
|
||||
}
|
||||
HASH_WALK_END;
|
||||
|
||||
@ -1120,6 +1116,7 @@ bfd_show_sessions(struct proto *P)
|
||||
struct protocol proto_bfd = {
|
||||
.name = "BFD",
|
||||
.template = "bfd%d",
|
||||
.class = PROTOCOL_BFD,
|
||||
.proto_size = sizeof(struct bfd_proto),
|
||||
.config_size = sizeof(struct bfd_config),
|
||||
.init = bfd_init,
|
||||
|
@ -140,11 +140,11 @@ struct bfd_session
|
||||
btime last_tx; /* Time of last sent periodic control packet */
|
||||
btime last_rx; /* Time of last received valid control packet */
|
||||
|
||||
timer2 *tx_timer; /* Periodic control packet timer */
|
||||
timer2 *hold_timer; /* Timer for session down detection time */
|
||||
timer *tx_timer; /* Periodic control packet timer */
|
||||
timer *hold_timer; /* Timer for session down detection time */
|
||||
|
||||
list request_list; /* List of client requests (struct bfd_request) */
|
||||
bird_clock_t last_state_change; /* Time of last state change */
|
||||
btime last_state_change; /* Time of last state change */
|
||||
u8 notify_running; /* 1 if notify hooks are running */
|
||||
|
||||
u8 rx_csn_known; /* Received crypto sequence number is known */
|
||||
|
@ -31,7 +31,7 @@ CF_KEYWORDS(BFD, MIN, IDLE, RX, TX, INTERVAL, MULTIPLIER, PASSIVE,
|
||||
|
||||
CF_GRAMMAR
|
||||
|
||||
CF_ADDTO(proto, bfd_proto)
|
||||
proto: bfd_proto ;
|
||||
|
||||
bfd_proto_start: proto_start BFD
|
||||
{
|
||||
|
281
proto/bfd/io.c
281
proto/bfd/io.c
@ -18,10 +18,10 @@
|
||||
#include "proto/bfd/io.h"
|
||||
|
||||
#include "lib/buffer.h"
|
||||
#include "lib/heap.h"
|
||||
#include "lib/lists.h"
|
||||
#include "lib/resource.h"
|
||||
#include "lib/event.h"
|
||||
#include "lib/timer.h"
|
||||
#include "lib/socket.h"
|
||||
|
||||
|
||||
@ -31,16 +31,12 @@ struct birdloop
|
||||
pthread_t thread;
|
||||
pthread_mutex_t mutex;
|
||||
|
||||
btime last_time;
|
||||
btime real_time;
|
||||
u8 use_monotonic_clock;
|
||||
|
||||
u8 stop_called;
|
||||
u8 poll_active;
|
||||
u8 wakeup_masked;
|
||||
int wakeup_fds[2];
|
||||
|
||||
BUFFER(timer2 *) timers;
|
||||
struct timeloop time;
|
||||
list event_list;
|
||||
list sock_list;
|
||||
uint sock_num;
|
||||
@ -57,6 +53,7 @@ struct birdloop
|
||||
*/
|
||||
|
||||
static pthread_key_t current_loop_key;
|
||||
extern pthread_key_t current_time_key;
|
||||
|
||||
static inline struct birdloop *
|
||||
birdloop_current(void)
|
||||
@ -68,6 +65,7 @@ static inline void
|
||||
birdloop_set_current(struct birdloop *loop)
|
||||
{
|
||||
pthread_setspecific(current_loop_key, loop);
|
||||
pthread_setspecific(current_time_key, loop ? &loop->time : &main_timeloop);
|
||||
}
|
||||
|
||||
static inline void
|
||||
@ -77,98 +75,6 @@ birdloop_init_current(void)
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Time clock
|
||||
*/
|
||||
|
||||
static void times_update_alt(struct birdloop *loop);
|
||||
|
||||
static void
|
||||
times_init(struct birdloop *loop)
|
||||
{
|
||||
struct timespec ts;
|
||||
int rv;
|
||||
|
||||
rv = clock_gettime(CLOCK_MONOTONIC, &ts);
|
||||
if (rv < 0)
|
||||
{
|
||||
log(L_WARN "Monotonic clock is missing");
|
||||
|
||||
loop->use_monotonic_clock = 0;
|
||||
loop->last_time = 0;
|
||||
loop->real_time = 0;
|
||||
times_update_alt(loop);
|
||||
return;
|
||||
}
|
||||
|
||||
if ((ts.tv_sec < 0) || (((s64) ts.tv_sec) > ((s64) 1 << 40)))
|
||||
log(L_WARN "Monotonic clock is crazy");
|
||||
|
||||
loop->use_monotonic_clock = 1;
|
||||
loop->last_time = ((s64) ts.tv_sec S) + (ts.tv_nsec / 1000);
|
||||
loop->real_time = 0;
|
||||
}
|
||||
|
||||
static void
|
||||
times_update_pri(struct birdloop *loop)
|
||||
{
|
||||
struct timespec ts;
|
||||
int rv;
|
||||
|
||||
rv = clock_gettime(CLOCK_MONOTONIC, &ts);
|
||||
if (rv < 0)
|
||||
die("clock_gettime: %m");
|
||||
|
||||
btime new_time = ((s64) ts.tv_sec S) + (ts.tv_nsec / 1000);
|
||||
|
||||
if (new_time < loop->last_time)
|
||||
log(L_ERR "Monotonic clock is broken");
|
||||
|
||||
loop->last_time = new_time;
|
||||
loop->real_time = 0;
|
||||
}
|
||||
|
||||
static void
|
||||
times_update_alt(struct birdloop *loop)
|
||||
{
|
||||
struct timeval tv;
|
||||
int rv;
|
||||
|
||||
rv = gettimeofday(&tv, NULL);
|
||||
if (rv < 0)
|
||||
die("gettimeofday: %m");
|
||||
|
||||
btime new_time = ((s64) tv.tv_sec S) + tv.tv_usec;
|
||||
btime delta = new_time - loop->real_time;
|
||||
|
||||
if ((delta < 0) || (delta > (60 S)))
|
||||
{
|
||||
if (loop->real_time)
|
||||
log(L_WARN "Time jump, delta %d us", (int) delta);
|
||||
|
||||
delta = 100 MS;
|
||||
}
|
||||
|
||||
loop->last_time += delta;
|
||||
loop->real_time = new_time;
|
||||
}
|
||||
|
||||
static void
|
||||
times_update(struct birdloop *loop)
|
||||
{
|
||||
if (loop->use_monotonic_clock)
|
||||
times_update_pri(loop);
|
||||
else
|
||||
times_update_alt(loop);
|
||||
}
|
||||
|
||||
btime
|
||||
current_time(void)
|
||||
{
|
||||
return birdloop_current()->last_time;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Wakeup code for birdloop
|
||||
*/
|
||||
@ -252,6 +158,16 @@ wakeup_kick(struct birdloop *loop)
|
||||
loop->wakeup_masked = 2;
|
||||
}
|
||||
|
||||
/* For notifications from outside */
|
||||
void
|
||||
wakeup_kick_current(void)
|
||||
{
|
||||
struct birdloop *loop = birdloop_current();
|
||||
|
||||
if (loop && loop->poll_active)
|
||||
wakeup_kick(loop);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Events
|
||||
@ -272,7 +188,7 @@ events_init(struct birdloop *loop)
|
||||
static void
|
||||
events_fire(struct birdloop *loop)
|
||||
{
|
||||
times_update(loop);
|
||||
times_update(&loop->time);
|
||||
ev_run_list(&loop->event_list);
|
||||
}
|
||||
|
||||
@ -291,154 +207,6 @@ ev2_schedule(event *e)
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Timers
|
||||
*/
|
||||
|
||||
#define TIMER_LESS(a,b) ((a)->expires < (b)->expires)
|
||||
#define TIMER_SWAP(heap,a,b,t) (t = heap[a], heap[a] = heap[b], heap[b] = t, \
|
||||
heap[a]->index = (a), heap[b]->index = (b))
|
||||
|
||||
static inline uint timers_count(struct birdloop *loop)
|
||||
{ return loop->timers.used - 1; }
|
||||
|
||||
static inline timer2 *timers_first(struct birdloop *loop)
|
||||
{ return (loop->timers.used > 1) ? loop->timers.data[1] : NULL; }
|
||||
|
||||
|
||||
static void
|
||||
tm2_free(resource *r)
|
||||
{
|
||||
timer2 *t = (timer2 *) r;
|
||||
|
||||
tm2_stop(t);
|
||||
}
|
||||
|
||||
static void
|
||||
tm2_dump(resource *r)
|
||||
{
|
||||
timer2 *t = (timer2 *) r;
|
||||
|
||||
debug("(code %p, data %p, ", t->hook, t->data);
|
||||
if (t->randomize)
|
||||
debug("rand %d, ", t->randomize);
|
||||
if (t->recurrent)
|
||||
debug("recur %d, ", t->recurrent);
|
||||
if (t->expires)
|
||||
debug("expires in %d ms)\n", (t->expires - current_time()) TO_MS);
|
||||
else
|
||||
debug("inactive)\n");
|
||||
}
|
||||
|
||||
|
||||
static struct resclass tm2_class = {
|
||||
"Timer",
|
||||
sizeof(timer2),
|
||||
tm2_free,
|
||||
tm2_dump,
|
||||
NULL,
|
||||
NULL
|
||||
};
|
||||
|
||||
timer2 *
|
||||
tm2_new(pool *p)
|
||||
{
|
||||
timer2 *t = ralloc(p, &tm2_class);
|
||||
t->index = -1;
|
||||
return t;
|
||||
}
|
||||
|
||||
void
|
||||
tm2_set(timer2 *t, btime when)
|
||||
{
|
||||
struct birdloop *loop = birdloop_current();
|
||||
uint tc = timers_count(loop);
|
||||
|
||||
if (!t->expires)
|
||||
{
|
||||
t->index = ++tc;
|
||||
t->expires = when;
|
||||
BUFFER_PUSH(loop->timers) = t;
|
||||
HEAP_INSERT(loop->timers.data, tc, timer2 *, TIMER_LESS, TIMER_SWAP);
|
||||
}
|
||||
else if (t->expires < when)
|
||||
{
|
||||
t->expires = when;
|
||||
HEAP_INCREASE(loop->timers.data, tc, timer2 *, TIMER_LESS, TIMER_SWAP, t->index);
|
||||
}
|
||||
else if (t->expires > when)
|
||||
{
|
||||
t->expires = when;
|
||||
HEAP_DECREASE(loop->timers.data, tc, timer2 *, TIMER_LESS, TIMER_SWAP, t->index);
|
||||
}
|
||||
|
||||
if (loop->poll_active && (t->index == 1))
|
||||
wakeup_kick(loop);
|
||||
}
|
||||
|
||||
void
|
||||
tm2_start(timer2 *t, btime after)
|
||||
{
|
||||
tm2_set(t, current_time() + MAX(after, 0));
|
||||
}
|
||||
|
||||
void
|
||||
tm2_stop(timer2 *t)
|
||||
{
|
||||
if (!t->expires)
|
||||
return;
|
||||
|
||||
struct birdloop *loop = birdloop_current();
|
||||
uint tc = timers_count(loop);
|
||||
|
||||
HEAP_DELETE(loop->timers.data, tc, timer2 *, TIMER_LESS, TIMER_SWAP, t->index);
|
||||
BUFFER_POP(loop->timers);
|
||||
|
||||
t->index = -1;
|
||||
t->expires = 0;
|
||||
}
|
||||
|
||||
static void
|
||||
timers_init(struct birdloop *loop)
|
||||
{
|
||||
BUFFER_INIT(loop->timers, loop->pool, 4);
|
||||
BUFFER_PUSH(loop->timers) = NULL;
|
||||
}
|
||||
|
||||
static void
|
||||
timers_fire(struct birdloop *loop)
|
||||
{
|
||||
btime base_time;
|
||||
timer2 *t;
|
||||
|
||||
times_update(loop);
|
||||
base_time = loop->last_time;
|
||||
|
||||
while (t = timers_first(loop))
|
||||
{
|
||||
if (t->expires > base_time)
|
||||
return;
|
||||
|
||||
if (t->recurrent)
|
||||
{
|
||||
btime when = t->expires + t->recurrent;
|
||||
|
||||
if (when <= loop->last_time)
|
||||
when = loop->last_time + t->recurrent;
|
||||
|
||||
if (t->randomize)
|
||||
when += random() % (t->randomize + 1);
|
||||
|
||||
tm2_set(t, when);
|
||||
}
|
||||
else
|
||||
tm2_stop(t);
|
||||
|
||||
t->hook(t);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Sockets
|
||||
*/
|
||||
@ -530,7 +298,7 @@ sockets_prepare(struct birdloop *loop)
|
||||
|
||||
struct pollfd *pfd = loop->poll_fd.data;
|
||||
sock **psk = loop->poll_sk.data;
|
||||
int i = 0;
|
||||
uint i = 0;
|
||||
node *n;
|
||||
|
||||
WALK_LIST(n, loop->sock_list)
|
||||
@ -586,7 +354,7 @@ sockets_fire(struct birdloop *loop)
|
||||
sock **psk = loop->poll_sk.data;
|
||||
int poll_num = loop->poll_fd.used - 1;
|
||||
|
||||
times_update(loop);
|
||||
times_update(&loop->time);
|
||||
|
||||
/* Last fd is internal wakeup fd */
|
||||
if (pfd[poll_num].revents & POLLIN)
|
||||
@ -634,11 +402,10 @@ birdloop_new(void)
|
||||
loop->pool = p;
|
||||
pthread_mutex_init(&loop->mutex, NULL);
|
||||
|
||||
times_init(loop);
|
||||
wakeup_init(loop);
|
||||
|
||||
events_init(loop);
|
||||
timers_init(loop);
|
||||
timers_init(&loop->time, p);
|
||||
sockets_init(loop);
|
||||
|
||||
return loop;
|
||||
@ -710,7 +477,7 @@ static void *
|
||||
birdloop_main(void *arg)
|
||||
{
|
||||
struct birdloop *loop = arg;
|
||||
timer2 *t;
|
||||
timer *t;
|
||||
int rv, timeout;
|
||||
|
||||
birdloop_set_current(loop);
|
||||
@ -719,13 +486,13 @@ birdloop_main(void *arg)
|
||||
while (1)
|
||||
{
|
||||
events_fire(loop);
|
||||
timers_fire(loop);
|
||||
timers_fire(&loop->time);
|
||||
|
||||
times_update(loop);
|
||||
times_update(&loop->time);
|
||||
if (events_waiting(loop))
|
||||
timeout = 0;
|
||||
else if (t = timers_first(loop))
|
||||
timeout = (tm2_remains(t) TO_MS) + 1;
|
||||
else if (t = timers_first(&loop->time))
|
||||
timeout = (tm_remains(t) TO_MS) + 1;
|
||||
else
|
||||
timeout = -1;
|
||||
|
||||
@ -756,7 +523,7 @@ birdloop_main(void *arg)
|
||||
if (rv)
|
||||
sockets_fire(loop);
|
||||
|
||||
timers_fire(loop);
|
||||
timers_fire(&loop->time);
|
||||
}
|
||||
|
||||
loop->stop_called = 0;
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user