0
0
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:
Jan Maria Matejka 2018-08-27 17:47:54 +02:00
commit e907d7cb7d
176 changed files with 9606 additions and 6156 deletions

2
.gitignore vendored
View File

@ -11,3 +11,5 @@
/config.status /config.status
/configure /configure
/sysdep/autoconf.h.in /sysdep/autoconf.h.in
/sysdep/autoconf.h.in~
/cscope.*

309
.gitlab-ci.yml Normal file
View 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: &centos-6-amd64_env
image: registry.labs.nic.cz/labs/bird:centos-6-amd64
tags:
- docker
- linux
- amd64
.centos-7-amd64: &centos-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

View File

@ -9,6 +9,8 @@ MAKEFLAGS += -r
CPPFLAGS=-I$(objdir) -I$(srcdir) @CPPFLAGS@ CPPFLAGS=-I$(objdir) -I$(srcdir) @CPPFLAGS@
CFLAGS=$(CPPFLAGS) @CFLAGS@ CFLAGS=$(CPPFLAGS) @CFLAGS@
LDFLAGS=@LDFLAGS@ LDFLAGS=@LDFLAGS@
M4FLAGS=@M4FLAGS@
BISONFLAGS=@BISONFLAGS@
LIBS=@LIBS@ LIBS=@LIBS@
DAEMON_LIBS=@DAEMON_LIBS@ DAEMON_LIBS=@DAEMON_LIBS@
CLIENT_LIBS=@CLIENT_LIBS@ CLIENT_LIBS=@CLIENT_LIBS@
@ -37,6 +39,11 @@ srcdir := @srcdir@
objdir := @objdir@ objdir := @objdir@
exedir := @exedir@ 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),.) ifeq ($(objdir),.)
objdir := $(realpath .) objdir := $(realpath .)
endif endif
@ -49,11 +56,15 @@ else
Q:= Q:=
endif endif
ifneq ($(COLOR),)
CFLAGS += -fdiagnostics-color=always
endif
# Meta rules # Meta rules
docgoals := docs userdocs progdocs docgoals := docs userdocs progdocs
testgoals := check test tests tests_run testgoals := check test tests tests_run
cleangoals := clean distclean testsclean cleangoals := clean distclean testsclean
.PHONY: all daemon cli $(docgoals) $(testgoals) $(cleangoals) tags .PHONY: all daemon cli $(docgoals) $(testgoals) $(cleangoals) tags cscope
all: daemon cli all: daemon cli
daemon: $(daemon) daemon: $(daemon)
@ -90,7 +101,7 @@ clean = $(eval $(call clean_in,$(1)))
include $(addsuffix /Makefile,$(addprefix $(srcdir)/,$(dirs))) include $(addsuffix /Makefile,$(addprefix $(srcdir)/,$(dirs)))
# Generic rules # Generic rules
# Object file rules
$(objdir)/%.o: $(srcdir)/%.c $(objdir)/.dir-stamp $(objdir)/sysdep/paths.h $(objdir)/%.o: $(srcdir)/%.c $(objdir)/.dir-stamp $(objdir)/sysdep/paths.h
$(E)echo CC -o $@ -c $< $(E)echo CC -o $@ -c $<
$(Q)$(CC) $(CFLAGS) -MMD -MP -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 $< $(E)echo CC -o $@ -c $<
$(Q)$(CC) $(CFLAGS) -MMD -MP -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 $(objdir)/%.S: $(srcdir)/%.c $(objdir)/.dir-stamp $(objdir)/sysdep/paths.h
$(E)echo CC -o $@ -S $< $(E)echo CC -o $@ -S $<
$(Q)$(CC) $(CFLAGS) -MMD -MP -o $@ -S $< $(Q)$(CC) $(CFLAGS) -MMD -MP -o $@ -S $<
@ -157,6 +177,9 @@ endif
tags: tags:
cd $(srcdir) ; etags -lc `find $(dirs) -name *.[chY]` cd $(srcdir) ; etags -lc `find $(dirs) -name *.[chY]`
cscope:
cd $(srcdir) ; find $(dirs) -name *.[chY] > cscope.files ; cscope -b
# Install # Install
install: all install: all

81
NEWS
View File

@ -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 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 with MPLS labels (RFC 3107)
o BGP MPLS/VPN support (RFC 4364) o BGP MPLS/VPN support (RFC 4364)
o BGP 6PE - IPv6 NLRI over IPv4 MPLS (RFC 4798) o BGP 6PE - IPv6 NLRI over IPv4 MPLS (RFC 4798)
o BGP IPv4 NLRI with an IPv6 Next Hop (RFC 5549) o BGP IPv4 NLRI with an IPv6 Next Hop (RFC 5549)
o BGP Confederations (RFC 5065) 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 exchanging LOCAL_PREF with eBGP peers
o BGP: Allow to specify interface for regular sessions o BGP: Allow to specify interface for regular sessions
o Babel support restored o OSPF: Support of address families in OSPFv3
o Static: Minor overhaul o OSPF: Enable ECMP and Link detection by default
o Netlink: Default kernel metric changed to 32 o RAdv: Support for more specific routes (RFC 4191)
o Flowspec: Limit tcp mask length to 12 bits o RAdv: Proper handling of prefix retraction
o Update of show route command o RIP: Enable ECMP and Link detection by default
o Redesign of RPKI handling
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 New RPKI-Router protocol 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 New build system
o Unit tests o Unit tests
o ...
Notes: Notes:
Protocols and tables are now connected using explicit channels, most related Tables are now defined with appropriate net type keyword. Protocols and tables
protocol options (table, import, export, ...) are now channel options. See are now connected by explicit channels, most related protocol options (table,
doc/bird.conf.example2 for configuration examples. 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) Version 1.6.3 (2016-12-21)

4
README
View File

@ -6,7 +6,7 @@
(c) 1998--2008 Martin Mares <mj@ucw.cz> (c) 1998--2008 Martin Mares <mj@ucw.cz>
(c) 1998--2000 Pavel Machek <pavel@ucw.cz> (c) 1998--2000 Pavel Machek <pavel@ucw.cz>
(c) 1998--2008 Ondrej Filip <feela@network.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 What do we support
================== ==================
o Both IPv4 and IPv6 (use --enable-ipv6 when configuring) o Both IPv4 and IPv6
o Multiple routing tables o Multiple routing tables
o Border Gateway Protocol (BGPv4) o Border Gateway Protocol (BGPv4)
o Routing Information Protocol (RIPv2, RIPng) o Routing Information Protocol (RIPv2, RIPng)

45
TODO
View File

@ -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
View File

@ -31,6 +31,106 @@ AC_DEFUN([BIRD_CHECK_PTHREADS],
CFLAGS="$bird_tmp_cflags" 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], AC_DEFUN([BIRD_CHECK_GCC_OPTION],
[ [
bird_tmp_cflags="$CFLAGS" bird_tmp_cflags="$CFLAGS"
@ -75,3 +175,24 @@ AC_DEFUN([BIRD_CHECK_PROG_FLAVOR_GNU],
) )
esac 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
])

View File

@ -22,13 +22,14 @@ protocol direct {
# Feed routes to kernel FIB # Feed routes to kernel FIB
protocol kernel { protocol kernel {
ipv4 { export all; }; ipv4 { export all; import all; };
# learn; # Learn all routes from the kernel learn; # Learn all routes from the kernel
# scan time 10; # Scan kernel tables every 10 seconds # scan time 10; # Scan kernel tables every 10 seconds
} }
protocol kernel { protocol kernel {
ipv6; ipv6 { import all; };
learn;
} }
# Static route feed # Static route feed
@ -52,6 +53,6 @@ protocol rip {
ipv4; ipv4;
} }
protocol ripng { protocol rip ng {
ipv6; ipv6;
} }

View File

@ -25,9 +25,10 @@
#include <fcntl.h> #include <fcntl.h>
#include <unistd.h> #include <unistd.h>
#include <errno.h> #include <errno.h>
#include <sys/select.h>
#include <sys/socket.h> #include <sys/socket.h>
#include <sys/un.h>
#include <sys/types.h> #include <sys/types.h>
#include <sys/un.h>
#include "nest/bird.h" #include "nest/bird.h"
#include "lib/resource.h" #include "lib/resource.h"

View File

