summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.gitlab-ci.yml22
-rw-r--r--NEWS30
-rw-r--r--aclocal.m46
-rw-r--r--conf/cf-lex.l3
-rw-r--r--conf/confbase.Y2
-rw-r--r--configure.ac3
-rw-r--r--doc/bird.sgml527
-rw-r--r--filter/config.Y7
-rw-r--r--filter/f-inst.c11
-rw-r--r--filter/test.conf57
-rw-r--r--lib/strtoul.c2
-rw-r--r--misc/bird.spec2
-rw-r--r--nest/config.Y2
-rw-r--r--nest/mpls.Y6
-rw-r--r--nest/mpls.c11
-rw-r--r--nest/mpls.h12
-rw-r--r--nest/protocol.h2
-rw-r--r--nest/route.h3
-rw-r--r--nest/rt-attr.c2
-rw-r--r--nest/rt-table.c1
-rw-r--r--proto/bgp/attrs.c6
-rw-r--r--proto/bgp/bgp.c101
-rw-r--r--proto/bgp/bgp.h17
-rw-r--r--proto/bgp/config.Y29
-rw-r--r--proto/bgp/packets.c84
-rw-r--r--proto/l3vpn/l3vpn.c9
-rw-r--r--proto/ospf/rt.c7
-rw-r--r--proto/static/static.c4
-rw-r--r--sysdep/config.h2
-rw-r--r--sysdep/linux/netlink.c2
-rw-r--r--sysdep/unix/io.c6
-rw-r--r--sysdep/unix/krt.Y8
-rw-r--r--sysdep/unix/krt.c13
-rw-r--r--sysdep/unix/krt.h4
34 files changed, 858 insertions, 145 deletions
diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
index c913d0f8..43f517cb 100644
--- a/.gitlab-ci.yml
+++ b/.gitlab-ci.yml
@@ -4,8 +4,8 @@ variables:
GIT_STRATEGY: fetch
DOCKER_CMD: docker --config="$HOME/.docker/$CI_JOB_ID/"
IMG_BASE: registry.nic.cz/labs/bird
- TOOLS_DIR: /var/lib/gitlab-runner/bird-tools
- STAYRTR_BINARY: /var/lib/gitlab-runner/stayrtr-0.1-108-g8d18a41-linux-x86_64
+ TOOLS_DIR: /home/gitlab-runner/bird-tools
+ STAYRTR_BINARY: /usr/bin/stayrtr
stages:
# - image
@@ -309,10 +309,10 @@ pkg-opensuse-15.3-amd64:
image: registry.nic.cz/labs/bird:opensuse-15.3-amd64
-build-birdlab:
+build-netlab:
stage: build
tags:
- - birdlab
+ - netlab
- amd64
script:
- DIR=$(pwd)
@@ -327,9 +327,9 @@ build-birdlab:
.test: &test-base
stage: test
- needs: [build-birdlab]
+ needs: [build-netlab]
tags:
- - birdlab
+ - netlab
- amd64
script:
- cd $TOOLS_DIR/netlab
@@ -476,6 +476,16 @@ test-kernel-learn:
variables:
TEST_NAME: cf-kernel-learn
+test-mpls-bgp:
+ <<: *test-base
+ variables:
+ TEST_NAME: cf-mpls-bgp
+
+test-mpls-bgp-l3vpn:
+ <<: *test-base
+ variables:
+ TEST_NAME: cf-mpls-bgp-l3vpn
+
.build-birdlab-base: &build-birdlab-base
stage: build
diff --git a/NEWS b/NEWS
index 30f3b932..d05bfe68 100644
--- a/NEWS
+++ b/NEWS
@@ -1,3 +1,33 @@
+Version 2.14 (2023-10-06)
+ o MPLS subsystem
+ o L3VPN: BGP/MPLS VPNs (RFC 4364)
+ o BGP: Access to unknown route attributes
+ o RAdv: Custom options
+ o Babel: RTT metric extension
+ o BMP: Refactored route monitoring
+ o BMP: Multiple instances of BMP protocol
+ o BMP: Both pre-policy and post-policy monitoring
+ o Experimental route aggregation
+ o Filter: Method framework
+ o Filter: Functions have return type statements
+ o Filter: New bytestring data type
+ o Kernel: Option to learn kernel routes
+ o Many bugfixes and improvements
+
+ Notes:
+
+ User-defined filter functions that return values now should have return type
+ statements. We still accept functions without such statement, if they could be
+ properly typed.
+
+ For loops allowed to use both existing iterator variables or ones defined in
+ the for statement. We no longer support the first case, all iterator variables
+ must be defined in the for statement (e.g. 'for int i in bgp_path ...').
+
+ Due to oversight, VRF interfaces were not included in respective VRFs, this is
+ fixed now.
+
+
Version 2.13.1 (2023-06-23)
o BGP: Fix role check when no capability option is present
o Filter: Fixed segfault when a case option had an empty block
diff --git a/aclocal.m4 b/aclocal.m4
index 1613d680..3fdc446f 100644
--- a/aclocal.m4
+++ b/aclocal.m4
@@ -201,6 +201,12 @@ AC_DEFUN([BIRD_ADD_GCC_OPTION],
fi
])
+AC_DEFUN([BIRD_CHECK_AND_ADD_GCC_OPTION],
+[
+ BIRD_CHECK_GCC_OPTION($@)
+ BIRD_ADD_GCC_OPTION($1,$2)
+])
+
# BIRD_CHECK_PROG_FLAVOR_GNU(PROGRAM-PATH, IF-SUCCESS, [IF-FAILURE])
# copied from autoconf internal _AC_PATH_PROG_FLAVOR_GNU
AC_DEFUN([BIRD_CHECK_PROG_FLAVOR_GNU],
diff --git a/conf/cf-lex.l b/conf/cf-lex.l
index 5fb88e03..2f95f2e1 100644
--- a/conf/cf-lex.l
+++ b/conf/cf-lex.l
@@ -242,7 +242,7 @@ WHITE [ \t]
return IP4;
}
-({XIGIT}{2}){16,}|{XIGIT}{2}(:{XIGIT}{2}){15,}|hex:({XIGIT}{2}(:?{XIGIT}{2})*)? {
+({XIGIT}{2}){16,}|{XIGIT}{2}(:{XIGIT}{2}){15,}|hex:({XIGIT}{2}*|{XIGIT}{2}(:{XIGIT}{2})*) {
char *s = yytext;
struct adata *bs;
@@ -739,6 +739,7 @@ cf_lex_init(int is_cli, struct config *c)
BEGIN(INITIAL);
c->root_scope = c->current_scope = cfg_allocz(sizeof(struct sym_scope));
+ c->root_scope->active = 1;
if (is_cli)
c->current_scope->next = config->root_scope;
diff --git a/conf/confbase.Y b/conf/confbase.Y
index 2c37bd4d..b2471198 100644
--- a/conf/confbase.Y
+++ b/conf/confbase.Y
@@ -294,7 +294,7 @@ net_roa6_: net_ip6_ MAX NUM AS NUM
net_mpls_: MPLS NUM
{
- $$ = cfg_alloc(sizeof(net_addr_roa6));
+ $$ = cfg_alloc(sizeof(net_addr_mpls));
net_fill_mpls($$, $2);
}
diff --git a/configure.ac b/configure.ac
index b2f18c90..0ed4d2d7 100644
--- a/configure.ac
+++ b/configure.ac
@@ -393,8 +393,7 @@ if test "$enable_debug" = yes ; then
LDFLAGS="$LDFLAGS -rdynamic"
CFLAGS="$CFLAGS -O0 -ggdb -g3"
- BIRD_CHECK_GCC_OPTION([bird_cv_c_option_dwarf4], [-gdwarf-4], [])
- BIRD_ADD_GCC_OPTION([bird_cv_c_option_dwarf4], [-gdwarf-4])
+ BIRD_CHECK_AND_ADD_GCC_OPTION([bird_cv_c_option_dwarf4], [-gdwarf-4], [])
AC_CHECK_HEADER([execinfo.h],
[
diff --git a/doc/bird.sgml b/doc/bird.sgml
index 394ca86c..419a4537 100644
--- a/doc/bird.sgml
+++ b/doc/bird.sgml
@@ -341,10 +341,9 @@ Configuration keywords are <cf/flow4/ and <cf/flow6/.
<sect1>MPLS switching rules
<label id="mpls-routes">
-<p>This nettype is currently a stub before implementing more support of <rfc id="3031">.
-BIRD currently does not support any label distribution protocol nor any label assignment method.
-Only the Kernel, Pipe and Static protocols can use MPLS tables.
-Configuration keyword is <cf/mpls/.
+<p>MPLS routes control MPLS forwarding in the same way as IP routes control IP
+forwarding. MPLS-aware routing protocols produce both labeled IP routes and
+corresponding MPLS routes. Configuration keyword is <cf/mpls/.
<itemize>
<item>(PK) MPLS label
@@ -403,6 +402,79 @@ regular <ref id="cli-down" name="down"> command. In this way routing neighbors
are notified about planned graceful restart and routes are kept in kernel table
after shutdown.
+<sect>MPLS
+<label id="mpls">
+
+<p>Multiprotocol Label Switching (MPLS) is a networking technology which works
+below IP routing but above the link (e.g. ethernet) layer. It is described in
+<rfc id="3031">.
+
+In regular IP forwarding, the destination address of a packet is independently
+examined in each hop, a route with longest prefix match is selected from the
+routing table, and packet is processed accordingly. In general, there is no
+difference between border routers and internal routers w.r.t. IP forwarding.
+
+In MPLS forwarding, when a packet enters the network, it is classified (based on
+destination address, ingress interface and other factors) into one of forwarding
+equivalence classes (FECs), then a header with a MPLS label identifying the FEC
+is attached to it, and the packet is forwarded. In internal routers, only the
+MPLS label is examined, the matching MPLS route is selected from the MPLS
+routing table, and the packet is processed accordingly. The specific value of
+MPLS label has local meaning only and may change between hops (that is why it is
+called label switching). When the packet leaves the network, the MPLS header is
+removed.
+
+The advantage of the MPLS approach is that other factors than the destination
+address can be considered and used consistently in the whole network, for
+example IP traffic with multiple overlapping private address ranges could be
+mixed together, or particular paths for specific flows could be defined. Another
+advantage is that MPLS forwarding by internal routers can be much simpler than
+IP forwarding, as instead of the longest prefix match algorithm it uses simpler
+exact match for MPLS route selection. The disadvantage is additional complexity
+in signaling. For further details, see <rfc id="3031">.
+
+MPLS-aware routing protocols not only distribute IP routing information, but
+they also distribute labels. Therefore, they produce labeled routes - routes
+representing label switched paths (LSPs) through the MPLS domain. Such routes
+have IP prefix and next hop address like regular (non-labeled) routes, but they
+also have local MPLS label (in route attribute <ref id="rta-mpls-label"
+name="mpls_label">) and outgoing MPLS label (as a part of the next hop). They
+are stored in regular IP routing tables.
+
+Labeled routes are used for exchange of routing information between routing
+protocols and for ingress (IP -&gt; MPLS) forwarding, but they are not directly
+used for MPLS forwarding. For that purpose <ref id="mpls-routes" name="MPLS
+routes"> are used. These are routes that have local MPLS label as a primary key
+and they are stored in the MPLS routing table.
+
+In BIRD, the whole process generally works this way: A MPLS-aware routing
+protocol (say BGP) receives routing information including remote label. It
+produces a route with attribute <ref id="rta-mpls-policy" name="mpls_policy">
+specifying desired <ref id="mpls-channel-label-policy" name="MPLS label policy">.
+Such route then passes the import filter (which could modify the MPLS label
+policy or perhaps assign a static label) and when it is accepted, a local MPLS
+label is selected (according to the label policy) and attached to the route,
+producing labeled route. When a new MPLS label is allocated, the MPLS-aware
+protocol automatically produces corresponding MPLS route. When all labeled
+routes that use specific local MPLS label are retracted, the corresponding MPLS
+route is retracted too.
+
+There are three important concepts for MPLS in BIRD: MPLS domains, MPLS tables
+and MPLS channels. MPLS domain represents an independent label space, all
+MPLS-aware protocols are associated with some MPLS domain. It is responsible for
+label management, handling label allocation requests from MPLS-aware protocols.
+MPLS table is just a routing table for MPLS routes. Routers usually have one
+MPLS domain and one MPLS table, with Kernel protocol to export MPLS routes into
+kernel FIB.
+
+MPLS channels make protocols MPLS-aware, they are responsible for keeping track
+of active FECs (and corresponding allocated labels), selecting FECs / local
+labels for labeled routes, and maintaining correspondence between labeled routes
+and MPLS routes.
+
+Note that local labels are allocated to individual MPLS-aware protocols and
+therefore it is not possible to share local labels between different protocols.
+
<chapt>Configuration
<label id="config">
@@ -631,6 +703,12 @@ include "tablename.conf";;
defined by this option. See the <ref id="rtable-opts"
name="routing table configuration section"> for routing table options.
+ <tag><label id="opt-mpls-domain">mpls domain <m/name/ [ { <m/option/; [<m/.../] } ]</tag>
+ Define a new MPLS domain. MPLS domains represent independent label
+ spaces and are responsible for MPLS label management. All MPLS-aware
+ protocols are associated with some MPLS domain. See the <ref id="mpls-opts"
+ name="MPLS configuration section"> for MPLS domain options.
+
<tag><label id="opt-eval">eval <m/expr/</tag>
Evaluates given filter expression. It is used by the developers for testing of filters.
</descrip>
@@ -941,7 +1019,7 @@ inherited from templates can be updated by new definitions.
<tag><label id="proto-export">export <m/filter/</tag>
This is similar to the <cf>import</cf> keyword, except that it works in
the direction from the routing table to the protocol. Default: <cf/none/
- (except for EBGP).
+ (except for EBGP and L3VPN).
<tag><label id="proto-import-keep-filtered">import keep filtered <m/switch/</tag>
Usually, if an import filter rejects a route, the route is forgotten.
@@ -1043,6 +1121,100 @@ protocol bgp from {
</code>
+<sect>MPLS options
+<label id="mpls-opts">
+
+<p>The MPLS domain definition is mandatory for a MPLS router. All MPLS channels
+and MPLS-aware protocols are associated with some MPLS domain (although usually
+implicitly with the sole one). In the MPLS domain definition you can configure
+details of MPLS label allocation. Currently, there is just one option,
+<cf/label range/.
+
+<p>Note that the MPLS subsystem is experimental, it is likely that there will be
+some backward-incompatible changes in the future.
+
+<descrip>
+ <tag><label id="mpls-domain-label-range">label range <m/name/ { start <m/number/; length <m/number/; [<m/.../] }</tag>
+ Define a new label range, or redefine implicit label ranges <cf/static/
+ and <cf/dynamic/. MPLS channels use configured label ranges for dynamic
+ label allocation, while <cf/static/ label range is used for static label
+ allocation. The label range definition must specify the extent of the
+ range. By default, the range <cf/static/ is 16-1000, while the range
+ <cf/dynamic/ is 1000-10000.
+</descrip>
+
+<p>MPLS channel should be defined in each MPLS-aware protocol in addition to its
+regular channels. It is responsible for label allocation and for announcing MPLS
+routes to the MPLS routing table. Besides common <ref id="channel-opts"
+name="channel options">, MPLS channels have some specific options:
+
+<descrip>
+ <tag><label id="mpls-channel-domain">domain <m/name/</tag>
+ Specify a MPLS domain to which this channel and protocol belongs.
+ Default: The first defined MPLS domain.
+
+ <tag><label id="mpls-channel-label-range">label range <m/name/</tag>
+ Use specific label range for dynamic label allocation. Note that static
+ labels always use the range <cf/static/. Default: the range <cf/dynamic/.
+
+ <tag><label id="mpls-channel-label-policy">label policy static|prefix|aggregate|vrf</tag>
+ Label policy specifies how routes are grouped to forwarding equivalence
+ classes (FECs) and how labels are assigned to them.
+
+ The policy <cf/static/ means no dynamic label allocation is done, and
+ static labels must be set in import filters using the route attribute
+ <ref id="rta-mpls-label" name="mpls_label">.
+
+ The policy <cf/prefix/ means each prefix uses separate label associated
+ with that prefix. When a labeled route is updated, it keeps the label.
+ This policy is appropriate for IGPs.
+
+ The policy <cf/aggregate/ means routes are grouped to FECs according to
+ their next hops (including next hop labels), and one label is used for
+ all routes in the same FEC. When a labeled route is updated, it may
+ change next hop, change FEC and therefore change label. This policy is
+ appropriate for BGP.
+
+ The policy <cf/vrf/ is only valid in L3VPN protocols. It uses one label
+ for all routes from a VRF, while replacing the original next hop with
+ lookup in the VRF.
+
+ Default: <cf/prefix/.
+</descrip>
+
+<p>This is a trivial example of MPLS setup:
+<code>
+mpls domain mdom {
+ label range bgprange { start 2000; length 1000; };
+}
+
+mpls table mtab;
+
+protocol static {
+ ipv6;
+ mpls;
+
+ route 2001:db8:1:1/64 mpls 100 via 2001:db8:1:2::1/64 mpls 200;
+}
+
+protocol bgp {
+ # regular channels
+ ipv6 mpls { ... };
+ vpn6 mpls { ... };
+
+ # MPLS channel
+ mpls {
+ # domain mdom;
+ # table mtab;
+ label range bgprange;
+ label policy aggregate;
+ };
+
+ ...
+}
+</code>
+
+
<chapt>Remote control
<label id="remote-control">
@@ -1404,22 +1576,18 @@ in the foot).
This is a sequences of arbitrary bytes. There are no ways to modify
bytestrings in filters. You can pass them between function, assign
them to variables of type <cf/bytestring/, print such values,
- compare bytestings (<cf/=, !=/). Bytestring literals are written
- in several ways:
-
- A sequence of hexadecimal digit pairs, optionally colon-separated.
- A bytestring specified this way must be at least 16 bytes (32 digits)
- long: <cf/01:23:45:67:89:ab:cd:ef:01:23:45:67:89:ab:cd:ef/ or
- <cf/0123456789abcdef0123456789abcdef/.
+ compare bytestings (<cf/=, !=/).
- A sequence of hexadecimal digit pairs of any lengh (including zero)
- with the <cf/hex:/ prefix. Colon separators can be inserted
- arbitrarily between any bytes: <cf/hex:/, <cf/hex:1234/,
- <cf/hex:1234:56:78/.
+ Bytestring literals are written as a sequence of hexadecimal digit
+ pairs, optionally colon-separated. A bytestring specified this way
+ must be either at least 16 bytes (32 digits) long, or prefixed by the
+ <cf/hex:/ prefix: <cf/01:23:45:67:89:ab:cd:ef:01:23:45:67:89:ab:cd:ef/,
+ <cf/0123456789abcdef0123456789abcdef/, <cf/hex:/, <cf/hex:12:34:56/,
+ <cf/hex:12345678/.
A bytestring can be made from a hex string using <cf/from_hex()/
- function. Source strings can use any number of colons, hyphens and
- spaces as byte separators: <cf/from_hex(" 1234 56:78 ab-cd-ef ")/.
+ function. Source strings can use any number of dots, colons, hyphens
+ and spaces as byte separators: <cf/from_hex(" 12.34 56:78 ab-cd-ef ")/.
<tag><label id="type-ip">ip</tag>
This type can hold a single IP address. The IPv4 addresses are stored as
@@ -1547,7 +1715,7 @@ in the foot).
Prefix <cf><m>ip1</m>/<m>len1</m></cf> matches prefix
pattern <cf><m>ip2</m>/<m>len2</m>{<m>l</m>,<m>h</m>}</cf> if the
first <cf>min(len1, len2)</cf> bits of <cf/ip1/ and <cf/ip2/ are
- identical and <cf>len1 &lt;= ip1 &lt;= len2</cf>. A valid prefix pattern
+ identical and <cf>l &lt;= len1 &lt;= h</cf>. A valid prefix pattern
has to satisfy <cf>low &lt;= high</cf>, but <cf/pxlen/ is not
constrained by <cf/low/ or <cf/high/. Obviously, a prefix matches a
prefix set literal if it matches any prefix pattern in the prefix set
@@ -1885,6 +2053,29 @@ Common route attributes are:
network for routes that do not have a native protocol metric attribute
(like <cf/ospf_metric1/ for OSPF routes). It is used mainly by BGP to
compare internal distances to boundary routers (see below).
+
+ <tag><label id="rta-mpls-label"><m/int/ mpls_label</tag>
+ Local MPLS label attached to the route. This attribute is produced by
+ MPLS-aware protocols for labeled routes. It can also be set in import
+ filters to assign static labels, but that also requires static MPLS
+ label policy.
+
+ <tag><label id="rta-mpls-policy"><m/enum/ mpls_policy</tag>
+ For MPLS-aware protocols, this attribute defines which
+ <ref id="mpls-channel-label-policy" name="MPLS label policy"> will be
+ used for the route. It can be set in import filters to change it on
+ per-route basis. Valid values are <cf/MPLS_POLICY_NONE/ (no label),
+ <cf/MPLS_POLICY_STATIC/ (static label), <cf/MPLS_POLICY_PREFIX/
+ (per-prefix label), <cf/MPLS_POLICY_AGGREGATE/ (aggregated label),
+ and <cf/MPLS_POLICY_VRF/ (per-VRF label). See <ref
+ id="mpls-channel-label-policy" name="MPLS label policy"> for details.
+
+ <tag><label id="rta-mpls-class"><m/int/ mpls_class</tag>
+ When <ref id="mpls-channel-label-policy" name="MPLS label policy"> is
+ set to <cf/aggregate/, it may be useful to apply more fine-grained
+ aggregation than just one based on next hops. When routes have different
+ value of this attribute, they will not be aggregated under one local
+ label even if they have the same next hops.
</descrip>
<p>Protocol-specific route attributes are described in the corresponding
@@ -2816,14 +3007,22 @@ using the following configuration parameters:
refresh requests. Disabling Route Refresh also disables Enhanced Route Refresh.
Default: on.
+ <tag><label id="bgp-require-route-refresh">require route refresh <m/switch/</tag>
+ If enabled, the BGP Route Refresh capability (<rfc id="2918">) must be
+ announced by the BGP neighbor, otherwise the BGP session will not be
+ established. Default: off.
+
<tag><label id="bgp-enable-enhanced-route-refresh">enable enhanced route refresh <m/switch/</tag>
- BGP protocol extension Enhanced Route Refresh (<rfc id="7313">) specifies explicit
- begin and end for Route Refresh (see previous option),
- therefore the receiver can remove
- stale routes that were not advertised during the exchange. This option
- specifies whether BIRD advertises this capability and supports
- related procedures.
- Default: on.
+ BGP protocol extension Enhanced Route Refresh (<rfc id="7313">)
+ specifies explicit begin and end for Route Refresh (see previous
+ option), therefore the receiver can remove stale routes that were not
+ advertised during the exchange. This option specifies whether BIRD
+ advertises this capability and supports related procedures. Default: on.
+
+ <tag><label id="bgp-require-enhanced-route-refresh">require enhanced route refresh <m/switch/</tag>
+ If enabled, the BGP Enhanced Route Refresh capability (<rfc id="7313">)
+ must be announced by the BGP neighbor, otherwise the BGP session
+ will not be established. Default: off.
<tag><label id="bgp-graceful-restart">graceful restart <m/switch/|aware</tag>
When a BGP speaker restarts or crashes, neighbors will discard all
@@ -2840,11 +3039,16 @@ using the following configuration parameters:
restart requires also configuration of other protocols. Default: aware.
<tag><label id="bgp-graceful-restart-time">graceful restart time <m/number/</tag>
- The restart time is announced in the BGP graceful restart capability
+ The restart time is announced in the BGP Graceful Restart capability
and specifies how long the neighbor would wait for the BGP session to
re-establish after a restart before deleting stale routes. Default:
120 seconds.
+ <tag><label id="bgp-require-graceful-restart">require graceful restart <m/switch/</tag>
+ If enabled, the BGP Graceful Restart capability (<rfc id="4724">)
+ must be announced by the BGP neighbor, otherwise the BGP session
+ will not be established. Default: off.
+
<tag><label id="bgp-long-lived-graceful-restart">long lived graceful restart <m/switch/|aware</tag>
The long-lived graceful restart is an extension of the traditional
<ref id="bgp-graceful-restart" name="BGP graceful restart">, where stale
@@ -2858,12 +3062,17 @@ using the following configuration parameters:
graceful restart is disabled.
<tag><label id="bgp-long-lived-stale-time">long lived stale time <m/number/</tag>
- The long-lived stale time is announced in the BGP long-lived graceful
- restart capability and specifies how long the neighbor would keep stale
+ The long-lived stale time is announced in the BGP Long-lived Graceful
+ Restart capability and specifies how long the neighbor would keep stale
routes depreferenced during long-lived graceful restart until either the
session is re-stablished and synchronized or the stale time expires and
routes are removed. Default: 3600 seconds.
+ <tag><label id="bgp-require-long-lived-graceful-restart">require long lived graceful restart <m/switch/</tag>
+ If enabled, the BGP Long-lived Graceful Restart capability (draft)
+ must be announced by the BGP neighbor, otherwise the BGP session
+ will not be established. Default: off.
+
<tag><label id="bgp-interpret-communities">interpret communities <m/switch/</tag>
<rfc id="1997"> demands that BGP speaker should process well-known
communities like no-export (65535, 65281) or no-advertise (65535,
@@ -2883,11 +3092,21 @@ using the following configuration parameters:
in neighbor's implementation of 4B AS extension. Even when disabled
(off), BIRD behaves internally as AS4-aware BGP router. Default: on.
+ <tag><label id="bgp-require-as4">require as4 <m/switch/</tag>
+ If enabled, the BGP 4B AS number capability (<rfc id="6793">) must be
+ announced by the BGP neighbor, otherwise the BGP session will not be
+ established. Default: off.
+
<tag><label id="bgp-enable-extended-messages">enable extended messages <m/switch/</tag>
The BGP protocol uses maximum message length of 4096 bytes. This option
provides an extension (<rfc id="8654">) to allow extended messages with
length up to 65535 bytes. Default: off.
+ <tag><label id="bgp-require-extended-messages">require extended messages <m/switch/</tag>
+ If enabled, the BGP Extended Message capability (<rfc id="8654">) must
+ be announced by the BGP neighbor, otherwise the BGP session will not be
+ established. Default: off.
+
<tag><label id="bgp-capabilities">capabilities <m/switch/</tag>
Use capability advertisement to advertise optional capabilities. This is
standard behavior for newer BGP implementations, but there might be some
@@ -2897,7 +3116,11 @@ using the following configuration parameters:
capability-related error.
<tag><label id="bgp-advertise-hostname">advertise hostname <m/switch/</tag>
- Advertise hostname capability along with the hostname. Default: off.
+ Advertise the hostname capability along with the hostname. Default: off.
+
+ <tag><label id="bgp-require-hostname">require hostname <m/switch/</tag>
+ If enabled, the hostname capability must be announced by the BGP
+ neighbor, otherwise the BGP session negotiation fails. Default: off.
<tag><label id="bgp-disable-after-error">disable after error <m/switch/</tag>
When an error is encountered (either locally or by the other side),
@@ -2945,6 +3168,23 @@ using the following configuration parameters:
negotiation. If the proposed hold time would lead to a lower value of
the keepalive time, the session is rejected with error. Default: none.
+ <tag><label id="bgp-send-hold-time">send hold time <m/number/</tag>
+ Maximum time in seconds betweeen successfull transmissions of BGP messages.
+ Send hold timer drops the session if the neighbor is sending keepalives,
+ but does not receive our messages, causing the TCP connection to stall.
+ This may happen due to malfunctioning or overwhelmed neighbor. See
+ <HTMLURL URL="https://datatracker.ietf.org/doc/draft-ietf-idr-bgp-sendholdtimer/"
+ name="draft-ietf-idr-bgp-sendholdtimer"> for more details.
+
+ Like the option <cf/keepalive time/, the effective value depends on the
+ negotiated hold time, as it is scaled to maintain proportion between the
+ send hold time and the keepalive time. If it is set to zero, the timer
+ is disabled. Default: double of the hold timer limit.
+
+ The option <cf/disable rx/ is intended only for testing this feature and
+ should not be used anywhere else. It discards received messages and
+ disables the hold timer.
+
<tag><label id="bgp-connect-delay-time">connect delay time <m/number/</tag>
Delay in seconds between protocol startup and the first attempt to
connect. Default: 5 seconds.
@@ -3062,6 +3302,18 @@ together with their appropriate channels follows.
</tabular>
</table>
+<p>The BGP protocol can be configured as MPLS-aware (by defining both AFI/SAFI
+channels and the MPLS channel). In such case the BGP protocol assigns labels to
+routes imported from MPLS-aware SAFIs (i.e. <cf/ipvX mpls/ and <cf/vpnX mpls/)
+and automatically announces corresponding MPLS route for each labeled route. As
+BGP generally processes a large amount of routes, it is suggested to set MPLS
+label policy to <cf/aggregate/.
+
+<p>Note that even BGP instances without MPLS channel and without local MPLS
+configuration can still propagate third-party MPLS labels, e.g. as route
+reflectors, they just will not assign local labels to imported routes and will
+not announce MPLS routes for local MPLS forwarding.
+
<p>Due to <rfc id="8212">, external BGP protocol requires explicit configuration
of import and export policies (in contrast to other protocols, where default
policies of <cf/import all/ and <cf/export none/ are used in absence of explicit
@@ -3211,15 +3463,30 @@ be used in explicit configuration.
just IPv4-mapped IPv6 addresses are used, as described in
<rfc id="4798"> and <rfc id="4659">. Default: off.
+ <tag><label id="bgp-require-extended-next-hop">require extended next hop <m/switch/</tag>
+ If enabled, the BGP Extended Next Hop Encoding capability (<rfc id="8950">)
+ must be announced by the BGP neighbor, otherwise the BGP session will
+ not be established. Note that this option is relevant just for IPv4 /
+ VPNv4 channels, as IPv6 / VPNv6 channels use a different mechanism not
+ signalled by a capability. Default: off.
+
<tag><label id="bgp-add-paths">add paths <m/switch/|rx|tx</tag>
Standard BGP can propagate only one path (route) per destination network
- (usually the selected one). This option controls the add-path protocol
+ (usually the selected one). This option controls the ADD-PATH protocol
extension, which allows to advertise any number of paths to a
- destination. Note that to be active, add-path has to be enabled on both
+ destination. Note that to be active, ADD-PATH has to be enabled on both
sides of the BGP session, but it could be enabled separately for RX and
TX direction. When active, all available routes accepted by the export
filter are advertised to the neighbor. Default: off.
+ <tag><label id="bgp-require-add-paths">require add paths <m/switch/</tag>
+ If enabled, the BGP ADD-PATH capability (<rfc id="7911">) must be
+ announced by the BGP neighbor, otherwise the BGP session will not be
+ established. Announced directions in the capability must be compatible
+ with locally configured directions. E.g., If <cf/add path tx/ is
+ configured locally, then the neighbor capability must announce RX.
+ Default: off.
+
<tag><label id="bgp-aigp">aigp <m/switch/|originate</tag>
The BGP protocol does not use a common metric like other routing
protocols, instead it uses a set of criteria for route selection
@@ -3365,14 +3632,18 @@ some of them (marked with `<tt/O/') are optional.
<tag><label id="bgp-otc">int bgp_otc [O]</tag>
This attribute is defined in <rfc id="9234">. OTC is a flag that marks
- routes that should be sent only to customers. If <ref id="bgp-role"
- name="local Role"> is configured it set automatically.
+ routes that should be sent only to customers. If <ref id="bgp-local-role"
+ name="local role"> is configured it set automatically.
</descrip>
-<p>For attributes unknown by BIRD, the user can assign a name (on top level)
-to an attribute by its number. This defined name can be used then to both set
-(by a bytestring literal, transitive) or unset the given attribute even though
-BIRD knows nothing about it:
+<p>For attributes unknown by BIRD, the user can assign a name (on top level) to
+an attribute by its number. This defined name can be used then to get, set (as a
+bytestring, transitive) or unset the given attribute even though BIRD knows
+nothing about it.
+
+<p>Note that it is not possible to define an attribute with the same number
+as one known by BIRD, therefore use of this statement carries a risk of
+incompatibility with future BIRD versions.
<tt><label id="bgp-attribute-custom">attribute bgp <m/number/ bytestring <m/name/;</tt>
@@ -3429,10 +3700,9 @@ future. It is not ready for production usage and therefore it is not compiled
by default and have to be enabled during installation by the configure option
<tt/--with-protocols=/.
-<p>The implementation is limited to monitor protocol state changes and routes
-in <ref id="bgp-import-table" name="BGP import tables"> (not regular routing
-tables), therefore import table must be enabled in BGP protocols. All BGP
-protocols are monitored automatically.
+<p>The implementation supports monitoring protocol state changes, pre-policy
+routes (in <ref id="bgp-import-table" name="BGP import tables">) and post-policy
+routes (in regular routing tables). All BGP protocols are monitored automatically.
<sect1>Example
<label id="bmp-exam">
@@ -3442,8 +3712,11 @@ protocol bmp {
# The monitoring station to connect to
station address ip 198.51.100.10 port 1790;
- # required option
+ # Monitor received routes (in import table)
monitoring rib in pre_policy;
+
+ # Monitor accepted routes (passed import filters)
+ monitoring rib in post_policy;
}
</code>
@@ -3568,9 +3841,8 @@ on the <cf/learn/ switch, such routes are either ignored or accepted to our
table).
<p>Note that routes created by OS kernel itself, namely direct routes
-representing IP subnets of associated interfaces, are not imported even with
-<cf/learn/ enabled. You can use <ref id="direct" name="Direct protocol"> to
-generate these direct routes.
+representing IP subnets of associated interfaces, are imported only with
+<cf/learn all/ enabled.
<p>If your OS supports only a single routing table, you can configure only one
instance of the Kernel protocol. If it supports multiple tables (in order to
@@ -3601,10 +3873,12 @@ channels.
Time in seconds between two consecutive scans of the kernel routing
table.
- <tag><label id="krt-learn">learn <m/switch/</tag>
+ <tag><label id="krt-learn">learn <m/switch/|all</tag>
Enable learning of routes added to the kernel routing tables by other
routing daemons or by the system administrator. This is possible only on
- systems which support identification of route authorship.
+ systems which support identification of route authorship. By default,
+ routes created by kernel (marked as "proto kernel") are not imported.
+ Use <cf/learn all/ option to import even these routes.
<tag><label id="krt-kernel-table">kernel table <m/number/</tag>
Select which kernel table should this particular instance of the Kernel
@@ -3714,7 +3988,7 @@ protocol kernel {
<p><code>
protocol kernel { # Primary routing table
learn; # Learn alien routes from the kernel
- persist; # Don't remove routes on bird shutdown
+ persist; # Do not remove routes on bird shutdown
scan time 10; # Scan kernel routing table every 10 seconds
ipv4 {
import all;
@@ -3732,6 +4006,140 @@ protocol kernel { # Secondary routing table
</code>
+<sect>L3VPN
+<label id="l3vpn">
+
+<sect1>Introduction
+<label id="l3vpn-intro">
+
+<p>The L3VPN protocol serves as a translator between IP routes and VPN
+routes. It is a component for BGP/MPLS IP VPNs (<rfc id="4364">) and implements
+policies defined there. In import direction (VPN -&gt; IP), VPN routes matching
+import target specification are stripped of route distinguisher and MPLS labels
+and announced as IP routes, In export direction (IP -&gt; VPN), IP routes are
+expanded with specific route distinguisher, export target communities and MPLS
+label and announced as labeled VPN routes. Unlike the Pipe protocol, the L3VPN
+protocol propagates just the best route for each network.
+
+<p>In BGP/MPLS IP VPNs, route distribution is controlled by Route Targets (RT).
+VRFs are associated with one or more RTs. Routes are also associated with one or
+more RTs, which are encoded as route target extended communities
+in <ref id="rta-bgp-ext-community" name="bgp_ext_community">. A route is then
+imported into each VRF that shares an associated Route Target. The L3VPN
+protocol implements this mechanism through mandatory <cf/import target/ and
+<cf/export target/ protocol options.
+
+<sect1>Configuration
+<label id="l3vpn-config">
+
+<p>L3VPN configuration consists of a few mandatory options and multiple channel
+definitions. For convenience, the default export filter in L3VPN channels is
+<cf/all/, as the primary way to control import and export of routes is through
+protocol options <cf/import target/ and <cf/export target/. If custom filters
+are used, note that the export filter of the input channel is applied before
+the route translation, while the import filter of the output channel is applied
+after that.
+
+<p>In contrast to the Pipe protocol, the L3VPN protocol can handle both IPv4 and
+IPv6 routes in one instance, also both IP side and VPN side are represented as
+separate channels, although that may change in the future. The L3VPN is always
+MPLS-aware protocol, therefore a MPLS channel is mandatory. Altogether, L3VPN
+could have up to 5 channels: <cf/ipv4/, <cf/ipv6/, <cf/vpn4/, <cf/vpn6/, and
+<cf/mpls/.
+
+<p><descrip>
+ <tag><label id="l3vpn-route-distinguisher">route distinguisher <m/vpnrd/</tag>
+ The route distinguisher that is attached to routes in the export
+ direction. Mandatory.
+
+ <tag><label id="l3vpn-rd">rd <m/vpnrd/</tag>
+ A shorthand for the option <cf/route distinguisher/.
+
+ <tag><label id="l3vpn-import-target">import target <m/ec/|<m/ec-set/</tag>
+ Route target extended communities specifying which routes should be
+ imported. Either one community or a set. A route is imported if there is
+ non-empty intersection between extended communities of the route and the
+ import target of the L3VPN protocol. Mandatory.
+
+ <tag><label id="l3vpn-export-target">export target <m/ec/|<m/ec-set/</tag>
+ Route target extended communities that are attached to the route in the
+ export direction. Either one community or a set. Other route target
+ extended communities are removed. Mandatory.
+
+ <tag><label id="l3vpn-route-target">route target <m/ec/|<m/ec-set/</tag>
+ A shorthand for both <cf/import target/ and <cf/export target/.
+</descrip>
+
+<sect1>Attributes
+<label id="l3vpn-attr">
+
+<p>The L3VPN protocol does not define any route attributes.
+
+<sect1>Example
+<label id="l3vpn-exam">
+
+<p>Here is an example of L3VPN setup with one VPN and BGP uplink. IP routes
+learned from a customer in the VPN are stored in <cf/vrf0vX/ tables, which are
+mapped to kernel VRF vrf0. Routes can also be exchanged through BGP with
+different sites hosting that VPN. Forwarding of VPN traffic through the network
+is handled by MPLS.
+
+<p>Omitted from the example are some routing protocol to exchange routes with
+the customer and some sort of MPLS-aware IGP to resolve next hops for BGP VPN
+routes.
+
+<code>
+# MPLS basics
+mpls domain mdom;
+mpls table mtab;
+
+protocol kernel krt_mpls {
+ mpls { table mtab; export all; };
+}
+
+vpn4 table vpntab4;
+vpn6 table vpntab6;
+
+# Exchange VPN routes through BGP
+protocol bgp {
+ vpn4 { table vpntab4; import all; export all; };
+ vpn6 { table vpntab6; import all; export all; };
+ mpls { label policy aggregate; };
+ local 10.0.0.1 as 10;
+ neighbor 10.0.0.2 as 10;
+}
+
+# VRF 0
+ipv4 table vrf0v4;
+ipv6 table vrf0v6;
+
+protocol kernel kernel0v4 {
+ vrf "vrf0";
+ ipv4 { table vrf0v4; export all; };
+ kernel table 100;
+}
+
+protocol kernel kernel0v6 {
+ vrf "vrf0";
+ ipv6 { table vrf0v6; export all; };
+ kernel table 100;
+}
+
+protocol l3vpn l3vpn0 {
+ vrf "vrf0";
+ ipv4 { table vrf0v4; };
+ ipv6 { table vrf0v6; };
+ vpn4 { table vpntab4; };
+ vpn6 { table vpntab6; };
+ mpls { label policy vrf; };
+
+ rd 10:12;
+ import target [(rt, 10, 32..40)];
+ export target [(rt, 10, 30), (rt, 10, 31)];
+}
+</code>
+
+
<sect>MRT
<label id="mrt">
@@ -4675,7 +5083,7 @@ definitions, prefix definitions and DNS definitions:
<label id="radv-custom-option-exam">
<p><code>
-custom option type 38 value hex:0e10:2001:0db8:000a:000b:0000:0000;
+custom option type 38 value hex:0e:10:20:01:0d:b8:00:0a:00:0b:00:00:00:00;
</code>
<tag><label id="radv-trigger">trigger <m/prefix/</tag>
@@ -5454,6 +5862,11 @@ but only routes of the same network type are allowed, as the static protocol
has just one channel. E.g., to have both IPv4 and IPv6 static routes, define two
static protocols, each with appropriate routes and channel.
+<p>The Static protocol can be configured as MPLS-aware (by defining both the
+primary channel and MPLS channel). In that case the Static protocol assigns
+labels to IP routes and automatically announces corresponding MPLS route for
+each labeled route.
+
<p>Global options:
<descrip>
@@ -5477,16 +5890,20 @@ static protocols, each with appropriate routes and channel.
<ref id="type-prefix" name="dependent on network type">.
<descrip>
- <tag>route <m/prefix/ via <m/ip/|<m/"interface"/ [<m/per-nexthop options/] [via ...]</tag>
- Regular routes may bear one or more <ref id="route-next-hop" name="next hops">.
- Every next hop is preceded by <cf/via/ and configured as shown.
+ <tag>route <m/prefix/ [mpls <m/number/] via <m/ip/|<m/"interface"/ [<m/per-nexthop options/] [via ...]</tag>
+ Regular routes may bear one or more <ref id="route-next-hop" name="next
+ hops">. Every next hop is preceded by <cf/via/ and configured as shown.
+
+ When the Static protocol is MPLS-aware, the optional <cf/mpls/ statement
+ after <m/prefix/ specifies a static label for the labeled route, instead
+ of using dynamically allocated label.
- <tag>route <m/prefix/ recursive <m/ip/ [mpls <m/num/[/<m/num/[/<m/num/[...]]]]</tag>
+ <tag>route <m/prefix/ [mpls <m/number/] recursive <m/ip/ [mpls <m/num/[/<m/num/[/<m/num/[...]]]]</tag>
Recursive nexthop resolves the given IP in the configured IGP table and
uses that route's next hop. The MPLS stacks are concatenated; on top is
the IGP's nexthop stack and on bottom is this route's stack.
- <tag>route <m/prefix/ blackhole|unreachable|prohibit</tag>
+ <tag>route <m/prefix/ [mpls <m/number/] blackhole|unreachable|prohibit</tag>
Special routes specifying to silently drop the packet, return it as
unreachable or return it as administratively prohibited. First two
targets are also known as <cf/drop/ and <cf/reject/.
diff --git a/filter/config.Y b/filter/config.Y
index cf25d874..f3ed2dc5 100644
--- a/filter/config.Y
+++ b/filter/config.Y
@@ -20,6 +20,7 @@ static inline u32 pair_a(u32 p) { return p >> 16; }
static inline u32 pair_b(u32 p) { return p & 0xFFFF; }
static struct symbol *this_function;
+static struct sym_scope *this_for_scope;
static struct f_method_scope {
struct f_inst *object;
@@ -974,7 +975,7 @@ var:
for_var:
type symbol { $$ = cf_define_symbol(new_config, $2, SYM_VARIABLE | $1, offset, f_new_var(sym_->scope)); }
- | CF_SYM_KNOWN { $$ = $1; cf_assert_symbol($1, SYM_VARIABLE); }
+ | CF_SYM_KNOWN { cf_error("Use of a pre-defined variable in for loop is not allowed"); }
;
cmd:
@@ -993,7 +994,9 @@ cmd:
new_config->current_scope->slots += 2;
} for_var IN
/* Parse term in the parent scope */
- { new_config->current_scope->active = 0; } term { new_config->current_scope->active = 1; }
+ { this_for_scope = new_config->current_scope; new_config->current_scope = this_for_scope->next; }
+ term
+ { new_config->current_scope = this_for_scope; this_for_scope = NULL; }
DO cmd {
cf_pop_block_scope(new_config);
$$ = f_for_cycle($3, $6, $9);
diff --git a/filter/f-inst.c b/filter/f-inst.c
index 4356a735..9cc46aa0 100644
--- a/filter/f-inst.c
+++ b/filter/f-inst.c
@@ -838,7 +838,10 @@
RESULT_(T_QUAD, i, e->u.data);
break;
case EAF_TYPE_OPAQUE:
- RESULT_(T_ENUM_EMPTY, i, 0);
+ if (da.f_type == T_ENUM_EMPTY)
+ RESULT_(T_ENUM_EMPTY, i, 0);
+ else
+ RESULT_(T_BYTESTRING, ad, e->u.ptr);
break;
case EAF_TYPE_IP_ADDRESS:
RESULT_(T_IP, ip, *((ip_addr *) e->u.ptr->data));
@@ -870,6 +873,12 @@
ARG_ANY(1);
DYNAMIC_ATTR;
ARG_TYPE(1, da.f_type);
+
+ FID_NEW_BODY;
+ if (da.f_type == T_ENUM_EMPTY)
+ cf_error("Setting opaque attribute is not allowed");
+
+ FID_INTERPRET_BODY;
{
struct ea_list *l = lp_alloc(fs->pool, sizeof(struct ea_list) + sizeof(eattr));
diff --git a/filter/test.conf b/filter/test.conf
index 22b4984a..f9289189 100644
--- a/filter/test.conf
+++ b/filter/test.conf
@@ -1939,6 +1939,63 @@ bt_test_suite(t_if_else, "Testing if-else statement");
/*
+ * Test for-in statement
+ * ---------------------
+ */
+
+function t_for_in()
+{
+ bgppath p = +empty+.prepend(5).prepend(4).prepend(3).prepend(2).prepend(1);
+ bgppath q = +empty+.prepend(30).prepend(20).prepend(10);
+ int a;
+
+ # basic for loop
+ a = 0;
+ for int i in p do {
+ a = 2 * a + i;
+ }
+ bt_assert(a = 57);
+
+ # basic for loop, no braces
+ a = 0;
+ for int i in p do a = 2 * a + i;
+ bt_assert(a = 57);
+
+ # for loop with empty body
+ a = 0;
+ for int i in p do { }
+ bt_assert(a = 0);
+
+ # for loop over empty path
+ a = 0;
+ for int i in +empty+ do a = 1;
+ bt_assert(a = 0);
+
+ # for loop with var name shadowing
+ a = 0;
+ for int p in p do {
+ a = 2 * a + p;
+ }
+ bt_assert(a = 57);
+
+ # nested for loops
+ a = 0;
+ int n = 1;
+ for int i in p do {
+ for int j in q do {
+ a = a + i * j * n;
+ n = n + 1;
+ }
+ }
+ bt_assert(a = 9300);
+}
+
+bt_test_suite(t_for_in, "Testing for-in statement");
+
+
+
+
+/*
* Unused functions -- testing only parsing
* ----------------------------------------
*/
diff --git a/lib/strtoul.c b/lib/strtoul.c
index e0c0142f..420931a4 100644
--- a/lib/strtoul.c
+++ b/lib/strtoul.c
@@ -87,7 +87,7 @@ bstrhextobin(const char *s, byte *b)
int v = fromxdigit(*s);
if (v < 0)
{
- if (strchr(" :-", *s) && !hi)
+ if (strchr(" -.:", *s) && !hi)
continue;
else
return -1;
diff --git a/misc/bird.spec b/misc/bird.spec
index 2fa6c2bf..93c80193 100644
--- a/misc/bird.spec
+++ b/misc/bird.spec
@@ -1,6 +1,6 @@
Summary: BIRD Internet Routing Daemon
Name: bird
-Version: 2.13.1
+Version: 2.14
Release: 1
Copyright: GPL
Group: Networking/Daemons
diff --git a/nest/config.Y b/nest/config.Y
index d9778473..63888a04 100644
--- a/nest/config.Y
+++ b/nest/config.Y
@@ -209,7 +209,7 @@ net_type:
| MPLS { $$ = NET_MPLS; }
;
-CF_ENUM(T_ENUM_NETTYPE, NET_, IP4, IP6, VPN4, VPN6, ROA4, ROA6, FLOW4, FLOW6, IP6_SADR)
+CF_ENUM(T_ENUM_NETTYPE, NET_, IP4, IP6, VPN4, VPN6, ROA4, ROA6, FLOW4, FLOW6, IP6_SADR, MPLS)
/* Creation of routing tables */
diff --git a/nest/mpls.Y b/nest/mpls.Y
index 0e755fec..726a834a 100644
--- a/nest/mpls.Y
+++ b/nest/mpls.Y
@@ -99,7 +99,11 @@ mpls_channel_start: MPLS
$$ = this_channel = channel_config_get(&channel_mpls, net_label[NET_MPLS], NET_MPLS, this_proto);
if (EMPTY_LIST(new_config->mpls_domains))
- cf_error("No MPLS domain defined");
+ {
+ int counter = 0;
+ mpls_domain_config_new(cf_default_name(new_config, "mpls%d", &counter));
+ cf_warn("No MPLS domain defined");
+ }
/* Default values for new channel */
if (!MPLS_CC->domain)
diff --git a/nest/mpls.c b/nest/mpls.c
index 54bfb402..9cdcd572 100644
--- a/nest/mpls.c
+++ b/nest/mpls.c
@@ -847,12 +847,12 @@ mpls_get_fec_by_label(struct mpls_fec_map *m, u32 label)
}
struct mpls_fec *
-mpls_get_fec_by_net(struct mpls_fec_map *m, const net_addr *net, u32 path_id)
+mpls_get_fec_by_net(struct mpls_fec_map *m, const net_addr *net, u64 path_id)
{
if (!m->net_hash.data)
HASH_INIT(m->net_hash, m->pool, 4);
- u32 hash = net_hash(net) ^ u32_hash(path_id);
+ u32 hash = net_hash(net) ^ u64_hash(path_id);
struct mpls_fec *fec = HASH_FIND(m->net_hash, NET, net, path_id, hash);
if (fec)
@@ -1059,10 +1059,13 @@ mpls_announce_fec(struct mpls_fec_map *m, struct mpls_fec *fec, const rta *src)
const u32 *labels = &src->nh.label[src->nh.labels - src->nh.labels_orig];
mpls_label_stack ms;
- /* Apply the hostentry with the original labelstack */
+ /* Reconstruct the original labelstack */
ms.len = src->nh.labels_orig;
memcpy(ms.stack, labels, src->nh.labels_orig * sizeof(u32));
- rta_apply_hostentry(a, src->hostentry, &ms);
+
+ /* The same hostentry, but different dependent table */
+ struct hostentry *s = src->hostentry;
+ rta_set_recursive_next_hop(m->channel->table, a, s->owner, s->addr, s->link, &ms);
}
net_addr_mpls n = NET_ADDR_MPLS(fec->label);
diff --git a/nest/mpls.h b/nest/mpls.h
index def490c1..c2bdf5ef 100644
--- a/nest/mpls.h
+++ b/nest/mpls.h
@@ -126,11 +126,6 @@ struct mpls_fec {
u32 label; /* Label for FEC */
u32 hash; /* Hash for primary key (net / rta) */
u32 uc; /* Number of LSPs for FEC */
- union { /* Extension part of key */
- u32 path_id; /* Source path_id */
- u32 class_id; /* Aaggregation class */
- };
-
u8 state; /* FEC state (MPLS_FEC_*) */
u8 policy; /* Label policy (MPLS_POLICY_*) */
@@ -138,6 +133,11 @@ struct mpls_fec {
struct mpls_fec *next_k; /* Next in mpls_fec.net_hash/rta_hash */
struct mpls_fec *next_l; /* Next in mpls_fec.label_hash */
+
+ union { /* Extension part of key */
+ u64 path_id; /* Source path_id */
+ u32 class_id; /* Aaggregation class */
+ };
union { /* Primary key */
struct rta *rta;
struct iface *iface;
@@ -169,7 +169,7 @@ void mpls_fec_map_reconfigure(struct mpls_fec_map *m, struct channel *C);
void mpls_fec_map_free(struct mpls_fec_map *m);
struct mpls_fec *mpls_find_fec_by_label(struct mpls_fec_map *x, u32 label);
struct mpls_fec *mpls_get_fec_by_label(struct mpls_fec_map *m, u32 label);
-struct mpls_fec *mpls_get_fec_by_net(struct mpls_fec_map *m, const net_addr *net, u32 path_id);
+struct mpls_fec *mpls_get_fec_by_net(struct mpls_fec_map *m, const net_addr *net, u64 path_id);
struct mpls_fec *mpls_get_fec_by_rta(struct mpls_fec_map *m, const rta *src, u32 class_id);
void mpls_free_fec(struct mpls_fec_map *x, struct mpls_fec *fec);
int mpls_handle_rte(struct mpls_fec_map *m, const net_addr *n, rte *r, linpool *lp, struct mpls_fec **locked_fec);
diff --git a/nest/protocol.h b/nest/protocol.h
index af2a5d68..c87d3814 100644
--- a/nest/protocol.h
+++ b/nest/protocol.h
@@ -624,7 +624,7 @@ struct channel_config *proto_cf_find_channel(struct proto_config *p, uint net_ty
static inline struct channel_config *proto_cf_main_channel(struct proto_config *pc)
{ return proto_cf_find_channel(pc, pc->net_type); }
static inline struct channel_config *proto_cf_mpls_channel(struct proto_config *pc)
-{ return proto_cf_find_channel(pc, NET_MPLS); }
+{ return (pc->net_type != NET_MPLS) ? proto_cf_find_channel(pc, NET_MPLS) : NULL; }
struct channel *proto_find_channel_by_table(struct proto *p, struct rtable *t);
struct channel *proto_find_channel_by_name(struct proto *p, const char *n);
diff --git a/nest/route.h b/nest/route.h
index 782bbac9..3e1340fa 100644
--- a/nest/route.h
+++ b/nest/route.h
@@ -246,7 +246,8 @@ struct hostentry {
ip_addr addr; /* IP address of host, part of key */
ip_addr link; /* (link-local) IP address of host, used as gw
if host is directly attached */
- struct rtable *tab; /* Dependent table, part of key */
+ rtable *tab; /* Dependent table, part of key */
+ rtable *owner; /* Nexthop owner table */
struct hostentry *next; /* Next in hash chain */
unsigned hash_key; /* Hash key */
unsigned uc; /* Use count */
diff --git a/nest/rt-attr.c b/nest/rt-attr.c
index 7beb119b..25936d81 100644
--- a/nest/rt-attr.c
+++ b/nest/rt-attr.c
@@ -1264,7 +1264,7 @@ rta__free(rta *a)
rta *
rta_do_cow(rta *o, linpool *lp)
{
- rta *r = lp_alloc(lp, rta_size(o));
+ rta *r = lp_alloc(lp, RTA_MAX_SIZE);
memcpy(r, o, rta_size(o));
for (struct nexthop **nhn = &(r->nh.next), *nho = o->nh.next; nho; nho = nho->next)
{
diff --git a/nest/rt-table.c b/nest/rt-table.c
index 287569f7..1b30e7dc 100644
--- a/nest/rt-table.c
+++ b/nest/rt-table.c
@@ -3477,6 +3477,7 @@ rt_get_hostentry(rtable *tab, ip_addr a, ip_addr ll, rtable *dep)
return he;
he = hc_new_hostentry(hc, tab->rp, a, link, dep, k);
+ he->owner = tab;
rt_update_hostentry(tab, he);
return he;
}
diff --git a/proto/bgp/attrs.c b/proto/bgp/attrs.c
index 9387ddba..4346cd5d 100644
--- a/proto/bgp/attrs.c
+++ b/proto/bgp/attrs.c
@@ -1152,6 +1152,12 @@ bgp_attr_known(uint code)
return (code < ARRAY_SIZE(bgp_attr_table)) && bgp_attr_table[code].name;
}
+const char *
+bgp_attr_name(uint code)
+{
+ return (code < ARRAY_SIZE(bgp_attr_table)) ? bgp_attr_table[code].name : NULL;
+}
+
void bgp_fix_attr_flags(ea_list *attrs)
{
for (u8 i = 0; i < attrs->count; i++)
diff --git a/proto/bgp/bgp.c b/proto/bgp/bgp.c
index f8146bdf..9d4671af 100644
--- a/proto/bgp/bgp.c
+++ b/proto/bgp/bgp.c
@@ -375,6 +375,8 @@ bgp_close_conn(struct bgp_conn *conn)
conn->keepalive_timer = NULL;
rfree(conn->hold_timer);
conn->hold_timer = NULL;
+ rfree(conn->send_hold_timer);
+ conn->send_hold_timer = NULL;
rfree(conn->tx_ev);
conn->tx_ev = NULL;
rfree(conn->sk);
@@ -546,9 +548,6 @@ bgp_conn_enter_openconfirm_state(struct bgp_conn *conn)
bgp_conn_set_state(conn, BS_OPENCONFIRM);
}
-static const struct bgp_af_caps dummy_af_caps = { };
-static const struct bgp_af_caps basic_af_caps = { .ready = 1 };
-
void
bgp_conn_enter_established_state(struct bgp_conn *conn)
{
@@ -604,20 +603,6 @@ bgp_conn_enter_established_state(struct bgp_conn *conn)
const struct bgp_af_caps *loc = bgp_find_af_caps(local, c->afi);
const struct bgp_af_caps *rem = bgp_find_af_caps(peer, c->afi);
- /* Use default if capabilities were not announced */
- if (!local->length && (c->afi == BGP_AF_IPV4))
- loc = &basic_af_caps;
-
- if (!peer->length && (c->afi == BGP_AF_IPV4))
- rem = &basic_af_caps;
-
- /* Ignore AFIs that were not announced in multiprotocol capability */
- if (!loc || !loc->ready)
- loc = &dummy_af_caps;
-
- if (!rem || !rem->ready)
- rem = &dummy_af_caps;
-
int active = loc->ready && rem->ready;
c->c.disabled = !active;
c->c.reloadable = p->route_refresh || c->cf->import_table;
@@ -690,6 +675,13 @@ bgp_conn_enter_established_state(struct bgp_conn *conn)
p->channel_map[c->index] = c;
}
+ /* Breaking rx_hook for simulating receive problem */
+ if (p->cf->disable_rx)
+ {
+ conn->sk->rx_hook = NULL;
+ tm_stop(conn->hold_timer);
+ }
+
/* proto_notify_state() will likely call bgp_feed_begin(), setting c->feed_state */
bgp_conn_set_state(conn, BS_ESTABLISHED);
@@ -1061,6 +1053,27 @@ bgp_keepalive_timeout(timer *t)
ev_run(conn->tx_ev);
}
+void
+bgp_send_hold_timeout(timer *t)
+{
+ struct bgp_conn *conn = t->data;
+ struct bgp_proto *p = conn->bgp;
+
+ if (conn->state == BS_CLOSE)
+ return;
+
+ /* Error codes not yet assigned by IANA */
+ uint code = 4;
+ uint subcode = 1;
+
+ /* Like bgp_error() but without NOTIFICATION */
+ bgp_log_error(p, BE_BGP_TX, "Error", code, subcode, NULL, 0);
+ bgp_store_error(p, conn, BE_BGP_TX, (code << 16) | subcode);
+ bgp_conn_enter_idle_state(conn);
+ bgp_update_startup_delay(p);
+ bgp_stop(p, 0, NULL, 0);
+}
+
static void
bgp_setup_conn(struct bgp_proto *p, struct bgp_conn *conn)
{
@@ -1075,6 +1088,7 @@ bgp_setup_conn(struct bgp_proto *p, struct bgp_conn *conn)
conn->connect_timer = tm_new_init(p->p.pool, bgp_connect_timeout, conn, 0, 0);
conn->hold_timer = tm_new_init(p->p.pool, bgp_hold_timeout, conn, 0, 0);
conn->keepalive_timer = tm_new_init(p->p.pool, bgp_keepalive_timeout, conn, 0, 0);
+ conn->send_hold_timer = tm_new_init(p->p.pool, bgp_send_hold_timeout, conn, 0, 0);
conn->tx_ev = ev_new_init(p->p.pool, bgp_kick_tx, conn);
}
@@ -2017,6 +2031,21 @@ bgp_postconfig(struct proto_config *CF)
if (interior && (cf->local_role != BGP_ROLE_UNDEFINED))
log(L_WARN "BGP roles are not recommended to be used within AS confederations");
+ if (cf->require_enhanced_refresh && !(cf->enable_refresh && cf->enable_enhanced_refresh))
+ cf_warn("Enhanced refresh required but disabled");
+
+ if (cf->require_as4 && !cf->enable_as4)
+ cf_warn("AS4 support required but disabled");
+
+ if (cf->require_extended_messages && !cf->enable_extended_messages)
+ cf_warn("Extended messages required but not enabled");
+
+ if (cf->require_gr && !cf->gr_mode)
+ cf_warn("Graceful restart required but not enabled");
+
+ if (cf->require_llgr && !cf->llgr_mode)
+ cf_warn("Long-lived graceful restart required but not enabled");
+
if (cf->require_roles && (cf->local_role == BGP_ROLE_UNDEFINED))
cf_error("Local role must be set if roles are required");
@@ -2140,6 +2169,12 @@ bgp_postconfig(struct proto_config *CF)
if (cc->secondary && !cc->c.table->sorted)
cf_error("BGP with secondary option requires sorted table");
+
+ if (cc->require_ext_next_hop && !cc->ext_next_hop)
+ cf_warn("Extended next hop required but not enabled");
+
+ if (cc->require_add_path && !cc->add_path)
+ cf_warn("ADD-PATH required but not enabled");
}
}
@@ -2184,20 +2219,30 @@ bgp_reconfigure(struct proto *P, struct proto_config *CF)
if (C->stale)
same = proto_configure_channel(P, &C, NULL) && same;
- if (same)
- proto_setup_mpls_map(P, RTS_BGP, 1);
+ /* Reset name counter */
+ p->dynamic_name_counter = 0;
- if (same && (p->start_state > BSS_PREPARE))
- bgp_update_bfd(p, new->bfd);
+ if (!same)
+ return 0;
/* We should update our copy of configuration ptr as old configuration will be freed */
- if (same)
- p->cf = new;
+ p->cf = new;
- /* Reset name counter */
- p->dynamic_name_counter = 0;
+ /* Check whether existing connections are compatible with required capabilities */
+ struct bgp_conn *ci = &p->incoming_conn;
+ if (((ci->state == BS_OPENCONFIRM) || (ci->state == BS_ESTABLISHED)) && !bgp_check_capabilities(ci))
+ return 0;
+
+ struct bgp_conn *co = &p->outgoing_conn;
+ if (((co->state == BS_OPENCONFIRM) || (co->state == BS_ESTABLISHED)) && !bgp_check_capabilities(co))
+ return 0;
- return same;
+ proto_setup_mpls_map(P, RTS_BGP, 1);
+
+ if (p->start_state > BSS_PREPARE)
+ bgp_update_bfd(p, new->bfd);
+
+ return 1;
}
#define TABLE(cf, NAME) ((cf)->NAME ? (cf)->NAME->table : NULL )
@@ -2605,7 +2650,9 @@ bgp_show_proto_info(struct proto *P)
tm_remains(p->conn->hold_timer), p->conn->hold_time);
cli_msg(-1006, " Keepalive timer: %t/%u",
tm_remains(p->conn->keepalive_timer), p->conn->keepalive_time);
- }
+ cli_msg(-1006, " Send hold timer: %t/%u",
+ tm_remains(p->conn->send_hold_timer), p->conn->send_hold_time);
+}
#if 0
struct bgp_stats *s = &p->stats;
diff --git a/proto/bgp/bgp.h b/proto/bgp/bgp.h
index 1ba3de5e..7127bc88 100644
--- a/proto/bgp/bgp.h
+++ b/proto/bgp/bgp.h
@@ -117,6 +117,8 @@ struct bgp_config {
int setkey; /* Set MD5 password to system SA/SP database */
u8 local_role; /* Set peering role with neighbor [RFC 9234] */
int require_roles; /* Require configured roles on both sides */
+ int send_hold_time;
+ int disable_rx; /* Stop reading messages after handshake (for simulating error) */
/* Times below are in seconds */
unsigned gr_time; /* Graceful restart timeout */
unsigned llgr_time; /* Long-lived graceful restart stale time */
@@ -138,6 +140,13 @@ struct bgp_config {
const char *dynamic_name; /* Name pattern for dynamic BGP */
int dynamic_name_digits; /* Minimum number of digits for dynamic names */
int check_link; /* Use iface link state for liveness detection */
+ int require_refresh; /* Require remote support for route refresh [RFC 2918] */
+ int require_enhanced_refresh; /* Require remote support for enhanced route refresh [RFC 7313] */
+ int require_as4; /* Require remote support for 4B AS numbers [RFC 6793] */
+ int require_extended_messages; /* Require remote support for extended messages [RFC 8654] */
+ int require_hostname; /* Require remote support for hostname [draft] */
+ int require_gr; /* Require remote support for graceful restart [RFC 4724] */
+ int require_llgr; /* Require remote support for long-lived graceful restart [draft] */
struct bfd_options *bfd; /* Use BFD for liveness detection */
};
@@ -159,7 +168,9 @@ struct bgp_channel_config {
u8 llgr_able; /* Allow full long-lived GR for the channel */
uint llgr_time; /* Long-lived graceful restart stale time */
u8 ext_next_hop; /* Allow both IPv4 and IPv6 next hops */
+ u8 require_ext_next_hop; /* Require remote support of IPv4 NLRI with IPv6 next hops [RFC 8950] */
u8 add_path; /* Use ADD-PATH extension [RFC 7911] */
+ u8 require_add_path; /* Require remote support of ADD-PATH extension [RFC 7911] */
u8 aigp; /* AIGP is allowed on this session */
u8 aigp_originate; /* AIGP is originated automatically */
u32 cost; /* IGP cost for direct next hops */
@@ -298,6 +309,7 @@ struct bgp_conn {
timer *connect_timer;
timer *hold_timer;
timer *keepalive_timer;
+ timer *send_hold_timer;
event *tx_ev;
u32 packets_to_send; /* Bitmap of packet types to be sent */
u32 channels_to_send; /* Bitmap of channels with packets to be sent */
@@ -306,7 +318,7 @@ struct bgp_conn {
int notify_code, notify_subcode, notify_size;
byte *notify_data;
- uint hold_time, keepalive_time; /* Times calculated from my and neighbor's requirements */
+ uint hold_time, keepalive_time, send_hold_time; /* Times calculated from my and neighbor's requirements */
};
struct bgp_proto {
@@ -549,6 +561,7 @@ void bgp_conn_enter_openconfirm_state(struct bgp_conn *conn);
void bgp_conn_enter_established_state(struct bgp_conn *conn);
void bgp_conn_enter_close_state(struct bgp_conn *conn);
void bgp_conn_enter_idle_state(struct bgp_conn *conn);
+void broke_bgp_listening(struct channel *C);
void bgp_handle_graceful_restart(struct bgp_proto *p);
void bgp_graceful_restart_done(struct bgp_channel *c);
void bgp_refresh_begin(struct bgp_channel *c);
@@ -611,6 +624,7 @@ bgp_unset_attr(ea_list **to, struct linpool *pool, uint code)
int bgp_encode_mp_reach_mrt(struct bgp_write_state *s, eattr *a, byte *buf, uint size);
+const char * bgp_attr_name(uint code);
int bgp_encode_attrs(struct bgp_write_state *s, ea_list *attrs, byte *buf, byte *end);
ea_list * bgp_decode_attrs(struct bgp_parse_state *s, byte *data, uint len);
void bgp_finish_attrs(struct bgp_parse_state *s, rta *a);
@@ -656,6 +670,7 @@ bgp_total_aigp_metric(rte *r)
void bgp_dump_state_change(struct bgp_conn *conn, uint old, uint new);
void bgp_prepare_capabilities(struct bgp_conn *conn);
+int bgp_check_capabilities(struct bgp_conn *conn);
const struct bgp_af_desc *bgp_get_af_desc(u32 afi);
const struct bgp_af_caps *bgp_find_af_caps(struct bgp_caps *caps, u32 afi);
void bgp_schedule_packet(struct bgp_conn *conn, struct bgp_channel *c, int type);
diff --git a/proto/bgp/config.Y b/proto/bgp/config.Y
index e7a2f5cb..1173ff06 100644
--- a/proto/bgp/config.Y
+++ b/proto/bgp/config.Y
@@ -32,7 +32,7 @@ CF_KEYWORDS(BGP, LOCAL, NEIGHBOR, AS, HOLD, TIME, CONNECT, RETRY, KEEPALIVE,
LIVED, STALE, IMPORT, IBGP, EBGP, MANDATORY, INTERNAL, EXTERNAL, SETS,
DYNAMIC, RANGE, NAME, DIGITS, BGP_AIGP, AIGP, ORIGINATE, COST, ENFORCE,
FIRST, FREE, VALIDATE, BASE, ROLE, ROLES, PEER, PROVIDER, CUSTOMER,
- RS_SERVER, RS_CLIENT, REQUIRE, BGP_OTC, GLOBAL)
+ RS_SERVER, RS_CLIENT, REQUIRE, BGP_OTC, GLOBAL, SEND)
%type <i> bgp_nh
%type <i32> bgp_afi
@@ -77,6 +77,7 @@ bgp_proto_start: proto_start BGP {
BGP_CFG->local_role = BGP_ROLE_UNDEFINED;
BGP_CFG->dynamic_name = "dynbgp";
BGP_CFG->check_link = -1;
+ BGP_CFG->send_hold_time = -1;
}
;
@@ -182,6 +183,7 @@ bgp_proto:
| bgp_proto CONNECT RETRY TIME expr ';' { BGP_CFG->connect_retry_time = $5; }
| bgp_proto KEEPALIVE TIME expr ';' { BGP_CFG->keepalive_time = $4; if (($4<1) || ($4>65535)) cf_error("Keepalive time must be in range 1-65535"); }
| bgp_proto MIN KEEPALIVE TIME expr ';' { BGP_CFG->min_keepalive_time = $5; }
+ | bgp_proto SEND HOLD TIME expr';' { BGP_CFG->send_hold_time = $5; }
| bgp_proto ERROR FORGET TIME expr ';' { BGP_CFG->error_amnesia_time = $5; }
| bgp_proto ERROR WAIT TIME expr ',' expr ';' { BGP_CFG->error_delay_time_min = $5; BGP_CFG->error_delay_time_max = $7; }
| bgp_proto DISABLE AFTER ERROR bool ';' { BGP_CFG->disable_after_error = $5; }
@@ -191,6 +193,13 @@ bgp_proto:
| bgp_proto ENABLE AS4 bool ';' { BGP_CFG->enable_as4 = $4; }
| bgp_proto ENABLE EXTENDED MESSAGES bool ';' { BGP_CFG->enable_extended_messages = $5; }
| bgp_proto ADVERTISE HOSTNAME bool ';' { BGP_CFG->enable_hostname = $4; }
+ | bgp_proto REQUIRE ROUTE REFRESH bool ';' { BGP_CFG->require_refresh = $5; }
+ | bgp_proto REQUIRE ENHANCED ROUTE REFRESH bool ';' { BGP_CFG->require_enhanced_refresh = $6; }
+ | bgp_proto REQUIRE AS4 bool ';' { BGP_CFG->require_as4 = $4; }
+ | bgp_proto REQUIRE EXTENDED MESSAGES bool ';' { BGP_CFG->require_extended_messages = $5; }
+ | bgp_proto REQUIRE HOSTNAME bool ';' { BGP_CFG->require_hostname = $4; }
+ | bgp_proto REQUIRE GRACEFUL RESTART bool ';' { BGP_CFG->require_gr = $5; }
+ | bgp_proto REQUIRE LONG LIVED GRACEFUL RESTART bool ';' { BGP_CFG->require_llgr = $7; }
| bgp_proto CAPABILITIES bool ';' { BGP_CFG->capabilities = $3; }
| bgp_proto PASSWORD text ';' { BGP_CFG->password = $3; }
| bgp_proto SETKEY bool ';' { BGP_CFG->setkey = $3; }
@@ -215,6 +224,7 @@ bgp_proto:
| bgp_proto ENFORCE FIRST AS bool ';' { BGP_CFG->enforce_first_as = $5; }
| bgp_proto LOCAL ROLE bgp_role_name ';' { BGP_CFG->local_role = $4; }
| bgp_proto REQUIRE ROLES bool ';' { BGP_CFG->require_roles = $4; }
+ | bgp_proto DISABLE RX bool ';' { BGP_CFG->disable_rx = $4; }
;
bgp_afi:
@@ -284,9 +294,11 @@ bgp_channel_item:
| LONG LIVED GRACEFUL RESTART bool { BGP_CC->llgr_able = $5; }
| LONG LIVED STALE TIME expr { BGP_CC->llgr_time = $5; }
| EXTENDED NEXT HOP bool { BGP_CC->ext_next_hop = $4; }
+ | REQUIRE EXTENDED NEXT HOP bool { BGP_CC->require_ext_next_hop = $5; if (BGP_AFI(BGP_CC->afi) != BGP_AFI_IPV4) cf_warn("Require extended next hop option ignored for non-IPv4 channels"); }
| ADD PATHS RX { BGP_CC->add_path = BGP_ADD_PATH_RX; }
| ADD PATHS TX { BGP_CC->add_path = BGP_ADD_PATH_TX; }
| ADD PATHS bool { BGP_CC->add_path = $3 ? BGP_ADD_PATH_FULL : 0; }
+ | REQUIRE ADD PATHS bool { BGP_CC->require_add_path = $4; }
| IMPORT TABLE bool { BGP_CC->import_table = $3; }
| EXPORT TABLE bool { BGP_CC->export_table = $3; }
| AIGP bool { BGP_CC->aigp = $2; BGP_CC->aigp_originate = 0; }
@@ -365,12 +377,15 @@ dynamic_attr: BGP_LARGE_COMMUNITY
dynamic_attr: BGP_OTC
{ $$ = f_new_dynamic_attr(EAF_TYPE_INT, T_INT, EA_CODE(PROTOCOL_BGP, BA_ONLY_TO_CUSTOMER)); } ;
-custom_attr: ATTRIBUTE BGP NUM type symbol ';' {
- if($3 > 255 || $3 < 1)
- cf_error("Invalid attribute number. (Given %i, must be 1-255.)", $3);
- if($4 != T_BYTESTRING)
- cf_error("Attribute type must be bytestring, not %s.", f_type_name($4));
- struct f_dynamic_attr* a = (struct f_dynamic_attr*) malloc(sizeof(struct f_dynamic_attr));
+custom_attr: ATTRIBUTE BGP expr type symbol ';' {
+ if ($3 > 255 || $3 < 1)
+ cf_error("Invalid attribute number (Given %i, must be 1-255)", $3);
+ if ($4 != T_BYTESTRING)
+ cf_error("Attribute type must be bytestring, not %s", f_type_name($4));
+ if (bgp_attr_name($3))
+ cf_error("Attribute BGP.%d already known as %s", $3, bgp_attr_name($3));
+
+ struct f_dynamic_attr *a = cfg_alloc(sizeof(struct f_dynamic_attr));
*a = f_new_dynamic_attr(f_type_attr($4), T_BYTESTRING, EA_CODE(PROTOCOL_BGP, $3));
a->flags = BAF_TRANSITIVE | BAF_OPTIONAL;
cf_define_symbol(new_config, $5, SYM_ATTRIBUTE, attribute, a);
diff --git a/proto/bgp/packets.c b/proto/bgp/packets.c
index b7df5a7a..e8cc4718 100644
--- a/proto/bgp/packets.c
+++ b/proto/bgp/packets.c
@@ -173,8 +173,11 @@ bgp_create_notification(struct bgp_conn *conn, byte *buf)
/* Capability negotiation as per RFC 5492 */
-const struct bgp_af_caps *
-bgp_find_af_caps(struct bgp_caps *caps, u32 afi)
+static const struct bgp_af_caps dummy_af_caps = { };
+static const struct bgp_af_caps basic_af_caps = { .ready = 1 };
+
+static const struct bgp_af_caps *
+bgp_find_af_caps_(struct bgp_caps *caps, u32 afi)
{
struct bgp_af_caps *ac;
@@ -185,6 +188,23 @@ bgp_find_af_caps(struct bgp_caps *caps, u32 afi)
return NULL;
}
+const struct bgp_af_caps *
+bgp_find_af_caps(struct bgp_caps *caps, u32 afi)
+{
+ const struct bgp_af_caps *ac = bgp_find_af_caps_(caps, afi);
+
+ /* Return proper capability if found */
+ if (ac)
+ return ac;
+
+ /* Use default if capabilities were not announced */
+ if (!caps->length && (afi == BGP_AF_IPV4))
+ return &basic_af_caps;
+
+ /* Ignore AFIs that were not announced in multiprotocol capability */
+ return &dummy_af_caps;
+}
+
static struct bgp_af_caps *
bgp_get_af_caps(struct bgp_caps **pcaps, u32 afi)
{
@@ -674,7 +694,7 @@ err:
return -1;
}
-static int
+int
bgp_check_capabilities(struct bgp_conn *conn)
{
struct bgp_proto *p = conn->bgp;
@@ -686,21 +706,54 @@ bgp_check_capabilities(struct bgp_conn *conn)
/* This is partially overlapping with bgp_conn_enter_established_state(),
but we need to run this just after we receive OPEN message */
+ if (p->cf->require_refresh && !remote->route_refresh)
+ return 0;
+
+ if (p->cf->require_enhanced_refresh && !remote->enhanced_refresh)
+ return 0;
+
+ if (p->cf->require_as4 && !remote->as4_support)
+ return 0;
+
+ if (p->cf->require_extended_messages && !remote->ext_messages)
+ return 0;
+
+ if (p->cf->require_hostname && !remote->hostname)
+ return 0;
+
+ if (p->cf->require_gr && !remote->gr_aware)
+ return 0;
+
+ if (p->cf->require_llgr && !remote->llgr_aware)
+ return 0;
+
+ /* No check for require_roles, as it uses error code 2.11 instead of 2.7 */
+
BGP_WALK_CHANNELS(p, c)
{
const struct bgp_af_caps *loc = bgp_find_af_caps(local, c->afi);
const struct bgp_af_caps *rem = bgp_find_af_caps(remote, c->afi);
/* Find out whether this channel will be active */
- int active = loc && loc->ready &&
- ((rem && rem->ready) || (!remote->length && (c->afi == BGP_AF_IPV4)));
+ int active = loc->ready && rem->ready;
/* Mandatory must be active */
if (c->cf->mandatory && !active)
return 0;
if (active)
+ {
+ if (c->cf->require_ext_next_hop && !rem->ext_next_hop)
+ return 0;
+
+ if (c->cf->require_add_path && (loc->add_path & BGP_ADD_PATH_RX) && !(rem->add_path & BGP_ADD_PATH_TX))
+ return 0;
+
+ if (c->cf->require_add_path && (loc->add_path & BGP_ADD_PATH_TX) && !(rem->add_path & BGP_ADD_PATH_RX))
+ return 0;
+
count++;
+ }
}
/* We need at least one channel active */
@@ -873,6 +926,10 @@ bgp_rx_open(struct bgp_conn *conn, byte *pkt, uint len)
(p->cf->keepalive_time * hold_time / p->cf->hold_time) :
hold_time / 3;
+ uint send_hold_time = (p->cf->send_hold_time >= 0) ?
+ (p->cf->send_hold_time * hold_time / p->cf->hold_time) :
+ 2 * hold_time;
+
/* Keepalive time might be rounded down to zero */
if (hold_time && !keepalive_time)
keepalive_time = 1;
@@ -886,7 +943,7 @@ bgp_rx_open(struct bgp_conn *conn, byte *pkt, uint len)
if (!id || (p->is_internal && id == p->local_id))
{ bgp_error(conn, 2, 3, pkt+24, -4); return; }
- /* RFC 5492 4 - check for required capabilities */
+ /* RFC 5492 5 - check for required capabilities */
if (p->cf->capabilities && !bgp_check_capabilities(conn))
{ bgp_error(conn, 2, 7, NULL, 0); return; }
@@ -981,6 +1038,7 @@ bgp_rx_open(struct bgp_conn *conn, byte *pkt, uint len)
/* Update our local variables */
conn->hold_time = hold_time;
conn->keepalive_time = keepalive_time;
+ conn->send_hold_time = send_hold_time;
conn->as4_session = conn->local_caps->as4_support && caps->as4_support;
conn->ext_messages = conn->local_caps->ext_messages && caps->ext_messages;
p->remote_id = id;
@@ -990,6 +1048,7 @@ bgp_rx_open(struct bgp_conn *conn, byte *pkt, uint len)
bgp_schedule_packet(conn, NULL, PKT_KEEPALIVE);
bgp_start_timer(conn->hold_timer, conn->hold_time);
+ bgp_start_timer(conn->send_hold_timer, conn->send_hold_time);
bgp_conn_enter_openconfirm_state(conn);
}
@@ -2440,7 +2499,7 @@ bgp_create_update_bmp(struct bgp_channel *c, byte *buf, struct bgp_bucket *buck,
.proto = p,
.channel = c,
.pool = tmp_linpool,
- .mp_reach = (c->afi != BGP_AF_IPV4) || (rem && rem->ext_next_hop),
+ .mp_reach = (c->afi != BGP_AF_IPV4) || rem->ext_next_hop,
.as4_session = 1,
.add_path = c->add_path_rx,
.mpls = c->desc->mpls,
@@ -3007,7 +3066,11 @@ bgp_send(struct bgp_conn *conn, uint type, uint len)
put_u16(buf+16, len);
buf[18] = type;
- return sk_send(sk, len);
+ int success = sk_send(sk, len);
+ if (success && ((conn->state == BS_ESTABLISHED) || (conn->state == BS_OPENCONFIRM)))
+ bgp_start_timer(conn->send_hold_timer, conn->send_hold_time);
+
+ return success;
}
/**
@@ -3167,6 +3230,10 @@ bgp_tx(sock *sk)
{
struct bgp_conn *conn = sk->data;
+ /* Pending message was passed to kernel */
+ if ((conn->state == BS_ESTABLISHED) || (conn->state == BS_OPENCONFIRM))
+ bgp_start_timer(conn->send_hold_timer, conn->send_hold_time);
+
DBG("BGP: TX hook\n");
uint max = 1024;
while (--max && (bgp_fire_tx(conn) > 0))
@@ -3208,6 +3275,7 @@ static struct {
{ 3, 10, "Invalid network field" },
{ 3, 11, "Malformed AS_PATH" },
{ 4, 0, "Hold timer expired" },
+ { 4, 1, "Send hold timer expired" }, /* Provisional [draft-ietf-idr-bgp-sendholdtimer] */
{ 5, 0, "Finite state machine error" }, /* Subcodes are according to [RFC6608] */
{ 5, 1, "Unexpected message in OpenSent state" },
{ 5, 2, "Unexpected message in OpenConfirm state" },
diff --git a/proto/l3vpn/l3vpn.c b/proto/l3vpn/l3vpn.c
index 2952f32b..8b56cd73 100644
--- a/proto/l3vpn/l3vpn.c
+++ b/proto/l3vpn/l3vpn.c
@@ -148,11 +148,6 @@ l3vpn_prepare_export_targets(struct l3vpn_proto *p)
ASSERT(p->export_target_length == len);
}
-/* Convert 64-bit RD to 32bit source ID, unfortunately it has collisions */
-static inline struct rte_src * l3vpn_get_source(struct l3vpn_proto *p, u64 rd)
-{ return rt_get_source(&p->p, (u32)(rd >> 32) ^ u32_hash(rd)); }
-//{ return p->p.main_source; }
-
static void
l3vpn_rt_notify(struct proto *P, struct channel *c0, net *net, rte *new, rte *old UNUSED)
{
@@ -182,14 +177,14 @@ l3vpn_rt_notify(struct proto *P, struct channel *c0, net *net, rte *new, rte *ol
case NET_VPN4:
net_fill_ip4(n, net4_prefix(n0), net4_pxlen(n0));
- src = l3vpn_get_source(p, ((const net_addr_vpn4 *) n0)->rd);
+ src = rt_get_source(&p->p, ((const net_addr_vpn4 *) n0)->rd);
dst = p->ip4_channel;
export = 0;
break;
case NET_VPN6:
net_fill_ip6(n, net6_prefix(n0), net6_pxlen(n0));
- src = l3vpn_get_source(p, ((const net_addr_vpn6 *) n0)->rd);
+ src = rt_get_source(&p->p, ((const net_addr_vpn6 *) n0)->rd);
dst = p->ip6_channel;
export = 0;
break;
diff --git a/proto/ospf/rt.c b/proto/ospf/rt.c
index 471bb586..efcd4e17 100644
--- a/proto/ospf/rt.c
+++ b/proto/ospf/rt.c
@@ -1796,7 +1796,12 @@ calc_next_hop(struct ospf_area *oa, struct top_hash_entry *en,
if (ifa->type == OSPF_IT_VLINK)
return new_nexthop(p, IPA_NONE, NULL, 0);
- /* FIXME: On physical PtP links we may skip next-hop altogether */
+ /*
+ * The type of the ospf_iface is PtP and the iface is a physical PtP link,
+ * so we can simply use the iface, and skip further resolving.
+ */
+ if (ifa->type == OSPF_IT_PTP && !(ifa->iface->flags & IF_MULTIACCESS))
+ return new_nexthop(p, IPA_NONE, ifa->iface, ifa->ecmp_weight);
if (ospf_is_v2(p) || ospf_is_ip6(p))
{
diff --git a/proto/static/static.c b/proto/static/static.c
index 0e80ad64..071803a8 100644
--- a/proto/static/static.c
+++ b/proto/static/static.c
@@ -472,6 +472,10 @@ static_postconfig(struct proto_config *CF)
struct static_config *cf = (void *) CF;
struct static_route *r;
+ /* If there is just a MPLS channel, use it as a main channel */
+ if (!CF->net_type && proto_cf_mpls_channel(CF))
+ CF->net_type = NET_MPLS;
+
if (! proto_cf_main_channel(CF))
cf_error("Channel not specified");
diff --git a/sysdep/config.h b/sysdep/config.h
index cf5787d0..ffab1670 100644
--- a/sysdep/config.h
+++ b/sysdep/config.h
@@ -13,7 +13,7 @@
#ifdef GIT_LABEL
#define BIRD_VERSION XSTR1(GIT_LABEL)
#else
-#define BIRD_VERSION "2.13.1"
+#define BIRD_VERSION "2.14"
#endif
/* Include parameters determined by configure script */
diff --git a/sysdep/linux/netlink.c b/sysdep/linux/netlink.c
index 1af78766..29446cab 100644
--- a/sysdep/linux/netlink.c
+++ b/sysdep/linux/netlink.c
@@ -1598,7 +1598,7 @@ nl_parse_route(struct nl_parse_state *s, struct nlmsghdr *h)
case RTPROT_KERNEL:
krt_src = KRT_SRC_KERNEL;
- return;
+ break;
case RTPROT_BIRD:
if (!s->scan)
diff --git a/sysdep/unix/io.c b/sysdep/unix/io.c
index 6aedcfb6..4b3eef48 100644
--- a/sysdep/unix/io.c
+++ b/sysdep/unix/io.c
@@ -2226,12 +2226,12 @@ io_loop(void)
for(;;)
{
times_update(&main_timeloop);
- events = ev_run_list(&global_event_list);
- events = ev_run_list_limited(&global_work_list, WORK_EVENTS_MAX) || events;
+ ev_run_list(&global_event_list);
+ ev_run_list_limited(&global_work_list, WORK_EVENTS_MAX);
timers_fire(&main_timeloop);
io_close_event();
- // FIXME
+ events = !EMPTY_LIST(global_event_list) || !EMPTY_LIST(global_work_list);
poll_tout = (events ? 0 : 3000); /* Time in milliseconds */
if (t = timers_first(&main_timeloop))
{
diff --git a/sysdep/unix/krt.Y b/sysdep/unix/krt.Y
index 5af6a4c8..90297d3f 100644
--- a/sysdep/unix/krt.Y
+++ b/sysdep/unix/krt.Y
@@ -32,6 +32,7 @@ CF_DECLS
CF_KEYWORDS(KERNEL, PERSIST, SCAN, TIME, LEARN, DEVICE, ROUTES, GRACEFUL, RESTART, KRT_SOURCE, KRT_METRIC, MERGE, PATHS)
CF_KEYWORDS(INTERFACE, PREFERRED)
+%type <i> kern_learn
%type <i> kern_mp_limit
%type <cc> kern_channel
@@ -49,6 +50,11 @@ kern_proto_start: proto_start KERNEL {
kern_proto: kern_proto_start proto_name '{' ;
kern_proto: kern_proto kern_item ';' ;
+kern_learn:
+ bool { $$ = $1 ? KRT_LEARN_ALIEN : KRT_LEARN_NONE; }
+ | ALL { $$ = KRT_LEARN_ALL; }
+ ;
+
kern_mp_limit:
/* empty */ { $$ = KRT_DEFAULT_ECMP_LIMIT; }
| LIMIT expr { $$ = $2; if (($2 <= 0) || ($2 > 255)) cf_error("Merge paths limit must be in range 1-255"); }
@@ -68,7 +74,7 @@ kern_item:
/* Scan time of 0 means scan on startup only */
THIS_KRT->scan_time = $3 S_;
}
- | LEARN bool {
+ | LEARN kern_learn {
THIS_KRT->learn = $2;
#ifndef KRT_ALLOW_LEARN
if ($2)
diff --git a/sysdep/unix/krt.c b/sysdep/unix/krt.c
index 9f95247f..3a4b24dc 100644
--- a/sysdep/unix/krt.c
+++ b/sysdep/unix/krt.c
@@ -639,12 +639,14 @@ krt_got_route(struct krt_proto *p, rte *e, s8 src)
#ifdef KRT_ALLOW_LEARN
switch (src)
{
- case KRT_SRC_KERNEL:
- goto ignore;
-
case KRT_SRC_REDIRECT:
goto delete;
+ case KRT_SRC_KERNEL:
+ if (KRT_CF->learn != KRT_LEARN_ALL)
+ goto ignore;
+ /* fallthrough */
+
case KRT_SRC_ALIEN:
if (KRT_CF->learn)
krt_learn_scan(p, e);
@@ -780,6 +782,11 @@ krt_got_route_async(struct krt_proto *p, rte *e, int new, s8 src)
break;
#ifdef KRT_ALLOW_LEARN
+ case KRT_SRC_KERNEL:
+ if (KRT_CF->learn != KRT_LEARN_ALL)
+ break;
+ /* fallthrough */
+
case KRT_SRC_ALIEN:
if (KRT_CF->learn)
{
diff --git a/sysdep/unix/krt.h b/sysdep/unix/krt.h
index 18a206e6..e25f0b12 100644
--- a/sysdep/unix/krt.h
+++ b/sysdep/unix/krt.h
@@ -27,6 +27,10 @@ struct kif_proto;
#define KRT_REF_SEEN 0x1 /* Seen in table */
#define KRT_REF_BEST 0x2 /* Best in table */
+#define KRT_LEARN_NONE 0 /* Do not learn */
+#define KRT_LEARN_ALIEN 1 /* Learn KRT_SRC_ALIEN routes */
+#define KRT_LEARN_ALL 2 /* Learn both KRT_SRC_ALIEN and KRT_SRC_KERNEL routes */
+
/* Whenever we recognize our own routes, we allow learing of foreign routes */
#ifdef CONFIG_SELF_CONSCIOUS