@ -11,7 +11,7 @@ BISON_DEBUG=-t
endif endif
$(conf-y-targets): $(s)confbase.Y $(s)flowspec.Y $(conf-y-targets): $(s)confbase.Y $(s)flowspec.Y
$(M4) -P $| $^ >$@ $(M4) $(M4FLAGS) -P $| $^ >$@
$(o)cf-parse.y: | $(s)gen_parser.m4 $(o)cf-parse.y: | $(s)gen_parser.m4
$(o)keywords.h: | $(s)gen_keywords.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.h: $(o)cf-parse.tab.c
$(o)cf-parse.tab.c: $(o)cf-parse.y $(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 $(o)cf-lex.c: $(s)cf-lex.l
$(FLEX) $(FLEX_DEBUG) -s -B -8 -Pcf_ -o$@ $< $(FLEX) $(FLEX_DEBUG) -s -B -8 -Pcf_ -o$@ $<

View File

@ -52,7 +52,7 @@
#include "lib/resource.h" #include "lib/resource.h"
#include "lib/string.h" #include "lib/string.h"
#include "lib/event.h" #include "lib/event.h"
#include "sysdep/unix/timer.h" #include "lib/timer.h"
#include "conf/conf.h" #include "conf/conf.h"
#include "filter/filter.h" #include "filter/filter.h"
@ -102,9 +102,9 @@ config_alloc(const char *name)
c->pool = p; c->pool = p;
c->mem = l; c->mem = l;
c->file_name = ndup; c->file_name = ndup;
c->load_time = now; c->load_time = current_time();
c->tf_route = c->tf_proto = (struct timeformat){"%T", "%F", 20*3600}; c->tf_route = c->tf_proto = TM_ISO_SHORT_MS;
c->tf_base = c->tf_log = (struct timeformat){"%F %T", NULL, 0}; c->tf_base = c->tf_log = TM_ISO_LONG_MS;
c->gr_wait = DEFAULT_GR_WAIT; c->gr_wait = DEFAULT_GR_WAIT;
return c; return c;
@ -219,11 +219,6 @@ global_commit(struct config *new, struct config *old)
if (!old) if (!old)
return 0; 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) if (!new->router_id)
{ {
new->router_id = old->router_id; new->router_id = old->router_id;
@ -307,7 +302,7 @@ config_done(void *unused UNUSED)
* config_commit - commit a configuration * config_commit - commit a configuration
* @c: new configuration * @c: new configuration
* @type: type of reconfiguration (RECONFIG_SOFT or RECONFIG_HARD) * @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 * When a configuration is parsed and prepared for use, the
* config_commit() function starts the process of reconfiguration. * config_commit() function starts the process of reconfiguration.
@ -331,7 +326,7 @@ config_done(void *unused UNUSED)
* are accepted. * are accepted.
*/ */
int int
config_commit(struct config *c, int type, int timeout) config_commit(struct config *c, int type, uint timeout)
{ {
if (shutting_down) if (shutting_down)
{ {
@ -340,8 +335,8 @@ config_commit(struct config *c, int type, int timeout)
} }
undo_available = 1; undo_available = 1;
if (timeout > 0) if (timeout)
tm_start(config_timer, timeout); tm_start(config_timer, timeout S);
else else
tm_stop(config_timer); tm_stop(config_timer);
@ -452,7 +447,7 @@ config_undo(void)
extern void cmd_reconfig_undo_notify(void); extern void cmd_reconfig_undo_notify(void);
static void static void
config_timeout(struct timer *t UNUSED) config_timeout(timer *t UNUSED)
{ {
log(L_INFO "Config timeout expired, starting undo"); log(L_INFO "Config timeout expired, starting undo");
cmd_reconfig_undo_notify(); cmd_reconfig_undo_notify();

View File

@ -13,7 +13,7 @@
#include "lib/ip.h" #include "lib/ip.h"
#include "lib/hash.h" #include "lib/hash.h"
#include "lib/resource.h" #include "lib/resource.h"
#include "sysdep/unix/timer.h" #include "lib/timer.h"
/* Configuration structure */ /* Configuration structure */
@ -32,16 +32,13 @@ struct config {
struct iface_patt *router_id_from; /* Configured list of router ID iface patterns */ struct iface_patt *router_id_from; /* Configured list of router ID iface patterns */
u32 router_id; /* Our Router ID */ 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_debug; /* Default protocol debug mask */
unsigned proto_default_mrtdump; /* Default protocol mrtdump mask */ unsigned proto_default_mrtdump; /* Default protocol mrtdump mask */
struct timeformat tf_route; /* Time format for 'show route' */ struct timeformat tf_route; /* Time format for 'show route' */
struct timeformat tf_proto; /* Time format for 'show protocol' */ struct timeformat tf_proto; /* Time format for 'show protocol' */
struct timeformat tf_log; /* Time format for the logfile */ struct timeformat tf_log; /* Time format for the logfile */
struct timeformat tf_base; /* Time format for other purposes */ 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 cli_debug; /* Tracing of CLI connections and commands */
int latency_debug; /* I/O loop tracks duration of each event */ 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 */ struct config *fallback; /* Link to regular config for CLI parsing */
int obstacle_count; /* Number of items blocking freeing of this config */ int obstacle_count; /* Number of items blocking freeing of this config */
int shutdown; /* This is a pseudo-config for daemon shutdown */ 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. */ /* 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 config_parse(struct config *);
int cli_parse(struct config *); int cli_parse(struct config *);
void config_free(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_confirm(void);
int config_undo(void); int config_undo(void);
void config_init(void); void config_init(void);

View File

@ -14,7 +14,7 @@ CF_HDR
#include "conf/conf.h" #include "conf/conf.h"
#include "lib/resource.h" #include "lib/resource.h"
#include "lib/socket.h" #include "lib/socket.h"
#include "sysdep/unix/timer.h" #include "lib/timer.h"
#include "lib/string.h" #include "lib/string.h"
#include "nest/protocol.h" #include "nest/protocol.h"
#include "nest/iface.h" #include "nest/iface.h"
@ -49,6 +49,8 @@ CF_DECLS
struct rtable_config *r; struct rtable_config *r;
struct channel_config *cc; struct channel_config *cc;
struct f_inst *x; struct f_inst *x;
struct f_dynamic_attr fda;
struct f_static_attr fsa;
struct filter *f; struct filter *f;
struct f_tree *e; struct f_tree *e;
struct f_trie *trie; struct f_trie *trie;
@ -60,7 +62,7 @@ CF_DECLS
struct lsadb_show_data *ld; struct lsadb_show_data *ld;
struct iface *iface; struct iface *iface;
void *g; void *g;
bird_clock_t time; btime time;
struct f_prefix px; struct f_prefix px;
struct proto_spec ps; struct proto_spec ps;
struct channel_limit cl; struct channel_limit cl;
@ -80,11 +82,10 @@ CF_DECLS
%type <iface> ipa_scope %type <iface> ipa_scope
%type <i> expr bool pxlen4 %type <i> expr bool pxlen4
%type <i32> expr_us %type <time> expr_us time
%type <time> datetime
%type <a> ipa %type <a> ipa
%type <net> net_ip4_ net_ip6_ net_ip6 net_ip_ net_ip net_or_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 <mls> label_stack_start label_stack
%type <t> text opttext %type <t> text opttext
@ -97,7 +98,7 @@ CF_DECLS
%left '!' %left '!'
%nonassoc '.' %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 CF_GRAMMAR
@ -112,12 +113,13 @@ conf_entries:
| conf_entries conf | conf_entries conf
; ;
CF_ADDTO(conf, ';') conf: ';' ;
/* Constant expressions */ /* Constant expressions */
CF_ADDTO(conf, definition) conf: definition ;
definition: definition:
DEFINE SYM '=' term ';' { DEFINE SYM '=' term ';' {
struct f_val *val = cfg_alloc(sizeof(struct f_val)); struct f_val *val = cfg_alloc(sizeof(struct f_val));
@ -137,9 +139,9 @@ expr:
expr_us: expr_us:
expr S { $$ = (u32) $1 * 1000000; } expr S { $$ = $1 S_; }
| expr MS { $$ = (u32) $1 * 1000; } | expr MS { $$ = $1 MS_; }
| expr US { $$ = (u32) $1 * 1; } | expr US { $$ = $1 US_; }
; ;
/* Switches */ /* Switches */
@ -178,10 +180,6 @@ pxlen4:
if ($2 > IP4_MAX_PREFIX_LENGTH) cf_error("Invalid prefix length %u", $2); if ($2 > IP4_MAX_PREFIX_LENGTH) cf_error("Invalid prefix length %u", $2);
$$ = $2; $$ = $2;
} }
| ':' IP4 {
$$ = ip4_masklen($2);
if ($$ == 255) cf_error("Invalid netmask %I4", $2);
}
; ;
net_ip4_: IP4 pxlen4 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); 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_ net_vpn4_: VPN_RD net_ip4_
{ {
$$ = cfg_alloc(sizeof(net_addr_vpn4)); $$ = 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); 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_ip_: net_ip4_ | net_ip6_ ;
net_vpn_: net_vpn4_ | net_vpn6_ ; net_vpn_: net_vpn4_ | net_vpn6_ ;
net_roa_: net_roa4_ | net_roa6_ ; net_roa_: net_roa4_ | net_roa6_ ;
@ -244,6 +267,8 @@ net_:
| net_vpn_ | net_vpn_
| net_roa_ | net_roa_
| net_flow_ | net_flow_
| net_ip6_sadr_
| net_mpls_
; ;
@ -308,11 +333,11 @@ label_stack:
} }
; ;
datetime: time:
TEXT { TEXT {
$$ = tm_parse_datetime($1); $$ = tm_parse_time($1);
if (!$$) if (!$$)
cf_error("Invalid date and time"); cf_error("Invalid date/time");
} }
; ;

View File

@ -11,16 +11,6 @@ CF_HDR
#define PARSER 1 #define PARSER 1
#include "nest/bird.h" #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" #include "lib/flowspec.h"

View File

@ -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 m4_define(CF_KEYWORDS, `m4_define([[CF_toks]],[[]])CF_iterate([[CF_keywd]], [[$@]])m4_ifelse(CF_toks,,,%token[[]]CF_toks
)DNL') )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 # CLI commands
m4_define(CF_CLI, `m4_define([[CF_cmd]], cmd_[[]]m4_translit($1, [[ ]], _))DNL m4_define(CF_CLI, `m4_define([[CF_cmd]], cmd_[[]]m4_translit($1, [[ ]], _))DNL
m4_divert(2)CF_KEYWORDS(m4_translit($1, [[ ]], [[,]])) 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') CF_cmd: $1 $2 END')
m4_define(CF_CLI_CMD, `') m4_define(CF_CLI_CMD, `')
m4_define(CF_CLI_HELP, `') m4_define(CF_CLI_HELP, `')
@ -62,10 +57,6 @@ m4_undivert(2)DNL
%% %%
m4_undivert(3)DNL m4_undivert(3)DNL
/* Dynamic rules */
m4_define(CF_RULE, [[$1: CF_rule_$1 ;]])
CF_dyn_rules
%% %%
m4_undivert(4)DNL m4_undivert(4)DNL
') ')

View File

@ -36,6 +36,12 @@ AC_ARG_ENABLE([libssh],
[enable_libssh=try] [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], AC_ARG_WITH([protocols],
[AS_HELP_STRING([--with-protocols=LIST], [include specified routing protocols @<:@all@:>@])], [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 "$BISON" && AC_MSG_ERROR([Bison is missing.])
test -z "$M4" && AC_MSG_ERROR([M4 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], BIRD_CHECK_PROG_FLAVOR_GNU([$M4],
[], [],
[AC_MSG_ERROR([Provided M4 is not GNU M4.])] [AC_MSG_ERROR([Provided M4 is not GNU M4.])]
@ -175,6 +195,8 @@ else
;; ;;
freebsd*) freebsd*)
sysdesc=bsd sysdesc=bsd
CPPFLAGS="$CPPFLAGS -I/usr/local/include"
LDFLAGS="$LDFLAGS -L/usr/local/lib"
;; ;;
kfreebsd*) kfreebsd*)
sysdesc=bsd sysdesc=bsd
@ -239,6 +261,20 @@ if test "$enable_libssh" != no ; then
fi fi
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="$proto_bfd babel bgp ospf pipe radv rip $proto_rpki static"
all_protocols=`echo $all_protocols | sed 's/ /,/g'` all_protocols=`echo $all_protocols | sed 's/ /,/g'`
@ -287,24 +323,26 @@ esac
AC_CHECK_HEADERS_ONCE([alloca.h syslog.h]) AC_CHECK_HEADERS_ONCE([alloca.h syslog.h])
AC_CHECK_MEMBERS([struct sockaddr.sa_len], [], [], [#include <sys/socket.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_C_BIGENDIAN(
[AC_DEFINE([CPU_BIG_ENDIAN], [1], [Define to 1 if cpu is big endian])], [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_DEFINE([CPU_LITTLE_ENDIAN], [1], [Define to 1 if cpu is little endian])],
[AC_MSG_ERROR([Cannot determine CPU endianity.])] [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 if test "$enable_debug" = yes ; then
AC_DEFINE([DEBUGGING], [1], [Define to 1 if debugging is enabled]) AC_DEFINE([DEBUGGING], [1], [Define to 1 if debugging is enabled])
LDFLAGS="$LDFLAGS -rdynamic" LDFLAGS="$LDFLAGS -rdynamic"
@ -358,12 +396,6 @@ if test "$enable_client" = yes ; then
[$TINFO_LIBS] [$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_CHECK_LIB([readline], [rl_crlf],
[AC_DEFINE([HAVE_RL_CRLF], [1], [Define to 1 if you have 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" LIBS="$BASE_LIBS"
CLIENT_LIBS="$HISTORY_LIBS $READLINE_LIBS $TINFO_LIBS" CLIENT_LIBS="$READLINE_LIBS $TINFO_LIBS"
fi fi
AC_SUBST([CLIENT]) AC_SUBST([CLIENT])
AC_SUBST([CLIENT_LIBS]) AC_SUBST([CLIENT_LIBS])
@ -396,6 +428,7 @@ AC_MSG_RESULT([ System configuration: $sysdesc])
AC_MSG_RESULT([ Debugging: $enable_debug]) AC_MSG_RESULT([ Debugging: $enable_debug])
AC_MSG_RESULT([ POSIX threads: $enable_pthreads]) AC_MSG_RESULT([ POSIX threads: $enable_pthreads])
AC_MSG_RESULT([ Routing protocols: $protocols]) AC_MSG_RESULT([ Routing protocols: $protocols])
AC_MSG_RESULT([ Kernel MPLS support: $enable_mpls_kernel])
AC_MSG_RESULT([ Client: $enable_client]) AC_MSG_RESULT([ Client: $enable_client])
rm -f $objdir/.*-stamp rm -f $objdir/.*-stamp

View File

@ -1,222 +1,204 @@
/* # This is a basic configuration file, which contains boilerplate options and
* This is an example configuration file # some basic examples. It allows the BIRD daemon to start but will not cause
* (for version 1.x.x, obsolete) # anything else to happen.
*/ #
# Please refer to the BIRD User's Guide documentation, which is also available
# Yes, even shell-like comments work... # online at http://bird.network.cz/ in HTML format, for more information on
# configuring BIRD and adding routing protocols.
# Configure logging # Configure logging
#log syslog { debug, trace, info, remote, warning, error, auth, fatal, bug }; log syslog all;
#log stderr all; # log "/var/log/bird.log" { debug, trace, info, remote, warning, error, auth, fatal, bug };
#log "tmp" all;
# Override router ID # Set router ID. It is a unique identification of your router, usually one of
#router id 198.51.100.1; # IPv4 addresses of the router. It is recommended to configure it explicitly.
# router id 198.51.100.1;
# You can define your own symbols... # Turn on global debugging of all protocols (all messages or just selected classes)
#define xyzzy = (120+10); # debug protocols all;
#define '1a-a1' = (30+40); # debug protocols { events, states };
# 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 internal watchdog # Turn on internal watchdog
#watchdog warning 5 s; # watchdog warning 5 s;
#watchdog timeout 30 s; # watchdog timeout 30 s;
# The direct protocol automatically generates device routes to # You can define your own constants
# all network interfaces. Can exist in as many instances as you wish # define my_asn = 65000;
# if you want to populate multiple routing tables with device routes. # define my_addr = 198.51.100.1;
#protocol direct {
# interface "-eth*", "*"; # Restrict network interfaces it works with
#}
# This pseudo-protocol performs synchronization between BIRD's routing # Tables master4 and master6 are defined by default
# tables and the kernel. If your kernel supports multiple routing tables # ipv4 table master4;
# (as Linux 2.2.x does), you can run multiple instances of the kernel # ipv6 table master6;
# 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)
}
# 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 { protocol device {
scan time 10; # Scan interfaces every 10 seconds
} }
# Static routes (again, there can be multiple instances, so that you # The direct protocol is not a real routing protocol. It automatically generates
# can disable/enable various groups of static routes on the fly). # 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 { protocol static {
# disabled; # Disable by default ipv4; # Again, IPv4 channel with default options
# table testable; # Connect to a non-default table
# preference 1000; # Default preference of routes # route 0.0.0.0/0 via 198.51.100.10;
# debug { states, routes, filters, interfaces, events, packets }; # route 192.0.2.0/24 blackhole;
# debug all;
# route 0.0.0.0/0 via 198.51.100.13;
# route 198.51.100.0/25 unreachable;
# route 10.0.0.0/8 unreachable; # route 10.0.0.0/8 unreachable;
# route 10.1.1.0:255.255.255.0 via 198.51.100.3; # route 10.2.0.0/24 via "eth0";
# route 10.1.2.0:255.255.255.0 via 198.51.100.3; # # Static routes can be defined with optional attributes
# route 10.1.3.0:255.255.255.0 via 198.51.100.4; # route 10.1.1.0/24 via 198.51.100.3 { rip_metric = 3; };
# route 10.2.0.0/24 via "arc0"; # 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. # Pipe protocol connects two routing tables. Beware of loops.
#protocol pipe { # protocol pipe {
# peer table testable; # table master4; # No ipv4/ipv6 channel definition like in other protocols
# Define what routes do we export to this protocol / import from it. # peer table mrib4;
# import all; # default is all # import all; # Direction peer table -> table
# export all; # default is none # export all; # Direction table -> peer table
# import none; # If you wish to disable imports # }
# import filter test_filter; # Use named filter
# import where source = RTS_DEVICE; # Use explicit filter
#}
# RIP aka Rest In Pieces... # RIP example, both RIP and RIPng are supported
#protocol rip MyRIP { # You can also use an explicit name # protocol rip {
# preference xyzzy; # ipv4 {
# debug all; # # Export direct, static routes and ones from RIP itself
# port 1520; # import all;
# period 7; # export where source ~ [ RTS_DEVICE, RTS_STATIC, RTS_RIP ];
# 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";
# }; # };
# authentication none; # interface "eth*" {
# import filter { print "importing"; accept; }; # update time 10; # Default period is 30
# export filter { print "exporting"; accept; }; # timeout time 60; # Default timeout is 180
#} # authentication cryptographic; # No authentication by default
# password "hello" { algorithm hmac sha256; }; # Default is MD5
# };
# }
#protocol ospf MyOSPF { # OSPF example, both OSPFv2 and OSPFv3 are supported
# tick 2; # protocol ospf v3 {
# rfc1583compat yes; # ipv6 {
# area 0.0.0.0 { # import all;
# stub no; # export where source = RTS_STATIC;
# };
# area 0 {
# interface "eth*" { # interface "eth*" {
# hello 9; # type broadcast; # Detected by default
# retransmit 6; # cost 10; # Interface metric
# cost 10; # hello 5; # Default hello perid 10 is too long
# transmit delay 5;
# dead count 5;
# wait 50;
# type broadcast;
# authentication simple;
# password "pass";
# }; # };
# interface "arc0" { # interface "tun*" {
# rx buffer large; # type ptp; # PtP mode, avoids DR selection
# type nonbroadcast; # cost 100; # Interface metric
# poll 14; # hello 5; # Default hello perid 10 is too long
# dead 75;
# neighbors {
# 10.1.1.2 eligible;
# 10.1.1.4;
# };
# strict nonbroadcast yes;
# }; # };
# interface "xxx0" { # interface "dummy0" {
# passwords { # stub; # Stub interface, just propagate it
# 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;
# }; # };
# }; # };
# 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 { # BGP example, explicit name 'uplink1' is used instead of default 'bgp1'
# disabled; # protocol bgp uplink1 {
# description "My BGP uplink"; # description "My BGP uplink";
# local as 65000; # local 198.51.100.1 as 65000;
# neighbor 198.51.100.130 as 64496; # neighbor 198.51.100.10 as 64496;
# multihop; # hold time 90; # Default is 240
# 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
# password "secret"; # Password used for MD5 authentication # 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 # ipv4 { # regular IPv4 unicast (1/1)
# export where source=RTS_STATIC; # import filter rt_import;
# export filter { # export where source ~ [ RTS_STATIC, RTS_BGP ];
# if source = RTS_STATIC then { # };
# bgp_community = -empty-; bgp_community = add(bgp_community,(65000,5678)); #
# bgp_origin = 0; # ipv6 { # regular IPv6 unicast (2/1)
# bgp_community = -empty-; bgp_community.add((65000,5678)); # import filter rt_import;
# if (65000,64501) ~ bgp_community then # export filter { # The same as 'where' expression above
# bgp_community.add((0, 1)); # if source ~ [ RTS_STATIC, RTS_BGP ]
# if bgp_path ~ [= 65000 =] then # then accept;
# bgp_path.prepend(65000); # else reject;
# accept; # };
# } # };
# 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 example. Using templates to define IBGP route reflector clients.
#template bgp rr_client { # template bgp rr_clients {
# disabled; # local 10.0.0.1 as 65000;
# local as 65000; # neighbor as 65000;
# multihop;
# rr client; # rr client;
# rr cluster id 1.0.0.1; # rr cluster id 1.0.0.1;
#}
# #
#protocol bgp rr_abcd from rr_client { # ipv4 {
# neighbor 10.1.4.7 as 65000; # 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;
# }

View File

@ -28,20 +28,15 @@ flow6 table flowtab6;
protocol device { protocol device {
scan time 10;
} }
protocol kernel kernel4 { protocol kernel kernel4 {
scan time 20;
ipv4 { ipv4 {
export all; export all;
}; };
} }
protocol kernel kernel6 { protocol kernel kernel6 {
scan time 20;
ipv6 { ipv6 {
export all; export all;
}; };
@ -169,8 +164,6 @@ protocol pipe {
} }
protocol ospf v2 ospf4 { protocol ospf v2 ospf4 {
# ecmp;
ipv4 { ipv4 {
import all; import all;
# export where source = RTS_STATIC; # export where source = RTS_STATIC;
@ -186,8 +179,6 @@ protocol ospf v2 ospf4 {
protocol ospf v3 ospf6 { protocol ospf v3 ospf6 {
# ecmp;
ipv6 { ipv6 {
import all; import all;
# export where source = RTS_STATIC; # export where source = RTS_STATIC;
@ -251,7 +242,7 @@ protocol bgp {
}; };
# IPv6 with MPLS labels (2/4) # IPv6 with MPLS labels (2/4)
ipv6 multicast { ipv6 mpls {
# explicit IPv6 table # explicit IPv6 table
table mtab6; table mtab6;
import all; import all;

File diff suppressed because it is too large Load Diff

View File

@ -12,8 +12,6 @@ CF_HDR
CF_DEFINES 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(u32 a, u32 b) { return (a << 16) | b; }
static inline u32 pair_a(u32 p) { return p >> 16; } static inline u32 pair_a(u32 p) { return p >> 16; }
static inline u32 pair_b(u32 p) { return p & 0xFFFF; } 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; u64 fm, to;
if (ipv4_used || (key >= 0x10000)) { if ((kind != EC_GENERIC) && (ipv4_used || (key >= 0x10000))) {
check_u16(vf); check_u16(vf);
if (vt == EC_ALL) if (vt == EC_ALL)
vt = 0xFFFF; 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 * 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(); struct f_inst *e = f_new_inst(FI_EMPTY);
e->code = 'E';
switch (dyn->aux & EAF_TYPE_MASK) { switch (dyn.type & EAF_TYPE_MASK) {
case EAF_TYPE_AS_PATH: case EAF_TYPE_AS_PATH:
e->aux = T_PATH; e->aux = T_PATH;
break; break;
@ -179,9 +176,9 @@ f_generate_empty(struct f_inst *dyn)
cf_error("Can't empty that attribute"); cf_error("Can't empty that attribute");
} }
dyn->code = P('e','S'); struct f_inst *s = f_new_inst_da(FI_EA_SET, dyn);
dyn->a1.p = e; s->a1.p = e;
return dyn; return s;
} }
@ -190,21 +187,19 @@ f_generate_dpair(struct f_inst *t1, struct f_inst *t2)
{ {
struct f_inst *rv; 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)) if ((t1->aux != T_INT) || (t2->aux != T_INT))
cf_error( "Can't operate with value of non-integer type in pair constructor"); cf_error( "Can't operate with value of non-integer type in pair constructor");
check_u16(t1->a2.i); check_u16(t1->a2.i);
check_u16(t2->a2.i); check_u16(t2->a2.i);
rv = f_new_inst(); rv = f_new_inst(FI_CONSTANT);
rv->code = 'c';
rv->aux = T_PAIR; rv->aux = T_PAIR;
rv->a2.i = pair(t1->a2.i, t2->a2.i); rv->a2.i = pair(t1->a2.i, t2->a2.i);
} }
else { else {
rv = f_new_inst(); rv = f_new_inst(FI_PAIR_CONSTRUCT);
rv->code = P('m', 'p');
rv->a1.p = t1; rv->a1.p = t1;
rv->a2.p = t2; 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; int c1 = 0, c2 = 0, ipv4_used = 0;
u32 key = 0, val2 = 0; u32 key = 0, val2 = 0;
if (tk->code == 'c') { if (tk->fi_code == FI_CONSTANT) {
c1 = 1; c1 = 1;
if (tk->aux == T_INT) { 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 */ /* IP->Quad implicit conversion */
else if (tk->code == 'C') { else if (tk->fi_code == FI_CONSTANT_INDIRECT) {
c1 = 1; c1 = 1;
struct f_val *val = tk->a1.p; 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"); 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) if (tv->aux != T_INT)
cf_error("Can't operate with value of non-integer type in EC constructor"); cf_error("Can't operate with value of non-integer type in EC constructor");
c2 = 1; c2 = 1;
@ -276,15 +271,13 @@ f_generate_ec(u16 kind, struct f_inst *tk, struct f_inst *tv)
} }
NEW_F_VAL; NEW_F_VAL;
rv = f_new_inst(); rv = f_new_inst(FI_CONSTANT_INDIRECT);
rv->code = 'C';
rv->a1.p = val; rv->a1.p = val;
val->type = T_EC; val->type = T_EC;
val->val.ec = ec; val->val.ec = ec;
} }
else { else {
rv = f_new_inst(); rv = f_new_inst(FI_EC_CONSTRUCT);
rv->code = P('m','c');
rv->aux = kind; rv->aux = kind;
rv->a1.p = tk; rv->a1.p = tk;
rv->a2.p = tv; 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; 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)) 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"); cf_error( "LC - Can't operate with value of non-integer type in tuple constructor");
rv = f_new_inst(); rv = f_new_inst(FI_CONSTANT_INDIRECT);
rv->code = 'C';
NEW_F_VAL; NEW_F_VAL;
rv->a1.p = 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 else
{ {
rv = cfg_allocz(sizeof(struct f_inst3)); rv = f_new_inst(FI_LC_CONSTRUCT);
rv->lineno = ifs->lino;
rv->code = P('m','l');
rv->a1.p = t1; rv->a1.p = t1;
rv->a2.p = t2; rv->a2.p = t2;
INST3(rv).p = t3; rv->a3.p = t3;
} }
return rv; 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 * Remove all new lines and doubled whitespaces
* and convert all tabulators to spaces * 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) assert_done(struct f_inst *expr, const char *start, const char *end)
{ {
struct f_inst *i; struct f_inst *i;
i = f_new_inst(); i = f_new_inst(FI_ASSERT);
i->code = P('a','s');
i->a1.p = expr; i->a1.p = expr;
if (end >= start) if (end >= start)
@ -399,7 +409,7 @@ CF_KEYWORDS(FUNCTION, PRINT, PRINTN, UNSET, RETURN,
TRUE, FALSE, RT, RO, UNKNOWN, GENERIC, TRUE, FALSE, RT, RO, UNKNOWN, GENERIC,
FROM, GW, NET, MASK, PROTO, SOURCE, SCOPE, DEST, IFNAME, IFINDEX, FROM, GW, NET, MASK, PROTO, SOURCE, SCOPE, DEST, IFNAME, IFINDEX,
PREFERENCE, PREFERENCE,
ROA_CHECK, ASN, ROA_CHECK, ASN, SRC,
IS_V4, IS_V6, IS_V4, IS_V6,
LEN, MAXLEN, LEN, MAXLEN,
DEFINED, DEFINED,
@ -412,7 +422,9 @@ CF_KEYWORDS(FUNCTION, PRINT, PRINTN, UNSET, RETURN,
%nonassoc THEN %nonassoc THEN
%nonassoc ELSE %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 <f> filter filter_body where_filter
%type <i> type break_command ec_kind %type <i> type break_command ec_kind
%type <i32> cnum %type <i32> cnum
@ -421,12 +433,12 @@ CF_KEYWORDS(FUNCTION, PRINT, PRINTN, UNSET, RETURN,
%type <v> set_atom switch_atom fipa %type <v> set_atom switch_atom fipa
%type <px> fprefix %type <px> fprefix
%type <s> decls declsn one_decl function_params %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 %type <t> get_cf_position
CF_GRAMMAR CF_GRAMMAR
CF_ADDTO(conf, filter_def) conf: filter_def ;
filter_def: filter_def:
FILTER SYM { $2 = cf_define_symbol($2, SYM_FILTER, NULL); cf_push_scope( $2 ); } FILTER SYM { $2 = cf_define_symbol($2, SYM_FILTER, NULL); cf_push_scope( $2 ); }
filter_body { filter_body {
@ -437,12 +449,12 @@ filter_def:
} }
; ;
CF_ADDTO(conf, filter_eval) conf: filter_eval ;
filter_eval: filter_eval:
EVAL term { f_eval_int($2); } EVAL term { f_eval_int($2); }
; ;
CF_ADDTO(conf, bt_test_suite) conf: bt_test_suite ;
bt_test_suite: bt_test_suite:
BT_TEST_SUITE '(' SYM ',' text ')' { BT_TEST_SUITE '(' SYM ',' text ')' {
if (!($3->class & SYM_FUNCTION)) if (!($3->class & SYM_FUNCTION))
@ -543,16 +555,13 @@ where_filter:
/* Construct 'IF term THEN ACCEPT; REJECT;' */ /* Construct 'IF term THEN ACCEPT; REJECT;' */
struct filter *f = cfg_alloc(sizeof(struct filter)); struct filter *f = cfg_alloc(sizeof(struct filter));
struct f_inst *i, *acc, *rej; struct f_inst *i, *acc, *rej;
acc = f_new_inst(); /* ACCEPT */ acc = f_new_inst(FI_PRINT_AND_DIE); /* ACCEPT */
acc->code = P('p',',');
acc->a1.p = NULL; acc->a1.p = NULL;
acc->a2.i = F_ACCEPT; acc->a2.i = F_ACCEPT;
rej = f_new_inst(); /* REJECT */ rej = f_new_inst(FI_PRINT_AND_DIE); /* REJECT */
rej->code = P('p',',');
rej->a1.p = NULL; rej->a1.p = NULL;
rej->a2.i = F_REJECT; rej->a2.i = F_REJECT;
i = f_new_inst(); /* IF */ i = f_new_inst(FI_CONDITION); /* IF */
i->code = '?';
i->a1.p = $2; i->a1.p = $2;
i->a2.p = acc; i->a2.p = acc;
i->next = rej; i->next = rej;
@ -571,8 +580,7 @@ function_body:
decls '{' cmds '}' { decls '{' cmds '}' {
if ($1) { if ($1) {
/* Prepend instruction to clear local variables */ /* Prepend instruction to clear local variables */
$$ = f_new_inst(); $$ = f_new_inst(FI_CLEAR_LOCAL_VARS);
$$->code = P('c','v');
$$->a1.p = $1; $$->a1.p = $1;
$$->next = $3; $$->next = $3;
} else } else
@ -580,7 +588,7 @@ function_body:
} }
; ;
CF_ADDTO(conf, function_def) conf: function_def ;
function_def: function_def:
FUNCTION SYM { DBG( "Beginning of function %s\n", $2->name ); FUNCTION SYM { DBG( "Beginning of function %s\n", $2->name );
$2 = cf_define_symbol($2, SYM_FUNCTION, NULL); $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: bgp_path_expr:
symbol { $$ = $1; } symbol { $$ = $1; }
@ -763,53 +771,39 @@ bgp_path_expr:
; ;
bgp_path: bgp_path:
PO bgp_path_tail1 PC { $$ = $2; } PO bgp_path_tail PC { $$ = $2; }
| '/' bgp_path_tail2 '/' { $$ = $2; }
; ;
bgp_path_tail1: bgp_path_tail:
NUM bgp_path_tail1 { $$ = cfg_allocz(sizeof(struct f_path_mask)); $$->next = $2; $$->kind = PM_ASN; $$->val = $1; } NUM bgp_path_tail { $$ = 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; } | 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_tail1 { $$ = 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_ASTERISK; }
| '?' bgp_path_tail1 { $$ = cfg_allocz(sizeof(struct f_path_mask)); $$->next = $2; $$->kind = PM_QUESTION; } | '?' bgp_path_tail { $$ = 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_expr bgp_path_tail { $$ = cfg_allocz(sizeof(struct f_path_mask)); $$->next = $2; $$->kind = PM_ASN_EXPR; $$->val = (uintptr_t) $1; }
| { $$ = NULL; } | { $$ = 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: constant:
NUM { $$ = f_new_inst(); $$->code = 'c'; $$->aux = T_INT; $$->a2.i = $1; } NUM { $$ = f_new_inst(FI_CONSTANT); $$->aux = T_INT; $$->a2.i = $1; }
| TRUE { $$ = f_new_inst(); $$->code = 'c'; $$->aux = T_BOOL; $$->a2.i = 1; } | TRUE { $$ = f_new_inst(FI_CONSTANT); $$->aux = T_BOOL; $$->a2.i = 1; }
| FALSE { $$ = f_new_inst(); $$->code = 'c'; $$->aux = T_BOOL; $$->a2.i = 0; } | FALSE { $$ = f_new_inst(FI_CONSTANT); $$->aux = T_BOOL; $$->a2.i = 0; }
| TEXT { $$ = f_new_inst(); $$->code = 'c'; $$->aux = T_STRING; $$->a2.p = $1; } | TEXT { $$ = f_new_inst(FI_CONSTANT); $$->aux = T_STRING; $$->a2.p = $1; }
| fipa { NEW_F_VAL; $$ = f_new_inst(); $$->code = 'C'; $$->a1.p = val; *val = $1; } | fipa { NEW_F_VAL; $$ = f_new_inst(FI_CONSTANT_INDIRECT); $$->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; } | 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(); $$->code = 'C'; val->type = T_NET; val->val.net = $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(); $$->code = 'c'; $$->aux = T_SET; $$->a2.p = build_tree($2); DBG( "ook\n" ); } | '[' 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(); $$->code = 'c'; $$->aux = T_PREFIX_SET; $$->a2.p = $2; } | '[' fprefix_set ']' { $$ = f_new_inst(FI_CONSTANT); $$->aux = T_PREFIX_SET; $$->a2.p = $2; }
| ENUM { $$ = f_new_inst(); $$->code = 'c'; $$->aux = $1 >> 16; $$->a2.i = $1 & 0xffff; } | ENUM { $$ = f_new_inst(FI_CONSTANT); $$->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; }
; ;
constructor: constructor:
'(' term ',' term ')' { $$ = f_generate_dpair($2, $4); } '(' term ',' term ')' { $$ = f_generate_dpair($2, $4); }
| '(' ec_kind ',' term ',' term ')' { $$ = f_generate_ec($2, $4, $6); } | '(' ec_kind ',' term ',' term ')' { $$ = f_generate_ec($2, $4, $6); }
| '(' term ',' term ',' term ')' { $$ = f_generate_lc($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 */ rtadot: /* EMPTY, we are not permitted RTA. prefix */
; ;
@ -820,8 +814,7 @@ function_call:
if ($1->class != SYM_FUNCTION) if ($1->class != SYM_FUNCTION)
cf_error("You can't call something which is not a function. Really."); cf_error("You can't call something which is not a function. Really.");
DBG("You are calling function %s\n", $1->name); DBG("You are calling function %s\n", $1->name);
$$ = f_new_inst(); $$ = f_new_inst(FI_CALL);
$$->code = P('c','a');
$$->a1.p = inst; $$->a1.p = inst;
$$->a2.p = $1->def; $$->a2.p = $1->def;
sym = $1->aux2; sym = $1->aux2;
@ -838,11 +831,9 @@ function_call:
symbol: symbol:
SYM { SYM {
$$ = f_new_inst();
switch ($1->class & 0xff00) { switch ($1->class & 0xff00) {
case SYM_CONSTANT: $$->code = 'C'; break; case SYM_CONSTANT: $$ = f_new_inst(FI_CONSTANT_INDIRECT); break;
case SYM_VARIABLE: $$->code = 'V'; break; case SYM_VARIABLE: $$ = f_new_inst(FI_VARIABLE); break;
default: cf_error("%s: variable expected.", $1->name); default: cf_error("%s: variable expected.", $1->name);
} }
@ -851,57 +842,58 @@ symbol:
} }
static_attr: static_attr:
FROM { $$ = f_new_inst(); $$->aux = T_IP; $$->a2.i = SA_FROM; $$->a1.i = 1; } FROM { $$ = f_new_static_attr(T_IP, SA_FROM, 1); }
| GW { $$ = f_new_inst(); $$->aux = T_IP; $$->a2.i = SA_GW; $$->a1.i = 1; } | GW { $$ = f_new_static_attr(T_IP, SA_GW, 1); }
| NET { $$ = f_new_inst(); $$->aux = T_NET; $$->a2.i = SA_NET; } | NET { $$ = f_new_static_attr(T_NET, SA_NET, 0); }
| PROTO { $$ = f_new_inst(); $$->aux = T_STRING; $$->a2.i = SA_PROTO; } | PROTO { $$ = f_new_static_attr(T_STRING, SA_PROTO, 0); }
| SOURCE { $$ = f_new_inst(); $$->aux = T_ENUM_RTS; $$->a2.i = SA_SOURCE; } | SOURCE { $$ = f_new_static_attr(T_ENUM_RTS, SA_SOURCE, 0); }
| SCOPE { $$ = f_new_inst(); $$->aux = T_ENUM_SCOPE; $$->a2.i = SA_SCOPE; $$->a1.i = 1; } | SCOPE { $$ = f_new_static_attr(T_ENUM_SCOPE, SA_SCOPE, 1); }
| DEST { $$ = f_new_inst(); $$->aux = T_ENUM_RTD; $$->a2.i = SA_DEST; $$->a1.i = 1; } | DEST { $$ = f_new_static_attr(T_ENUM_RTD, SA_DEST, 1); }
| IFNAME { $$ = f_new_inst(); $$->aux = T_STRING; $$->a2.i = SA_IFNAME; } | IFNAME { $$ = f_new_static_attr(T_STRING, SA_IFNAME, 0); }
| IFINDEX { $$ = f_new_inst(); $$->aux = T_INT; $$->a2.i = SA_IFINDEX; } | IFINDEX { $$ = f_new_static_attr(T_INT, SA_IFINDEX, 0); }
; ;
term: term:
'(' term ')' { $$ = $2; } '(' term ')' { $$ = $2; }
| term '+' term { $$ = f_new_inst(); $$->code = '+'; $$->a1.p = $1; $$->a2.p = $3; } | term '+' term { $$ = f_new_inst(FI_ADD); $$->a1.p = $1; $$->a2.p = $3; }
| term '-' term { $$ = f_new_inst(); $$->code = '-'; $$->a1.p = $1; $$->a2.p = $3; } | term '-' term { $$ = f_new_inst(FI_SUBTRACT); $$->a1.p = $1; $$->a2.p = $3; }
| term '*' term { $$ = f_new_inst(); $$->code = '*'; $$->a1.p = $1; $$->a2.p = $3; } | term '*' term { $$ = f_new_inst(FI_MULTIPLY); $$->a1.p = $1; $$->a2.p = $3; }
| term '/' term { $$ = f_new_inst(); $$->code = '/'; $$->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(); $$->code = '&'; $$->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(); $$->code = '|'; $$->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(); $$->code = P('=','='); $$->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(); $$->code = P('!','='); $$->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(); $$->code = '<'; $$->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(); $$->code = P('<','='); $$->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(); $$->code = '<'; $$->a1.p = $3; $$->a2.p = $1; } | term '>' term { $$ = f_new_inst(FI_LT); $$->a1.p = $3; $$->a2.p = $1; }
| term GEQ term { $$ = f_new_inst(); $$->code = P('<','='); $$->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(); $$->code = '~'; $$->a1.p = $1; $$->a2.p = $3; } | term '~' term { $$ = f_new_inst(FI_MATCH); $$->a1.p = $1; $$->a2.p = $3; }
| term NMA term { $$ = f_new_inst(); $$->code = P('!','~'); $$->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(); $$->code = '!'; $$->a1.p = $2; } | '!' term { $$ = f_new_inst(FI_NOT); $$->a1.p = $2; }
| DEFINED '(' term ')' { $$ = f_new_inst(); $$->code = P('d','e'); $$->a1.p = $3; } | DEFINED '(' term ')' { $$ = f_new_inst(FI_DEFINED); $$->a1.p = $3; }
| symbol { $$ = $1; } | symbol { $$ = $1; }
| constant { $$ = $1; } | constant { $$ = $1; }
| constructor { $$ = $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 '.' IS_V4 { $$ = f_new_inst(FI_IS_V4); $$->a1.p = $1; }
| term '.' TYPE { $$ = f_new_inst(); $$->code = 'T'; $$->a1.p = $1; } | term '.' TYPE { $$ = f_new_inst(FI_TYPE); $$->a1.p = $1; }
| term '.' IP { $$ = f_new_inst(); $$->code = P('c','p'); $$->a1.p = $1; $$->aux = T_IP; } | term '.' IP { $$ = f_new_inst(FI_IP); $$->a1.p = $1; $$->aux = T_IP; }
| term '.' RD { $$ = f_new_inst(); $$->code = P('R','D'); $$->a1.p = $1; $$->aux = T_RD; } | term '.' RD { $$ = f_new_inst(FI_ROUTE_DISTINGUISHER); $$->a1.p = $1; $$->aux = T_RD; }
| term '.' LEN { $$ = f_new_inst(); $$->code = 'L'; $$->a1.p = $1; } | term '.' LEN { $$ = f_new_inst(FI_LENGTH); $$->a1.p = $1; }
| term '.' MAXLEN { $$ = f_new_inst(); $$->code = P('R','m'); $$->a1.p = $1; } | term '.' MAXLEN { $$ = f_new_inst(FI_ROA_MAXLEN); $$->a1.p = $1; }
| term '.' ASN { $$ = f_new_inst(); $$->code = P('R','a'); $$->a1.p = $1; } | term '.' ASN { $$ = f_new_inst(FI_ROA_ASN); $$->a1.p = $1; }
| term '.' MASK '(' term ')' { $$ = f_new_inst(); $$->code = P('i','M'); $$->a1.p = $1; $$->a2.p = $5; } | term '.' SRC { $$ = f_new_inst(FI_SADR_SRC); $$->a1.p = $1; }
| term '.' FIRST { $$ = f_new_inst(); $$->code = P('a','f'); $$->a1.p = $1; } | term '.' MASK '(' term ')' { $$ = f_new_inst(FI_IP_MASK); $$->a1.p = $1; $$->a2.p = $5; }
| term '.' LAST { $$ = f_new_inst(); $$->code = P('a','l'); $$->a1.p = $1; } | term '.' FIRST { $$ = f_new_inst(FI_AS_PATH_FIRST); $$->a1.p = $1; }
| term '.' LAST_NONAGGREGATED { $$ = f_new_inst(); $$->code = P('a','L'); $$->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 */ /* Communities */
/* This causes one shift/reduce conflict /* This causes one shift/reduce conflict
@ -911,19 +903,19 @@ term:
| rtadot dynamic_attr '.' RESET{ } | rtadot dynamic_attr '.' RESET{ }
*/ */
| '+' EMPTY '+' { $$ = f_new_inst(); $$->code = 'E'; $$->aux = T_PATH; } | '+' EMPTY '+' { $$ = f_new_inst(FI_EMPTY); $$->aux = T_PATH; }
| '-' EMPTY '-' { $$ = f_new_inst(); $$->code = 'E'; $$->aux = T_CLIST; } | '-' EMPTY '-' { $$ = f_new_inst(FI_EMPTY); $$->aux = T_CLIST; }
| '-' '-' EMPTY '-' '-' { $$ = f_new_inst(); $$->code = 'E'; $$->aux = T_ECLIST; } | '-' '-' EMPTY '-' '-' { $$ = f_new_inst(FI_EMPTY); $$->aux = T_ECLIST; }
| '-' '-' '-' EMPTY '-' '-' '-' { $$ = f_new_inst(); $$->code = 'E'; $$->aux = T_LCLIST; } | '-' '-' '-' EMPTY '-' '-' '-' { $$ = f_new_inst(FI_EMPTY); $$->aux = T_LCLIST; }
| PREPEND '(' term ',' term ')' { $$ = f_new_inst(); $$->code = P('A','p'); $$->a1.p = $3; $$->a2.p = $5; } | PREPEND '(' term ',' term ')' { $$ = f_new_inst(FI_PATH_PREPEND); $$->a1.p = $3; $$->a2.p = $5; }
| ADD '(' term ',' term ')' { $$ = f_new_inst(); $$->code = P('C','a'); $$->a1.p = $3; $$->a2.p = $5; $$->aux = 'a'; } | ADD '(' term ',' term ')' { $$ = f_new_inst(FI_CLIST_ADD_DEL); $$->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'; } | DELETE '(' term ',' term ')' { $$ = f_new_inst(FI_CLIST_ADD_DEL); $$->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'; } | 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 ')' { $$ = f_generate_roa_check($3, NULL, NULL); }
| ROA_CHECK '(' rtable ',' term ',' term ')' { $$ = f_generate_roa_check($3, $5, $7); } | 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'); } */ /* | term '.' LEN { $$->code = P('P','l'); } */
@ -934,8 +926,7 @@ term:
if ($1->class != SYM_FUNCTION) if ($1->class != SYM_FUNCTION)
cf_error("You can't call something which is not a function. Really."); cf_error("You can't call something which is not a function. Really.");
DBG("You are calling function %s\n", $1->name); DBG("You are calling function %s\n", $1->name);
$$ = f_new_inst(); $$ = f_new_inst(FI_CALL);
$$->code = P('c','a');
$$->a1.p = inst; $$->a1.p = inst;
$$->a2.p = $1->def; $$->a2.p = $1->def;
sym = $1->aux2; sym = $1->aux2;
@ -960,7 +951,7 @@ break_command:
; ;
print_one: 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; } print_list: /* EMPTY */ { $$ = NULL; }
@ -974,15 +965,13 @@ print_list: /* EMPTY */ { $$ = NULL; }
; ;
var_listn: term { var_listn: term {
$$ = f_new_inst(); $$ = f_new_inst(FI_SET);
$$->code = 's';
$$->a1.p = NULL; $$->a1.p = NULL;
$$->a2.p = $1; $$->a2.p = $1;
$$->next = NULL; $$->next = NULL;
} }
| term ',' var_listn { | term ',' var_listn {
$$ = f_new_inst(); $$ = f_new_inst(FI_SET);
$$->code = 's';
$$->a1.p = NULL; $$->a1.p = NULL;
$$->a2.p = $1; $$->a2.p = $1;
$$->next = $3; $$->next = $3;
@ -995,73 +984,63 @@ var_list: /* EMPTY */ { $$ = NULL; }
cmd: cmd:
IF term THEN block { IF term THEN block {
$$ = f_new_inst(); $$ = f_new_inst(FI_CONDITION);
$$->code = '?';
$$->a1.p = $2; $$->a1.p = $2;
$$->a2.p = $4; $$->a2.p = $4;
} }
| IF term THEN block ELSE block { | IF term THEN block ELSE block {
struct f_inst *i = f_new_inst(); struct f_inst *i = f_new_inst(FI_CONDITION);
i->code = '?';
i->a1.p = $2; i->a1.p = $2;
i->a2.p = $4; i->a2.p = $4;
$$ = f_new_inst(); $$ = f_new_inst(FI_CONDITION);
$$->code = '?';
$$->a1.p = i; $$->a1.p = i;
$$->a2.p = $6; $$->a2.p = $6;
} }
| SYM '=' term ';' { | SYM '=' term ';' {
$$ = f_new_inst();
DBG( "Ook, we'll set value\n" ); DBG( "Ook, we'll set value\n" );
if (($1->class & ~T_MASK) != SYM_VARIABLE) if (($1->class & ~T_MASK) != SYM_VARIABLE)
cf_error( "You may set only variables." ); cf_error( "You may set only variables." );
$$->code = 's'; $$ = f_new_inst(FI_SET);
$$->a1.p = $1; $$->a1.p = $1;
$$->a2.p = $3; $$->a2.p = $3;
} }
| RETURN term ';' { | RETURN term ';' {
$$ = f_new_inst();
DBG( "Ook, we'll return the value\n" ); DBG( "Ook, we'll return the value\n" );
$$->code = 'r'; $$ = f_new_inst(FI_RETURN);
$$->a1.p = $2; $$->a1.p = $2;
} }
| rtadot dynamic_attr '=' term ';' { | rtadot dynamic_attr '=' term ';' {
$$ = $2; $$ = f_new_inst_da(FI_EA_SET, $2);
$$->code = P('e','S');
$$->a1.p = $4; $$->a1.p = $4;
} }
| rtadot static_attr '=' term ';' { | rtadot static_attr '=' term ';' {
$$ = $2; $$ = f_new_inst_sa(FI_RTA_SET, $2);
if (!$$->a1.i) if (!$$->a1.i)
cf_error( "This static attribute is read-only."); cf_error( "This static attribute is read-only.");
$$->code = P('a','S');
$$->a1.p = $4; $$->a1.p = $4;
} }
| PREFERENCE '=' term ';' { | PREFERENCE '=' term ';' {
$$ = f_new_inst(); $$ = f_new_inst(FI_PREF_SET);
$$->code = P('P','S');
$$->a1.p = $3; $$->a1.p = $3;
} }
| UNSET '(' rtadot dynamic_attr ')' ';' { | UNSET '(' rtadot dynamic_attr ')' ';' {
$$ = $4; $$ = f_new_inst_da(FI_EA_SET, $4);
$$->aux = EAF_TYPE_UNDEF | EAF_TEMP; $$->aux = EAF_TYPE_UNDEF | EAF_TEMP;
$$->code = P('e','S');
$$->a1.p = NULL; $$->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; } | function_call ';' { $$ = $1; }
| CASE term '{' switch_body '}' { | CASE term '{' switch_body '}' {
$$ = f_new_inst(); $$ = f_new_inst(FI_SWITCH);
$$->code = P('S','W');
$$->a1.p = $2; $$->a1.p = $2;
$$->a2.p = build_tree( $4 ); $$->a2.p = build_tree( $4 );
} }
| rtadot dynamic_attr '.' EMPTY ';' { $$ = f_generate_empty($2); } | 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 '.' PREPEND '(' term ')' ';' { $$ = f_generate_complex( FI_PATH_PREPEND, 'x', $2, $6 ); }
| rtadot dynamic_attr '.' ADD '(' term ')' ';' { $$ = f_generate_complex( P('C','a'), 'a', $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( P('C','a'), 'd', $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( P('C','a'), 'f', $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); } | BT_ASSERT '(' get_cf_position term get_cf_position ')' ';' { $$ = assert_done($4, $3 + 1, $5 - 1); }
; ;

View File

@ -2,6 +2,7 @@
* Filters: utility functions * Filters: utility functions
* *
* Copyright 1998 Pavel Machek <pavel@ucw.cz> * 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. * Can be freely distributed and used under the terms of the GNU GPL.
*/ */
@ -13,43 +14,48 @@
#define P(a,b) ((a<<8) | b) #define P(a,b) ((a<<8) | b)
struct f_inst * struct f_inst *
f_new_inst(void) f_new_inst(enum f_instruction_code fi_code)
{ {
struct f_inst * ret; struct f_inst * ret;
ret = cfg_alloc(sizeof(struct f_inst)); ret = cfg_allocz(sizeof(struct f_inst));
ret->code = ret->aux = 0; ret->fi_code = fi_code;
ret->arg1 = ret->arg2 = ret->next = NULL;
ret->lineno = ifs->lino; ret->lineno = ifs->lino;
return ret; return ret;
} }
struct f_inst * 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 *ret = f_new_inst(fi_code);
struct f_inst *f = f_new_inst(); ret->aux = (da.f_type << 8) | da.type;
f->aux = type; ret->a2.i = da.ea_code;
f->a2.i = code; return ret;
return f; }
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 ) ) * Generate set_dynamic( operation( get_dynamic(), argument ) )
*/ */
struct f_inst * 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(), struct f_inst *set_dyn = f_new_inst_da(FI_EA_SET, da),
*oper = f_new_inst(), *oper = f_new_inst(operation),
*get_dyn = dyn; *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->aux = operation_aux;
oper->a1.p = get_dyn; oper->a1.p = get_dyn;
oper->a2.p = argument; oper->a2.p = argument;
set_dyn->code = P('e','S');
set_dyn->a1.p = oper; set_dyn->a1.p = oper;
return set_dyn; 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) 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)); 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.lineno = ifs->lino;
ret->i.arg1 = prefix; ret->i.arg1 = prefix;
ret->i.arg2 = asn; ret->i.arg2 = asn;
@ -71,6 +77,22 @@ f_generate_roa_check(struct rtable_config *table, struct f_inst *prefix, struct
return &ret->i; 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 * char *
filter_name(struct filter *filter) filter_name(struct filter *filter)
{ {

File diff suppressed because it is too large Load Diff

View File

@ -14,9 +14,78 @@
#include "nest/route.h" #include "nest/route.h"
#include "nest/attrs.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 { /* Instruction */
struct f_inst *next; /* Structure is 16 bytes, anyway */ 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_* */ u16 aux; /* Extension to instruction code, T_*, EA_*, EAF_* */
union { union {
uint i; uint i;
@ -26,6 +95,10 @@ struct f_inst { /* Instruction */
uint i; uint i;
void *p; void *p;
} a2; /* The second argument */ } a2; /* The second argument */
union {
int i;
void *p;
} a3; /* The third argument */
int lineno; int lineno;
}; };
@ -38,17 +111,6 @@ struct f_inst_roa_check {
struct rtable_config *rtc; 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 { struct f_prefix {
net_addr net; net_addr net;
u8 lo, hi; u8 lo, hi;
@ -70,15 +132,32 @@ struct f_val {
} 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 { struct filter {
char *name; char *name;
struct f_inst *root; struct f_inst *root;
}; };
struct f_inst *f_new_inst(void); struct f_inst *f_new_inst(enum f_instruction_code fi_code);
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_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_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); 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 ea_list;
struct rte; 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_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); struct f_val f_eval(struct f_inst *expr, struct linpool *tmp_pool);
uint f_eval_int(struct f_inst *expr); uint f_eval_int(struct f_inst *expr);
u32 f_eval_asn(struct f_inst *expr);
char *filter_name(struct filter *filter); char *filter_name(struct filter *filter);
int filter_same(struct filter *new, struct filter *old); 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_ACCEPT NULL
#define FILTER_REJECT ((void *) 1) #define FILTER_REJECT ((void *) 1)
#define FILTER_UNDEF ((void *) 2) /* Used in BGP */
/* Type numbers must be in 0..0xff range */ /* Type numbers must be in 0..0xff range */
#define T_MASK 0xff #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_RTD 0x34
#define T_ENUM_ROA 0x35 #define T_ENUM_ROA 0x35
#define T_ENUM_NETTYPE 0x36 #define T_ENUM_NETTYPE 0x36
#define T_ENUM_RA_PREFERENCE 0x37
/* new enums go here */ /* new enums go here */
#define T_ENUM_EMPTY 0x3f /* Special hack for atomic_aggr */ #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 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 */ /* Bird Tests */
struct f_bt_test_suite { struct f_bt_test_suite {

View File

@ -589,14 +589,11 @@ function mkpath(int a; int b)
function t_path() function t_path()
bgpmask pm1; bgpmask pm1;
bgpmask pm2;
bgppath p2; bgppath p2;
{ {
pm1 = / 4 3 2 1 /; pm1 = [= 4 3 2 1 =];
pm2 = [= 4 3 2 1 =];
bt_assert(pm1 = pm2); bt_assert(format(pm1) = "[= 4 3 2 1 =]");
bt_assert(format(pm2) = "[= 4 3 2 1 =]");
bt_assert(+empty+ = +empty+); bt_assert(+empty+ = +empty+);
bt_assert(10 !~ +empty+); bt_assert(10 !~ +empty+);
@ -609,17 +606,14 @@ bgppath p2;
bt_assert(format(p2) = "(path 4 3 2 1)"); bt_assert(format(p2) = "(path 4 3 2 1)");
bt_assert(p2.len = 4); bt_assert(p2.len = 4);
bt_assert(p2 ~ pm1); bt_assert(p2 ~ pm1);
bt_assert(p2 ~ pm2);
bt_assert(3 ~ p2); bt_assert(3 ~ p2);
bt_assert(p2 ~ [2, 10..20]); bt_assert(p2 ~ [2, 10..20]);
bt_assert(p2 ~ [4, 10..20]); bt_assert(p2 ~ [4, 10..20]);
p2 = prepend(p2, 5); p2 = prepend(p2, 5);
bt_assert(p2 !~ pm1); bt_assert(p2 !~ pm1);
bt_assert(p2 !~ pm2);
bt_assert(10 !~ p2); bt_assert(10 !~ p2);
bt_assert(p2 !~ [8, ten..(2*ten)]); bt_assert(p2 !~ [8, ten..(2*ten)]);
bt_assert(p2 ~ / ? 4 3 2 1 /);
bt_assert(p2 ~ [= * 4 3 * 1 =]); bt_assert(p2 ~ [= * 4 3 * 1 =]);
bt_assert(p2 ~ [= (3+2) (2*2) 3 2 1 =]); bt_assert(p2 ~ [= (3+2) (2*2) 3 2 1 =]);
bt_assert(p2 ~ mkpath(5, 4)); bt_assert(p2 ~ mkpath(5, 4));

View File

@ -55,29 +55,28 @@ protocol static {
rip_metric = rip_metric + 5; rip_metric = rip_metric + 5;
print rip_metric; print rip_metric;
# bgp_community = -empty-;
# TODO: uncomment this part after finishing BGP integration version print "hi";
# bgp_community = add(bgp_community, (1,2));
# bgp_community = -empty-; print "hello";
# print "hi"; bgp_community = add(bgp_community, (2,3));
# bgp_community = add(bgp_community, (1,2)); bgp_community.add((4,5));
# print "hello"; print "community = ", bgp_community;
# bgp_community = add(bgp_community, (2,3)); bgp_community.delete((2,3));
# bgp_community.add((4,5)); print "community = ", bgp_community;
# print "community = ", bgp_community; bgp_community.empty;
# bgp_community.delete((2,3)); print "community = ", bgp_community;
# print "community = ", bgp_community; print "done";
# bgp_community.empty;
# print "community = ", bgp_community; accept;
# print "done";
}; };
}; };
route 0.0.0.0/0 via 195.113.31.113; route 0.0.0.0/0 via 195.113.31.113;
route 62.168.0.0/25 reject; route 62.168.0.0/25 reject;
route 1.2.3.4/32 via 195.113.31.124; route 1.2.3.4/32 via 195.113.31.124;
route 10.0.0.0/8 reject; route 10.0.0.0/8 reject;
route 10.1.1.0:255.255.255.0 via 62.168.0.3; route 10.1.1.0/24 via 62.168.0.3;
route 10.1.2.0:255.255.255.0 via 62.168.0.3; route 10.1.2.0/24 via 62.168.0.3;
route 10.1.3.0:255.255.255.0 via 62.168.0.4; route 10.1.3.0/24 via 62.168.0.4;
route 10.2.0.0/24 via "arc0"; route 10.2.0.0/24 via "arc0";
} }

View File

@ -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) obj := $(src-o-files)
$(all-daemon) $(all-daemon)

View File

@ -9,7 +9,6 @@
#ifndef _BIRD_BIRDLIB_H_ #ifndef _BIRD_BIRDLIB_H_
#define _BIRD_BIRDLIB_H_ #define _BIRD_BIRDLIB_H_
#include "sysdep/unix/timer.h"
#include "lib/alloca.h" #include "lib/alloca.h"
/* Ugly structure offset handling macros */ /* Ugly structure offset handling macros */
@ -70,10 +69,11 @@ static inline int u64_cmp(u64 i1, u64 i2)
/* Microsecond time */ /* Microsecond time */
typedef s64 btime; typedef s64 btime;
//typedef s64 bird_clock_t;
#define S_ *1000000 #define S_ * (btime) 1000000
#define MS_ *1000 #define MS_ * (btime) 1000
#define US_ *1 #define US_ * (btime) 1
#define TO_S /1000000 #define TO_S /1000000
#define TO_MS /1000 #define TO_MS /1000
#define TO_US /1 #define TO_US /1
@ -82,39 +82,26 @@ typedef s64 btime;
#define S S_ #define S S_
#define MS MS_ #define MS MS_
#define US US_ #define US US_
#define NS /1000
#endif #endif
#define TIME_INFINITY ((s64) 0x7fffffffffffffff)
/* Rate limiting */ /* Rate limiting */
struct tbf { struct tbf {
bird_clock_t timestamp; /* Last update */ btime timestamp; /* Last update */
u16 count; /* Available tokens */ u64 count; /* Available micro-tokens */
u16 burst; /* Max number of tokens */ u16 burst; /* Max number of tokens */
u16 rate; /* Rate of replenishment */ u16 rate; /* Rate of replenishment (tokens / sec) */
u16 mark; /* Whether last op was limited */ u32 drop; /* Number of failed request since last successful */
}; };
/* Default TBF values for rate limiting log messages */ /* Default TBF values for rate limiting log messages */
#define TBF_DEFAULT_LOG_LIMITS { .rate = 1, .burst = 5 } #define TBF_DEFAULT_LOG_LIMITS { .rate = 1, .burst = 5 }
void tbf_update(struct tbf *f); int tbf_limit(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;
}
/* Logging and dying */ /* Logging and dying */
@ -165,9 +152,9 @@ void debug(const char *msg, ...); /* Printf to debug output */
#endif #endif
#ifdef DEBUGGING #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 #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 #endif
/* Pseudorandom numbers */ /* Pseudorandom numbers */

View File

@ -13,10 +13,14 @@
#include "lib/resource.h" #include "lib/resource.h"
#include "sysdep/config.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_TYPE(v) typeof(* (v).data)
#define BUFFER_SIZE(v) ((v).size * sizeof(* (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) \ #define BUFFER_INIT(v,pool,isize) \
({ \ ({ \
(v).used = 0; \ (v).used = 0; \

View File

@ -55,6 +55,7 @@ t_ev_run_list(void)
resource_init(); resource_init();
olock_init(); olock_init();
timer_init();
io_init(); io_init();
rt_init(); rt_init();
if_init(); if_init();

View File

@ -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 */ /* A data structure for keep a state of flow builder */
struct flow_builder { struct flow_builder {
BUFFER(byte) data; BUFFER_(byte) data;
enum flow_type this_type; enum flow_type this_type;
enum flow_type last_type; enum flow_type last_type;
u16 last_op_offset; /* Position of last operator in data.data */ u16 last_op_offset; /* Position of last operator in data.data */

View File

@ -70,8 +70,8 @@ t_first_part(void)
net_addr_flow4 *f; net_addr_flow4 *f;
NET_ADDR_FLOW4_(f, ip4_build(10,0,0,1), 24, ((byte[]) { 0x00, 0x00, 0xab })); NET_ADDR_FLOW4_(f, ip4_build(10,0,0,1), 24, ((byte[]) { 0x00, 0x00, 0xab }));
const byte const *under240 = &f->data[1]; const byte *under240 = &f->data[1];
const byte const *above240 = &f->data[2]; const byte *above240 = &f->data[2];
/* Case 0x00 0x00 */ /* Case 0x00 0x00 */
bt_assert(flow4_first_part(f) == NULL); bt_assert(flow4_first_part(f) == NULL);

View File

@ -230,4 +230,11 @@ mem_hash(void *p, uint s)
return mem_hash_value(&h); 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 #endif

View File

@ -241,6 +241,9 @@ static inline int ip6_is_v4mapped(ip6_addr a)
#define ipa_classify(x) ip6_classify(&(x)) #define ipa_classify(x) ip6_classify(&(x))
#define ipa_is_link_local(x) ip6_is_link_local(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 */ /* XXXX remove */
static inline int ipa_classify_net(ip_addr a) static inline int ipa_classify_net(ip_addr a)
{ return ipa_zero2(a) ? (IADDR_HOST | SCOPE_UNIVERSE) : ipa_classify(a); } { return ipa_zero2(a) ? (IADDR_HOST | SCOPE_UNIVERSE) : ipa_classify(a); }

98
lib/macro.h Normal file
View 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

View File

@ -11,7 +11,7 @@
* *
* Linear memory pools are collections of memory blocks which * Linear memory pools are collections of memory blocks which
* support very fast allocation of new blocks, but are able to free only * 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, * Example: Each configuration is described by a complex system of structures,
* linked lists and function trees which are all allocated from a single linear * 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 { struct linpool {
resource r; resource r;
byte *ptr, *end; 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 */ struct lp_chunk *first_large; /* Large chunks */
uint chunk_size, threshold, total, total_large; uint chunk_size, threshold, total, total_large;
}; };
@ -69,7 +69,6 @@ linpool
*lp_new(pool *p, uint blk) *lp_new(pool *p, uint blk)
{ {
linpool *m = ralloc(p, &lp_class); linpool *m = ralloc(p, &lp_class);
m->plast = &m->first;
m->chunk_size = blk; m->chunk_size = blk;
m->threshold = 3*blk/4; m->threshold = 3*blk/4;
return m; return m;
@ -114,22 +113,25 @@ lp_alloc(linpool *m, uint size)
} }
else else
{ {
if (m->current) if (m->current && m->current->next)
{ {
/* Still have free chunks from previous incarnation (before lp_flush()) */ /* Still have free chunks from previous incarnation (before lp_flush()) */
c = m->current; c = m->current->next;
m->current = c->next;
} }
else else
{ {
/* Need to allocate a new chunk */ /* Need to allocate a new chunk */
c = xmalloc(sizeof(struct lp_chunk) + m->chunk_size); c = xmalloc(sizeof(struct lp_chunk) + m->chunk_size);
m->total += m->chunk_size; m->total += m->chunk_size;
*m->plast = c;
m->plast = &c->next;
c->next = NULL; c->next = NULL;
c->size = m->chunk_size; 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->ptr = c->data + size;
m->end = c->data + m->chunk_size; m->end = c->data + m->chunk_size;
} }
@ -190,9 +192,11 @@ lp_flush(linpool *m)
{ {
struct lp_chunk *c; struct lp_chunk *c;
/* Relink all normal chunks to free list and free all large chunks */ /* Move ptr to the first chunk and free all large chunks */
m->ptr = m->end = NULL; m->current = c = m->first;
m->current = m->first; m->ptr = c ? c->data : NULL;
m->end = c ? c->data + m->chunk_size : NULL;
while (c = m->first_large) while (c = m->first_large)
{ {
m->first_large = c->next; m->first_large = c->next;
@ -201,6 +205,50 @@ lp_flush(linpool *m)
m->total_large = 0; 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 static void
lp_free(resource *r) lp_free(resource *r)
{ {

105
lib/net.c
View File

@ -6,50 +6,54 @@
const char * const net_label[] = { const char * const net_label[] = {
[NET_IP4] = "ipv4", [NET_IP4] = "ipv4",
[NET_IP6] = "ipv6", [NET_IP6] = "ipv6",
[NET_VPN4] = "vpn4", [NET_VPN4] = "vpn4",
[NET_VPN6] = "vpn6", [NET_VPN6] = "vpn6",
[NET_ROA4] = "roa4", [NET_ROA4] = "roa4",
[NET_ROA6] = "roa6", [NET_ROA6] = "roa6",
[NET_FLOW4] = "flow4", [NET_FLOW4] = "flow4",
[NET_FLOW6] = "flow6", [NET_FLOW6] = "flow6",
[NET_IP6_SADR]= "ipv6-sadr",
[NET_MPLS] = "mpls", [NET_MPLS] = "mpls",
}; };
const u16 net_addr_length[] = { const u16 net_addr_length[] = {
[NET_IP4] = sizeof(net_addr_ip4), [NET_IP4] = sizeof(net_addr_ip4),
[NET_IP6] = sizeof(net_addr_ip6), [NET_IP6] = sizeof(net_addr_ip6),
[NET_VPN4] = sizeof(net_addr_vpn4), [NET_VPN4] = sizeof(net_addr_vpn4),
[NET_VPN6] = sizeof(net_addr_vpn6), [NET_VPN6] = sizeof(net_addr_vpn6),
[NET_ROA4] = sizeof(net_addr_roa4), [NET_ROA4] = sizeof(net_addr_roa4),
[NET_ROA6] = sizeof(net_addr_roa6), [NET_ROA6] = sizeof(net_addr_roa6),
[NET_FLOW4] = 0, [NET_FLOW4] = 0,
[NET_FLOW6] = 0, [NET_FLOW6] = 0,
[NET_IP6_SADR]= sizeof(net_addr_ip6_sadr),
[NET_MPLS] = sizeof(net_addr_mpls), [NET_MPLS] = sizeof(net_addr_mpls),
}; };
const u8 net_max_prefix_length[] = { const u8 net_max_prefix_length[] = {
[NET_IP4] = IP4_MAX_PREFIX_LENGTH, [NET_IP4] = IP4_MAX_PREFIX_LENGTH,
[NET_IP6] = IP6_MAX_PREFIX_LENGTH, [NET_IP6] = IP6_MAX_PREFIX_LENGTH,
[NET_VPN4] = IP4_MAX_PREFIX_LENGTH, [NET_VPN4] = IP4_MAX_PREFIX_LENGTH,
[NET_VPN6] = IP6_MAX_PREFIX_LENGTH, [NET_VPN6] = IP6_MAX_PREFIX_LENGTH,
[NET_ROA4] = IP4_MAX_PREFIX_LENGTH, [NET_ROA4] = IP4_MAX_PREFIX_LENGTH,
[NET_ROA6] = IP6_MAX_PREFIX_LENGTH, [NET_ROA6] = IP6_MAX_PREFIX_LENGTH,
[NET_FLOW4] = IP4_MAX_PREFIX_LENGTH, [NET_FLOW4] = IP4_MAX_PREFIX_LENGTH,
[NET_FLOW6] = IP6_MAX_PREFIX_LENGTH, [NET_FLOW6] = IP6_MAX_PREFIX_LENGTH,
[NET_IP6_SADR]= IP6_MAX_PREFIX_LENGTH,
[NET_MPLS] = 0, [NET_MPLS] = 0,
}; };
const u16 net_max_text_length[] = { const u16 net_max_text_length[] = {
[NET_IP4] = 18, /* "255.255.255.255/32" */ [NET_IP4] = 18, /* "255.255.255.255/32" */
[NET_IP6] = 43, /* "ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff/128" */ [NET_IP6] = 43, /* "ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff/128" */
[NET_VPN4] = 40, /* "4294967296:4294967296 255.255.255.255/32" */ [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_VPN6] = 65, /* "4294967296:4294967296 ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff/128" */
[NET_ROA4] = 34, /* "255.255.255.255/32-32 AS4294967295" */ [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_ROA6] = 60, /* "ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff/128-128 AS4294967295" */
[NET_FLOW4] = 0, /* "flow4 { ... }" */ [NET_FLOW4] = 0, /* "flow4 { ... }" */
[NET_FLOW6] = 0, /* "flow6 { ... }" */ [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" */ [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); return flow4_net_format(buf, buflen, &n->flow4);
case NET_FLOW6: case NET_FLOW6:
return flow6_net_format(buf, buflen, &n->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: case NET_MPLS:
return bsnprintf(buf, buflen, "%u", n->mpls.label); return bsnprintf(buf, buflen, "%u", n->mpls.label);
} }
@ -124,6 +130,7 @@ net_pxmask(const net_addr *a)
case NET_VPN6: case NET_VPN6:
case NET_ROA6: case NET_ROA6:
case NET_FLOW6: case NET_FLOW6:
case NET_IP6_SADR:
return ipa_from_ip6(ip6_mkmask(net6_pxlen(a))); return ipa_from_ip6(ip6_mkmask(net6_pxlen(a)));
case NET_MPLS: 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); return net_compare_flow4((const net_addr_flow4 *) a, (const net_addr_flow4 *) b);
case NET_FLOW6: case NET_FLOW6:
return net_compare_flow6((const net_addr_flow6 *) a, (const net_addr_flow6 *) b); 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: case NET_MPLS:
return net_compare_mpls((const net_addr_mpls *) a, (const net_addr_mpls *) b); 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_ROA6: return NET_HASH(n, roa6);
case NET_FLOW4: return NET_HASH(n, flow4); case NET_FLOW4: return NET_HASH(n, flow4);
case NET_FLOW6: return NET_HASH(n, flow6); 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); case NET_MPLS: return NET_HASH(n, mpls);
default: bug("invalid type"); default: bug("invalid type");
} }
@ -198,6 +208,7 @@ net_validate(const net_addr *n)
case NET_ROA6: return NET_VALIDATE(n, roa6); case NET_ROA6: return NET_VALIDATE(n, roa6);
case NET_FLOW4: return NET_VALIDATE(n, flow4); case NET_FLOW4: return NET_VALIDATE(n, flow4);
case NET_FLOW6: return NET_VALIDATE(n, flow6); 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); case NET_MPLS: return NET_VALIDATE(n, mpls);
default: return 0; default: return 0;
} }
@ -222,6 +233,9 @@ net_normalize(net_addr *N)
case NET_FLOW6: case NET_FLOW6:
return net_normalize_ip6(&n->ip6); return net_normalize_ip6(&n->ip6);
case NET_IP6_SADR:
return net_normalize_ip6_sadr(&n->ip6_sadr);
case NET_MPLS: case NET_MPLS:
return; return;
} }
@ -246,6 +260,9 @@ net_classify(const net_addr *N)
case NET_FLOW6: case NET_FLOW6:
return ip6_zero(n->ip6.prefix) ? (IADDR_HOST | SCOPE_UNIVERSE) : ip6_classify(&n->ip6.prefix); 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: case NET_MPLS:
return IADDR_HOST | SCOPE_UNIVERSE; 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)), return ip6_zero(ip6_and(ip6_xor(ipa_to_ip6(a), net6_prefix(n)),
ip6_mkmask(net6_pxlen(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: case NET_MPLS:
default: default:
return 0; 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); 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
View File

@ -21,8 +21,9 @@
#define NET_ROA6 6 #define NET_ROA6 6
#define NET_FLOW4 7 #define NET_FLOW4 7
#define NET_FLOW6 8 #define NET_FLOW6 8
#define NET_MPLS 9 #define NET_IP6_SADR 9
#define NET_MAX 10 #define NET_MPLS 10
#define NET_MAX 11
#define NB_IP4 (1 << NET_IP4) #define NB_IP4 (1 << NET_IP4)
#define NB_IP6 (1 << NET_IP6) #define NB_IP6 (1 << NET_IP6)
@ -32,12 +33,13 @@
#define NB_ROA6 (1 << NET_ROA6) #define NB_ROA6 (1 << NET_ROA6)
#define NB_FLOW4 (1 << NET_FLOW4) #define NB_FLOW4 (1 << NET_FLOW4)
#define NB_FLOW6 (1 << NET_FLOW6) #define NB_FLOW6 (1 << NET_FLOW6)
#define NB_IP6_SADR (1 << NET_IP6_SADR)
#define NB_MPLS (1 << NET_MPLS) #define NB_MPLS (1 << NET_MPLS)
#define NB_IP (NB_IP4 | NB_IP6) #define NB_IP (NB_IP4 | NB_IP6)
#define NB_VPN (NB_VPN4 | NB_VPN6) #define NB_VPN (NB_VPN4 | NB_VPN6)
#define NB_FLOW (NB_FLOW4 | NB_FLOW6) #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 #define NB_ANY 0xffffffff
@ -45,7 +47,7 @@ typedef struct net_addr {
u8 type; u8 type;
u8 pxlen; u8 pxlen;
u16 length; u16 length;
u8 data[16]; u8 data[20];
u64 align[0]; u64 align[0];
} net_addr; } net_addr;
@ -76,6 +78,7 @@ typedef struct net_addr_vpn6 {
u8 pxlen; u8 pxlen;
u16 length; u16 length;
ip6_addr prefix; ip6_addr prefix;
u32 padding;
u64 rd; u64 rd;
} net_addr_vpn6; } net_addr_vpn6;
@ -120,6 +123,15 @@ typedef struct net_addr_mpls {
u32 label; u32 label;
} net_addr_mpls; } 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 { typedef union net_addr_union {
net_addr n; net_addr n;
net_addr_ip4 ip4; net_addr_ip4 ip4;
@ -130,6 +142,7 @@ typedef union net_addr_union {
net_addr_roa6 roa6; net_addr_roa6 roa6;
net_addr_flow4 flow4; net_addr_flow4 flow4;
net_addr_flow6 flow6; net_addr_flow6 flow6;
net_addr_ip6_sadr ip6_sadr;
net_addr_mpls mpls; net_addr_mpls mpls;
} net_addr_union; } 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 }) ((net_addr_vpn4) { NET_VPN4, pxlen, sizeof(net_addr_vpn4), prefix, rd })
#define NET_ADDR_VPN6(prefix,pxlen,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) \ #define NET_ADDR_ROA4(prefix,pxlen,max_pxlen,asn) \
((net_addr_roa4) { NET_ROA4, pxlen, sizeof(net_addr_roa4), prefix, 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) \ #define NET_ADDR_FLOW6(prefix,pxlen,dlen) \
((net_addr_flow6) { NET_FLOW6, pxlen, sizeof(net_addr_ip6) + dlen, prefix }) ((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) \ #define NET_ADDR_MPLS(label) \
((net_addr_mpls) { NET_MPLS, 20, sizeof(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) 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); } { *(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) static inline void net_fill_mpls(net_addr *a, u32 label)
{ *(net_addr_mpls *)a = NET_ADDR_MPLS(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); 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) static inline int net_val_match(u8 type, u32 mask)
{ return !!((1 << type) & 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) static inline int net_is_ip(const net_addr *a)
{ return (a->type == NET_IP4) || (a->type == NET_IP6); } { 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) static inline int net_is_vpn(const net_addr *a)
{ return (a->type == NET_VPN4) || (a->type == NET_VPN6); } { 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) static inline ip4_addr net4_prefix(const net_addr *a)
{ return ((net_addr_ip4 *) a)->prefix; } { 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_VPN6:
case NET_ROA6: case NET_ROA6:
case NET_FLOW6: case NET_FLOW6:
case NET_IP6_SADR:
return ipa_from_ip6(net6_prefix(a)); return ipa_from_ip6(net6_prefix(a));
case NET_MPLS: 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) 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); } { 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) static inline int net_equal_mpls(const net_addr_mpls *a, const net_addr_mpls *b)
{ return !memcmp(a, b, sizeof(net_addr_mpls)); } { 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) 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); } { 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) static inline int net_zero_ip4(const net_addr_ip4 *a)
{ return !a->pxlen && ip4_zero(a->prefix); } { 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; } { return !a->pxlen && ip6_zero(a->prefix) && !a->max_pxlen && !a->asn; }
static inline int net_zero_flow4(const net_addr_flow4 *a) 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) 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) static inline int net_zero_mpls(const net_addr_mpls *a)
{ return !a->label; } { 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) 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)); } { 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) static inline int net_compare_mpls(const net_addr_mpls *a, const net_addr_mpls *b)
{ return uint_cmp(a->label, b->label); } { 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) static inline void net_copy_flow6(net_addr_flow6 *dst, const net_addr_flow6 *src)
{ memcpy(dst, src, src->length); } { 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) static inline void net_copy_mpls(net_addr_mpls *dst, const net_addr_mpls *src)
{ memcpy(dst, src, sizeof(net_addr_mpls)); } { 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) static inline u32 net_hash_flow6(const net_addr_flow6 *n)
{ return ip6_hash(n->prefix) ^ ((u32) n->pxlen << 26); } { 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) static inline u32 net_hash_mpls(const net_addr_mpls *n)
{ return n->label; } { 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) static inline int net_validate_mpls(const net_addr_mpls *n)
{ return n->label < (1 << 20); } { 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); 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) static inline void net_normalize_vpn6(net_addr_vpn6 *n)
{ net_normalize_ip6((net_addr_ip6 *) 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); 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 net_format(const net_addr *N, char *buf, int buflen);
int rd_format(const u64 rd, 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 ipa_in_netX(const ip_addr A, const net_addr *N);
int net_in_netX(const net_addr *A, const net_addr *N); int net_in_netX(const net_addr *A, const net_addr *N);
void net_init(void);
#endif #endif

View File

@ -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 * 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 * 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 IPv4 address), |%lR| for 64bit Router / Network ID (u64
* value printed as eight :-separated octets) and |%m| resp. |%M| for error * value printed as eight :-separated octets), |%t| for time values (btime) with
* messages (uses strerror() to translate @errno code to message text). On the * specified subsecond precision, and |%m| resp. |%M| for error messages (uses
* other hand, it doesn't support floating point numbers. * 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 * Result: number of characters of the output string or -1 if
* the buffer space was insufficient. * the buffer space was insufficient.
@ -139,6 +140,8 @@ int bvsnprintf(char *buf, int size, const char *fmt, va_list args)
int i, base; int i, base;
u32 x; u32 x;
u64 X; u64 X;
btime t;
s64 t1, t2;
char *str, *start; char *str, *start;
const char *s; const char *s;
char ipbuf[NET_MAX_TEXT_LENGTH+1]; char ipbuf[NET_MAX_TEXT_LENGTH+1];
@ -267,6 +270,17 @@ int bvsnprintf(char *buf, int size, const char *fmt, va_list args)
*str++ = ' '; *str++ = ' ';
continue; 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': case 'p':
if (field_width == -1) { if (field_width == -1) {
field_width = 2*sizeof(void *); field_width = 2*sizeof(void *);
@ -279,7 +293,6 @@ int bvsnprintf(char *buf, int size, const char *fmt, va_list args)
return -1; return -1;
continue; continue;
case 'n': case 'n':
if (qualifier == 'l') { if (qualifier == 'l') {
long * ip = va_arg(args, long *); long * ip = va_arg(args, long *);
@ -360,6 +373,50 @@ int bvsnprintf(char *buf, int size, const char *fmt, va_list args)
s = ipbuf; s = ipbuf;
goto str; 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" */ /* integer number formats - set up the flags and "break" */
case 'o': case 'o':
base = 8; 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); str = number(str, num, base, field_width, precision, flags, size);
if (!str) if (!str)
return -1; return -1;
done: ;
} }
if (!size) if (!size)
return -1; return -1;

View File

@ -56,6 +56,15 @@ t_simple(void)
BSPRINTF(2, "-1", buf, "%d", -1); BSPRINTF(2, "-1", buf, "%d", -1);
BSPRINTF(11, "-2147483648", buf, "%d", -2147483648); 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; return 1;
} }

View File

@ -59,11 +59,18 @@ void mb_free(void *);
typedef struct linpool linpool; typedef struct linpool linpool;
typedef struct lp_state {
void *current, *large;
byte *ptr;
} lp_state;
linpool *lp_new(pool *, unsigned blk); linpool *lp_new(pool *, unsigned blk);
void *lp_alloc(linpool *, unsigned size); /* Aligned */ void *lp_alloc(linpool *, unsigned size); /* Aligned */
void *lp_allocu(linpool *, unsigned size); /* Unaligned */ void *lp_allocu(linpool *, unsigned size); /* Unaligned */
void *lp_allocz(linpool *, unsigned size); /* With clear */ void *lp_allocz(linpool *, unsigned size); /* With clear */
void lp_flush(linpool *); /* Free everything, but leave linpool */ 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; extern const int lp_chunk_size;
#define LP_GAS 1024 #define LP_GAS 1024

View File

@ -52,6 +52,7 @@ typedef struct birdsock {
int ttl; /* Time To Live, -1 = default */ int ttl; /* Time To Live, -1 = default */
u32 flags; u32 flags;
struct iface *iface; /* Interface; specify this for broad/multicast sockets */ 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 */ byte *rbuf, *rpos; /* NULL=allocate automatically */
uint fast_rx; /* RX has higher priority in event loop */ uint fast_rx; /* RX has higher priority in event loop */

View File

@ -8,22 +8,30 @@
*/ */
#include "nest/bird.h" #include "nest/bird.h"
#include "lib/timer.h"
void int
tbf_update(struct tbf *f) tbf_limit(struct tbf *f)
{ {
bird_clock_t delta = now - f->timestamp; btime delta = current_time() - f->timestamp;
if (delta == 0) if (delta > 0)
return;
f->timestamp = now;
if ((0 < delta) && (delta < f->burst))
{ {
u32 next = f->count + delta * f->rate; u64 next = f->count + delta * f->rate;
f->count = MIN(next, f->burst); u64 burst = (u64) f->burst << 20;
f->count = MIN(next, burst);
f->timestamp += delta;
}
if (f->count < 1000000)
{
f->drop++;
return 1;
} }
else else
f->count = f->burst; {
f->count -= 1000000;
f->drop = 0;
return 0;
}
} }

378
lib/timer.c Normal file
View 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(&current_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
View 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

View File

@ -1,6 +1,6 @@
Summary: BIRD Internet Routing Daemon Summary: BIRD Internet Routing Daemon
Name: bird Name: bird
Version: 2.0.0 Version: 2.0.2
Release: 1 Release: 1
Copyright: GPL Copyright: GPL
Group: Networking/Daemons Group: Networking/Daemons

View 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

View 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

View 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

View 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

View 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

View 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

View 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

View 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

View 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

View 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

View 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

View 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

View 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

View 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

View 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

View File

@ -805,12 +805,11 @@ as_path_match(const struct adata *path, struct f_path_mask *mask)
val2 = val = mask->val; val2 = val = mask->val;
goto step; goto step;
case PM_ASN_EXPR: case PM_ASN_EXPR:
val2 = val = f_eval_asn((struct f_inst *) mask->val); ASSERT(0);
goto step;
case PM_ASN_RANGE: case PM_ASN_RANGE:
val = mask->val; val = mask->val;
val2 = mask->val2; val2 = mask->val2;
goto step; goto step;
case PM_QUESTION: case PM_QUESTION:
step: step:
nh = nl = -1; nh = nl = -1;

View File

@ -43,14 +43,14 @@ t_as_path_match(void)
bt_debug("Prepending ASN: %10u \n", val); bt_debug("Prepending ASN: %10u \n", val);
if (i == 0) if (i == 0)
first_prepended = val; first_prepended = val;
if (i == AS_PATH_LENGTH-1) if (i == AS_PATH_LENGTH-1)
last_prepended = val; last_prepended = val;
mask[i].kind = PM_ASN; mask[i].kind = PM_ASN;
mask[i].val = val; mask[i].val = val;
if (i) 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"); bt_assert_msg(as_path_match(as_path, &mask[AS_PATH_LENGTH-1]), "Mask should match with AS path");

View File

@ -536,6 +536,13 @@ ec_set_sort(struct linpool *pool, struct adata *src)
return dst; 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 static int
lc_set_cmp(const void *X, const void *Y) lc_set_cmp(const void *X, const void *Y)

View File

@ -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 *ec_set_sort(struct linpool *pool, struct adata *src);
struct adata *lc_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 #endif

View File

@ -46,7 +46,7 @@ static inline void cf_check_bfd(int use UNUSED) { }
#else #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"); } static inline void cf_check_bfd(int use) { if (use) cf_error("BFD not available"); }

View File

@ -147,7 +147,7 @@ cli_printf(cli *c, int code, char *msg, ...)
} }
else if (cd == CLI_ASYNC_CODE) else if (cd == CLI_ASYNC_CODE)
{ {
size = 1; buf[0] = '+'; size = 1; buf[0] = '+';
errcode = cd; errcode = cd;
} }
else else

View File

@ -25,12 +25,12 @@ cmd_show_status(void)
byte tim[TM_DATETIME_BUFFER_SIZE]; byte tim[TM_DATETIME_BUFFER_SIZE];
cli_msg(-1000, "BIRD " BIRD_VERSION); 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, "Router ID is %R", config->router_id);
cli_msg(-1011, "Current server time is %s", tim); 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); 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); cli_msg(-1011, "Last reconfiguration on %s", tim);
graceful_restart_show_status(); graceful_restart_show_status();

View File

@ -65,18 +65,19 @@ proto_postconfig(void)
CF_DECLS CF_DECLS
CF_KEYWORDS(ROUTER, ID, PROTOCOL, TEMPLATE, PREFERENCE, DISABLED, DEBUG, ALL, OFF, DIRECT) 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(INTERFACE, IMPORT, EXPORT, FILTER, NONE, VRF, TABLE, STATES, ROUTES, FILTERS)
CF_KEYWORDS(IPV4, IPV6, VPN4, VPN6, ROA4, ROA6) 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(RECEIVE, LIMIT, ACTION, WARN, BLOCK, RESTART, DISABLE, KEEP, FILTERED)
CF_KEYWORDS(PASSWORD, FROM, PASSIVE, TO, ID, EVENTS, PACKETS, PROTOCOLS, INTERFACES) CF_KEYWORDS(PASSWORD, FROM, PASSIVE, TO, ID, EVENTS, PACKETS, PROTOCOLS, INTERFACES)
CF_KEYWORDS(ALGORITHM, KEYED, HMAC, MD5, SHA1, SHA256, SHA384, SHA512) CF_KEYWORDS(ALGORITHM, KEYED, HMAC, MD5, SHA1, SHA256, SHA384, SHA512)
CF_KEYWORDS(PRIMARY, STATS, COUNT, BY, FOR, COMMANDS, PREEXPORT, NOEXPORT, GENERATE) 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(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) CF_KEYWORDS(GRACEFUL, RESTART, WAIT, MAX, FLUSH, AS)
/* For r_args_channel */ /* 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, CF_ENUM(T_ENUM_RTS, RTS_, DUMMY, STATIC, INHERIT, DEVICE, STATIC_DEVICE, REDIRECT,
RIP, OSPF, OSPF_IA, OSPF_EXT1, OSPF_EXT2, BGP, PIPE, BABEL) RIP, OSPF, OSPF_IA, OSPF_EXT1, OSPF_EXT2, BGP, PIPE, BABEL)
@ -102,7 +103,7 @@ CF_GRAMMAR
/* Setting of router ID */ /* Setting of router ID */
CF_ADDTO(conf, rtrid) conf: rtrid ;
rtrid: rtrid:
ROUTER ID idval ';' { new_config->router_id = $3; } ROUTER ID idval ';' { new_config->router_id = $3; }
@ -123,25 +124,7 @@ idval:
} }
; ;
conf: gr_opts ;
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)
gr_opts: GRACEFUL RESTART WAIT expr ';' { new_config->gr_wait = $4; } ; 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: net_type:
IPV4 { $$ = NET_IP4; } IPV4 { $$ = NET_IP4; }
| IPV6 { $$ = NET_IP6; } | IPV6 { $$ = NET_IP6; }
| IPV6 SADR { $$ = NET_IP6_SADR; }
| VPN4 { $$ = NET_VPN4; } | VPN4 { $$ = NET_VPN4; }
| VPN6 { $$ = NET_VPN6; } | VPN6 { $$ = NET_VPN6; }
| ROA4 { $$ = NET_ROA4; } | ROA4 { $$ = NET_ROA4; }
| ROA6 { $$ = NET_ROA6; } | ROA6 { $$ = NET_ROA6; }
| FLOW4{ $$ = NET_FLOW4; } | FLOW4{ $$ = NET_FLOW4; }
| FLOW6{ $$ = NET_FLOW6; } | 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 */ /* Creation of routing tables */
CF_ADDTO(conf, table) conf: table ;
table_sorted: table_sorted:
{ $$ = 0; } { $$ = 0; }
| SORTED { $$ = 1; } | SORTED { $$ = 1; }
; ;
@ -181,7 +166,7 @@ table: net_type TABLE SYM table_sorted {
/* Definition of protocols */ /* Definition of protocols */
CF_ADDTO(conf, proto { proto_postconfig(); }) conf: proto { proto_postconfig(); } ;
proto_start: proto_start:
PROTOCOL { $$ = SYM_PROTO; } PROTOCOL { $$ = SYM_PROTO; }
@ -224,12 +209,13 @@ proto_item:
| MRTDUMP mrtdump_mask { this_proto->mrtdump = $2; } | MRTDUMP mrtdump_mask { this_proto->mrtdump = $2; }
| ROUTER ID idval { this_proto->router_id = $3; } | ROUTER ID idval { this_proto->router_id = $3; }
| DESCRIPTION text { this_proto->dsc = $2; } | DESCRIPTION text { this_proto->dsc = $2; }
| VRF text { this_proto->vrf = if_get_by_name($2); }
; ;
channel_start: net_type 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: channel_item:
@ -295,7 +281,8 @@ limit_spec:
| OFF { $$ = (struct channel_limit){}; } | OFF { $$ = (struct channel_limit){}; }
; ;
CF_ADDTO(conf, debug_default)
conf: debug_default ;
debug_default: debug_default:
DEBUG PROTOCOLS debug_mask { new_config->proto_default_debug = $3; } DEBUG PROTOCOLS debug_mask { new_config->proto_default_debug = $3; }
@ -304,6 +291,31 @@ debug_default:
/* MRTDUMP PROTOCOLS is in systep/unix/config.Y */ /* 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 */ /* Interface patterns */
iface_patt_node_init: iface_patt_node_init:
@ -355,7 +367,7 @@ tos:
/* Direct device route protocol */ /* Direct device route protocol */
CF_ADDTO(proto, dev_proto '}') proto: dev_proto '}' ;
dev_proto_start: proto_start DIRECT { dev_proto_start: proto_start DIRECT {
this_proto = proto_config_new(&proto_device, $1); this_proto = proto_config_new(&proto_device, $1);
@ -443,9 +455,9 @@ password_item:
password_item_begin: password_item_begin:
PASSWORD text { PASSWORD text {
if (!this_p_list) { if (!this_p_list) {
this_p_list = cfg_alloc(sizeof(list)); this_p_list = cfg_alloc(sizeof(list));
init_list(this_p_list); init_list(this_p_list);
password_id = 1; password_id = 1;
} }
this_p_item = cfg_alloc(sizeof (struct password_item)); this_p_item = cfg_alloc(sizeof (struct password_item));
this_p_item->password = $2; this_p_item->password = $2;
@ -462,12 +474,12 @@ password_item_begin:
password_item_params: password_item_params:
/* empty */ { } /* empty */ { }
| GENERATE FROM datetime ';' password_item_params { this_p_item->genfrom = $3; } | GENERATE FROM time ';' password_item_params { this_p_item->genfrom = $3; }
| GENERATE TO datetime ';' password_item_params { this_p_item->gento = $3; } | GENERATE TO time ';' password_item_params { this_p_item->gento = $3; }
| ACCEPT FROM datetime ';' password_item_params { this_p_item->accfrom = $3; } | ACCEPT FROM time ';' password_item_params { this_p_item->accfrom = $3; }
| ACCEPT TO datetime ';' password_item_params { this_p_item->accto = $3; } | ACCEPT TO time ';' password_item_params { this_p_item->accto = $3; }
| FROM datetime ';' password_item_params { this_p_item->genfrom = this_p_item->accfrom = $2; } | FROM time ';' 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; } | 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."); } | 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; } | ALGORITHM password_algorithm ';' password_item_params { this_p_item->alg = $2; }
; ;
@ -614,6 +626,7 @@ r_args_for:
} }
| net_vpn4_ | net_vpn4_
| net_vpn6_ | net_vpn6_
| net_ip6_sadr_
| VPN_RD IP4 { | VPN_RD IP4 {
$$ = cfg_alloc(sizeof(net_addr_vpn4)); $$ = cfg_alloc(sizeof(net_addr_vpn4));
net_fill_vpn4($$, $2, IP4_MAX_PREFIX_LENGTH, $1); net_fill_vpn4($$, $2, IP4_MAX_PREFIX_LENGTH, $1);
@ -622,6 +635,10 @@ r_args_for:
$$ = cfg_alloc(sizeof(net_addr_vpn6)); $$ = cfg_alloc(sizeof(net_addr_vpn6));
net_fill_vpn6($$, $2, IP6_MAX_PREFIX_LENGTH, $1); 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 { | SYM {
if ($1->class == (SYM_CONSTANT | T_IP)) if ($1->class == (SYM_CONSTANT | T_IP))
{ {
@ -655,6 +672,7 @@ r_args_channel:
| IPV6 { $$ = "ipv6"; } | IPV6 { $$ = "ipv6"; }
| IPV6_MC { $$ = "ipv6-mc"; } | IPV6_MC { $$ = "ipv6-mc"; }
| IPV6_MPLS { $$ = "ipv6-mpls"; } | IPV6_MPLS { $$ = "ipv6-mpls"; }
| IPV6_SADR { $$ = "ipv6-sadr"; }
| VPN4 { $$ = "vpn4"; } | VPN4 { $$ = "vpn4"; }
| VPN4_MC { $$ = "vpn4-mc"; } | VPN4_MC { $$ = "vpn4-mc"; }
| VPN4_MPLS { $$ = "vpn4-mpls"; } | VPN4_MPLS { $$ = "vpn4-mpls"; }
@ -728,12 +746,12 @@ echo_size:
} }
; ;
CF_CLI(DISABLE, proto_patt, <protocol> | \"<pattern>\" | all, [[Disable protocol]]) CF_CLI(DISABLE, proto_patt opttext, (<protocol> | \"<pattern>\" | all) [message], [[Disable protocol]])
{ proto_apply_cmd($2, proto_cmd_disable, 1, 0); } ; { proto_apply_cmd($2, proto_cmd_disable, 1, (uintptr_t) $3); } ;
CF_CLI(ENABLE, proto_patt, <protocol> | \"<pattern>\" | all, [[Enable protocol]]) CF_CLI(ENABLE, proto_patt opttext, (<protocol> | \"<pattern>\" | all) [message], [[Enable protocol]])
{ proto_apply_cmd($2, proto_cmd_enable, 1, 0); } ; { proto_apply_cmd($2, proto_cmd_enable, 1, (uintptr_t) $3); } ;
CF_CLI(RESTART, proto_patt, <protocol> | \"<pattern>\" | all, [[Restart protocol]]) CF_CLI(RESTART, proto_patt opttext, (<protocol> | \"<pattern>\" | all) [message], [[Restart protocol]])
{ proto_apply_cmd($2, proto_cmd_restart, 1, 0); } ; { proto_apply_cmd($2, proto_cmd_restart, 1, (uintptr_t) $3); } ;
CF_CLI(RELOAD, proto_patt, <protocol> | \"<pattern>\" | all, [[Reload protocol]]) CF_CLI(RELOAD, proto_patt, <protocol> | \"<pattern>\" | all, [[Reload protocol]])
{ proto_apply_cmd($2, proto_cmd_reload, 1, CMD_RELOAD); } ; { proto_apply_cmd($2, proto_cmd_reload, 1, CMD_RELOAD); } ;
CF_CLI(RELOAD IN, proto_patt, <protocol> | \"<pattern>\" | all, [[Reload protocol (just imported routes)]]) 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; } | TEXT { $$.ptr = $1; $$.patt = 1; }
; ;
CF_ADDTO(dynamic_attr, IGP_METRIC dynamic_attr: IGP_METRIC { $$ = f_new_dynamic_attr(EAF_TYPE_INT, T_INT, EA_GEN_IGP_METRIC); } ;
{ $$ = f_new_dynamic_attr(EAF_TYPE_INT, T_INT, EA_GEN_IGP_METRIC); })
CF_CODE CF_CODE

View File

@ -32,11 +32,14 @@
#include "lib/resource.h" #include "lib/resource.h"
#include "lib/string.h" #include "lib/string.h"
#include "conf/conf.h" #include "conf/conf.h"
#include "sysdep/unix/krt.h"
static pool *if_pool; static pool *if_pool;
list iface_list; list iface_list;
static void if_recalc_preferred(struct iface *i);
/** /**
* ifa_dump - dump interface address * ifa_dump - dump interface address
* @a: interface address descriptor * @a: interface address descriptor
@ -46,10 +49,11 @@ list iface_list;
void void
ifa_dump(struct ifa *a) 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, debug("\t%I, net %N bc %I -> %I%s%s%s%s\n", a->ip, &a->prefix, a->brd, a->opposite,
(a->flags & IF_UP) ? "" : " DOWN", (a->flags & IA_PRIMARY) ? " PRIMARY" : "",
(a->flags & IA_PRIMARY) ? "" : " SEC", (a->flags & IA_SECONDARY) ? " SEC" : "",
(a->flags & IA_PEER) ? "PEER" : ""); (a->flags & IA_HOST) ? " HOST" : "",
(a->flags & IA_PEER) ? " PEER" : "");
} }
/** /**
@ -89,7 +93,8 @@ if_dump(struct iface *i)
WALK_LIST(a, i->addrs) WALK_LIST(a, i->addrs)
{ {
ifa_dump(a); 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; 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)) 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; return IF_CHANGE_TOO_MUCH;
c = 0; c = 0;
if ((i->flags ^ j->flags) & IF_UP) 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->flags = from->flags | (to->flags & IF_TMP_DOWN);
to->mtu = from->mtu; to->mtu = from->mtu;
to->master_index = from->master_index;
to->master = from->master;
} }
static inline void static inline void
ifa_send_notify(struct proto *p, unsigned c, struct ifa *a) 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) if (p->debug & D_IFACES)
log(L_TRACE "%s < %s address %N on interface %s %s", log(L_TRACE "%s < address %N on interface %s %s",
p->name, (a->flags & IA_PRIMARY) ? "primary" : "secondary", p->name, &a->prefix, a->iface->name,
&a->prefix, a->iface->name, (c & IF_CHANGE_UP) ? "added" : "removed"); (c & IF_CHANGE_UP) ? "added" : "removed");
p->ifa_notify(p, c, a); p->ifa_notify(p, c, a);
} }
} }
@ -174,7 +183,9 @@ ifa_notify_change(unsigned c, struct ifa *a)
static inline void static inline void
if_send_notify(struct proto *p, unsigned c, struct iface *i) 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) if (p->debug & D_IFACES)
log(L_TRACE "%s < interface %s %s", p->name, i->name, 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_DOWN) ? "goes down" :
(c & IF_CHANGE_MTU) ? "changes MTU" : (c & IF_CHANGE_MTU) ? "changes MTU" :
(c & IF_CHANGE_LINK) ? "changes link" : (c & IF_CHANGE_LINK) ? "changes link" :
(c & IF_CHANGE_PREFERRED) ? "changes preferred address" :
(c & IF_CHANGE_CREATE) ? "created" : (c & IF_CHANGE_CREATE) ? "created" :
"sends unknown event"); "sends unknown event");
p->if_notify(p, c, i); p->if_notify(p, c, i);
@ -210,20 +222,14 @@ if_notify_change(unsigned c, struct iface *i)
if (c & IF_CHANGE_DOWN) if (c & IF_CHANGE_DOWN)
WALK_LIST(a, i->addrs) WALK_LIST(a, i->addrs)
{ ifa_notify_change_(IF_CHANGE_DOWN, a);
a->flags = (i->flags & ~IA_FLAGS) | (a->flags & IA_FLAGS);
ifa_notify_change_(IF_CHANGE_DOWN, a);
}
WALK_LIST(p, proto_list) WALK_LIST(p, proto_list)
if_send_notify(p, c, i); if_send_notify(p, c, i);
if (c & IF_CHANGE_UP) if (c & IF_CHANGE_UP)
WALK_LIST(a, i->addrs) WALK_LIST(a, i->addrs)
{ ifa_notify_change_(IF_CHANGE_UP, a);
a->flags = (i->flags & ~IA_FLAGS) | (a->flags & IA_FLAGS);
ifa_notify_change_(IF_CHANGE_UP, a);
}
if (c & IF_CHANGE_UP) if (c & IF_CHANGE_UP)
neigh_if_up(i); neigh_if_up(i);
@ -232,24 +238,25 @@ if_notify_change(unsigned c, struct iface *i)
neigh_if_link(i); neigh_if_link(i);
} }
static unsigned static uint
if_recalc_flags(struct iface *i, unsigned flags) if_recalc_flags(struct iface *i UNUSED, uint flags)
{ {
if ((flags & (IF_SHUTDOWN | IF_TMP_DOWN)) || if ((flags & IF_ADMIN_UP) &&
!(flags & IF_ADMIN_UP) || !(flags & (IF_SHUTDOWN | IF_TMP_DOWN)) &&
!i->addr) !(i->master_index && !i->master))
flags &= ~IF_UP;
else
flags |= IF_UP; flags |= IF_UP;
else
flags &= ~IF_UP;
return flags; return flags;
} }
static void 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); i->flags = if_recalc_flags(i, flags);
if ((i->flags ^ of) & IF_UP) if ((i->flags ^ of) & IF_UP)
if_notify_change((i->flags & IF_UP) ? IF_CHANGE_UP : IF_CHANGE_DOWN, i); 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) WALK_LIST(i, iface_list)
if (!strcmp(new->name, i->name)) if (!strcmp(new->name, i->name))
{ {
new->addr = i->addr;
new->flags = if_recalc_flags(new, new->flags); new->flags = if_recalc_flags(new, new->flags);
c = if_what_changed(i, new); c = if_what_changed(i, new);
if (c & IF_CHANGE_TOO_MUCH) /* Changed a lot, convert it to down/up */ 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); DBG("Interface %s changed too much -- forcing down/up transition\n", i->name);
if_change_flags(i, i->flags | IF_TMP_DOWN); if_change_flags(i, i->flags | IF_TMP_DOWN);
rem_node(&i->n); 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(&new->addrs, &i->addrs, sizeof(i->addrs));
memcpy(i, new, sizeof(*i)); 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; goto newif;
} }
@ -339,13 +348,16 @@ if_start_update(void)
{ {
i->flags &= ~IF_UPDATED; i->flags &= ~IF_UPDATED;
WALK_LIST(a, i->addrs) WALK_LIST(a, i->addrs)
a->flags &= ~IF_UPDATED; a->flags &= ~IA_UPDATED;
} }
} }
void void
if_end_partial_update(struct iface *i) if_end_partial_update(struct iface *i)
{ {
if (i->flags & IF_NEEDS_RECALC)
if_recalc_preferred(i);
if (i->flags & IF_TMP_DOWN) if (i->flags & IF_TMP_DOWN)
if_change_flags(i, i->flags & ~IF_TMP_DOWN); if_change_flags(i, i->flags & ~IF_TMP_DOWN);
} }
@ -363,7 +375,7 @@ if_end_update(void)
else else
{ {
WALK_LIST_DELSAFE(a, b, i->addrs) WALK_LIST_DELSAFE(a, b, i->addrs)
if (!(a->flags & IF_UPDATED)) if (!(a->flags & IA_UPDATED))
ifa_delete(a); ifa_delete(a);
if_end_partial_update(i); if_end_partial_update(i);
} }
@ -460,40 +472,99 @@ if_get_by_name(char *name)
return i; return i;
} }
struct ifa *kif_choose_primary(struct iface *i); static inline void
if_set_preferred(struct ifa **pos, struct ifa *new)
static int
ifa_recalc_primary(struct iface *i)
{ {
struct ifa *a = kif_choose_primary(i); if (*pos)
(*pos)->flags &= ~IA_PRIMARY;
if (new)
new->flags |= IA_PRIMARY;
if (a == i->addr) *pos = new;
return 0; }
if (i->addr) static void
i->addr->flags &= ~IA_PRIMARY; 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; /* Secondary address is never selected */
rem_node(&a->n); if (a->flags & IA_SECONDARY)
add_head(&i->addrs, &a->n); 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; if (a4 != i->addr4)
return 1; {
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 void
ifa_recalc_all_primary_addresses(void) if_recalc_all_preferred_addresses(void)
{ {
struct iface *i; struct iface *i;
WALK_LIST(i, iface_list) WALK_LIST(i, iface_list)
{ {
if (ifa_recalc_primary(i)) if_recalc_preferred(i);
if_change_flags(i, i->flags | IF_TMP_DOWN);
} if (i->flags & IF_TMP_DOWN)
if_change_flags(i, i->flags & ~IF_TMP_DOWN);
}
} }
static inline int static inline int
@ -525,7 +596,7 @@ ifa_update(struct ifa *a)
b->scope == a->scope && b->scope == a->scope &&
!((b->flags ^ a->flags) & IA_PEER)) !((b->flags ^ a->flags) & IA_PEER))
{ {
b->flags |= IF_UPDATED; b->flags |= IA_UPDATED;
return b; return b;
} }
ifa_delete(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)) 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)); b = mb_alloc(if_pool, sizeof(struct ifa));
memcpy(b, a, sizeof(struct ifa)); memcpy(b, a, sizeof(struct ifa));
add_tail(&i->addrs, &b->n); add_tail(&i->addrs, &b->n);
b->flags = (i->flags & ~IA_FLAGS) | (a->flags & IA_FLAGS); b->flags |= IA_UPDATED;
if (ifa_recalc_primary(i))
if_change_flags(i, i->flags | IF_TMP_DOWN); i->flags |= IF_NEEDS_RECALC;
if (b->flags & IF_UP) if (i->flags & IF_UP)
ifa_notify_change(IF_CHANGE_CREATE | IF_CHANGE_UP, b); ifa_notify_change(IF_CHANGE_CREATE | IF_CHANGE_UP, b);
return b; return b;
} }
@ -564,16 +635,24 @@ ifa_delete(struct ifa *a)
if (ifa_same(b, a)) if (ifa_same(b, a))
{ {
rem_node(&b->n); 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 (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); mb_free(b);
return; return;
} }
@ -740,16 +819,17 @@ iface_patts_equal(list *a, list *b, int (*comp)(struct iface_patt *, struct ifac
static void static void
if_show_addr(struct ifa *a) 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)) if (ipa_nonzero(a->opposite))
bsprintf(opp, ", opposite %I", a->opposite); bsprintf(opp, "opposite %I, ", a->opposite);
else else
opp[0] = 0; opp[0] = 0;
cli_msg(-1003, "\t%I/%d (%s%s, scope %s)",
a->ip, a->prefix.pxlen, cli_msg(-1003, "\t%I/%d (%s%sscope %s)",
(a->flags & IA_PRIMARY) ? "Primary" : (a->flags & IA_SECONDARY) ? "Secondary" : "Unselected", a->ip, a->prefix.pxlen, flg, opp, ip_scope_text(a->scope));
opp, ip_scope_text(a->scope));
} }
void void
@ -764,7 +844,13 @@ if_show(void)
if (i->flags & IF_SHUTDOWN) if (i->flags & IF_SHUTDOWN)
continue; 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)) if (!(i->flags & IF_MULTIACCESS))
type = "PtP"; type = "PtP";
else else
@ -778,10 +864,13 @@ if_show(void)
(i->flags & IF_LOOPBACK) ? " Loopback" : "", (i->flags & IF_LOOPBACK) ? " Loopback" : "",
(i->flags & IF_IGNORE) ? " Ignored" : "", (i->flags & IF_IGNORE) ? " Ignored" : "",
i->mtu); i->mtu);
if (i->addr)
if_show_addr(i->addr);
WALK_LIST(a, i->addrs) 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); if_show_addr(a);
} }
cli_msg(0, ""); cli_msg(0, "");
@ -791,16 +880,25 @@ void
if_show_summary(void) if_show_summary(void)
{ {
struct iface *i; 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) WALK_LIST(i, iface_list)
{ {
if (i->addr) byte a4[IPA_MAX_TEXT_LENGTH + 17];
bsprintf(addr, "%I/%d", i->addr->ip, i->addr->prefix.pxlen); byte a6[IPA_MAX_TEXT_LENGTH + 17];
if (i->addr4)
bsprintf(a4, "%I/%d", i->addr4->ip, i->addr4->prefix.pxlen);
else else
addr[0] = 0; a4[0] = 0;
cli_msg(-1005, "%-9s %-5s %s", i->name, (i->flags & IF_UP) ? "up" : "DOWN", addr);
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, ""); cli_msg(0, "");
} }

View File

@ -34,12 +34,17 @@ struct iface {
unsigned flags; unsigned flags;
unsigned mtu; unsigned mtu;
unsigned index; /* OS-dependent interface index */ 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 */ 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 */ 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_MULTIACCESS 2
#define IF_BROADCAST 4 #define IF_BROADCAST 4
#define IF_MULTICAST 8 #define IF_MULTICAST 8
@ -70,7 +75,10 @@ struct iface {
#define IF_JUST_CREATED 0x10000000 /* Send creation event as soon as possible */ #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_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 */ /* Interface change events */
@ -79,8 +87,14 @@ struct iface {
#define IF_CHANGE_MTU 4 #define IF_CHANGE_MTU 4
#define IF_CHANGE_CREATE 8 /* Seen this interface for the first time */ #define IF_CHANGE_CREATE 8 /* Seen this interface for the first time */
#define IF_CHANGE_LINK 0x10 #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_TOO_MUCH 0x40000000 /* Used internally */
#define IF_CHANGE_PREFERRED (IF_CHANGE_ADDR4 | IF_CHANGE_ADDR6 | IF_CHANGE_LLV6)
void if_init(void); void if_init(void);
void if_dump(struct iface *); void if_dump(struct iface *);
void if_dump_all(void); 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_index(unsigned);
struct iface *if_find_by_name(char *); struct iface *if_find_by_name(char *);
struct iface *if_get_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 */ /* The Neighbor Cache */
@ -110,29 +124,21 @@ typedef struct neighbor {
ip_addr addr; /* Address of the neighbor */ ip_addr addr; /* Address of the neighbor */
struct ifa *ifa; /* Ifa on related iface */ struct ifa *ifa; /* Ifa on related iface */
struct iface *iface; /* Interface it's connected to */ struct iface *iface; /* Interface it's connected to */
struct iface *ifreq; /* Requested iface, NULL for any */
struct proto *proto; /* Protocol this belongs to */ struct proto *proto; /* Protocol this belongs to */
void *data; /* Protocol-specific data */ void *data; /* Protocol-specific data */
unsigned aux; /* Protocol-specific data */ uint aux; /* Protocol-specific data */
unsigned flags; u16 flags; /* NEF_* flags */
int scope; /* Address scope, -1 for unreachable sticky neighbors, s16 scope; /* Address scope, -1 for unreachable neighbors,
SCOPE_HOST when it's our own address */ SCOPE_HOST when it's our own address */
} neighbor; } neighbor;
#define NEF_STICKY 1 #define NEF_STICKY 1
#define NEF_ONLINK 2 #define NEF_ONLINK 2
#define NEF_BIND 4 /* Used internally for neighbors bound to an iface */ #define NEF_IFACE 4 /* Entry for whole iface */
#define NEF_IFACE 8 /* Neighbors bound to iface */
neighbor *neigh_find(struct proto *, ip_addr *, unsigned flags); neighbor *neigh_find(struct proto *p, ip_addr a, struct iface *ifa, uint 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;
}
void neigh_dump(neighbor *); void neigh_dump(neighbor *);
void neigh_dump_all(void); void neigh_dump_all(void);

View File

@ -45,6 +45,7 @@ olock_same(struct object_lock *x, struct object_lock *y)
return return
x->type == y->type && x->type == y->type &&
x->iface == y->iface && x->iface == y->iface &&
x->vrf == y->vrf &&
x->port == y->port && x->port == y->port &&
x->inst == y->inst && x->inst == y->inst &&
ipa_equal(x->addr, y->addr); ipa_equal(x->addr, y->addr);

View File

@ -30,6 +30,7 @@ struct object_lock {
uint port; /* ... port number */ uint port; /* ... port number */
uint inst; /* ... instance ID */ uint inst; /* ... instance ID */
struct iface *iface; /* ... interface */ struct iface *iface; /* ... interface */
struct iface *vrf; /* ... or VRF (if iface is unknown) */
void (*hook)(struct object_lock *); /* Called when the lock succeeds */ void (*hook)(struct object_lock *); /* Called when the lock succeeds */
void *data; /* User data */ void *data; /* User data */
/* ... internal to lock manager, don't touch ... */ /* ... internal to lock manager, don't touch ... */

View File

@ -28,4 +28,3 @@
void mrt_dump_message(struct proto *p, u16 type, u16 subtype, byte *buf, u32 len); void mrt_dump_message(struct proto *p, u16 type, u16 subtype, byte *buf, u32 len);
#endif #endif

View File

@ -2,6 +2,8 @@
* BIRD -- Neighbor Cache * BIRD -- Neighbor Cache
* *
* (c) 1998--2000 Martin Mares <mj@ucw.cz> * (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. * Can be freely distributed and used under the terms of the GNU GPL.
*/ */
@ -10,31 +12,38 @@
* DOC: Neighbor cache * DOC: Neighbor cache
* *
* Most routing protocols need to associate their internal state data with * Most routing protocols need to associate their internal state data with
* neighboring routers, check whether an address given as the next hop * neighboring routers, check whether an address given as the next hop attribute
* attribute of a route is really an address of a directly connected host * of a route is really an address of a directly connected host and which
* and which interface is it connected through. Also, they often need to * interface is it connected through. Also, they often need to be notified when
* be notified when a neighbor ceases to exist or when their long awaited * a neighbor ceases to exist or when their long awaited neighbor becomes
* neighbor becomes connected. The neighbor cache is there to solve all * connected. The neighbor cache is there to solve all these problems.
* these problems.
* *
* The neighbor cache maintains a collection of neighbor entries. Each * The neighbor cache maintains a collection of neighbor entries. Each entry
* entry represents one IP address corresponding to either our directly * represents one IP address corresponding to either our directly connected
* connected neighbor or our own end of the link (when the scope of the * neighbor or our own end of the link (when the scope of the address is set to
* address is set to %SCOPE_HOST) together with per-neighbor data belonging to a * %SCOPE_HOST) together with per-neighbor data belonging to a single protocol.
* 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 * Neighbor cache entries are stored in a hash table, which is indexed by triple
* table (to allow fast retrieval based on the IP address of the node) and * (protocol, IP, requested-iface), so if both regular and iface-bound neighbors
* two linked lists: one global and one per-interface (allowing quick * are requested, they are represented by two neighbor cache entries. Active
* processing of interface change events). Inactive entries exist only * entries are also linked in per-interface list (allowing quick processing of
* when the protocol has explicitly requested it via the %NEF_STICKY * interface change events). Inactive entries exist only when the protocol has
* flag because it wishes to be notified when the node will again become * explicitly requested it via the %NEF_STICKY flag because it wishes to be
* a neighbor. Such entries are enqueued in a special list which is walked * notified when the node will again become a neighbor. Such entries are instead
* whenever an interface changes its state to up. * 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 * When a neighbor event occurs (a neighbor gets disconnected or a sticky
* inactive neighbor becomes connected), the protocol hook neigh_notify() * inactive neighbor becomes connected), the protocol hook neigh_notify() is
* is called to advertise the change. * called to advertise the change.
*/ */
#undef LOCAL_DEBUG #undef LOCAL_DEBUG
@ -42,125 +51,151 @@
#include "nest/bird.h" #include "nest/bird.h"
#include "nest/iface.h" #include "nest/iface.h"
#include "nest/protocol.h" #include "nest/protocol.h"
#include "lib/hash.h"
#include "lib/resource.h" #include "lib/resource.h"
#define NEIGH_HASH_SIZE 256 #define NEIGH_HASH_SIZE 256
#define NEIGH_HASH_OFFSET 24 #define NEIGH_HASH_OFFSET 24
static slab *neigh_slab; 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 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 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; 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)) if (!(i->flags & IF_UP))
return *ap = NULL, -1;
/* Regular neighbors */
WALK_LIST(b, i->addrs)
{ {
*ap = NULL; if (b->flags & IA_PEER)
return -1; {
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) /* Handle ONLINK flag */
{ if (flags & NEF_ONLINK)
*ap = b; return *ap = NULL, ipa_classify(a) & IADDR_SCOPE_MASK;
if (ipa_equal(*a, b->ip)) return *ap = NULL, -1;
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 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 scope;
return -1;
} }
/** /**
* neigh_find - find or create a neighbor entry. * neigh_find - find or create a neighbor entry
* @p: protocol which asks for the entry. * @p: protocol which asks for the entry
* @a: pointer to IP address of the node to be searched for. * @a: IP address of the node to be searched for
* @flags: 0 or %NEF_STICKY if you want to create a sticky entry. * @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 * Search the neighbor cache for a node with given IP address. Iface can be
* it's found, a pointer to the neighbor entry is returned. If no * specified for link-local addresses or for cases, where neighbor is expected
* such entry exists and the node is directly connected on * on given interface. If it is found, a pointer to the neighbor entry is
* one of our active interfaces, a new entry is created and returned * returned. If no such entry exists and the node is directly connected on one
* to the caller with protocol-dependent fields initialized to zero. * of our active interfaces, a new entry is created and returned to the caller
* If the node is not connected directly or *@a is not a valid unicast * with protocol-dependent fields initialized to zero. If the node is not
* IP address, neigh_find() returns %NULL. * connected directly or *@a is not a valid unicast IP address, neigh_find()
* returns %NULL.
*/ */
neighbor * neighbor *
neigh_find(struct proto *p, ip_addr *a, unsigned flags) neigh_find(struct proto *p, ip_addr a, struct iface *iface, uint flags)
{
return neigh_find2(p, a, NULL, flags);
}
neighbor *
neigh_find2(struct proto *p, ip_addr *a, struct iface *ifa, unsigned flags)
{ {
neighbor *n; neighbor *n;
int class, scope = -1; int class, scope = -1;
uint h = neigh_hash(p, a); uint h = neigh_hash(p, a, iface);
struct iface *i; struct iface *ifreq = iface;
struct ifa *addr; struct ifa *addr = NULL;
WALK_LIST(n, neigh_hash_table[h]) /* Search the cache */ 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; return n;
class = ipa_classify(*a); if (flags & NEF_IFACE)
if (class < 0) /* Invalid address */ {
return NULL; if (ipa_nonzero(a) || !iface)
if (((class & IADDR_SCOPE_MASK) == SCOPE_HOST) || return NULL;
(((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;
}
else else
WALK_LIST(i, iface_list) {
if ((scope = if_connected(a, i, &addr)) >= 0) class = ipa_classify(a);
{ if (class < 0) /* Invalid address */
ifa = i; return NULL;
break; 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 means i don't know neighbor */
/* scope >= 0 implies ifa != NULL */ /* scope >= 0 <=> iface != NULL */
if ((scope < 0) && !(flags & NEF_STICKY)) if ((scope < 0) && !(flags & NEF_STICKY))
return NULL; return NULL;
@ -168,52 +203,15 @@ neigh_find2(struct proto *p, ip_addr *a, struct iface *ifa, unsigned flags)
n = sl_alloc(neigh_slab); n = sl_alloc(neigh_slab);
memset(n, 0, sizeof(neighbor)); memset(n, 0, sizeof(neighbor));
n->addr = *a; add_tail(&neigh_hash_table[h], &n->n);
if (scope >= 0) add_tail((scope >= 0) ? &iface->neighbors : &sticky_neigh_list, &n->if_n);
{ n->addr = a;
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;
n->ifa = addr; n->ifa = addr;
n->iface = iface;
n->ifreq = ifreq;
n->proto = p; n->proto = p;
n->data = NULL;
n->aux = 0;
n->flags = flags; n->flags = flags;
n->scope = scope; 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; return n;
} }
@ -222,30 +220,26 @@ neigh_find_iface(struct proto *p, struct iface *ifa)
* neigh_dump - dump specified neighbor entry. * neigh_dump - dump specified neighbor entry.
* @n: the entry to dump * @n: the entry to dump
* *
* This functions dumps the contents of a given neighbor entry * This functions dumps the contents of a given neighbor entry to debug output.
* to debug output.
*/ */
void void
neigh_dump(neighbor *n) neigh_dump(neighbor *n)
{ {
debug("%p %I ", n, n->addr); debug("%p %I %s %s ", n, n->addr,
if (n->iface) n->iface ? n->iface->name : "[]",
debug("%s ", n->iface->name); n->ifreq ? n->ifreq->name : "[]");
else
debug("[] ");
debug("%s %p %08x scope %s", n->proto->name, n->data, n->aux, ip_scope_text(n->scope)); debug("%s %p %08x scope %s", n->proto->name, n->data, n->aux, ip_scope_text(n->scope));
if (n->flags & NEF_STICKY) if (n->flags & NEF_STICKY)
debug(" STICKY"); debug(" STICKY");
if (n->flags & NEF_IFACE) if (n->flags & NEF_ONLINK)
debug(" IFACE"); debug(" ONLINK");
debug("\n"); debug("\n");
} }
/** /**
* neigh_dump_all - dump all neighbor entries. * neigh_dump_all - dump all neighbor entries.
* *
* This function dumps the contents of the neighbor cache to * This function dumps the contents of the neighbor cache to debug output.
* debug output.
*/ */
void void
neigh_dump_all(void) neigh_dump_all(void)
@ -254,73 +248,109 @@ neigh_dump_all(void)
int i; int i;
debug("Known neighbors:\n"); 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++) for(i=0; i<NEIGH_HASH_SIZE; i++)
WALK_LIST(n, neigh_hash_table[i]) WALK_LIST(n, neigh_hash_table[i])
neigh_dump(n); neigh_dump(n);
debug("\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 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); DBG("Waking up sticky neighbor %I\n", n->addr);
n->iface = i; n->iface = i;
n->ifa = a; n->ifa = a;
n->scope = scope; n->scope = scope;
if (! (n->flags & NEF_IFACE)) rem_node(&n->if_n);
{ add_tail(&i->neighbors, &n->if_n);
add_tail(&i->neighbors, &n->if_n);
rem_node(&n->n);
add_tail(&neigh_hash_table[neigh_hash(n->proto, &n->addr)], &n->n);
}
if (n->proto->neigh_notify && (n->proto->proto_state != PS_STOP)) neigh_notify(n);
n->proto->neigh_notify(n);
} }
static void static void
neigh_down(neighbor *n) neigh_down(neighbor *n)
{ {
DBG("Flushing neighbor %I on %s\n", n->addr, n->iface->name); 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->ifa = NULL;
n->scope = -1; 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); n->ifa = ifa;
rem_node(&n->n); neigh_notify(n);
} }
if (n->proto->neigh_notify && (n->proto->proto_state != PS_STOP)) return;
n->proto->neigh_notify(n); }
if (n->flags & NEF_STICKY) /* Major change - going down and/or going up */
{
add_tail(&sticky_neigh_list, &n->n);
/* Respawn neighbor if there is another matching prefix */ if (n->scope >= 0)
struct iface *i; neigh_down(n);
struct ifa *a;
int scope;
if (!n->iface) if ((n->scope < 0) && !(n->flags & NEF_STICKY))
WALK_LIST(i, iface_list) {
if ((scope = if_connected(&n->addr, i, &a)) >= 0) neigh_free(n);
{ return;
neigh_up(n, i, scope, a); }
return;
}
}
if (! (n->flags & (NEF_STICKY | NEF_IFACE))) if (scope >= 0)
sl_free(neigh_slab, n); neigh_up(n, iface, ifa, scope);
} }
@ -336,21 +366,11 @@ neigh_down(neighbor *n)
void void
neigh_if_up(struct iface *i) neigh_if_up(struct iface *i)
{ {
struct ifa *a;
neighbor *n; neighbor *n;
node *x, *y; node *x, *y;
int scope;
/* Wake up all iface neighbors */ WALK_LIST2_DELSAFE(n, x, y, sticky_neigh_list, if_n)
WALK_LIST2_DELSAFE(n, x, y, i->neighbors, if_n) neigh_update(n, i);
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);
} }
/** /**
@ -359,8 +379,7 @@ neigh_if_up(struct iface *i)
* *
* Notify the neighbor cache that an interface has ceased to exist. * Notify the neighbor cache that an interface has ceased to exist.
* *
* It causes all entries belonging to neighbors connected to this interface * It causes all neighbors connected to this interface to be updated or removed.
* to be flushed.
*/ */
void void
neigh_if_down(struct iface *i) neigh_if_down(struct iface *i)
@ -369,16 +388,15 @@ neigh_if_down(struct iface *i)
node *x, *y; node *x, *y;
WALK_LIST2_DELSAFE(n, x, y, i->neighbors, if_n) 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 * neigh_if_link - notify neighbor cache about interface link change
* @i: the interface in question * @i: the interface in question
* *
* Notify the neighbor cache that an interface changed link state. * Notify the neighbor cache that an interface changed link state. All owners of
* All owners of neighbor entries connected to this interface are * neighbor entries connected to this interface are notified.
* notified.
*/ */
void void
neigh_if_link(struct iface *i) neigh_if_link(struct iface *i)
@ -387,8 +405,7 @@ neigh_if_link(struct iface *i)
node *x, *y; node *x, *y;
WALK_LIST2_DELSAFE(n, x, y, i->neighbors, if_n) WALK_LIST2_DELSAFE(n, x, y, i->neighbors, if_n)
if (n->proto->neigh_notify && (n->proto->proto_state != PS_STOP)) neigh_notify(n);
n->proto->neigh_notify(n);
} }
/** /**
@ -405,21 +422,16 @@ void
neigh_ifa_update(struct ifa *a) neigh_ifa_update(struct ifa *a)
{ {
struct iface *i = a->iface; struct iface *i = a->iface;
struct ifa *aa;
node *x, *y;
neighbor *n; 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) WALK_LIST2_DELSAFE(n, x, y, i->neighbors, if_n)
if (n->ifa && (if_connected(&n->addr, i, &aa) != n->scope)) neigh_update(n, i);
neigh_down(n);
/* Wake up all sticky neighbors that are reachable now */ /* Wake up all sticky neighbors that are reachable now */
WALK_LIST_DELSAFE(n, x, sticky_neigh_list) WALK_LIST2_DELSAFE(n, x, y, sticky_neigh_list, if_n)
if ((!n->iface || n->iface == i) && neigh_update(n, i);
((scope = if_connected(&n->addr, i, &aa)) >= 0))
neigh_up(n, i, scope, aa);
} }
static inline void static inline void
@ -427,10 +439,8 @@ neigh_prune_one(neighbor *n)
{ {
if (n->proto->proto_state != PS_DOWN) if (n->proto->proto_state != PS_DOWN)
return; return;
rem_node(&n->n);
if (n->if_n.next) neigh_free(n);
rem_node(&n->if_n);
sl_free(neigh_slab, n);
} }
/** /**
@ -451,10 +461,6 @@ neigh_prune(void)
for(i=0; i<NEIGH_HASH_SIZE; i++) for(i=0; i<NEIGH_HASH_SIZE; i++)
WALK_LIST_DELSAFE(n, m, neigh_hash_table[i]) WALK_LIST_DELSAFE(n, m, neigh_hash_table[i])
neigh_prune_one(n); 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)); 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++) for(int i = 0; i < NEIGH_HASH_SIZE; i++)
init_list(&neigh_hash_table[i]); init_list(&neigh_hash_table[i]);
init_list(&sticky_neigh_list);
} }

View File

@ -10,6 +10,7 @@
#include "nest/bird.h" #include "nest/bird.h"
#include "nest/password.h" #include "nest/password.h"
#include "lib/string.h" #include "lib/string.h"
#include "lib/timer.h"
#include "lib/mac.h" #include "lib/mac.h"
struct password_item *last_password_item = NULL; 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 *pi;
struct password_item *pf = NULL; struct password_item *pf = NULL;
btime now_ = current_real_time();
if (l) if (l)
{ {
WALK_LIST(pi, *l) WALK_LIST(pi, *l)
{ {
if ((pi->genfrom < now_real) && (pi->gento > now_real)) if ((pi->genfrom < now_) && (pi->gento > now_))
{ {
if (first_fit) if (first_fit)
return pi; return pi;
@ -41,12 +43,13 @@ struct password_item *
password_find_by_id(list *l, uint id) password_find_by_id(list *l, uint id)
{ {
struct password_item *pi; struct password_item *pi;
btime now_ = current_real_time();
if (!l) if (!l)
return NULL; return NULL;
WALK_LIST(pi, *l) 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 pi;
return NULL; return NULL;
@ -56,12 +59,13 @@ struct password_item *
password_find_by_value(list *l, char *pass, uint size) password_find_by_value(list *l, char *pass, uint size)
{ {
struct password_item *pi; struct password_item *pi;
btime now_ = current_real_time();
if (!l) if (!l)
return NULL; return NULL;
WALK_LIST(pi, *l) 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 pi;
return NULL; return NULL;

View File

@ -10,15 +10,13 @@
#ifndef PASSWORD_H #ifndef PASSWORD_H
#define PASSWORD_H #define PASSWORD_H
#include "sysdep/unix/timer.h"
struct password_item { struct password_item {
node n; node n;
char *password; /* Key data, null terminated */ char *password; /* Key data, null terminated */
uint length; /* Key length, without null */ uint length; /* Key length, without null */
uint id; /* Key ID */ uint id; /* Key ID */
uint alg; /* MAC algorithm */ uint alg; /* MAC algorithm */
bird_clock_t accfrom, accto, genfrom, gento; btime accfrom, accto, genfrom, gento;
}; };
extern struct password_item *last_password_item; extern struct password_item *last_password_item;

View File

@ -281,7 +281,7 @@ int import_control(struct proto *p, rte **e, ea_list **attrs, struct linpool *po
/** /**
* rte_recalculate - prepare routes for comparison * rte_recalculate - prepare routes for comparison
* @table: a routing table * @table: a routing table
* @net: a network entry * @net: a network entry
* @new: new route for the network * @new: new route for the network
* @old: old route for the network * @old: old route for the network

View File

@ -13,6 +13,7 @@
#include "lib/resource.h" #include "lib/resource.h"
#include "lib/lists.h" #include "lib/lists.h"
#include "lib/event.h" #include "lib/event.h"
#include "lib/timer.h"
#include "lib/string.h" #include "lib/string.h"
#include "conf/conf.h" #include "conf/conf.h"
#include "nest/route.h" #include "nest/route.h"
@ -24,6 +25,7 @@ pool *proto_pool;
list proto_list; list proto_list;
static list protocol_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) #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; 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 void proto_rethink_goal(struct proto *p);
static char *proto_state_name(struct proto *p); static char *proto_state_name(struct proto *p);
static void channel_verify_limits(struct channel *c); 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->channel_state = CS_DOWN;
c->export_state = ES_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; c->reloadable = 1;
CALL(c->channel->init, c, cf); CALL(c->channel->init, c, cf);
@ -341,7 +344,7 @@ channel_set_state(struct channel *c, uint state)
return; return;
c->channel_state = state; c->channel_state = state;
c->last_state_change = now; c->last_state_change = current_time();
switch (state) switch (state)
{ {
@ -436,7 +439,7 @@ static void
channel_request_reload(struct channel *c) channel_request_reload(struct channel *c)
{ {
ASSERT(c->channel_state == CS_UP); ASSERT(c->channel_state == CS_UP);
// ASSERT(channel_reloadable(c)); ASSERT(channel_reloadable(c));
c->proto->reload_routes(c); c->proto->reload_routes(c);
@ -454,11 +457,10 @@ const struct channel_class channel_basic = {
}; };
void * 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 channel_config *cf = NULL;
struct rtable_config *tab = NULL; struct rtable_config *tab = NULL;
const char *name = NULL;
if (net_type) 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"); cf_error("Different channel type");
tab = new_config->def_tables[net_type]; tab = new_config->def_tables[net_type];
name = net_label[net_type];
} }
if (!cc) 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 = cfg_allocz(cc->config_size);
cf->name = name; cf->name = name;
cf->channel = cc; cf->channel = cc;
cf->parent = proto;
cf->table = tab; cf->table = tab;
cf->out_filter = FILTER_REJECT; 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; 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 * struct channel_config *
channel_copy_config(struct channel_config *src, struct proto_config *proto) 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))) if ((c->table != cf->table->table) || (cf->ra_mode && (c->ra_mode != cf->ra_mode)))
return 0; return 0;
int import_changed = !filter_same(c->in_filter, cf->in_filter); /* Note that filter_same() requires arguments in (new, old) order */
int export_changed = !filter_same(c->out_filter, cf->out_filter); 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) if (c->preference != cf->preference)
import_changed = 1; import_changed = 1;
@ -535,6 +558,9 @@ channel_reconfigure(struct channel *c, struct channel_config *cf)
channel_verify_limits(c); channel_verify_limits(c);
if (export_changed)
c->last_tx_filter_change = current_time();
/* Execute channel-specific reconfigure hook */ /* Execute channel-specific reconfigure hook */
if (c->channel->reconfigure && !c->channel->reconfigure(c, cf)) if (c->channel->reconfigure && !c->channel->reconfigure(c, cf))
return 0; return 0;
@ -672,7 +698,8 @@ proto_init(struct proto_config *c, node *n)
struct proto *p = pr->init(c); struct proto *p = pr->init(c);
p->proto_state = PS_DOWN; 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); insert_node(&p->n, n);
p->event = ev_new(proto_pool); 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 there is a too big change in core attributes, ... */
if ((nc->protocol != oc->protocol) || if ((nc->protocol != oc->protocol) ||
(nc->net_type != oc->net_type) || (nc->net_type != oc->net_type) ||
(nc->disabled != p->disabled)) (nc->disabled != p->disabled) ||
(nc->vrf != oc->vrf))
return 0; return 0;
p->name = nc->name; p->name = nc->name;
@ -977,6 +1005,7 @@ proto_rethink_goal(struct proto *p)
proto_remove_channels(p); proto_remove_channels(p);
rem_node(&p->n); rem_node(&p->n);
rfree(p->event); rfree(p->event);
mb_free(p->message);
mb_free(p); mb_free(p);
if (!nc) if (!nc)
return; 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 * graceful_restart_recovery - request initial graceful restart recovery
@ -1083,9 +1112,8 @@ graceful_restart_init(void)
} }
graceful_restart_state = GRS_ACTIVE; graceful_restart_state = GRS_ACTIVE;
gr_wait_timer = tm_new(proto_pool); gr_wait_timer = tm_new_init(proto_pool, graceful_restart_done, NULL, 0, 0);
gr_wait_timer->hook = graceful_restart_done; tm_start(gr_wait_timer, config->gr_wait S);
tm_start(gr_wait_timer, config->gr_wait);
} }
/** /**
@ -1099,7 +1127,7 @@ graceful_restart_init(void)
* restart wait timer fires (but there are still some locks). * restart wait timer fires (but there are still some locks).
*/ */
static void static void
graceful_restart_done(struct timer *t UNUSED) graceful_restart_done(timer *t UNUSED)
{ {
log(L_INFO "Graceful restart done"); log(L_INFO "Graceful restart done");
graceful_restart_state = GRS_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, "Graceful restart recovery in progress");
cli_msg(-24, " Waiting for %d channels to recover", graceful_restart_locks); 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) proto_build(struct protocol *p)
{ {
add_tail(&protocol_list, &p->n); add_tail(&protocol_list, &p->n);
if (p->attr_class) ASSERT(p->class);
{ ASSERT(!class_to_protocol[p->class]);
ASSERT(!attr_class_to_protocol[p->attr_class]); class_to_protocol[p->class] = p;
attr_class_to_protocol[p->attr_class] = p;
}
} }
/* FIXME: convert this call to some protocol hook */ /* FIXME: convert this call to some protocol hook */
@ -1298,7 +1324,7 @@ protos_build(void)
int proto_restart; int proto_restart;
static void static void
proto_shutdown_loop(struct timer *t UNUSED) proto_shutdown_loop(timer *t UNUSED)
{ {
struct proto *p, *p_next; 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_sched = restart ? PDS_RESTART : PDS_DISABLE;
p->down_code = code; 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; return;
p->proto_state = state; p->proto_state = state;
p->last_state_change = now; p->last_state_change = current_time();
switch (state) switch (state)
{ {
@ -1620,19 +1679,20 @@ channel_show_info(struct channel *c)
} }
void 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]; byte buf[256], tbuf[TM_DATETIME_BUFFER_SIZE];
/* First protocol - show header */ /* First protocol - show header */
if (!cnt) 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; buf[0] = 0;
if (p->proto->get_status) if (p->proto->get_status)
p->proto->get_status(p, buf); p->proto->get_status(p, buf);
tm_format_datetime(tbuf, &config->tf_proto, p->last_state_change); tm_format_time(tbuf, &config->tf_proto, p->last_state_change);
cli_msg(-1002, "%-8s %-8s %-8s %-5s %-10s %s", cli_msg(-1002, "%-10s %-10s %-10s %-6s %-12s %s",
p->name, p->name,
p->proto->name, p->proto->name,
p->main_channel ? p->main_channel->table->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) if (p->cf->dsc)
cli_msg(-1006, " Description: %s", 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) if (p->cf->router_id)
cli_msg(-1006, " Router ID: %R", 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) if (p->proto->show_proto_info)
p->proto->show_proto_info(p); p->proto->show_proto_info(p);
@ -1661,7 +1725,7 @@ proto_cmd_show(struct proto *p, uint verbose, int cnt)
} }
void 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) 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); log(L_INFO "Disabling protocol %s", p->name);
p->disabled = 1; p->disabled = 1;
p->down_code = PDC_CMD_DISABLE; p->down_code = PDC_CMD_DISABLE;
proto_set_message(p, (char *) arg, -1);
proto_rethink_goal(p); proto_rethink_goal(p);
cli_msg(-9, "%s: disabled", p->name); cli_msg(-9, "%s: disabled", p->name);
} }
void 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) 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); log(L_INFO "Enabling protocol %s", p->name);
p->disabled = 0; p->disabled = 0;
proto_set_message(p, (char *) arg, -1);
proto_rethink_goal(p); proto_rethink_goal(p);
cli_msg(-11, "%s: enabled", p->name); cli_msg(-11, "%s: enabled", p->name);
} }
void 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) 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); log(L_INFO "Restarting protocol %s", p->name);
p->disabled = 1; p->disabled = 1;
p->down_code = PDC_CMD_RESTART; p->down_code = PDC_CMD_RESTART;
proto_set_message(p, (char *) arg, -1);
proto_rethink_goal(p); proto_rethink_goal(p);
p->disabled = 0; p->disabled = 0;
proto_rethink_goal(p); proto_rethink_goal(p);
@ -1710,7 +1777,7 @@ proto_cmd_restart(struct proto *p, uint arg UNUSED, int cnt UNUSED)
} }
void 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; struct channel *c;
@ -1749,19 +1816,19 @@ proto_cmd_reload(struct proto *p, uint dir, int cnt UNUSED)
} }
void 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; p->debug = mask;
} }
void 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; p->mrtdump = mask;
} }
static void 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) if (s->class != SYM_PROTO)
{ {
@ -1774,7 +1841,7 @@ proto_apply_cmd_symbol(struct symbol *s, void (* cmd)(struct proto *, uint, int)
} }
static void 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; struct proto *p;
int cnt = 0; int cnt = 0;
@ -1790,8 +1857,8 @@ proto_apply_cmd_patt(char *patt, void (* cmd)(struct proto *, uint, int), uint a
} }
void void
proto_apply_cmd(struct proto_spec ps, void (* cmd)(struct proto *, uint, int), proto_apply_cmd(struct proto_spec ps, void (* cmd)(struct proto *, uintptr_t, int),
int restricted, uint arg) int restricted, uintptr_t arg)
{ {
if (restricted && cli_access_restricted()) if (restricted && cli_access_restricted())
return; return;

View File

@ -12,7 +12,6 @@
#include "lib/lists.h" #include "lib/lists.h"
#include "lib/resource.h" #include "lib/resource.h"
#include "lib/event.h" #include "lib/event.h"
#include "sysdep/unix/timer.h"
#include "nest/route.h" #include "nest/route.h"
#include "conf/conf.h" #include "conf/conf.h"
@ -38,12 +37,31 @@ struct symbol;
* Routing Protocol * 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 { struct protocol {
node n; node n;
char *name; char *name;
char *template; /* Template for automatic generation of names */ char *template; /* Template for automatic generation of names */
int name_counter; /* Counter for automatic name generation */ 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 preference; /* Default protocol preference */
uint channel_mask; /* Mask of accepted channel types (NB_*) */ uint channel_mask; /* Mask of accepted channel types (NB_*) */
uint proto_size; /* Size of protocol data structure */ uint proto_size; /* Size of protocol data structure */
@ -59,7 +77,7 @@ struct protocol {
int (*shutdown)(struct proto *); /* Stop the instance */ int (*shutdown)(struct proto *); /* Stop the instance */
void (*cleanup)(struct proto *); /* Called after shutdown when protocol became hungry/down */ 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_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_*) */ 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 (*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 */ 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 */ u32 router_id; /* Protocol specific router ID */
list channels; /* List of channel configs (struct channel_config) */ 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 */ /* 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) */ list channels; /* List of channels to rtables (struct channel) */
struct channel *main_channel; /* Primary channel */ struct channel *main_channel; /* Primary channel */
struct rte_src *main_source; /* Primary route source */ 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) */ char *name; /* Name of this instance (== cf->name) */
u32 debug; /* Debugging flags */ u32 debug; /* Debugging flags */
@ -159,8 +179,9 @@ struct proto {
byte down_sched; /* Shutdown is scheduled for later (PDS_*) */ byte down_sched; /* Shutdown is scheduled for later (PDS_*) */
byte down_code; /* Reason for shutdown (PDC_* codes) */ byte down_code; /* Reason for shutdown (PDC_* codes) */
u32 hash_key; /* Random key used for hashing of neighbors */ 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 *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: * General protocol hooks:
@ -170,7 +191,7 @@ struct proto {
* rt_notify Notify protocol about routing table updates. * rt_notify Notify protocol about routing table updates.
* neigh_notify Notify protocol about neighbor cache events. * neigh_notify Notify protocol about neighbor cache events.
* make_tmp_attrs Construct ea_list from private attrs stored in rte. * 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. * import_control Called as the first step of the route importing process.
* It can construct a new rte, add private attributes and * It can construct a new rte, add private attributes and
* decide whether the route shall be imported: 1=yes, -1=no, * 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 (*if_notify)(struct proto *, unsigned flags, struct iface *i);
void (*ifa_notify)(struct proto *, unsigned flags, struct ifa *a); 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); void (*neigh_notify)(struct neighbor *neigh);
struct ea_list *(*make_tmp_attrs)(struct rte *rt, struct linpool *pool); struct ea_list *(*make_tmp_attrs)(struct rte *rt, struct linpool *pool);
void (*store_tmp_attrs)(struct rte *rt, struct ea_list *attrs); void (*store_tmp_attrs)(struct rte *rt);
int (*import_control)(struct proto *, struct rte **rt, struct ea_list **attrs, struct linpool *pool); int (*import_control)(struct proto *, struct rte **rt, struct linpool *pool);
void (*reload_routes)(struct channel *); void (*reload_routes)(struct channel *);
void (*feed_begin)(struct channel *, int initial); void (*feed_begin)(struct channel *, int initial);
void (*feed_end)(struct channel *); void (*feed_end)(struct channel *);
@ -208,6 +229,7 @@ struct proto {
int (*rte_better)(struct rte *, struct rte *); int (*rte_better)(struct rte *, struct rte *);
int (*rte_same)(struct rte *, struct rte *); int (*rte_same)(struct rte *, struct rte *);
int (*rte_mergable)(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_insert)(struct network *, struct rte *);
void (*rte_remove)(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_new(struct proto_config *);
void *proto_config_new(struct protocol *, int class); void *proto_config_new(struct protocol *, int class);
void proto_copy_config(struct proto_config *dest, struct proto_config *src); 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_recovery(void);
void graceful_restart_init(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_limit(struct channel_limit *l, const char *dsc);
void channel_show_info(struct channel *c); void channel_show_info(struct channel *c);
void proto_cmd_show(struct proto *, uint, int); void proto_cmd_show(struct proto *, uintptr_t, int);
void proto_cmd_disable(struct proto *, uint, int); void proto_cmd_disable(struct proto *, uintptr_t, int);
void proto_cmd_enable(struct proto *, uint, int); void proto_cmd_enable(struct proto *, uintptr_t, int);
void proto_cmd_restart(struct proto *, uint, int); void proto_cmd_restart(struct proto *, uintptr_t, int);
void proto_cmd_reload(struct proto *, uint, int); void proto_cmd_reload(struct proto *, uintptr_t, int);
void proto_cmd_debug(struct proto *, uint, int); void proto_cmd_debug(struct proto *, uintptr_t, int);
void proto_cmd_mrtdump(struct proto *, uint, 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 *); struct proto *proto_get_named(struct symbol *, struct protocol *);
#define CMD_RELOAD 0 #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; return pc->router_id ? pc->router_id : pc->global->router_id;
} }
static inline struct ea_list * static inline void
rte_make_tmp_attrs(struct rte *rt, struct linpool *pool) rte_make_tmp_attrs(struct rte **rt, struct linpool *pool)
{ {
struct ea_list *(*mta)(struct rte *rt, struct linpool *pool); struct ea_list *(*mta)(struct rte *rt, struct linpool *pool);
mta = rt->attrs->src->proto->make_tmp_attrs; mta = (*rt)->attrs->src->proto->make_tmp_attrs;
return mta ? mta(rt, pool) : NULL; 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 */ /* Moved from route.h to avoid dependency conflicts */
@ -334,7 +361,7 @@ void proto_notify_state(struct proto *p, unsigned state);
* *
* HUNGRY ----> FEEDING * HUNGRY ----> FEEDING
* ^ | * ^ |
* | V * | V
* FLUSHING <---- HAPPY * FLUSHING <---- HAPPY
* *
* States: HUNGRY Protocol either administratively down (i.e., * 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 (*dump_attrs)(struct rte *); /* Dump protocol-dependent attributes */
void (*get_status)(struct proto *, byte *buf); /* Get instance status (for `show protocols' command) */ 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_*) */ 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 (*show_proto_info)(struct proto *); /* Show protocol info (for `show protocols all' command) */
@ -458,6 +485,7 @@ struct channel_config {
const char *name; const char *name;
const struct channel_class *channel; 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 rtable_config *table; /* Table we're attached to */
struct filter *in_filter, *out_filter; /* Attached filters */ struct filter *in_filter, *out_filter; /* Attached filters */
struct channel_limit rx_limit; /* Limit for receiving routes from protocol 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_lock; /* Graceful restart mechanism should wait for this channel */
u8 gr_wait; /* Route export to channel is postponed until graceful restart */ 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); } static inline void channel_close(struct channel *c) { channel_set_state(c, CS_FLUSHING); }
void channel_request_feeding(struct channel *c); 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); int channel_reconfigure(struct channel *c, struct channel_config *cf);

View File

@ -11,7 +11,6 @@
#include "lib/lists.h" #include "lib/lists.h"
#include "lib/resource.h" #include "lib/resource.h"
#include "sysdep/unix/timer.h"
#include "lib/net.h" #include "lib/net.h"
struct ea_list; 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_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_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_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_route(struct fib *, const net_addr *); /* Longest-match routing lookup */
void fib_delete(struct fib *, void *); /* Remove fib entry */ void fib_delete(struct fib *, void *); /* Remove fib entry */
void fib_free(struct fib *); /* Destroy the fib */ 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; \ type *z; \
for(;;) { \ for(;;) { \
if (!fn_) \ if (!fn_) \
{ \ { \
if (++hpos_ >= count_) \ if (++hpos_ >= count_) \
break; \ break; \
fn_ = (fib)->hash_table[hpos_]; \ fn_ = (fib)->hash_table[hpos_]; \
@ -159,8 +158,8 @@ typedef struct rtable {
* obstacle from this routing table. * obstacle from this routing table.
*/ */
struct event *rt_event; /* Routing table event */ struct event *rt_event; /* Routing table event */
btime gc_time; /* Time of last GC */
int gc_counter; /* Number of operations since 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 prune_state; /* Table prune state, 1 -> scheduled, 2-> running */
byte hcu_scheduled; /* Hostcache update is scheduled */ byte hcu_scheduled; /* Hostcache update is scheduled */
byte nhu_state; /* Next Hop Update state */ byte nhu_state; /* Next Hop Update state */
@ -213,7 +212,7 @@ typedef struct rte {
byte flags; /* Flags (REF_...) */ byte flags; /* Flags (REF_...) */
byte pflags; /* Protocol-specific flags */ byte pflags; /* Protocol-specific flags */
word pref; /* Route preference */ word pref; /* Route preference */
bird_clock_t lastmod; /* Last modified */ btime lastmod; /* Last modified */
union { /* Protocol-dependent data (metrics etc.) */ union { /* Protocol-dependent data (metrics etc.) */
#ifdef CONFIG_RIP #ifdef CONFIG_RIP
struct { struct {
@ -232,10 +231,12 @@ typedef struct rte {
#ifdef CONFIG_BGP #ifdef CONFIG_BGP
struct { struct {
u8 suppressed; /* Used for deterministic MED comparison */ u8 suppressed; /* Used for deterministic MED comparison */
s8 stale; /* Route is LLGR_STALE, -1 if unknown */
} bgp; } bgp;
#endif #endif
#ifdef CONFIG_BABEL #ifdef CONFIG_BABEL
struct { struct {
u16 seqno; /* Babel seqno */
u16 metric; /* Babel metric */ u16 metric; /* Babel metric */
u64 router_id; /* Babel router id */ u64 router_id; /* Babel router id */
} babel; } babel;
@ -254,6 +255,7 @@ typedef struct rte {
#define REF_FILTERED 2 /* Route is rejected by import filter */ #define REF_FILTERED 2 /* Route is rejected by import filter */
#define REF_STALE 4 /* Route is stale in a refresh cycle */ #define REF_STALE 4 /* Route is stale in a refresh cycle */
#define REF_DISCARD 8 /* Route is scheduled for discard */ #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 */ /* 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); } 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_commit(struct config *new, struct config *old);
void rt_lock_table(rtable *); void rt_lock_table(rtable *);
void rt_unlock_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(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) 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; } { 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); 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 */ /* rte_update() moved to protocol.h to avoid dependency conflicts */
int rt_examine(rtable *t, net_addr *a, struct proto *p, struct filter *filter); 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_begin(rtable *t, struct channel *c);
void rt_refresh_end(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 rt_schedule_prune(rtable *t);
void rte_dump(rte *); void rte_dump(rte *);
void rte_free(rte *); void rte_free(rte *);
@ -309,6 +312,8 @@ int rt_feed_channel(struct channel *c);
void rt_feed_channel_abort(struct channel *c); void rt_feed_channel_abort(struct channel *c);
struct rtable_config *rt_new_table(struct symbol *s, uint addr_type); 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 { struct rt_show_data_rtable {
node n; node n;
@ -455,7 +460,7 @@ static inline int rte_is_reachable(rte *r)
*/ */
typedef struct eattr { 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 flags; /* Protocol-dependent flags */
byte type; /* Attribute type and several flags (EAF_...) */ byte type; /* Attribute type and several flags (EAF_...) */
union { union {
@ -464,19 +469,11 @@ typedef struct eattr {
} u; } u;
} eattr; } 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_CODE(proto,id) (((proto) << 8) | (id))
#define EA_PROTO(ea) ((ea) >> 8) #define EA_PROTO(ea) ((ea) >> 8)
#define EA_ID(ea) ((ea) & 0xff) #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_CODE_MASK 0xffff
#define EA_ALLOW_UNDEF 0x10000 /* ea_find: allow EAF_TYPE_UNDEF */ #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); 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); 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) #define NEXTHOP_MAX_SIZE (sizeof(struct nexthop) + sizeof(u32)*MPLS_MAX_LABEL_STACK)
static inline size_t nexthop_size(const struct nexthop *nh) 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; } 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(rta *);
void rta_dump_all(void); 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); 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); 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_lock_hostentry(struct hostentry *he) { if (he) he->uc++; }
static inline void rt_unlock_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 * 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_STATIC 200 /* Static route */
#define DEF_PREF_OSPF 150 /* OSPF intra-area, inter-area and type 1 external routes */ #define DEF_PREF_OSPF 150 /* OSPF intra-area, inter-area and type 1 external routes */
#define DEF_PREF_BABEL 130 /* Babel */ #define DEF_PREF_BABEL 130 /* Babel */

View File

@ -88,9 +88,6 @@ static struct idm src_ids;
static HASH(struct rte_src) src_hash; static HASH(struct rte_src) src_hash;
struct protocol *attr_class_to_protocol[EAP_MAX];
static void static void
rte_src_init(void) rte_src_init(void)
{ {
@ -553,29 +550,47 @@ ea_do_sort(ea_list *e)
while (ss); while (ss);
} }
/**
* In place discard duplicates, undefs and temporary attributes in sorted
* ea_list. We use stable sort for this reason.
**/
static inline void static inline void
ea_do_prune(ea_list *e) ea_do_prune(ea_list *e)
{ {
eattr *s, *d, *l, *s0; eattr *s, *d, *l, *s0;
int i = 0; int i = 0;
/* Discard duplicates and undefs. Do you remember sorting was stable? */ s = d = e->attrs; /* Beginning of the list. @s is source, @d is destination. */
s = d = e->attrs; l = e->attrs + e->count; /* End of the list */
l = e->attrs + e->count;
/* Walk from begin to end. */
while (s < l) while (s < l)
{ {
s0 = s++; s0 = s++;
/* Find a consecutive block of the same attribute */
while (s < l && s->id == s[-1].id) while (s < l && s->id == s[-1].id)
s++; s++;
/* s0 is the most recent version, s[-1] the oldest one */
if ((s0->type & EAF_TYPE_MASK) != EAF_TYPE_UNDEF) /* Now s0 is the most recent version, s[-1] the oldest one */
{ /* Drop undefs */
*d = *s0; if ((s0->type & EAF_TYPE_MASK) == EAF_TYPE_UNDEF)
d->type = (d->type & ~(EAF_ORIGINATED|EAF_FRESH)) | (s[-1].type & EAF_ORIGINATED); continue;
d++;
i++; /* 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; e->count = i;
} }
@ -851,7 +866,7 @@ ea_show(struct cli *c, eattr *e)
byte buf[CLI_MSG_SIZE]; byte buf[CLI_MSG_SIZE];
byte *pos = buf, *end = buf + sizeof(buf); 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); pos += bsprintf(pos, "%s.", p->name);
if (p->get_attr) if (p->get_attr)
@ -1131,15 +1146,7 @@ rta_lookup(rta *o)
ASSERT(!(o->aflags & RTAF_CACHED)); ASSERT(!(o->aflags & RTAF_CACHED));
if (o->eattrs) if (o->eattrs)
{ ea_normalize(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);
}
h = rta_hash(o); h = rta_hash(o);
for(r=rta_hash_table[h & rta_cache_mask]; r; r=r->next) 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", static char *rts[] = { "RTS_DUMMY", "RTS_STATIC", "RTS_INHERIT", "RTS_DEVICE",
"RTS_STAT_DEV", "RTS_REDIR", "RTS_RIP", "RTS_STAT_DEV", "RTS_REDIR", "RTS_RIP",
"RTS_OSPF", "RTS_OSPF_IA", "RTS_OSPF_EXT1", "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" }; static char *rtd[] = { "", " DEV", " HOLE", " UNREACH", " PROHIBIT" };
debug("p=%s uc=%d %s %s%s h=%04x", debug("p=%s uc=%d %s %s%s h=%04x",
@ -1253,17 +1260,15 @@ rta_dump_all(void)
} }
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", static char *src_names[] = { "dummy", "static", "inherit", "device", "static-device", "redirect",
"RIP", "OSPF", "OSPF-IA", "OSPF-E1", "OSPF-E2", "BGP", "pipe" }; "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)); cli_printf(c, -1008, "\tType: %s %s", src_names[a->source], ip_scope_text(a->scope));
if (!eal)
eal = a->eattrs; for(ea_list *eal = a->eattrs; eal; eal=eal->next)
for(; eal; eal=eal->next) for(int i=0; i<eal->count; i++)
for(i=0; i<eal->count; i++)
ea_show(c, &eal->attrs[i]); ea_show(c, &eal->attrs[i]);
} }

View File

@ -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_proto *p = (void *) P;
struct rt_dev_config *cf = (void *) P->cf; struct rt_dev_config *cf = (void *) P->cf;
struct channel *c; struct channel *c;
net_addr *net = &ad->prefix;
if (!EMPTY_LIST(cf->iface_list) && 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 "*" */ /* Empty list is automatically treated as "*" */
return; return;
@ -53,13 +54,20 @@ dev_ifa_notify(struct proto *P, uint flags, struct ifa *ad)
if (!c) if (!c)
return; 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) if (flags & IF_CHANGE_DOWN)
{ {
DBG("dev_if_notify: %s:%I going down\n", ad->iface->name, ad->ip); DBG("dev_if_notify: %s:%I going down\n", ad->iface->name, ad->ip);
/* Use iface ID as local source ID */ /* Use iface ID as local source ID */
struct rte_src *src = rt_get_source(P, ad->iface->index); 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) 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); a = rta_lookup(&a0);
e = rte_get_temp(a); e = rte_get_temp(a);
e->pflags = 0; 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 * static struct proto *
dev_init(struct proto_config *CF) dev_init(struct proto_config *CF)
{ {
struct proto *P = proto_new(CF); struct proto *P = proto_new(CF);
struct rt_dev_proto *p = (void *) P; 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->ip4_channel, cf->ip4_channel);
proto_configure_channel(P, &p->ip6_channel, proto_cf_find_channel(CF, NET_IP6)); proto_configure_channel(P, &p->ip6_channel, cf->ip6_channel);
P->if_notify = dev_if_notify; P->if_notify = dev_if_notify;
P->ifa_notify = dev_ifa_notify; P->ifa_notify = dev_ifa_notify;
@ -136,8 +160,8 @@ dev_reconfigure(struct proto *P, struct proto_config *CF)
return 0; return 0;
return return
proto_configure_channel(P, &p->ip4_channel, proto_cf_find_channel(CF, NET_IP4)) && proto_configure_channel(P, &p->ip4_channel, n->ip4_channel) &&
proto_configure_channel(P, &p->ip6_channel, proto_cf_find_channel(CF, NET_IP6)); proto_configure_channel(P, &p->ip6_channel, n->ip6_channel);
return 1; return 1;
} }
@ -159,13 +183,15 @@ dev_copy_config(struct proto_config *dest, struct proto_config *src)
} }
struct protocol proto_device = { struct protocol proto_device = {
.name = "Direct", .name = "Direct",
.template = "direct%d", .template = "direct%d",
.class = PROTOCOL_DIRECT,
.preference = DEF_PREF_DIRECT, .preference = DEF_PREF_DIRECT,
.channel_mask = NB_IP, .channel_mask = NB_IP | NB_IP6_SADR,
.proto_size = sizeof(struct rt_dev_proto), .proto_size = sizeof(struct rt_dev_proto),
.config_size = sizeof(struct rt_dev_config), .config_size = sizeof(struct rt_dev_config),
.init = dev_init, .postconfig = dev_postconfig,
.reconfigure = dev_reconfigure, .init = dev_init,
.copy_config = dev_copy_config .reconfigure = dev_reconfigure,
.copy_config = dev_copy_config
}; };

View File

@ -13,6 +13,9 @@ struct rt_dev_config {
struct proto_config c; struct proto_config c;
list iface_list; /* list of struct iface_patt */ list iface_list; /* list of struct iface_patt */
int check_link; int check_link;
struct channel_config *ip4_channel;
struct channel_config *ip6_channel;
}; };
struct rt_dev_proto { struct rt_dev_proto {

View File

@ -32,6 +32,24 @@
* Basic FIB operations are performed by functions defined by this module, * Basic FIB operations are performed by functions defined by this module,
* enumerating of FIB contents is accomplished by using the FIB_WALK() macro * enumerating of FIB contents is accomplished by using the FIB_WALK() macro
* or FIB_ITERATE_START() if you want to do it asynchronously. * 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 #undef LOCAL_DEBUG
@ -74,8 +92,7 @@ fib_ht_free(struct fib_node **h)
} }
static u32 static inline u32 fib_hash(struct fib *f, const net_addr *a);
fib_hash(struct fib *f, const net_addr *a);
/** /**
* fib_init - initialize a new FIB * 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) fib_hash(struct fib *f, const net_addr *a)
{ {
ASSERT(f->addr_type == a->type); /* Same as FIB_HASH() */
return net_hash(a) >> f->hash_shift;
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");
}
} }
void * 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_ROA6: return FIB_FIND(f, a, roa6);
case NET_FLOW4: return FIB_FIND(f, a, flow4); case NET_FLOW4: return FIB_FIND(f, a, flow4);
case NET_FLOW6: return FIB_FIND(f, a, flow6); 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"); 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_ROA6: FIB_INSERT(f, a, e, roa6); return;
case NET_FLOW4: FIB_INSERT(f, a, e, flow4); return; case NET_FLOW4: FIB_INSERT(f, a, e, flow4); return;
case NET_FLOW6: FIB_INSERT(f, a, e, flow6); 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"); default: bug("invalid type");
} }
} }
@ -547,22 +556,17 @@ found:
void void
fib_check(struct fib *f) fib_check(struct fib *f)
{ {
#if 0 uint i, ec, nulls;
uint i, ec, lo, nulls;
ec = 0; ec = 0;
for(i=0; i<f->hash_size; i++) for(i=0; i<f->hash_size; i++)
{ {
struct fib_node *n; struct fib_node *n;
lo = 0;
for(n=f->hash_table[i]; n; n=n->next) for(n=f->hash_table[i]; n; n=n->next)
{ {
struct fib_iterator *j, *j0; struct fib_iterator *j, *j0;
uint h0 = ipa_hash(n->prefix); uint h0 = fib_hash(f, n->addr);
if (h0 < lo) if (h0 != i)
bug("fib_check: discord in hash chains");
lo = h0;
if ((h0 >> f->hash_shift) != i)
bug("fib_check: mishashed %x->%x (order %d)", h0, i, f->hash_order); bug("fib_check: mishashed %x->%x (order %d)", h0, i, f->hash_order);
j0 = (struct fib_iterator *) n; j0 = (struct fib_iterator *) n;
nulls = 0; nulls = 0;
@ -583,7 +587,6 @@ fib_check(struct fib *f)
} }
if (ec != f->entries) if (ec != f->entries)
bug("fib_check: invalid entry count (%d != %d)", ec, f->entries); bug("fib_check: invalid entry count (%d != %d)", ec, f->entries);
#endif
return; return;
} }
@ -602,7 +605,7 @@ fib_histogram(struct fib *f)
for (e = f->hash_table[i]; e != NULL; e = e->next) for (e = f->hash_table[i]; e != NULL; e = e->next)
j++; j++;
if (j > 0) 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"); log(L_WARN "Histogram dump end");

View File

@ -29,41 +29,35 @@ rt_show_table(struct cli *c, struct rt_show_data *d)
} }
static void 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 from[IPA_MAX_TEXT_LENGTH+8];
byte tm[TM_DATETIME_BUFFER_SIZE], info[256]; byte tm[TM_DATETIME_BUFFER_SIZE], info[256];
rta *a = e->attrs; rta *a = e->attrs;
int primary = (e->net->routes == e); int primary = (e->net->routes == e);
int sync_error = (e->net->n.flags & KRF_SYNC_ERROR); 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; 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)) if (ipa_nonzero(a->from) && !ipa_equal(a->from, a->nh.gw))
bsprintf(from, " from %I", a->from); bsprintf(from, " from %I", a->from);
else else
from[0] = 0; from[0] = 0;
get_route_info = a->src->proto->proto->get_route_info; get_route_info = a->src->proto->proto->get_route_info;
if (get_route_info || d->verbose) /* Need to normalize the extended attributes */
{ if ((get_route_info || d->verbose) && !rta_is_cached(a))
/* Need to normalize the extended attributes */ ea_normalize(a->eattrs);
ea_list *t = tmpa;
t = ea_append(t, a->eattrs);
tmpa = alloca(ea_scan(t));
ea_merge(t, tmpa);
ea_sort(tmpa);
}
if (get_route_info) if (get_route_info)
get_route_info(e, info, tmpa); get_route_info(e, info);
else else
bsprintf(info, " (%d)", e->pref); bsprintf(info, " (%d)", e->pref);
if (d->last_table != d->tab) if (d->last_table != d->tab)
rt_show_table(c, d); 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); a->src->proto->name, tm, from, primary ? (sync_error ? " !" : " *") : "", info);
if (a->dest == RTD_UNICAST) 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 mpls[MPLS_MAX_LABEL_STACK*12 + 5], *lsp = mpls;
char *onlink = (nh->flags & RNF_ONLINK) ? " onlink" : ""; char *onlink = (nh->flags & RNF_ONLINK) ? " onlink" : "";
char weight[16] = "";
if (nh->labels) if (nh->labels)
{ {
lsp += bsprintf(lsp, " mpls %d", nh->label[0]); lsp += bsprintf(lsp, " mpls %d", nh->label[0]);
for (int i=1;i<nh->labels; i++) for (int i=1;i<nh->labels; i++)
lsp += bsprintf(lsp, "/%d", nh->label[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'; *lsp = '\0';
if (a->nh.next) if (a->nh.next)
cli_printf(c, -1007, "\tvia %I%s on %s%s weight %d", bsprintf(weight, " weight %d", nh->weight + 1);
nh->gw, mpls, nh->iface->name, onlink, 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 else
cli_printf(c, -1007, "\tvia %I%s on %s%s", cli_printf(c, -1007, "\tdev %s%s%s",
nh->gw, mpls, nh->iface->name, onlink); nh->iface->name, mpls, onlink, weight);
} }
if (d->verbose) if (d->verbose)
rta_show(c, a, tmpa); rta_show(c, a);
} }
static void static void
@ -97,7 +95,6 @@ rt_show_net(struct cli *c, net *n, struct rt_show_data *d)
{ {
rte *e, *ee; rte *e, *ee;
byte ia[NET_MAX_TEXT_LENGTH+1]; byte ia[NET_MAX_TEXT_LENGTH+1];
struct ea_list *tmpa;
struct channel *ec = d->tab->export_channel; struct channel *ec = d->tab->export_channel;
int first = 1; int first = 1;
int pass = 0; int pass = 0;
@ -117,7 +114,7 @@ rt_show_net(struct cli *c, net *n, struct rt_show_data *d)
continue; continue;
ee = e; 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 */ /* Export channel is down, do not try to export routes to it */
if (ec && (ec->export_state == ES_DOWN)) 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 */ /* Special case for merged export */
if ((d->export_mode == RSEM_EXPORT) && (ec->ra_mode == RA_MERGED)) if ((d->export_mode == RSEM_EXPORT) && (ec->ra_mode == RA_MERGED))
{ {
rte *rt_free; 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; pass = 1;
if (!e) if (!e)
@ -136,7 +133,7 @@ rt_show_net(struct cli *c, net *n, struct rt_show_data *d)
else if (d->export_mode) else if (d->export_mode)
{ {
struct proto *ep = ec->proto; 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) if (ec->ra_mode == RA_OPTIMAL || ec->ra_mode == RA_MERGED)
pass = 1; 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. * command may change the export filter and do not update routes.
*/ */
int do_export = (ic > 0) || 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)) if (do_export != (d->export_mode == RSEM_EXPORT))
goto skip; 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)) if (d->show_protocol && (d->show_protocol != e->attrs->src->proto))
goto skip; 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; goto skip;
if (d->stats < 2) if (d->stats < 2)
rt_show_rte(c, ia, e, d, tmpa); rt_show_rte(c, ia, e, d);
d->show_counter++; d->show_counter++;
ia[0] = 0; ia[0] = 0;
@ -414,4 +411,3 @@ rt_show(struct rt_show_data *d)
cli_msg(8001, "Network not found"); cli_msg(8001, "Network not found");
} }
} }

View File

@ -39,6 +39,7 @@
#include "lib/string.h" #include "lib/string.h"
#include "conf/conf.h" #include "conf/conf.h"
#include "filter/filter.h" #include "filter/filter.h"
#include "lib/hash.h"
#include "lib/string.h" #include "lib/string.h"
#include "lib/alloca.h" #include "lib/alloca.h"
@ -85,6 +86,45 @@ net_route_ip6(rtable *t, net_addr_ip6 *n)
return r; 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 * void *
net_route(rtable *tab, const net_addr *n) net_route(rtable *tab, const net_addr *n)
{ {
@ -105,6 +145,9 @@ net_route(rtable *tab, const net_addr *n)
case NET_ROA6: case NET_ROA6:
return net_route_ip6(tab, (net_addr_ip6 *) n0); 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: default:
return NULL; return NULL;
} }
@ -275,11 +318,11 @@ rte_cow_rta(rte *r, linpool *lp)
if (!rta_is_cached(r->attrs)) if (!rta_is_cached(r->attrs))
return r; return r;
rte *e = rte_cow(r); r = rte_cow(r);
rta *a = rta_do_cow(r->attrs, lp); rta *a = rta_do_cow(r->attrs, lp);
rta_free(e->attrs); rta_free(r->attrs);
e->attrs = a; r->attrs = a;
return e; return r;
} }
static int /* Actually better or at least as good as */ 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 * 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 proto *p = c->proto;
struct filter *filter = c->out_filter; struct filter *filter = c->out_filter;
struct proto_stats *stats = &c->stats; struct proto_stats *stats = &c->stats;
ea_list *tmpb = NULL;
rte *rt; rte *rt;
int v; int v;
rt = rt0; rt = rt0;
*rt_free = NULL; *rt_free = NULL;
if (!tmpa) rte_make_tmp_attrs(&rt, pool);
tmpa = &tmpb;
*tmpa = rte_make_tmp_attrs(rt, pool); v = p->import_control ? p->import_control(p, &rt, pool) : 0;
v = p->import_control ? p->import_control(p, &rt, tmpa, pool) : 0;
if (v < 0) if (v < 0)
{ {
if (silent) 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) || 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 (v)
{ {
if (silent) if (silent)
@ -411,13 +451,13 @@ export_filter_(struct channel *c, rte *rt0, rte **rt_free, ea_list **tmpa, linpo
} }
static inline rte * 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 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 *p = c->proto;
struct proto_stats *stats = &c->stats; 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) else if (old)
rte_trace_out(D_ROUTES, p, old, "removed"); rte_trace_out(D_ROUTES, p, old, "removed");
} }
if (!new) p->rt_notify(p, c, net, new, old);
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);
} }
static void static void
@ -514,7 +542,6 @@ rt_notify_basic(struct channel *c, net *net, rte *new0, rte *old0, int refeed)
rte *old = old0; rte *old = old0;
rte *new_free = NULL; rte *new_free = NULL;
rte *old_free = NULL; rte *old_free = NULL;
ea_list *tmpa = NULL;
if (new) if (new)
c->stats.exp_updates_received++; 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++; c->stats.exp_withdraws_received++;
/* /*
* This is a tricky part - we don't know whether route 'old' was * This is a tricky part - we don't know whether route 'old' was exported to
* exported to protocol 'p' or was filtered by the export filter. * protocol 'p' or was filtered by the export filter. We try to run the export
* We try to run the export filter to know this to have a correct * filter to know this to have a correct value in 'old' argument of rte_update
* value in 'old' argument of rte_update (and proper filter value) * (and proper filter value).
* *
* FIXME - this is broken because 'configure soft' may change * This is broken because 'configure soft' may change filters but keep routes.
* filters but keep routes. Refeed is expected to be called after * Refeed cycle is expected to be called after change of the filters and with
* change of the filters and with old == new, therefore we do not * old == new, therefore we do not even try to run the filter on an old route.
* even try to run the filter on an old route, This may lead to * This may lead to 'spurious withdraws' but ensure that there are no 'missing
* 'spurious withdraws' but ensure that there are no 'missing
* withdraws'. * withdraws'.
* *
* This is not completely safe as there is a window between * This is not completely safe as there is a window between reconfiguration
* reconfiguration and the end of refeed - if a newly filtered * and the end of refeed - if a newly filtered route disappears during this
* route disappears during this period, proper withdraw is not * period, proper withdraw is not sent (because old would be also filtered)
* sent (because old would be also filtered) and the route is * and the route is not refeeded (because it disappeared before that).
* 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) if (new)
new = export_filter(c, new, &new_free, &tmpa, 0); new = export_filter(c, new, &new_free, 0);
if (old && !refeed) if (old && !(refeed || (old->lastmod <= c->last_tx_filter_change)))
old = export_filter(c, old, &old_free, NULL, 1); old = export_filter(c, old, &old_free, 1);
if (!new && !old) if (!new && !old)
{ {
@ -562,13 +589,13 @@ rt_notify_basic(struct channel *c, net *net, rte *new0, rte *old0, int refeed)
#ifdef CONFIG_PIPE #ifdef CONFIG_PIPE
if ((p->proto == &proto_pipe) && !new0 && (p != old0->sender->proto)) 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 #endif
return; return;
} }
do_rt_notify(c, net, new, old, tmpa, refeed); do_rt_notify(c, net, new, old, refeed);
/* Discard temporary rte's */ /* Discard temporary rte's */
if (new_free) 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 *old_best = NULL;
rte *new_free = NULL; rte *new_free = NULL;
rte *old_free = NULL; rte *old_free = NULL;
ea_list *tmpa = NULL;
/* Used to track whether we met old_changed position. If before_old is 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. */ 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 */ /* First, find the new_best route - first accepted by filters */
for (r=net->routes; rte_is_valid(r); r=r->next) 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; break;
/* Note if we walked around the position of old_changed route */ /* Note if we walked around the position of old_changed route */
@ -613,9 +639,9 @@ rt_notify_accepted(struct channel *c, net *net, rte *new_changed, rte *old_chang
old_meet = 1; old_meet = 1;
} }
/* /*
* Second, handle the feed case. That means we do not care for * Second, handle the feed case. That means we do not care for
* old_best. It is NULL for feed, and the new_best for refeed. * old_best. It is NULL for feed, and the new_best for refeed.
* For refeed, there is a hack similar to one in rt_notify_basic() * For refeed, there is a hack similar to one in rt_notify_basic()
* to ensure withdraws in case of changed filters * to ensure withdraws in case of changed filters
*/ */
@ -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 * - We found new_best the same as new_changed, therefore it cannot
* be old_best and we have to continue search for old_best. * 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 */ /* First case */
if (old_meet) 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; goto found;
/* Second case */ /* Second case */
@ -674,18 +710,18 @@ rt_notify_accepted(struct channel *c, net *net, rte *new_changed, rte *old_chang
/* Fourth case */ /* Fourth case */
for (r=r->next; rte_is_valid(r); r=r->next) 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; goto found;
if (r == before_old) 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; goto found;
} }
/* Implicitly, old_best is NULL and new_best is non-NULL */ /* Implicitly, old_best is NULL and new_best is non-NULL */
found: 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 */ /* Discard temporary rte's */
if (new_free) if (new_free)
@ -702,7 +738,7 @@ nexthop_merge_rta(struct nexthop *nhs, rta *a, linpool *pool, int max)
} }
rte * 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 proto *p = c->proto;
struct nexthop *nhs = NULL; 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)) if (!rte_is_valid(best0))
return NULL; 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)) if (!best || !rte_is_reachable(best))
return 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)) if (!rte_mergable(best0, rt0))
continue; continue;
rt = export_filter_(c, rt0, &tmp, NULL, pool, 1); rt = export_filter_(c, rt0, &tmp, pool, 1);
if (!rt) if (!rt)
continue; 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 *old_best_free = NULL;
rte *new_changed_free = NULL; rte *new_changed_free = NULL;
rte *old_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() */ /* 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) if ((new_best == old_best) && !refeed)
{ {
new_changed = rte_mergable(new_best, new_changed) ? 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) ? 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) if (!new_changed && !old_changed)
return; return;
@ -792,15 +827,15 @@ rt_notify_merged(struct channel *c, net *net, rte *new_changed, rte *old_changed
/* Prepare new merged route */ /* Prepare new merged route */
if (new_best) 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) */ /* Prepare old merged route (without proper merged next hops) */
/* There are some issues with running filter on old route - see rt_notify_basic() */ /* There are some issues with running filter on old route - see rt_notify_basic() */
if (old_best && !refeed) 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) 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 */ /* Discard temporary rte's */
if (new_best_free) 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 * @new_best: the new best route for the same network
* @old_best: the previous 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. * @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 * This function gets a routing table update and announces it
* to all protocols that acccepts given type of route announcement * to all protocols that acccepts given type of route announcement
@ -897,7 +932,9 @@ rte_validate(rte *e)
return 0; 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)) if ((c < 0) || !(c & IADDR_HOST) || ((c & IADDR_SCOPE_MASK) <= SCOPE_LINK))
{ {
log(L_WARN "Ignoring bogus route %N received via %s", 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) if (new)
new->lastmod = now; new->lastmod = current_time();
/* Log the route change */ /* Log the route change */
if (p->debug & D_ROUTES) 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 && if (!net->routes &&
(table->gc_counter++ >= table->config->gc_max_ops) && (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); rt_schedule_prune(table);
if (old_ok && p->rte_remove) 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 *p = c->proto;
struct proto_stats *stats = &c->stats; struct proto_stats *stats = &c->stats;
struct filter *filter = c->in_filter; struct filter *filter = c->in_filter;
ea_list *tmpa = NULL;
rte *dummy = NULL; rte *dummy = NULL;
net *nn; net *nn;
@ -1334,11 +1370,11 @@ rte_update2(struct channel *c, const net_addr *n, rte *new, struct rte_src *src)
} }
else else
{ {
tmpa = rte_make_tmp_attrs(new, rte_update_pool); rte_make_tmp_attrs(&new, rte_update_pool);
if (filter && (filter != FILTER_REJECT)) if (filter && (filter != FILTER_REJECT))
{ {
ea_list *old_tmpa = tmpa; ea_list *oldea = new->attrs->eattrs;
int fr = f_run(filter, &new, &tmpa, rte_update_pool, 0); int fr = f_run(filter, &new, rte_update_pool, 0);
if (fr > F_ACCEPT) if (fr > F_ACCEPT)
{ {
stats->imp_updates_filtered++; 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; new->flags |= REF_FILTERED;
} }
if (tmpa != old_tmpa && src->proto->store_tmp_attrs) if (new->attrs->eattrs != oldea && src->proto->store_tmp_attrs)
src->proto->store_tmp_attrs(new, tmpa); src->proto->store_tmp_attrs(new);
} }
} }
if (!rta_is_cached(new->attrs)) /* Need to copy attributes */ if (!rta_is_cached(new->attrs)) /* Need to copy attributes */
@ -1384,7 +1420,7 @@ rte_update2(struct channel *c, const net_addr *n, rte *new, struct rte_src *src)
/* Independent call to rte_announce(), used from next hop /* Independent call to rte_announce(), used from next hop
recalculation, outside of rte_update(). new must be non-NULL */ recalculation, outside of rte_update(). new must be non-NULL */
static inline void static inline void
rte_announce_i(rtable *tab, unsigned type, net *net, rte *new, rte *old, rte_announce_i(rtable *tab, unsigned type, net *net, rte *new, rte *old,
rte *new_best, rte *old_best) rte *new_best, rte *old_best)
{ {
@ -1401,6 +1437,28 @@ rte_discard(rte *old) /* Non-filtered route deletion, used during garbage collec
rte_update_unlock(); 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 */ /* Check rtable for best route to given net whether it would be exported do p */
int int
rt_examine(rtable *t, net_addr *a, struct proto *p, struct filter *filter) 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(); rte_update_lock();
/* Rest is stripped down export_filter() */ /* Rest is stripped down export_filter() */
ea_list *tmpa = rte_make_tmp_attrs(rt, rte_update_pool); rte_make_tmp_attrs(&rt, rte_update_pool);
int v = p->import_control ? p->import_control(p, &rt, &tmpa, rte_update_pool) : 0; int v = p->import_control ? p->import_control(p, &rt, rte_update_pool) : 0;
if (v == RIC_PROCESS) 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 */ /* Discard temporary rte */
if (rt != n->routes) if (rt != n->routes)
@ -1485,6 +1543,26 @@ rt_refresh_end(rtable *t, struct channel *c)
rt_schedule_prune(t); 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 * rte_dump - dump a route
@ -1497,7 +1575,7 @@ rte_dump(rte *e)
{ {
net *n = e->net; net *n = e->net;
debug("%-1N ", n->n.addr); 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); rta_dump(e->attrs);
if (e->attrs->src->proto->proto->dump_attrs) if (e->attrs->src->proto->proto->dump_attrs)
e->attrs->src->proto->proto->dump_attrs(e); e->attrs->src->proto->proto->dump_attrs(e);
@ -1595,22 +1673,19 @@ rt_event(void *ptr)
} }
void 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)); bzero(t, sizeof(*t));
t->name = name; t->name = cf->name;
t->config = cf; 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); fib_init(&t->fib, p, t->addr_type, sizeof(net), OFFSETOF(net, n), 0, NULL);
init_list(&t->channels); init_list(&t->channels);
if (cf) t->rt_event = ev_new(p);
{ t->rt_event->hook = rt_event;
t->rt_event = ev_new(p); t->rt_event->data = t;
t->rt_event->hook = rt_event; t->gc_time = current_time();
t->rt_event->data = t;
t->gc_time = now;
}
} }
/** /**
@ -1679,6 +1754,7 @@ again:
rescan: rescan:
for (e=n->routes; e; e=e->next) for (e=n->routes; e; e=e->next)
{
if (e->sender->flush_active || (e->flags & REF_DISCARD)) if (e->sender->flush_active || (e->flags & REF_DISCARD))
{ {
if (limit <= 0) if (limit <= 0)
@ -1694,6 +1770,22 @@ again:
goto rescan; 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 */ if (!n->routes) /* Orphaned FIB entry */
{ {
FIB_ITERATE_PUT(fit); FIB_ITERATE_PUT(fit);
@ -1708,7 +1800,7 @@ again:
#endif #endif
tab->gc_counter = 0; tab->gc_counter = 0;
tab->gc_time = now; tab->gc_time = current_time();
/* state change 2->0, 3->1 */ /* state change 2->0, 3->1 */
tab->prune_state &= 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 * rt_commit - commit new routing table configuration
* @new: new configuration * @new: new configuration
@ -2058,11 +2157,10 @@ rt_commit(struct config *new, struct config *old)
rtable *ot = o->table; rtable *ot = o->table;
if (!ot->deleted) if (!ot->deleted)
{ {
struct symbol *sym = cf_find_symbol(new, o->name); r = rt_find_table_config(new, o->name);
if (sym && sym->class == SYM_TABLE && !new->shutdown) if (r && (r->addr_type == o->addr_type) && !new->shutdown)
{ {
DBG("\t%s: same\n", o->name); DBG("\t%s: same\n", o->name);
r = sym->def;
r->table = ot; r->table = ot;
ot->name = r->name; ot->name = r->name;
ot->config = r; 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)); rtable *t = mb_alloc(rt_table_pool, sizeof(struct rtable));
DBG("\t%s: created\n", r->name); 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); add_tail(&routing_tables, &t->n);
r->table = t; 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 static inline u32
hc_hash(ip_addr a, rtable *dep) hc_hash(ip_addr a, rtable *dep)
{ {
@ -2356,7 +2447,7 @@ if_local_addr(ip_addr a, struct iface *i)
return 0; return 0;
} }
static u32 static u32
rt_get_igp_metric(rte *rt) rt_get_igp_metric(rte *rt)
{ {
eattr *ea = ea_find(rt->attrs->eattrs, EA_GEN_IGP_METRIC); eattr *ea = ea_find(rt->attrs->eattrs, EA_GEN_IGP_METRIC);
@ -2388,12 +2479,13 @@ static int
rt_update_hostentry(rtable *tab, struct hostentry *he) rt_update_hostentry(rtable *tab, struct hostentry *he)
{ {
rta *old_src = he->src; rta *old_src = he->src;
int direct = 0;
int pxlen = 0; int pxlen = 0;
/* Reset the hostentry */ /* Reset the hostentry */
he->src = NULL; he->src = NULL;
he->nexthop_linkable = 0;
he->dest = RTD_UNREACHABLE; he->dest = RTD_UNREACHABLE;
he->nexthop_linkable = 0;
he->igp_metric = 0; he->igp_metric = 0;
net_addr he_addr; net_addr he_addr;
@ -2413,9 +2505,7 @@ rt_update_hostentry(rtable *tab, struct hostentry *he)
goto done; goto done;
} }
he->dest = a->dest; if (a->dest == RTD_UNICAST)
he->nexthop_linkable = 1;
if (he->dest == RTD_UNICAST)
{ {
for (struct nexthop *nh = &(a->nh); nh; nh = nh->next) for (struct nexthop *nh = &(a->nh); nh; nh = nh->next)
if (ipa_zero(nh->gw)) if (ipa_zero(nh->gw))
@ -2428,12 +2518,13 @@ rt_update_hostentry(rtable *tab, struct hostentry *he)
goto done; goto done;
} }
he->nexthop_linkable = 0; direct++;
break;
} }
} }
he->src = rta_clone(a); he->src = rta_clone(a);
he->dest = a->dest;
he->nexthop_linkable = !direct;
he->igp_metric = rt_get_igp_metric(e); he->igp_metric = rt_get_igp_metric(e);
} }

View File

File diff suppressed because it is too large Load Diff

View File

@ -2,6 +2,8 @@
* BIRD -- The Babel protocol * BIRD -- The Babel protocol
* *
* Copyright (c) 2015--2016 Toke Hoiland-Jorgensen * 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. * Can be freely distributed and used under the terms of the GNU GPL.
* *
@ -21,10 +23,10 @@
#include "lib/lists.h" #include "lib/lists.h"
#include "lib/socket.h" #include "lib/socket.h"
#include "lib/string.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_METRIC EA_CODE(PROTOCOL_BABEL, 0)
#define EA_BABEL_ROUTER_ID EA_CODE(EAP_BABEL, 1) #define EA_BABEL_ROUTER_ID EA_CODE(PROTOCOL_BABEL, 1)
#define BABEL_MAGIC 42 #define BABEL_MAGIC 42
#define BABEL_VERSION 2 #define BABEL_VERSION 2
@ -32,25 +34,28 @@
#define BABEL_INFINITY 0xFFFF #define BABEL_INFINITY 0xFFFF
#define BABEL_HELLO_INTERVAL_WIRED 4 /* Default hello intervals in seconds */ #define BABEL_HELLO_INTERVAL_WIRED (4 S_) /* Default hello intervals in seconds */
#define BABEL_HELLO_INTERVAL_WIRELESS 4 #define BABEL_HELLO_INTERVAL_WIRELESS (4 S_)
#define BABEL_HELLO_LIMIT 12
#define BABEL_UPDATE_INTERVAL_FACTOR 4 #define BABEL_UPDATE_INTERVAL_FACTOR 4
#define BABEL_IHU_INTERVAL_FACTOR 3 #define BABEL_IHU_INTERVAL_FACTOR 3
#define BABEL_IHU_EXPIRY_FACTOR(X) ((X)*3/2) /* 1.5 */ #define BABEL_HOLD_TIME_FACTOR 4 /* How long we keep unreachable route relative to update interval */
#define BABEL_HELLO_EXPIRY_FACTOR(X) ((X)*3/2) /* 1.5 */ #define BABEL_IHU_EXPIRY_FACTOR(X) ((btime)(X)*7/2) /* 3.5 */
#define BABEL_ROUTE_EXPIRY_FACTOR(X) ((X)*7/2) /* 3.5 */ #define BABEL_HELLO_EXPIRY_FACTOR(X) ((btime)(X)*3/2) /* 1.5 */
#define BABEL_ROUTE_REFRESH_INTERVAL 2 /* Seconds before route expiry to send route request */ #define BABEL_ROUTE_EXPIRY_FACTOR(X) ((btime)(X)*7/2) /* 3.5 */
#define BABEL_HOLD_TIME 10 /* Expiry time for our own routes */ #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_WIRED 96
#define BABEL_RXCOST_WIRELESS 256 #define BABEL_RXCOST_WIRELESS 256
#define BABEL_INITIAL_HOP_COUNT 255 #define BABEL_INITIAL_HOP_COUNT 255
#define BABEL_MAX_SEND_INTERVAL 5 #define BABEL_MAX_SEND_INTERVAL 5 /* Unused ? */
#define BABEL_TIME_UNITS 100 /* On-wire times are counted in centiseconds */
#define BABEL_SEQNO_REQUEST_EXPIRY 60
#define BABEL_GARBAGE_INTERVAL 300
/* Max interval that will not overflow when carried as 16-bit centiseconds */ /* 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_OVERHEAD (IP6_HEADER_LENGTH+UDP_HEADER_LENGTH)
#define BABEL_MIN_MTU (512 + BABEL_OVERHEAD) #define BABEL_MIN_MTU (512 + BABEL_OVERHEAD)
@ -80,7 +85,10 @@ enum babel_tlv_type {
enum babel_subtlv_type { enum babel_subtlv_type {
BABEL_SUBTLV_PAD1 = 0, BABEL_SUBTLV_PAD1 = 0,
BABEL_SUBTLV_PADN = 1 BABEL_SUBTLV_PADN = 1,
/* Mandatory subtlvs */
BABEL_SUBTLV_SOURCE_PREFIX = 128,
}; };
enum babel_iface_type { enum babel_iface_type {
@ -102,8 +110,12 @@ enum babel_ae_type {
struct babel_config { struct babel_config {
struct proto_config c; 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 { struct babel_iface_config {
@ -111,11 +123,12 @@ struct babel_iface_config {
u16 rxcost; u16 rxcost;
u8 type; u8 type;
u8 limit; /* Minimum number of Hellos to keep link up */
u8 check_link; u8 check_link;
uint port; uint port;
u16 hello_interval; uint hello_interval; /* Hello interval, in us */
u16 ihu_interval; uint ihu_interval; /* IHU interval, in us */
u16 update_interval; uint update_interval; /* Update interval, in us */
u16 rx_buffer; /* RX buffer size, 0 for MTU */ u16 rx_buffer; /* RX buffer size, 0 for MTU */
u16 tx_length; /* TX packet length limit (including headers), 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) */ list interfaces; /* Interfaces we really know about (struct babel_iface) */
u64 router_id; u64 router_id;
u16 update_seqno; /* To be increased on request */ u16 update_seqno; /* To be increased on request */
u8 update_seqno_inc; /* Request for update_seqno increase */
u8 triggered; /* For triggering global updates */ u8 triggered; /* For triggering global updates */
slab *route_slab; slab *route_slab;
slab *source_slab; slab *source_slab;
slab *msg_slab; slab *msg_slab;
slab *seqno_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 */ 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 */ u16 hello_seqno; /* To be increased on each hello */
bird_clock_t next_hello; btime next_hello;
bird_clock_t next_regular; btime next_regular;
bird_clock_t next_triggered; btime next_triggered;
bird_clock_t want_triggered; btime want_triggered;
timer *timer; timer *timer;
event *send_event; event *send_event;
@ -186,13 +198,18 @@ struct babel_neighbor {
struct babel_iface *ifa; struct babel_iface *ifa;
ip_addr addr; 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; u8 hello_cnt;
u16 hello_map; u16 hello_map;
u16 next_hello_seqno; u16 next_hello_seqno;
uint last_hello_int;
/* expiry timers */ /* expiry timers */
bird_clock_t hello_expiry; btime hello_expiry;
bird_clock_t ihu_expiry; btime ihu_expiry;
list routes; /* Routes this neighbour has sent us (struct babel_route) */ list routes; /* Routes this neighbour has sent us (struct babel_route) */
}; };
@ -203,7 +220,7 @@ struct babel_source {
u64 router_id; u64 router_id;
u16 seqno; u16 seqno;
u16 metric; u16 metric;
bird_clock_t expires; btime expires;
}; };
struct babel_route { struct babel_route {
@ -212,37 +229,46 @@ struct babel_route {
struct babel_entry *e; struct babel_entry *e;
struct babel_neighbor *neigh; struct babel_neighbor *neigh;
u8 feasible;
u16 seqno; u16 seqno;
u16 advert_metric;
u16 metric; u16 metric;
u16 advert_metric;
u64 router_id; u64 router_id;
ip_addr next_hop; ip_addr next_hop;
bird_clock_t refresh_time; btime refresh_time;
bird_clock_t expires; btime expires;
u16 expiry_interval; };
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_entry {
struct babel_proto *proto; struct babel_route *selected;
struct babel_route *selected_in;
struct babel_route *selected_out;
bird_clock_t updated;
list sources; /* Source entries for this prefix (struct babel_source). */
list routes; /* Routes for this prefix (struct babel_route) */ 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; struct fib_node n;
}; };
/* Stores forwarded seqno requests for duplicate suppression. */ #define BABEL_ENTRY_DUMMY 0 /* No outgoing route */
struct babel_seqno_request { #define BABEL_ENTRY_VALID 1 /* Valid outgoing route */
node n; #define BABEL_ENTRY_STALE 2 /* Stale outgoing route, waiting for GC */
net_addr net;
u64 router_id;
u16 seqno;
bird_clock_t updated;
};
/* /*
@ -252,7 +278,7 @@ struct babel_seqno_request {
struct babel_msg_ack_req { struct babel_msg_ack_req {
u8 type; u8 type;
u16 nonce; u16 nonce;
u16 interval; uint interval;
ip_addr sender; ip_addr sender;
}; };
@ -264,7 +290,7 @@ struct babel_msg_ack {
struct babel_msg_hello { struct babel_msg_hello {
u8 type; u8 type;
u16 seqno; u16 seqno;
u16 interval; uint interval;
ip_addr sender; ip_addr sender;
}; };
@ -272,7 +298,7 @@ struct babel_msg_ihu {
u8 type; u8 type;
u8 ae; u8 ae;
u16 rxcost; u16 rxcost;
u16 interval; uint interval;
ip_addr addr; ip_addr addr;
ip_addr sender; ip_addr sender;
}; };
@ -280,11 +306,14 @@ struct babel_msg_ihu {
struct babel_msg_update { struct babel_msg_update {
u8 type; u8 type;
u8 wildcard; u8 wildcard;
u16 interval; uint interval;
u16 seqno; u16 seqno;
u16 metric; u16 metric;
u64 router_id; u64 router_id;
net_addr net; union {
net_addr net;
net_addr_ip6_sadr net_sadr;
};
ip_addr next_hop; ip_addr next_hop;
ip_addr sender; ip_addr sender;
}; };
@ -292,7 +321,10 @@ struct babel_msg_update {
struct babel_msg_route_request { struct babel_msg_route_request {
u8 type; u8 type;
u8 full; u8 full;
net_addr net; union {
net_addr net;
net_addr_ip6_sadr net_sadr;
};
}; };
struct babel_msg_seqno_request { struct babel_msg_seqno_request {
@ -300,7 +332,10 @@ struct babel_msg_seqno_request {
u8 hop_count; u8 hop_count;
u16 seqno; u16 seqno;
u64 router_id; u64 router_id;
net_addr net; union {
net_addr net;
net_addr_ip6_sadr net_sadr;
};
ip_addr sender; ip_addr sender;
}; };
@ -320,6 +355,8 @@ struct babel_msg_node {
union babel_msg msg; 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 */ /* babel.c */
void babel_handle_ack_req(union babel_msg *msg, struct babel_iface *ifa); 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_interfaces(struct proto *P, char *iff);
void babel_show_neighbors(struct proto *P, char *iff); void babel_show_neighbors(struct proto *P, char *iff);
void babel_show_entries(struct proto *P); void babel_show_entries(struct proto *P);
void babel_show_routes(struct proto *P);
/* packets.c */ /* packets.c */
void babel_enqueue(union babel_msg *msg, struct babel_iface *ifa); void babel_enqueue(union babel_msg *msg, struct babel_iface *ifa);

View File

@ -2,6 +2,8 @@
* BIRD -- Babel Configuration * BIRD -- Babel Configuration
* *
* Copyright (c) 2015-2016 Toke Hoiland-Jorgensen * 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. * Can be freely distributed and used under the terms of the GNU GPL.
*/ */
@ -20,24 +22,27 @@ CF_DEFINES
CF_DECLS CF_DECLS
CF_KEYWORDS(BABEL, METRIC, RXCOST, HELLO, UPDATE, INTERVAL, PORT, WIRED, CF_KEYWORDS(BABEL, INTERFACE, METRIC, RXCOST, HELLO, UPDATE, INTERVAL, PORT,
WIRELESS, RX, TX, BUFFER, LENGTH, CHECK, LINK, BABEL_METRIC, NEXT, HOP, TYPE, WIRED, WIRELESS, RX, TX, BUFFER, PRIORITY, LENGTH, CHECK, LINK,
IPV4, IPV6) NEXT, HOP, IPV4, IPV6, BABEL_METRIC, SHOW, INTERFACES, NEIGHBORS,
ENTRIES, RANDOMIZE, ROUTER, ID)
CF_GRAMMAR CF_GRAMMAR
CF_ADDTO(proto, babel_proto) proto: babel_proto ;
babel_proto_start: proto_start BABEL babel_proto_start: proto_start BABEL
{ {
this_proto = proto_config_new(&proto_babel, $1); this_proto = proto_config_new(&proto_babel, $1);
init_list(&BABEL_CFG->iface_list); init_list(&BABEL_CFG->iface_list);
BABEL_CFG->hold_time = 1 S_;
}; };
babel_proto_item: babel_proto_item:
proto_item proto_item
| proto_channel | proto_channel
| INTERFACE babel_iface | INTERFACE babel_iface
| RANDOMIZE ROUTER ID bool { BABEL_CFG->randomize_router_id = $4; }
; ;
babel_proto_opts: babel_proto_opts:
@ -56,6 +61,7 @@ babel_iface_start:
init_list(&this_ipatt->ipn_list); init_list(&this_ipatt->ipn_list);
BABEL_IFACE->port = BABEL_PORT; BABEL_IFACE->port = BABEL_PORT;
BABEL_IFACE->type = BABEL_IFACE_TYPE_WIRED; BABEL_IFACE->type = BABEL_IFACE_TYPE_WIRED;
BABEL_IFACE->limit = BABEL_HELLO_LIMIT;
BABEL_IFACE->tx_tos = IP_PREC_INTERNET_CONTROL; BABEL_IFACE->tx_tos = IP_PREC_INTERNET_CONTROL;
BABEL_IFACE->tx_priority = sk_priority_control; BABEL_IFACE->tx_priority = sk_priority_control;
BABEL_IFACE->check_link = 1; BABEL_IFACE->check_link = 1;
@ -83,16 +89,19 @@ babel_iface_finish:
if (!BABEL_IFACE->update_interval) if (!BABEL_IFACE->update_interval)
BABEL_IFACE->update_interval = MIN_(BABEL_IFACE->hello_interval*BABEL_UPDATE_INTERVAL_FACTOR, BABEL_MAX_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_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: babel_iface_item:
| PORT expr { BABEL_IFACE->port = $2; if (($2<1) || ($2>65535)) cf_error("Invalid port number"); } | 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"); } | 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"); } | LIMIT expr { BABEL_IFACE->limit = $2; if (($2<1) || ($2>16)) cf_error("Limit must be in range 1-16"); }
| UPDATE INTERVAL expr { BABEL_IFACE->update_interval = $3; if (($3<1) || ($3>BABEL_MAX_INTERVAL)) cf_error("Invalid update interval"); }
| TYPE WIRED { BABEL_IFACE->type = BABEL_IFACE_TYPE_WIRED; } | TYPE WIRED { BABEL_IFACE->type = BABEL_IFACE_TYPE_WIRED; }
| TYPE WIRELESS { BABEL_IFACE->type = BABEL_IFACE_TYPE_WIRELESS; } | 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"); } | 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 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; } | TX tos { BABEL_IFACE->tx_tos = $2; }
@ -116,7 +125,7 @@ babel_iface_opt_list:
babel_iface: babel_iface:
babel_iface_start iface_patt_list_nopx babel_iface_opt_list babel_iface_finish; 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]]); 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]]) CF_CLI(SHOW BABEL ENTRIES, optsym opttext, [<name>], [[Show information about Babel prefix entries]])
{ babel_show_entries(proto_get_named($4, &proto_babel)); }; { 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_CODE
CF_END CF_END

View File

@ -2,6 +2,8 @@
* BIRD -- The Babel protocol * BIRD -- The Babel protocol
* *
* Copyright (c) 2015--2016 Toke Hoiland-Jorgensen * 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. * 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 { struct babel_tlv_hello {
u8 type; u8 type;
u8 length; u8 length;
u16 reserved; u16 flags;
u16 seqno; u16 seqno;
u16 interval; u16 interval;
} PACKED; } PACKED;
@ -103,9 +105,20 @@ struct babel_tlv_seqno_request {
u8 addr[0]; u8 addr[0];
} PACKED; } 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 { struct babel_parse_state {
@ -121,6 +134,7 @@ struct babel_parse_state {
u8 def_ip6_prefix_seen; /* def_ip6_prefix is valid */ u8 def_ip6_prefix_seen; /* def_ip6_prefix is valid */
u8 def_ip4_prefix_seen; /* def_ip4_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 current_tlv_endpos; /* End of self-terminating TLVs (offset from start) */
u8 sadr_enabled;
}; };
enum parse_result { enum parse_result {
@ -162,17 +176,17 @@ bytes_equal(u8 *b1, u8 *b2, uint maxlen)
return i; return i;
} }
static inline u16 static inline uint
get_time16(const void *p) get_time16(const void *p)
{ {
u16 v = get_u16(p) / BABEL_TIME_UNITS; uint v = get_u16(p) * BABEL_TIME_UNITS;
return MAX(1, v); return MAX(BABEL_MIN_INTERVAL, v);
} }
static inline void 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 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_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_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_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_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); 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_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_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 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 { struct babel_tlv_data {
u8 min_length; 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_tlv_hello *tlv = (void *) hdr;
struct babel_msg_hello *msg = &m->hello; 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->type = BABEL_TLV_HELLO;
msg->seqno = get_u16(&tlv->seqno); msg->seqno = get_u16(&tlv->seqno);
msg->interval = get_time16(&tlv->interval); 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) if (tlv->omitted && !state->def_ip4_prefix_seen)
return PARSE_ERROR; return PARSE_ERROR;
/* Need next hop for v4 routes */ /* Update must have next hop, unless it is retraction */
if (ipa_zero(state->next_hop_ip4)) if (ipa_zero(state->next_hop_ip4) && (msg->metric != BABEL_INFINITY))
return PARSE_ERROR; return PARSE_IGNORE;
/* Merge saved prefix and received prefix parts */ /* Merge saved prefix and received prefix parts */
memcpy(buf, state->def_ip4_prefix, tlv->omitted); 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); ip4_addr prefix4 = get_ip4(buf);
net_fill_ip4(&msg->net, prefix4, tlv->plen); 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); put_ip4(state->def_ip4_prefix, prefix4);
state->def_ip4_prefix_seen = 1; 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); ip6_addr prefix6 = get_ip6(buf);
net_fill_ip6(&msg->net, prefix6, tlv->plen); 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); put_ip6(state->def_ip6_prefix, prefix6);
state->def_ip6_prefix_seen = 1; 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 = ((u64) _I2(prefix6)) << 32 | _I3(prefix6);
state->router_id_seen = 1; state->router_id_seen = 1;
@ -748,7 +772,7 @@ babel_write_update(struct babel_tlv *hdr, union babel_msg *m,
else else
{ {
put_ip6_px(tlv->addr, &msg->net); 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)); put_ip6(state->def_ip6_prefix, net6_prefix(&msg->net));
state->def_ip6_pxlen = tlv->plen; 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->seqno, msg->seqno);
put_u16(&tlv->metric, msg->metric); 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; return len0 + len;
} }
static int static int
babel_read_route_request(struct babel_tlv *hdr, union babel_msg *m, 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_tlv_route_request *tlv = (void *) hdr;
struct babel_msg_route_request *msg = &m->route_request; 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); read_ip6_px(&msg->net, tlv->addr, tlv->plen);
state->current_tlv_endpos += BYTES(tlv->plen); state->current_tlv_endpos += BYTES(tlv->plen);
if (state->sadr_enabled)
net_make_ip6_sadr(&msg->net);
return PARSE_SUCCESS; return PARSE_SUCCESS;
case BABEL_AE_IP6_LL: 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); 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; 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); read_ip6_px(&msg->net, tlv->addr, tlv->plen);
state->current_tlv_endpos += BYTES(tlv->plen); state->current_tlv_endpos += BYTES(tlv->plen);
if (state->sadr_enabled)
net_make_ip6_sadr(&msg->net);
return PARSE_SUCCESS; return PARSE_SUCCESS;
case BABEL_AE_IP6_LL: 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; tlv->hop_count = msg->hop_count;
put_u64(&tlv->router_id, msg->router_id); 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; 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 static inline int
babel_read_subtlvs(struct babel_tlv *hdr, babel_read_subtlvs(struct babel_tlv *hdr,
union babel_msg *msg UNUSED, union babel_msg *msg,
struct babel_parse_state *state) struct babel_parse_state *state)
{ {
struct babel_tlv *tlv; struct babel_tlv *tlv;
byte *pos, *end = (byte *) hdr + TLV_LENGTH(hdr);
int res;
for (tlv = (void *) hdr + state->current_tlv_endpos; for (tlv = (void *) hdr + state->current_tlv_endpos;
(void *) tlv < (void *) hdr + TLV_LENGTH(hdr); (byte *) tlv < end;
tlv = NEXT_TLV(tlv)) 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 * 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 * use a switch for dispatch instead of the mapping array we use for TLVs
*/ */
switch (tlv->type) switch (tlv->type)
{ {
case BABEL_SUBTLV_PAD1: case BABEL_SUBTLV_SOURCE_PREFIX:
case BABEL_SUBTLV_PADN: res = babel_read_source_prefix(tlv, msg, state);
/* FIXME: Framing errors in PADN are silently ignored, see babel_process_packet() */ if (res != PARSE_SUCCESS)
return res;
break; break;
case BABEL_SUBTLV_PADN:
default: default:
/* Unknown mandatory subtlv; PARSE_IGNORE ignores the whole TLV */ /* Unknown mandatory subtlv; PARSE_IGNORE ignores the whole TLV */
if (tlv->type > 128) if (tlv->type >= 128)
{
DBG("Babel: Mandatory subtlv %d found; skipping TLV\n", tlv->type);
return PARSE_IGNORE; return PARSE_IGNORE;
}
break; break;
} }
} }
@ -1186,6 +1345,7 @@ babel_process_packet(struct babel_pkt_header *pkt, int len,
.ifa = ifa, .ifa = ifa,
.saddr = saddr, .saddr = saddr,
.next_hop_ip6 = saddr, .next_hop_ip6 = saddr,
.sadr_enabled = babel_sadr_enabled(p),
}; };
if ((pkt->magic != BABEL_MAGIC) || (pkt->version != BABEL_VERSION)) 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); sk->iface->name, sk->faddr, sk->laddr);
/* Silently ignore my own packets */ /* Silently ignore my own packets */
if (ipa_equal(ifa->iface->addr->ip, sk->faddr)) if (ipa_equal(sk->faddr, sk->saddr))
return 1; return 1;
if (!ipa_is_link_local(sk->faddr)) if (!ipa_is_link_local(sk->faddr))
@ -1329,6 +1489,8 @@ babel_open_socket(struct babel_iface *ifa)
sk->sport = ifa->cf->port; sk->sport = ifa->cf->port;
sk->dport = ifa->cf->port; sk->dport = ifa->cf->port;
sk->iface = ifa->iface; sk->iface = ifa->iface;
sk->saddr = ifa->addr;
sk->vrf = p->p.vrf;
sk->rx_hook = babel_rx_hook; sk->rx_hook = babel_rx_hook;
sk->tx_hook = babel_tx_hook; sk->tx_hook = babel_tx_hook;

View File

@ -64,16 +64,15 @@
* ready, the protocol just creates a BFD request like any other protocol. * 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|, * The protocol uses a new generic event loop (structure &birdloop) from |io.c|,
* which supports sockets, timers and events like the main loop. Timers * which supports sockets, timers and events like the main loop. A birdloop is
* (structure &timer2) are new microsecond based timers, while sockets and * associated with a thread (field @thread) in which event hooks are executed.
* events are the same. A birdloop is associated with a thread (field @thread) * Most functions for setting event sources (like sk_start() or tm_start()) must
* in which event hooks are executed. Most functions for setting event sources * be called from the context of that thread. Birdloop allows to temporarily
* (like sk_start() or tm2_start()) must be called from the context of that * acquire the context of that thread for the main thread by calling
* thread. Birdloop allows to temporarily acquire the context of that thread for * birdloop_enter() and then birdloop_leave(), which also ensures mutual
* the main thread by calling birdloop_enter() and then birdloop_leave(), which * exclusion with all event hooks. Note that resources associated with a
* also ensures mutual exclusion with all event hooks. Note that resources * birdloop (like timers) should be attached to the independent resource pool,
* associated with a birdloop (like timers) should be attached to the * detached from the main resource tree.
* independent resource pool, detached from the main resource tree.
* *
* There are two kinds of interaction between the BFD core (running in the BFD * 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 * 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); bfd_lock_sessions(p);
s->loc_state = state; s->loc_state = state;
s->loc_diag = diag; s->loc_diag = diag;
s->last_state_change = current_time();
notify = !NODE_VALID(&s->n); notify = !NODE_VALID(&s->n);
if (notify) if (notify)
@ -176,7 +176,7 @@ bfd_session_update_tx_interval(struct bfd_session *s)
return; return;
/* Set timer relative to last tx_timer event */ /* 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 static void
@ -190,7 +190,7 @@ bfd_session_update_detection_time(struct bfd_session *s, int kick)
if (!s->last_rx) if (!s->last_rx)
return; return;
tm2_set(s->hold_timer, s->last_rx + timeout); tm_set(s->hold_timer, s->last_rx + timeout);
} }
static void static void
@ -211,16 +211,16 @@ bfd_session_control_tx_timer(struct bfd_session *s, int reset)
goto stop; goto stop;
/* So TX timer should run */ /* So TX timer should run */
if (reset || !tm2_active(s->tx_timer)) if (reset || !tm_active(s->tx_timer))
{ {
s->last_tx = 0; s->last_tx = 0;
tm2_start(s->tx_timer, 0); tm_start(s->tx_timer, 0);
} }
return; return;
stop: stop:
tm2_stop(s->tx_timer); tm_stop(s->tx_timer);
s->last_tx = 0; s->last_tx = 0;
} }
@ -379,7 +379,7 @@ bfd_find_session_by_addr(struct bfd_proto *p, ip_addr addr)
} }
static void static void
bfd_tx_timer_hook(timer2 *t) bfd_tx_timer_hook(timer *t)
{ {
struct bfd_session *s = t->data; struct bfd_session *s = t->data;
@ -388,7 +388,7 @@ bfd_tx_timer_hook(timer2 *t)
} }
static void static void
bfd_hold_timer_hook(timer2 *t) bfd_hold_timer_hook(timer *t)
{ {
bfd_session_timeout(t->data); 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->passive = ifa->cf->passive;
s->tx_csn = random_u32(); s->tx_csn = random_u32();
s->tx_timer = tm2_new_init(p->tpool, bfd_tx_timer_hook, s, 0, 0); s->tx_timer = tm_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->hold_timer = tm_new_init(p->tpool, bfd_hold_timer_hook, s, 0, 0);
bfd_session_update_tx_interval(s); bfd_session_update_tx_interval(s);
bfd_session_control_tx_timer(s, 1); bfd_session_control_tx_timer(s, 1);
init_list(&s->request_list); 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); 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; 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) if (!nb)
{ {
log(L_ERR "%s: Invalid remote address %I%J", p->p.name, n->addr, n->iface); 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; diag = s->loc_diag;
bfd_unlock_sessions(p); bfd_unlock_sessions(p);
/* FIXME: convert to btime and move to bfd_session_update_state() */
s->last_state_change = now;
s->notify_running = 1; s->notify_running = 1;
WALK_LIST_DELSAFE(n, nn, s->request_list) WALK_LIST_DELSAFE(n, nn, s->request_list)
bfd_request_notify(SKIP_BACK(struct bfd_request, n, n), state, diag); 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]; byte tbuf[TM_DATETIME_BUFFER_SIZE];
struct bfd_proto *p = (struct bfd_proto *) P; struct bfd_proto *p = (struct bfd_proto *) P;
uint state, diag UNUSED; uint state, diag UNUSED;
u32 tx_int, timeout; btime tx_int, timeout;
const char *ifname; const char *ifname;
if (p->p.proto_state != PS_UP) if (p->p.proto_state != PS_UP)
@ -1101,15 +1098,14 @@ bfd_show_sessions(struct proto *P)
state = s->loc_state; state = s->loc_state;
diag = s->loc_diag; diag = s->loc_diag;
ifname = (s->ifa && s->ifa->iface) ? s->ifa->iface->name : "---"; 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; tx_int = s->last_tx ? MAX(s->des_min_tx_int, s->rem_min_rx_int) : 0;
timeout = (MAX(s->req_min_rx_int, s->rem_min_tx_int) TO_MS) * s->rem_detect_mult; timeout = (btime) MAX(s->req_min_rx_int, s->rem_min_tx_int) * s->rem_detect_mult;
state = (state < 4) ? state : 0; 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", cli_msg(-1020, "%-25I %-10s %-10s %-10s %7t %7t",
s->addr, ifname, bfd_state_names[state], tbuf, s->addr, ifname, bfd_state_names[state], tbuf, tx_int, timeout);
tx_int / 1000, tx_int % 1000, timeout / 1000, timeout % 1000);
} }
HASH_WALK_END; HASH_WALK_END;
@ -1120,6 +1116,7 @@ bfd_show_sessions(struct proto *P)
struct protocol proto_bfd = { struct protocol proto_bfd = {
.name = "BFD", .name = "BFD",
.template = "bfd%d", .template = "bfd%d",
.class = PROTOCOL_BFD,
.proto_size = sizeof(struct bfd_proto), .proto_size = sizeof(struct bfd_proto),
.config_size = sizeof(struct bfd_config), .config_size = sizeof(struct bfd_config),
.init = bfd_init, .init = bfd_init,

View File

@ -140,11 +140,11 @@ struct bfd_session
btime last_tx; /* Time of last sent periodic control packet */ btime last_tx; /* Time of last sent periodic control packet */
btime last_rx; /* Time of last received valid control packet */ btime last_rx; /* Time of last received valid control packet */
timer2 *tx_timer; /* Periodic control packet timer */ timer *tx_timer; /* Periodic control packet timer */
timer2 *hold_timer; /* Timer for session down detection time */ timer *hold_timer; /* Timer for session down detection time */
list request_list; /* List of client requests (struct bfd_request) */ 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 notify_running; /* 1 if notify hooks are running */
u8 rx_csn_known; /* Received crypto sequence number is known */ u8 rx_csn_known; /* Received crypto sequence number is known */

Some files were not shown because too many files have changed in this diff Show More