New Upstream Release - bird2

Ready changes

Summary

Merged new upstream version: 2.13.1 (was: 2.0.12).

Diff

diff --git a/ChangeLog b/ChangeLog
index e938d58..91cf09f 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,461 @@
+commit 52bae235b716a3c8d629ddf1306178568c69833f
+Author: Maria Matejka <mq@ucw.cz>
+Date:   Thu Jun 22 16:14:12 2023 +0200
+
+    NEWS and version update
+
+commit 1499a335f6f44a0fd85365e404c2a11842d7f75c
+Author: Maria Matejka <mq@ucw.cz>
+Date:   Thu Jun 22 16:07:28 2023 +0200
+
+    Filter: Fixed segfault when a case option had an empty block
+    
+    Thanks to Kobayashi_Bairuo <noc@tohunet.com> for reporting.
+
+commit ac7341a18146bf0f0b2c60477c4292a9cd428a87
+Author: Ondrej Zajicek <santiago@crfreenet.org>
+Date:   Fri May 19 01:02:57 2023 +0200
+
+    BGP: Fix role check when no capability option is present
+    
+    When an OPEN message without capability options was parsed, the remote
+    role field was not initialized with the proper (non-zero) default value,
+    so it was interpreted as if 'provider' was announced.
+    
+    Thanks to Mikhail Grishin for the bugreport.
+
+commit ca0f239c72486cebfe171e335e3b8a86f5999714
+Author: Ondrej Zajicek <santiago@crfreenet.org>
+Date:   Fri Apr 21 20:24:43 2023 +0200
+
+    NEWS and version update
+
+commit 1a1e13cc2fc2df82e2319b7beeb18bb8eb92fbd2
+Author: Ondrej Zajicek <santiago@crfreenet.org>
+Date:   Fri Apr 21 19:29:17 2023 +0200
+
+    Filter: Disable some trie formatting tests
+    
+    Trie formatting works slightly different with 4-way tries than with
+    16-way ones, so these tests generated false error. Block them for now.
+
+commit 52450bc96dcedbc30cbb2e282c6706ad9e5e5774
+Merge: d61505b0 f3b599af
+Author: Ondrej Zajicek <santiago@crfreenet.org>
+Date:   Fri Apr 21 04:47:55 2023 +0200
+
+    Merge branch 'bmp'
+
+commit f3b599afe5bde0c7f232421743041b305bb8afa7
+Author: Ondrej Zajicek <santiago@crfreenet.org>
+Date:   Fri Apr 21 04:42:13 2023 +0200
+
+    BMP: Add some basic documentation
+
+commit d61505b039bf0aa6697e28b2a4e07907c89ba1fb
+Author: Luiz Amaral <email@luiz.eng.br>
+Date:   Fri Apr 21 01:37:30 2023 +0200
+
+    BSD: IPv4 over IPv6 nexthop support on FreeBSD
+    
+    The support for IPv4 routes with IPv6 nexthops was implemented in FreeBSD
+    13.1, this patch allows to import and export such routes from/to kernel.
+    
+    Minor change from committer.
+
+commit 335409248ea932e93ce4361564b8e92d0b83b071
+Author: Maria Matejka <mq@ucw.cz>
+Date:   Thu Apr 20 19:33:00 2023 +0200
+
+    Linpool: Fix lp_restore()
+    
+    When lp_save() is called on an empty linpool, then some allocation is
+    done, then lp_restore() is called, the linpool is restored but the used
+    chunks are inaccessible. Fix it.
+
+commit 976dec048a25fc22efb07fa73be1316c95046420
+Author: Ondrej Zajicek <santiago@crfreenet.org>
+Date:   Thu Apr 20 17:14:45 2023 +0200
+
+    BMP: Silence some log messages
+    
+    Hooks called from BGP to BMP should not log warning when BMP is not
+    connected, that is not an error (and we do not want to flood logs with
+    a ton of messages).
+    
+    Blocked sk_send() should not log warning, that is expected situation.
+    Error during sk_send() is handled in error hook anyway.
+
+commit 2c7d2141ac86b0d482d3221447d1ad920c557108
+Author: Ondrej Zajicek <santiago@crfreenet.org>
+Date:   Thu Apr 20 16:13:58 2023 +0200
+
+    BMP: Fix connection management
+    
+    Replace broken TCP connection management with a simple state machine.
+    Handle failed attempts properly with a timeout, detect and handle TCP
+    connection close and try to reconnect after that. Remove useless
+    'station_connected' flag.
+    
+    Keep open messages saved even after the BMP session establishment,
+    so they can be used after BMP session flaps.
+    
+    Use proper log messages for session events.
+
+commit 010df43519b12e83b0ff2cba9e344cba698586bb
+Author: Ondrej Zajicek <santiago@crfreenet.org>
+Date:   Tue Apr 18 18:57:54 2023 +0200
+
+    BMP: Fix reconfiguration
+    
+    It is not supported, but at least it must update internal config
+    pointer to not keep old one.
+
+commit 02164814b49a3385caae0ea10aa042487c6002d2
+Author: Ondrej Zajicek <santiago@crfreenet.org>
+Date:   Tue Apr 18 17:21:13 2023 +0200
+
+    BMP: Allow build without BMP and disable BMP build by default
+    
+    It has still several important issues to be enabled by default.
+
+commit fbeef4b74dfd73fb86b1ccc5dd1c6109e3c21624
+Author: Ondrej Zajicek <santiago@crfreenet.org>
+Date:   Tue Apr 18 15:13:24 2023 +0200
+
+    BMP: Move initialization to bmp_start()
+    
+    That fixes BMP socket allocation from an invalid pool.
+
+commit 04e3a76c9417d35acdfba96a11327e99000fe47d
+Author: Ondrej Zajicek <santiago@crfreenet.org>
+Date:   Tue Apr 18 15:09:21 2023 +0200
+
+    BMP: Fix missing template
+    
+    It is mandatory for protocol.
+
+commit 3925e65938e7c778f650d62a721dec7a66c19ab3
+Author: Ondrej Zajicek (work) <santiago@crfreenet.org>
+Date:   Thu Apr 15 18:32:47 2021 +0200
+
+    BMP: Add some missing bmp_buffer_free() calls
+    
+    They were inadvertently removed during recent code refactoring.
+    
+    Thanks to Dawid Macek for the bugreport and patch.
+
+commit 4d56b70dc5facdf4b839b76bf80c93856bcbb121
+Author: Ondrej Zajicek (work) <santiago@crfreenet.org>
+Date:   Mon Mar 29 04:43:04 2021 +0200
+
+    BMP: Remove duplicate functions for update encoding
+    
+    Use existing BGP functions also for BMP update encoding.
+
+commit 568fd666136fcf7a37eb445d18b478b6464536c4
+Author: Ondrej Zajicek (work) <santiago@crfreenet.org>
+Date:   Sun Mar 28 16:41:53 2021 +0200
+
+    BMP: Integrate bmp_conn to bmp_proto
+    
+    There is only one socket per BMP instance, no need to have separate
+    struct (like in BGP).
+
+commit 4adebdf198d6e2ca1afcd7cb9bfac81725e7b24e
+Author: Ondrej Zajicek (work) <santiago@crfreenet.org>
+Date:   Sun Mar 28 15:36:59 2021 +0200
+
+    BMP: Minor cleanups
+    
+    Remove redundant 'disable' option, simplify IP address serialization,
+    and remove useless macros.
+
+commit a995ed43860eb139a13456242aa12486179fac86
+Author: Ondrej Zajicek (work) <santiago@crfreenet.org>
+Date:   Sun Mar 28 15:13:23 2021 +0200
+
+    BMP: Do not use global instance ptr internally
+    
+    Use local variable to refence relevant instance instead of using global
+    instance ptr. Also, use 'p' variable instead of 'bmp' so we can use
+    common macros like TRACE().
+
+commit ad16e351773f4b606dd8b4dbbe77c2cb35bf5133
+Author: Ondrej Zajicek (work) <santiago@crfreenet.org>
+Date:   Sun Mar 28 04:30:11 2021 +0200
+
+    BMP: Remove superfluous error handling
+    
+    Most error handling code was was for cases that cannot happen,
+    or they would be code bugs (and should use ASSERT()). Keep error
+    handling for just for I/O errors, like in rest of BIRD.
+
+commit a848dad40aa618e5e24417e4ef46b62c860de679
+Author: Pawel Maslanka <pmaslank@akamai.com>
+Date:   Mon Mar 29 22:45:21 2021 +0200
+
+    BMP protocol support
+    
+    Initial implementation of a basic subset of the BMP (BGP Monitoring
+    Protocol, RFC 7854) from Akamai team. Submitted for further review
+    and improvement.
+
+commit 9e44ace3928a19560058dc713fcbff3a8bad3b3c
+Author: Trisha Biswas <tbiswas@fastly.com>
+Date:   Fri Apr 14 04:28:37 2023 +0200
+
+    BGP: Add 'allow bgp_med' option for EBGP sessions
+    
+    This option allows to treat bgp_med as regular transitive attribute
+    on EBGP sessions (without hacks in filters).
+    
+    Minor changes from committer.
+
+commit dc139fb6438f0864e83a9dc71e7c212c5acaf3ef
+Author: Jakub Ružička <jakub.ruzicka@nic.cz>
+Date:   Thu Apr 13 20:06:55 2023 +0200
+
+    Increase tests timeout
+    
+    Tests may take longer than 5 s to complete on slow/virtual machines.
+
+commit f881b98d9b48e7a60c46dffc29009a86dac63233
+Author: Ondrej Zajicek <santiago@crfreenet.org>
+Date:   Tue Apr 4 05:20:49 2023 +0200
+
+    BGP: Fix bgp_med handling
+    
+    Missing translation from BGP attribute ID to eattr ID in bgp_unset_attr()
+    broke automatic removal of bgp_med during export to EBGP peers.
+    
+    Thanks to Edward Sun for the bugreport.
+
+commit 231c63851e3a56201dd02abfbf3fce47a80f8ae0
+Author: Johannes Moos <johannes.moos@de-cix.net>
+Date:   Sat Mar 18 15:33:48 2023 +0100
+
+    Add missing references to "show route in" in the cli-help and doc.
+    
+    The feature of showing all prefixes inside the given one has been added
+    in v2.0.9 but not well documented. Fixing it by this update.
+    
+    Text in doc and commit message added by commiter.
+
+commit 2b712554d18dfb09274c003315a573f8578270ec
+Author: Maria Matejka <mq@ucw.cz>
+Date:   Thu Mar 16 19:23:19 2023 +0100
+
+    BGP: Free bind applies also to outbound connections
+    
+    Even though the free bind option is primarily meant to alleviate problems
+    with addresses assigned too late, it's also possible to use BIRD with AnyIP
+    configuration, assigning whole ranges to the machine. Therefore free bind
+    allows also to create an outbound connection from specific address even though
+    such address is not assigned.
+
+commit 6b38285f587be9d4b128ae816bc25cb338f7f07c
+Author: Ondrej Zajicek <santiago@crfreenet.org>
+Date:   Mon Mar 6 11:57:40 2023 +0100
+
+    Net: Replace runtime checks with STATIC_ASSERT()
+
+commit 0f679438f36d8c2a31dfe490007e983b085caef6
+Author: Petr Vaněk <arkamar@atlas.cz>
+Date:   Mon Mar 6 11:19:30 2023 +0100
+
+    Printf test suite fails on systems with musl libc because tests for "%m"
+    and "%M" formats expect "Input/output error" message but musl returns
+    "I/O error". Proposed change compares the printf output with string
+    returned from strerror function for EIO constant.
+    
+    See-also: https://bugs.gentoo.org/836713
+    
+    Minor change from committer.
+
+commit 2f080b543296aa2fa18bf7451b5174d942b0a952
+Author: Maria Matejka <mq@ucw.cz>
+Date:   Thu Feb 16 14:22:23 2023 +0100
+
+    Config: Dropping filter instruction trees after linearization
+
+commit 6c058ae40cf33d6d36c0159d0c40c9925c8e60d8
+Author: Maria Matejka <mq@ucw.cz>
+Date:   Wed Feb 22 14:45:20 2023 +0100
+
+    Linpool flush drops all the allocated pages but one
+    
+    When a linpool is used to allocate a one-off big load of memory, it
+    makes no sense to keep that amount of memory for future use inside the
+    linpool. Contrary to previous implementations where the memory was
+    directly free()d, we now use the page allocator which has an internal
+    cache which keeps the released pages for us and subsequent allocations
+    simply get these released pages back.
+    
+    And even if the page cleanup routine kicks in inbetween, the pages get
+    only madvise()d, not munmap()ed so performance aspects are negligible.
+    
+    This may fix some memory usage peaks in extreme cases.
+
+commit 913ec57f27b06845e3698e8ea08821d39b9575cf
+Author: Ondrej Zajicek <santiago@crfreenet.org>
+Date:   Sun Feb 19 15:35:07 2023 +0100
+
+    BGP: Update RFC references
+
+commit 501256cfc8c1fb5e225c81c4d3300b7c219baf63
+Author: Ondrej Zajicek <santiago@crfreenet.org>
+Date:   Sun Feb 19 15:34:43 2023 +0100
+
+    Babel: Update RFC references
+
+commit eefb29679957fed3724e6d5db2ddf112e28f646f
+Author: Ondrej Zajicek <santiago@crfreenet.org>
+Date:   Sun Feb 19 03:59:10 2023 +0100
+
+    Conf: Fix too early free of old configuration
+    
+    The change 371eb49043d225d2bab8149187b813a14b4b86d2 introduced early free
+    of old_config. Unfortunately, it did not properly check whether it is not
+    still in use (blocked by obstacle during reconfiguration). Fix that.
+    
+    It also means that we still could have a short peak when three configs
+    are in use (when a new reconfig is requeste while the previous one is
+    still active).
+
+commit ee919658948772105d0bd3b4535ba28883484f2c
+Author: Toke Høiland-Jørgensen <toke@toke.dk>
+Date:   Tue Feb 14 18:18:32 2023 +0100
+
+    Babel: Keep separate auth PC counters for unicast and multicast
+    
+    The babel protocol normally sends all its messages as multicast packets,
+    but the protocol specification allows most messages to be sent as either
+    unicast or multicast, and the two can be mixed freely. In particular, the
+    babeld implementation can be configured to unicast updates to all peers
+    instead of sending them as unicast.
+    
+    Daniel discovered that this can cause problems with the packet counter
+    checks in the MAC extension due to packet reordering. This happens on WiFi
+    networks where clients have power save enabled (which is quite common in
+    infrastructure networks): in this case, the access point will buffer all
+    multicast traffic and only send it out along with its beacons, leading to a
+    maximum buffering in default Linux-based access point configuration of up
+    to 200 ms.
+    
+    This means that a Babel sender that mixes unicast and multicast messages
+    can have the unicast messages overtake the multicast messages because of
+    this buffering; when authentication is enabled, this causes the receiver to
+    discard the multicast message when it does arrive because it now has a
+    packet counter value less than the unicast message that arrived before it.
+    Daniel observed that this happens frequently enough that Babel ceases to
+    work entirely when runner over a WiFi network.
+    
+    The issue has been described in draft-ietf-babel-mac-relaxed, which is
+    currently pending RFC publication. That also describes two mitigation
+    mechanisms: Keeping separate PC counters for unicast and multicast, and
+    using a reorder window for PC values. This patch implements the former as
+    that is the simplest, and resolves the particular issue seen on WiFi.
+    
+    Thanks to Daniel Gröber for the bugreport.
+    
+    Minor changes from committer.
+
+commit eecc3f02e41bcb91d463c4c1189fd56bc44e6514
+Author: Andreas Rammhold <andreas@rammhold.de>
+Date:   Tue Feb 14 16:17:03 2023 +0100
+
+    Babel: Implement IPv4 via IPv6 extension (RFC 9229)
+    
+    The patch implements an IPv4 via IPv6 extension (RFC 9229) to the Babel
+    routing protocol (RFC 8966) that allows annoncing routes to an IPv4
+    prefix with an IPv6 next hop, which makes it possible for IPv4 traffic
+    to flow through interfaces that have not been assigned an IPv4 address.
+    
+    The implementation is compatible with the current Babeld version.
+    
+    Thanks to Toke Høiland-Jørgensen for early review on this work.
+    
+    Minor changes from committer.
+
+commit 0851fcde651301a886fefc574a4f739bf68119b9
+Author: Maria Matejka <mq@ucw.cz>
+Date:   Fri Feb 3 09:12:34 2023 +0100
+
+    Documentation: Adding roadmap as decided in January 2023
+
+commit 0ab62f267449d6496e753625e37924357fb0aa95
+Author: Ondrej Zajicek <santiago@crfreenet.org>
+Date:   Wed Feb 1 19:30:21 2023 +0100
+
+    Build: Partial revert of one of previous changes
+    
+    There are many compatibility issues with echo -e, scratch that.
+
+commit ba348b2029f3c51559bed0bd752c4365793f0ea9
+Author: Ondrej Zajicek <santiago@crfreenet.org>
+Date:   Wed Feb 1 17:46:48 2023 +0100
+
+    Build: Minor improvement to build output
+
+commit 23f3dd5cfbe8681b4e71c8d7345bdeaafc86e077
+Author: Ondrej Zajicek <santiago@crfreenet.org>
+Date:   Wed Feb 1 16:15:13 2023 +0100
+
+    Nest: Minor cleanup in buildsystem
+    
+    There ware missing dependencies for proto-build.c generation, which
+    sometimes lead to failed builds, and ignores changes in the set of
+    built protocols. Fix that, and also improve formatting of proto-build.c
+
+commit dc4c5f51f83f97100b207136ecfde8ff94e597e6
+Author: Toke Høiland-Jørgensen <toke@toke.dk>
+Date:   Tue Jan 31 15:52:14 2023 +0100
+
+    Babel: Initialise source seqno from incoming message
+    
+    When creating a new babel_source object we initialise the seqno to 0. The
+    caller will update the source object with the right metric and seqno value,
+    for both newly created and old source objects. However if we initialise the
+    source object seqno to 0 that may actually turn out to be a valid (higher)
+    seqno than the one in the routing table, because of seqno wrapping. In this
+    case the source metric will not be set properly, which breaks feasibility
+    tracking for subsequent updates.
+    
+    To fix this, add a new initial_seqno argument to babel_get_source() which
+    is used when allocating a new object, and set that to the seqno value of
+    the update we're sending.
+    
+    Thanks to Juliusz Chroboczek for the bugreport.
+
+commit 96d7c4679df49b34be004177b10a99210af5f141
+Author: Ondrej Zajicek <santiago@crfreenet.org>
+Date:   Mon Jan 30 23:49:20 2023 +0100
+
+    Babel: Improve clarity of unfeasible update handling.
+    
+    Add a comment and (unnecessary) check to make correctness obvious.
+
+commit 3e7e4a71868bc519aacc0eb785471b46fc345a5c
+Author: Toke Høiland-Jørgensen <toke@toke.dk>
+Date:   Mon Jan 30 23:36:39 2023 +0100
+
+    Babel: Fix missing modulo comparison of seqnos
+    
+    Juliusz noticed there were a couple of places we were doing straight
+    inequality comparisons of seqnos in Babel. This is wrong because seqnos can
+    wrap: so we need to use the modulo-64k comparison function for these cases
+    as well.
+    
+    Introduce a strict-inequality version of the modulo-comparison for this
+    purpose.
+
+commit 72230d3ca37d34cafa0442c23f83121ae1fc41be
+Author: Alexander Zubkov <green@qrator.net>
+Date:   Mon Jan 23 14:23:00 2023 +0100
+
+    Small fix of indenting
+
 commit 5437104afad90f2a74ce52946f06da607d021e2b
 Author: Ondrej Zajicek <santiago@crfreenet.org>
 Date:   Mon Jan 23 02:23:19 2023 +0100
diff --git a/Makefile.in b/Makefile.in
index 95ab148..839efe2 100644
--- a/Makefile.in
+++ b/Makefile.in
@@ -161,10 +161,11 @@ $(client) $(daemon):
 	$(Q)$(CC) $(LDFLAGS) -o $@ $^ $(LIBS)
 
 $(objdir)/sysdep/paths.h: Makefile
-	echo  >$@ "/* Generated by Makefile, don't edit manually! */"
-	echo >>$@ "#define PATH_CONFIG_FILE \"@CONFIG_FILE@\""
-	echo >>$@ "#define PATH_CONTROL_SOCKET \"@CONTROL_SOCKET@\""
-	if test -n "@iproutedir@" ; then echo >>$@ "#define PATH_IPROUTE_DIR \"@iproutedir@\"" ; fi
+	$(E)echo GEN $@
+	$(Q)echo  >$@ "/* Generated by Makefile, don't edit manually! */"
+	$(Q)echo >>$@ "#define PATH_CONFIG_FILE \"@CONFIG_FILE@\""
+	$(Q)echo >>$@ "#define PATH_CONTROL_SOCKET \"@CONTROL_SOCKET@\""
+	$(Q)if test -n "@iproutedir@" ; then echo >>$@ "#define PATH_IPROUTE_DIR \"@iproutedir@\"" ; fi
 
 # Unit tests rules
 
diff --git a/NEWS b/NEWS
index 575df30..30f3b93 100644
--- a/NEWS
+++ b/NEWS
@@ -1,3 +1,29 @@
+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
+
+  This is a bugfix version.
+
+Version 2.13 (2023-04-21)
+  o Babel: IPv4 via IPv6 extension (RFC 9229)
+  o Babel: Improve authentication on lossy networks
+  o BGP: New 'allow bgp_med' option
+  o BSD: Support for IPv4 routes with IPv6 nexthop on FreeBSD
+  o Experimental BMP protocol implementation
+  o Important bugfixes
+
+  Notes:
+
+  We changed versioning scheme from <epoch>.<major>.<minor> to more common
+  <major>.<minor>.<patch> . From now on, you may expect that BIRD 2.13.x will be
+  strictly only fixing bugs found in 2.13, whereas BIRD 2.14 will also contain
+  new features.
+
+  This BIRD version contains an alpha release of BMP protocol implementation.
+  It is not ready for production usage and therefore it is not compiled by
+  default and have to be enabled during installation.
+
+
 Version 2.0.12 (2023-01-23)
   o Filter: New 'onlink' route attribute
   o Compile-time option to use 4-way tries instead of 16-way ones
diff --git a/conf/conf.c b/conf/conf.c
index 4e31de2..7ef729b 100644
--- a/conf/conf.c
+++ b/conf/conf.c
@@ -197,8 +197,12 @@ cleanup:
 void
 config_free(struct config *c)
 {
-  if (c)
-    rfree(c->pool);
+  if (!c)
+    return;
+
+  ASSERT(!c->obstacle_count);
+
+  rfree(c->pool);
 }
 
 /**
@@ -207,10 +211,14 @@ config_free(struct config *c)
  * This function frees the old configuration (%old_config) that is saved for the
  * purpose of undo. It is useful before parsing a new config when reconfig is
  * requested, to avoid keeping three (perhaps memory-heavy) configs together.
+ * Configuration is not freed when it is still active during reconfiguration.
  */
 void
 config_free_old(void)
 {
+  if (!old_config || old_config->obstacle_count)
+    return;
+
   tm_stop(config_timer);
   undo_available = 0;
 
diff --git a/configure b/configure
index 6394d18..dcc0c1e 100755
--- a/configure
+++ b/configure
@@ -5640,6 +5640,7 @@ fi
 
 
 
+
 { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking protocols" >&5
 printf %s "checking protocols... " >&6; }
 protocols=`echo "$with_protocols" | sed 's/,/ /g'`
diff --git a/configure.ac b/configure.ac
index d73eec2..c9c5203 100644
--- a/configure.ac
+++ b/configure.ac
@@ -323,6 +323,7 @@ fi
 AH_TEMPLATE([CONFIG_BABEL], 	[Babel protocol])
 AH_TEMPLATE([CONFIG_BFD],	[BFD protocol])
 AH_TEMPLATE([CONFIG_BGP],	[BGP protocol])
+AH_TEMPLATE([CONFIG_BMP],	[BMP protocol])
 AH_TEMPLATE([CONFIG_MRT],	[MRT protocol])
 AH_TEMPLATE([CONFIG_OSPF],	[OSPF protocol])
 AH_TEMPLATE([CONFIG_PIPE],	[Pipe protocol])
diff --git a/debian/changelog b/debian/changelog
index 9a5e6f7..2371089 100644
--- a/debian/changelog
+++ b/debian/changelog
@@ -1,3 +1,12 @@
+bird2 (2.13.1-1) UNRELEASED; urgency=low
+
+  * New upstream release.
+  * Drop patch 0001-Increase-tests-timeout.patch, present upstream.
+  * Drop patch 0002-Conf-Fix-too-early-free.patch, present upstream.
+  * Drop patch 0003-BGP-Fix-bgp_med-handling.patch, present upstream.
+
+ -- Debian Janitor <janitor@jelmer.uk>  Fri, 23 Jun 2023 08:45:30 -0000
+
 bird2 (2.0.12-7) unstable; urgency=medium
 
   [ Miao Wang ]
diff --git a/debian/patches/0001-Increase-tests-timeout.patch b/debian/patches/0001-Increase-tests-timeout.patch
deleted file mode 100644
index 6a6713b..0000000
--- a/debian/patches/0001-Increase-tests-timeout.patch
+++ /dev/null
@@ -1,26 +0,0 @@
-From c851aa3c9c7060ccc0085a3cdd1975b1a68454f4 Mon Sep 17 00:00:00 2001
-From: =?UTF-8?q?Jakub=20Ru=C5=BEi=C4=8Dka?= <jakub.ruzicka@nic.cz>
-Date: Tue, 24 Jan 2023 12:54:15 +0100
-Subject: [PATCH] Increase tests timeout
-
-Tests may take longer than 5 s to complete on slow/virtual machines.
----
- test/birdtest.h | 2 +-
- 1 file changed, 1 insertion(+), 1 deletion(-)
-
-diff --git a/test/birdtest.h b/test/birdtest.h
-index ad5f8f9..cfeebb9 100644
---- a/test/birdtest.h
-+++ b/test/birdtest.h
-@@ -40,7 +40,7 @@ static inline u64 bt_random(void)
- void bt_log_suite_result(int result, const char *fmt, ...);
- void bt_log_suite_case_result(int result, const char *fmt, ...);
- 
--#define BT_TIMEOUT 			5	/* Default timeout in seconds */
-+#define BT_TIMEOUT 			60	/* Default timeout in seconds */
- #define BT_FORKING 			1	/* Forking is enabled in default */
- 
- #define BT_RANDOM_SEED 			0x5097d2bb
--- 
-2.30.2
-
diff --git a/debian/patches/0002-Conf-Fix-too-early-free.patch b/debian/patches/0002-Conf-Fix-too-early-free.patch
deleted file mode 100644
index 4012798..0000000
--- a/debian/patches/0002-Conf-Fix-too-early-free.patch
+++ /dev/null
@@ -1,53 +0,0 @@
-From eefb29679957fed3724e6d5db2ddf112e28f646f Mon Sep 17 00:00:00 2001
-From: Ondrej Zajicek <santiago@crfreenet.org>
-Date: Sun, 19 Feb 2023 03:59:10 +0100
-Subject: [PATCH] Conf: Fix too early free of old configuration
-
-The change 371eb49043d225d2bab8149187b813a14b4b86d2 introduced early free
-of old_config. Unfortunately, it did not properly check whether it is not
-still in use (blocked by obstacle during reconfiguration). Fix that.
-
-It also means that we still could have a short peak when three configs
-are in use (when a new reconfig is requeste while the previous one is
-still active).
----
- conf/conf.c | 12 ++++++++++--
- 1 file changed, 10 insertions(+), 2 deletions(-)
-
-diff --git a/conf/conf.c b/conf/conf.c
-index 4e31de29..7ef729b3 100644
---- a/conf/conf.c
-+++ b/conf/conf.c
-@@ -197,8 +197,12 @@ cleanup:
- void
- config_free(struct config *c)
- {
--  if (c)
--    rfree(c->pool);
-+  if (!c)
-+    return;
-+
-+  ASSERT(!c->obstacle_count);
-+
-+  rfree(c->pool);
- }
- 
- /**
-@@ -207,10 +211,14 @@ config_free(struct config *c)
-  * This function frees the old configuration (%old_config) that is saved for the
-  * purpose of undo. It is useful before parsing a new config when reconfig is
-  * requested, to avoid keeping three (perhaps memory-heavy) configs together.
-+ * Configuration is not freed when it is still active during reconfiguration.
-  */
- void
- config_free_old(void)
- {
-+  if (!old_config || old_config->obstacle_count)
-+    return;
-+
-   tm_stop(config_timer);
-   undo_available = 0;
- 
--- 
-2.30.2
-
diff --git a/debian/patches/0003-BGP-Fix-bgp_med-handling.patch b/debian/patches/0003-BGP-Fix-bgp_med-handling.patch
deleted file mode 100644
index 47e9115..0000000
--- a/debian/patches/0003-BGP-Fix-bgp_med-handling.patch
+++ /dev/null
@@ -1,31 +0,0 @@
-From f881b98d9b48e7a60c46dffc29009a86dac63233 Mon Sep 17 00:00:00 2001
-From: Ondrej Zajicek <santiago@crfreenet.org>
-Date: Tue, 4 Apr 2023 05:20:49 +0200
-Subject: [PATCH] BGP: Fix bgp_med handling
-
-Missing translation from BGP attribute ID to eattr ID in bgp_unset_attr()
-broke automatic removal of bgp_med during export to EBGP peers.
-
-Thanks to Edward Sun for the bugreport.
----
- proto/bgp/bgp.h | 4 +++-
- 1 file changed, 3 insertions(+), 1 deletion(-)
-
-diff --git a/proto/bgp/bgp.h b/proto/bgp/bgp.h
-index a36949c2..0461ea28 100644
---- a/proto/bgp/bgp.h
-+++ b/proto/bgp/bgp.h
-@@ -587,7 +587,9 @@ bgp_set_attr_data(ea_list **to, struct linpool *pool, uint code, uint flags, voi
-   bgp_set_attr(to, pool, code, flags, (uintptr_t) a);
- }
- 
--#define bgp_unset_attr(to, pool, code) ea_unset_attr(to, pool, 0, code)
-+static inline void
-+bgp_unset_attr(ea_list **to, struct linpool *pool, uint code)
-+{ ea_unset_attr(to, pool, 0, EA_CODE(PROTOCOL_BGP, code)); }
- 
- int bgp_encode_mp_reach_mrt(struct bgp_write_state *s, eattr *a, byte *buf, uint size);
- 
--- 
-2.39.2
-
diff --git a/debian/patches/series b/debian/patches/series
index 4d002aa..e69de29 100644
--- a/debian/patches/series
+++ b/debian/patches/series
@@ -1,3 +0,0 @@
-0001-Increase-tests-timeout.patch
-0002-Conf-Fix-too-early-free.patch
-0003-BGP-Fix-bgp_med-handling.patch
diff --git a/doc/bird.sgml b/doc/bird.sgml
index 001fcbd..39eae4c 100644
--- a/doc/bird.sgml
+++ b/doc/bird.sgml
@@ -1113,14 +1113,16 @@ This argument can be omitted if there exists only a single instance.
 	Show the list of symbols defined in the configuration (names of
 	protocols, routing tables etc.).
 
-	<tag><label id="cli-show-route">show route [[for] <m/prefix/|<m/IP/] [table (<m/t/ | all)] [(import|export) table <m/p/.<m/c/] [filter <m/f/|where <m/c/] [(export|preexport|noexport) <m/p/] [protocol <m/p/] [(stats|count)] [<m/options/]</tag>
+	<tag><label id="cli-show-route">show route [[(for|in)] <m/prefix/|for <m/IP/] [table (<m/t/|all)] [(import|export) table <m/p/.<m/c/] [filter <m/f/|where <m/cond/] [(export|preexport|noexport) <m/p/] [protocol <m/p/] [(stats|count)] [<m/options/]</tag>
 	Show contents of specified routing tables, that is routes, their metrics
 	and (in case the <cf/all/ switch is given) all their attributes.
 
 	<p>You can specify a <m/prefix/ if you want to print routes for a
 	specific network. If you use <cf>for <m/prefix or IP/</cf>, you'll get
 	the entry which will be used for forwarding of packets to the given
-	destination. By default, all routes for each network are printed with
+	destination. Finally, if you use <cf>in <m/prefix/</cf>, you get all
+	prefixes covered by the given prefix.
+	By default, all routes for each network are printed with
 	the selected one at the top, unless <cf/primary/ is given in which case
 	only the selected route is shown.
 
@@ -1885,7 +1887,7 @@ protocol sections.
 <label id="babel-intro">
 
 <p>The Babel protocol
-(<rfc id="6126">) is a loop-avoiding distance-vector routing protocol that is
+(<rfc id="8966">) is a loop-avoiding distance-vector routing protocol that is
 robust and efficient both in ordinary wired networks and in wireless mesh
 networks. Babel is conceptually very simple in its operation and "just works"
 in its default configuration, though some configuration is possible and in some
@@ -1929,6 +1931,7 @@ protocol babel [<name>] {
 		check link <switch>;
 		next hop ipv4 <address>;
 		next hop ipv6 <address>;
+		extended next hop <switch>;
 		authentication none|mac [permissive];
 		password "&lt;text&gt;";
 		password "&lt;text&gt;" {
@@ -2033,6 +2036,11 @@ protocol babel [<name>] {
       source for Babel packets will be used. In normal operation, it should not
       be necessary to set this option.
 
+      <tag><label id="babel-extended-next-hop">extended next hop <m/switch/</tag>
+      If enabled, BIRD will accept and emit IPv4 routes with an IPv6 next
+      hop when IPv4 addresses are absent from the interface as described in
+      <rfc id="9229">. Default: yes.
+
       <tag><label id="babel-authentication">authentication none|mac [permissive]</tag>
       Selects authentication method to be used. <cf/none/ means that packets
       are not authenticated at all, <cf/mac/ means MAC authentication is
@@ -2394,6 +2402,8 @@ avoid routing loops.
 <item> <rfc id="8092"> - BGP Large Communities Attribute
 <item> <rfc id="8203"> - BGP Administrative Shutdown Communication
 <item> <rfc id="8212"> - Default EBGP Route Propagation Behavior without Policies
+<item> <rfc id="8654"> - Extended Message Support for BGP
+<item> <rfc id="9072"> - Extended Optional Parameters Length for BGP OPEN Message
 <item> <rfc id="9117"> - Revised Validation Procedure for BGP Flow Specifications
 <item> <rfc id="9234"> - Route Leak Prevention and Detection Using Roles
 </itemize>
@@ -2615,12 +2625,19 @@ using the following configuration parameters:
 	keeps MED attribute). Default: disabled.
 
 	<tag><label id="bgp-allow-local-pref">allow bgp_local_pref <m/switch/</tag>
-	A standard BGP implementation do not send the Local Preference attribute
-	to eBGP neighbors and ignore this attribute if received from eBGP
+	Standard BGP implementations do not send the Local Preference attribute
+	to EBGP neighbors and ignore this attribute if received from EBGP
 	neighbors, as per <rfc id="4271">.  When this option is enabled on an
-	eBGP session, this attribute will be sent to and accepted from the peer,
+	EBGP session, this attribute will be sent to and accepted from the peer,
+	which is useful for example if you have a setup like in <rfc id="7938">.
+	The option does not affect IBGP sessions. Default: off.
+
+	<tag><label id="bgp-allow-med">allow bgp_med <m/switch/</tag>
+	Standard BGP implementations do not propagate the MULTI_EXIT_DESC
+	attribute unless it is configured locally. When this option is enabled
+	on an EBGP session, this attribute will be sent to the peer regardless,
 	which is useful for example if you have a setup like in <rfc id="7938">.
-	The option does not affect iBGP sessions. Default: off.
+	The option does not affect IBGP sessions. Default: off.
 
 	<tag><label id="bgp-allow-local-as">allow local as [<m/number/]</tag>
 	BGP prevents routing loops by rejecting received routes with the local
@@ -3251,6 +3268,35 @@ protocol bgp {
 </code>
 
 
+<sect>BMP
+<label id="bmp">
+
+<p>The BGP Monitoring Protocol is used for monitoring BGP sessions and obtaining
+routing table data. The current implementation in BIRD is a preliminary release
+with a limited feature set, it will be subject to significant changes in the
+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.
+
+<sect1>Example
+<label id="bmp-exam">
+
+<p><code>
+protocol bmp {
+	# The monitoring station to connect to
+	station address ip 198.51.100.10 port 1790;
+
+	# required option
+	monitoring rib in pre_policy;
+}
+</code>
+
+
 <sect>Device
 <label id="device">
 
diff --git a/doc/roadmap.md b/doc/roadmap.md
new file mode 100644
index 0000000..f6b9d47
--- /dev/null
+++ b/doc/roadmap.md
@@ -0,0 +1,307 @@
+# Project roadmap
+
+## Planned for 2023
+
+### SNMP AgentX plugin for BIRD status export
+Allow for easier status monitoring.
+
+### BGP Monitoring Protocol (BMP)
+BGP Monitoring Protocol (RFC 7854) is a protocol between a BGP speaker and
+a monitoring node, which is notified about route updates and neighbor state
+changes of the BGP speaker.
+
+### Better coverage of automatic tests
+Functionality tests should cover more possible configurations and
+combinations. Integration tests should run automatically between different OS
+versions and HW architectures. Experimental support for performance regression tests.
+
+### Release 3.0-alpha1
+Missing: MRT, merging
+
+### Show BFD sessions details
+CLI command showing detailed information about BFD sessions state
+
+### Review and merge Babel extended next hop patches (RFC 9229)
+Babel extension to allow IPv4 routes with IPv6 next hop. Patch on mailing list.
+
+### Consolidate protocol statistics
+Consolidate protocol statistics, make them useful for SNMP plugin and implement
+'show XX stats' command.
+
+### TCP-AO if it appears in Linux and BSD upstream
+Resolve whether we should or shouldn't control the kernel key management.
+Design and implement our side for both Linux and BSD.
+
+### Conditional routes (v3)
+Filters should be extended to allow conditional expressions based on a number of
+matching routes in a routing table. This would allow to specify aggregate routes
+using a static protocol and conditions like 'if there is at least 1000 routes
+from this BGP protocol, accept this default route'. This feature comes handy
+when a router needs to detect whether its BGP upstream is alive and working.
+Based of number of routes received, the router can then announce or retract a
+default route to OSPF, making multi-exit network routing simpler and more
+effective.
+
+### Aggregating routes
+Requested by customer: aggregating multiple routes by a common set of attributes.
+
+Implementation choice: the user specifies
+
+    EXPORT filter before aggregation AGGREGATE ON list of expressions to compare MERGE what to do with the remaining attributes
+
+Example usage:
+
+* aggregating information from multiple internal BGP routes into one external
+* creating a multipath route from multiple BGP routes (currently done by MERGE PATHS)
+* (in future) computing a minimal route set for kernel to make forwarding faster instead of writing the received full BGP set there
+
+### PREF64 option in RA (RFC 8781)
+Inform hosts about prefix used to synthesize NAT64 addresses. Requested in list:
+http://trubka.network.cz/pipermail/bird-users/2022-November/016401.html
+
+### Logging via UDP
+Got a patch, probably never merged. May be useful.
+http://trubka.network.cz/pipermail/bird-users/2022-January/015893.html
+
+### BGP Tunnel Encapsulation Attribute (RFC 9012)
+Packets sent to BGP next hop may be encapsulated using various tunnel
+technologies. Useful for L3VPN.
+
+### BGP AS Cones and ASPA support
+Extend the RPKI protocol with AS Cones and ASPA loading. Implement AS Cones
+and ASPA validation routines. There may be some pending patches from QRator.
+
+### DHCPv6 relay agent
+DHCPv6 relay agents (RFC 8415, RFC 8987) forward DHCPv6 messages between clients and
+servers. They also ensure that prefixes delegated by DHCPv6-PD are routable,
+i.e. they should generate routes for these prefixes.
+
+### Nexthop attributes and ECMP filtering
+Currently we have route attributes, but with ECMP routes it is necessary to
+store per-nexthop data (like weight or encapsulation). We also do not have
+proper way to manipulate with multiple nexthops from filters. Attributes should
+be extended to allow per-nexthop ones and filters should be extended to allow
+access multiple nexthops and their attributes.
+
+### Performance accounting
+Extended internal statistics about time spent in different modules of BIRD. If
+the route server admin checks why it takes 15 minutes to converge, this should
+give some basic info about performance. [MM: Internally needed by 3.0, already in progress]
+
+### MPLS support
+Finalize and merge improved MPLS infrastructure (including MPLS label allocator
+and supporting code), improve its reconfiguration support and support for
+segment routing.
+
+### BGP Segment Routing Extension (RFC 8669)
+Receive and announce Segment Identifiers (SIDs) for BGP next hops.
+
+## Backlog for following years
+
+*The order of these items is not significant.*
+
+### Flowspec attribute filtering
+Flowspec routes have many parameters, but these are not accessible from filters.
+Filters should be extended to access all these attributes, but first it is
+necessary to cleanup attribute handling in filters.
+
+### BGP Optimal Route Reflection (RFC 9107)
+Implement BGP best route selection on route reflectors to adhere to POV of
+client, not RR. Also requested by somebody, don't remember who and when.
+
+### OSPF Traffic engineering extensions (RFC 3630)
+Requested in list. May include lots of other RFC's as we have neglected this
+feature for a long time.
+http://trubka.network.cz/pipermail/bird-users/2022-January/015911.html
+
+### IPv6 preference in documentation (?)
+Address world's reluctance of legacy IPv4 deprecation by updating the
+documentation in such a way that IPv6 is preferred and first seen.
+
+### BGP local prefix leak prevention (?)
+Reject local prefixes on eBGP sessions by default to prevent leaks to public Internet.
+Unless explicitly enabled by config, of course.
+
+### Re-bogonization of 240/4 legacy range (?)
+We shouldn't believe that every operator does the
+filtering right and they could simply rely on pre-2.0.10 behavior which
+filtered this out by default.
+
+### IPv4 multicast
+Basic infrastructure for IPv4 multicast routing, including nettypes for
+multicast routes and multicast requests, multicast kernel protocol and IGMPv2
+protocol.
+
+### PIM-BIDIR
+Bidirectional PIM (RFC 5015) is a multicast routing protocol, variant of PIM-SM.
+It uses bidirectional shared trees rooted in Rendezvous Point (RP) to connect
+sources and receivers.
+
+There is an old branch containing this. We should have merged this years ago.
+
+### Improved VRF support
+BIRD has working VRF support, but it needs improvements. VRF entities should be
+first-class objects with explicit configuration, with a set of properties and
+default values (like default routing tables, or router ID) for associated
+protocols. Default kernel table ID should be autodetected. There should be
+better handling of VRF route leaking - when a route is propagated between VRFs,
+its nexthop should reflects that. Setup of VRFs in OS is out of scope.
+
+### Linux kernel nexthop abstraction
+Netlink allows setting nexthops as objects and using them in routes. It should
+be much faster than conventional route update.
+
+### Protocol attributes for filtering
+Filters can access route attributes, but sometimes it could be useful to access
+attributes of associated protocol (like neighbor-as or neighbor-ip for BGP
+protocol). But it would require to have internal object model (below) first,
+as we do not want to implement it independently for each protocol attribute.
+
+### Mutable static routes
+Extension to the static protocol that would allow to add/remove/change static
+routes from CLI.
+
+### Multipipe
+Pipe-like protocol: When a route is exported to this protocol, it runs its
+filter extended with capability to announce any number of new routes to any
+table from one filter run. Its primary purpose is to allow user-specified
+route aggregation and other non-linear operations.
+
+### BGP minimum route advertisement interval (MRAI)
+BGP specifies minimum interval between route advertisements for the same
+network. This is not implemented in BIRD. It should be implemented for 3.0 to
+avoid unnecessary re-routing spikes.
+
+### OSPF unnumbered interfaces
+The OSPFv2 protocol allows interfaces that do not have proper IP range but have
+peer IP addresses (like PtP links). It should be extended to also allow true
+unnumbered interfaces with no addresses (by using an IP address from some
+loopback device). This would require to have stricter separation between IP
+addresses and interfaces in OSPFv2.
+
+### OSPF Segment Routing Extension (RFC 8665)
+MPLS label distribution using segment routing and simple OSPF extension.
+
+### MPLS Label Distribution Protocol (LDP)
+Label Distribution Protocol (RFC 5036) is a protocol for establishing
+label-switched paths and distributing of MPLS labels between MPLS routers.
+These paths and labels are based on existing unlabeled routing information.
+
+### IPv6 multicast
+Basic infrastructure for IPv6 multicast routing, including nettypes for
+multicast routes and multicast requests, multicast kernel protocol and MLDv1
+protocol. Most of these (with the exception of MLDv1) is just a variant of
+IPv4 multicast.
+
+### IGMP/MLD multicast proxy
+A simple IGMP/MLD multicast proxy, which sends IGMP/MLD requests on a configured
+uplink interface based on received requests on downlink interfaces, and updates
+associated multicast routes.
+
+### Source-specific multicast (SSM)
+Infrastructure for multicasts should be extended to handle source-specific
+multicasts. Extend multicast nettypes to include source addresses, handle them
+in multicast kernel protocols and implement IGMPv3/MLDv2 protocols.
+
+### PIM-SSM
+PIM-SSM is a source-specific multicast routing protocol, a subset of PIM-SM
+protocol (RFC 7761). It is restricted to source-specific multicasts, which
+eliminates many problematic parts of PIM-SM.
+
+### Seamless BFD
+New version of BFD negotiation defined in RFC 7880-7886 enables faster
+continuity tests by dissemination discriminators by the governing protocols.
+
+### OSPF Graceful Link Shutdown
+To enable seamless maintenance of single links, OSPF can advertise such a link
+getting down in advance, allowing to re-route. Defined in RFC 8379.
+
+## Long-term
+
+### Internal object model
+We need to define explicit internal object model, where existing objects
+(protocols, channels, tables, routes, interfaces ...) and their properties are
+described in a way that allows introspection sufficient for implementing
+features (control protocol, CLI, filter access, perhaps reconfiguration) in a
+generic manner.
+
+### Generic configuration model
+Configuration options are implicitly defined by the configuration parsing code.
+We need to define explicit configuration model independent of the parsing code
+and generic parsing code using that model. This will allow uniform validation of
+configuration properties, generic access to configuration from control protocol
+and possibly independent configuration backends (like one for Netconf).
+
+### New control protocol
+BIRD should have a well-documented machine readable protocol. Requirements for
+such protocol are:
+
+* Generic machine readable abstract-tree representation (like CBOR)
+* Both request/reply and subscribe/notify access patterns
+* Access objects and properties using internal object model
+* In-band introspection based on internal object model
+
+From Maria's notes:
+
+* CBOR-based protocol for both control and route exports
+* Python3 library with example implementation of CLI
+* (maybe) Ansible modules
+* RFC 9164: CBOR tags for IP addresses and prefices
+* RFC 9254: YANG-CBOR mapping
+* RFC 9277: Stable storage of CBOR (files)
+
+## Perhaps
+
+### IS-IS
+IS-IS routing protocol is a nice-to-have alternative to OSPF.
+
+### BGPsec
+BGPsec (RFC 8205) is a new path security extension to BGP.
+
+### PIM-SM
+PIM-SM (RFC 7761) is a prevailing multicast routing protocol, but more
+complicated than planned PIM-BIDIR and PIM-SSM.
+
+### Netconf
+Network Configuration Protocol (RFC 6241) is a XML/JSON protocol for
+configuration management of network devices. It would benefit from generic
+configuration model (above).
+
+### NetConf overlay
+Machine-friendly config file editor daemon (standalone) with standard NetConf
+interface on one side and BIRD config file + reconfiguration requests on the
+other side. Python3 seems to be better choice than C for this kind of work.
+
+### Backend for 802.11r
+Let's assume a bunch of boxes, all having some public wifi APs and some (secure) uplinks.
+Design and implement an automatic backbone protocol to allow for simple almost-zeroconf
+setup of e.g. a conference room or train / bus public wifi or even a local home network,
+all with hostapd seamlessly transferring clients between APs via 802.11r.
+Possible collab with Turris.
+
+### BFD Multipoint Connectivity
+Checking whether multiple "receivers" can communicate with a single "sender".
+Possibly useful after merging PIM-BIDIR and implementing other PIMs. RFC 8562-8563.
+
+### BGP Link State extension
+BGP-LS allows to transport information about network topology across BGP links.
+This should help e.g. to run traffic-engineering between more confederated ASs.
+Also needed to implement Seamless BFD on BGP: RFC 9247
+
+### Locator/ID Separation Protocol
+LISP intends to break up addressing to Routing Locators and Endpoint
+Identifiers. This may help multihoming networks in future. RFC 9299-9306.
+
+### Backend for IPv6 Multihoming without BGP
+Implement and configure BIRD in such a way that local nodes are seamlessly
+connected to the Internet via multiple upstreams, using Network Prefix
+Translation and other techniques. Possible collab with Turris.
+
+## Minor
+
+* RFC 8510: OSPF LLS Extension for Local Interface ID Advertisement
+* RFC 8538: BGP Graceful Restart Hard Reset
+* RFC 8326: BGP Graceful Session Shutdown Community auto-apply
+* RFC 8962: Become part of the IETF Protocol Police
+* RFC 9072: Extended Optional Parameters Length for BGP OPEN Message
+* RFC 9339: OSPF Reverse Metric
diff --git a/filter/decl.m4 b/filter/decl.m4
index b602686..7c863bd 100644
--- a/filter/decl.m4
+++ b/filter/decl.m4
@@ -200,7 +200,7 @@ FID_INTERPRET_BODY()')
 #	Executing another filter line. This replaces the recursion
 #	that was needed in the former implementation.
 m4_define(LINEX, `FID_INTERPRET_EXEC()LINEX_($1)FID_INTERPRET_NEW()return $1 FID_INTERPRET_BODY()')
-m4_define(LINEX_, `do {
+m4_define(LINEX_, `do if ($1) {
   fstk->estk[fstk->ecnt].pos = 0;
   fstk->estk[fstk->ecnt].line = $1;
   fstk->estk[fstk->ecnt].ventry = fstk->vcnt;
@@ -227,9 +227,7 @@ if (!f_same(f1->fl$1, f2->fl$1)) return 0;
 FID_ITERATE_BODY()m4_dnl
 if (whati->fl$1) BUFFER_PUSH(fit->lines) = whati->fl$1;
 FID_INTERPRET_EXEC()m4_dnl
-do { if (whati->fl$1) {
-  LINEX_(whati->fl$1);
-} } while(0)
+LINEX_(whati->fl$1)
 FID_INTERPRET_NEW()m4_dnl
 return whati->f$1
 FID_INTERPRET_BODY()')
@@ -486,7 +484,7 @@ f_instruction_name_(enum f_instruction_code fi)
 static inline struct f_inst *
 fi_new(enum f_instruction_code fi_code)
 {
-  struct f_inst *what = cfg_allocz(sizeof(struct f_inst));
+  struct f_inst *what = tmp_allocz(sizeof(struct f_inst));
   what->lineno = ifs->lino;
   what->size = 1;
   what->fi_code = fi_code;
diff --git a/filter/f-inst.c b/filter/f-inst.c
index e4b47ff..3343685 100644
--- a/filter/f-inst.c
+++ b/filter/f-inst.c
@@ -1327,7 +1327,6 @@
 	FID_HIC(,break,return NULL);
       }
     }
-    /* It is actually possible to have t->data NULL */
 
     LINEX(t->data);
   }
diff --git a/filter/f-inst.h b/filter/f-inst.h
index e35f71c..72b080f 100644
--- a/filter/f-inst.h
+++ b/filter/f-inst.h
@@ -100,8 +100,6 @@ static inline struct f_dynamic_attr f_new_dynamic_attr_bit(u8 bit, enum f_type f
 { return (struct f_dynamic_attr) { .type = EAF_TYPE_BITFIELD, .bit = bit, .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_inst *f_generate_complex(enum f_instruction_code fi_code, 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);
 
 /* Hook for call bt_assert() function in configuration */
 extern void (*bt_assert_hook)(int result, const struct f_line_item *assert);
diff --git a/filter/test.conf b/filter/test.conf
index 1d291c6..e9e3af8 100644
--- a/filter/test.conf
+++ b/filter/test.conf
@@ -38,6 +38,18 @@ bt_test_same(onef, onef, 1);
 bt_test_same(onef, oneg, 1);
 bt_test_same(onef, twof, 0);
 
+/*
+ *	Testing filter corner cases
+ *	---------------------------
+ */
+
+function t_nothing() {}
+bt_test_suite(t_nothing, "Testing nothing");
+
+function t_metanothing() { t_nothing(); }
+bt_test_suite(t_metanothing, "Testing meta nothing");
+
+
 /*
  * 	Testing boolean expressions
  * 	---------------------------
@@ -76,6 +88,14 @@ bt_test_suite(t_bool, "Testing boolean expressions");
  *	----------------
  */
 
+function aux_t_int(int t; int u)
+{
+	case t {
+		1: {}
+		else: {}
+	}
+}
+
 define four = 4;
 define xyzzy = (120+10);
 define '1a-a1' = (xyzzy-100);
@@ -120,7 +140,10 @@ function t_int()
 		else: bt_assert(false);
 	}
 
-
+	aux_t_int(1, 2);
+	aux_t_int(1, 3);
+	aux_t_int(2, 3);
+	aux_t_int(2, 2);
 }
 
 bt_test_suite(t_int, "Testing integers");
@@ -542,10 +565,10 @@ prefix set pxs;
 	bt_assert(format([ 0.0.0.0/0 ]) = "[0.0.0.0/0]");
 	bt_assert(format([ 10.10.0.0/32 ]) = "[10.10.0.0/32{0.0.0.1}]");
 	bt_assert(format([ 10.10.0.0/17 ]) = "[10.10.0.0/17{0.0.128.0}]");
-	bt_assert(format([ 10.10.0.0/17{17,19} ]) = "[10.10.0.0/17{0.0.224.0}]"); # 224 = 128+64+32
+	# bt_assert(format([ 10.10.0.0/17{17,19} ]) = "[10.10.0.0/17{0.0.224.0}]"); # 224 = 128+64+32
 	bt_assert(format([ 10.10.128.0/17{18,19} ]) = "[10.10.128.0/18{0.0.96.0}, 10.10.192.0/18{0.0.96.0}]"); # 96 = 64+32
-	bt_assert(format([ 10.10.64.0/18- ]) = "[0.0.0.0/0, 0.0.0.0/1{128.0.0.0}, 0.0.0.0/2{64.0.0.0}, 0.0.0.0/3{32.0.0.0}, 10.10.0.0/16{255.255.0.0}, 10.10.0.0/17{0.0.128.0}, 10.10.64.0/18{0.0.64.0}]");
-	bt_assert(format([ 10.10.64.0/18+ ]) = "[10.10.64.0/18{0.0.96.0}, 10.10.64.0/20{0.0.31.255}, 10.10.80.0/20{0.0.31.255}, 10.10.96.0/20{0.0.31.255}, 10.10.112.0/20{0.0.31.255}]");
+	# bt_assert(format([ 10.10.64.0/18- ]) = "[0.0.0.0/0, 0.0.0.0/1{128.0.0.0}, 0.0.0.0/2{64.0.0.0}, 0.0.0.0/3{32.0.0.0}, 10.10.0.0/16{255.255.0.0}, 10.10.0.0/17{0.0.128.0}, 10.10.64.0/18{0.0.64.0}]");
+	# bt_assert(format([ 10.10.64.0/18+ ]) = "[10.10.64.0/18{0.0.96.0}, 10.10.64.0/20{0.0.31.255}, 10.10.80.0/20{0.0.31.255}, 10.10.96.0/20{0.0.31.255}, 10.10.112.0/20{0.0.31.255}]");
 
 	bt_assert(format([ 10.10.160.0/19 ]) = "[10.10.160.0/19{0.0.32.0}]");
 	bt_assert(format([ 10.10.160.0/19{19,22} ]) = "[10.10.160.0/19{0.0.32.0}, 10.10.160.0/20{0.0.28.0}, 10.10.176.0/20{0.0.28.0}]"); # 28 = 16+8+4
@@ -557,10 +580,10 @@ prefix set pxs;
 	bt_assert(format([ 11:22:33:44::/64+ ]) = "[11:22:33:44::/64{::1:ffff:ffff:ffff:ffff}]");
 
 	bt_assert(format([ 11:22:33:44::/65 ]) = "[11:22:33:44::/65{::8000:0:0:0}]");
-	bt_assert(format([ 11:22:33:44::/65{65,67} ]) = "[11:22:33:44::/65{::e000:0:0:0}]"); # e = 8+4+2
+	# bt_assert(format([ 11:22:33:44::/65{65,67} ]) = "[11:22:33:44::/65{::e000:0:0:0}]"); # e = 8+4+2
 	bt_assert(format([ 11:22:33:44:8000::/65{66,67} ]) = "[11:22:33:44:8000::/66{::6000:0:0:0}, 11:22:33:44:c000::/66{::6000:0:0:0}]"); # 6 = 4+2
-	bt_assert(format([ 11:22:33:44:4000::/66- ]) = "[::/0, ::/1{8000::}, ::/2{4000::}, ::/3{2000::}, 11:22:33:44::/64{ffff:ffff:ffff:ffff::}, 11:22:33:44::/65{::8000:0:0:0}, 11:22:33:44:4000::/66{::4000:0:0:0}]");
-	bt_assert(format([ 11:22:33:44:4000::/66+ ]) = "[11:22:33:44:4000::/66{::6000:0:0:0}, 11:22:33:44:4000::/68{::1fff:ffff:ffff:ffff}, 11:22:33:44:5000::/68{::1fff:ffff:ffff:ffff}, 11:22:33:44:6000::/68{::1fff:ffff:ffff:ffff}, 11:22:33:44:7000::/68{::1fff:ffff:ffff:ffff}]");
+	# bt_assert(format([ 11:22:33:44:4000::/66- ]) = "[::/0, ::/1{8000::}, ::/2{4000::}, ::/3{2000::}, 11:22:33:44::/64{ffff:ffff:ffff:ffff::}, 11:22:33:44::/65{::8000:0:0:0}, 11:22:33:44:4000::/66{::4000:0:0:0}]");
+	# bt_assert(format([ 11:22:33:44:4000::/66+ ]) = "[11:22:33:44:4000::/66{::6000:0:0:0}, 11:22:33:44:4000::/68{::1fff:ffff:ffff:ffff}, 11:22:33:44:5000::/68{::1fff:ffff:ffff:ffff}, 11:22:33:44:6000::/68{::1fff:ffff:ffff:ffff}, 11:22:33:44:7000::/68{::1fff:ffff:ffff:ffff}]");
 	bt_assert(format([ 11:22:33:44:c000::/67 ]) = "[11:22:33:44:c000::/67{::2000:0:0:0}]");
 	bt_assert(format([ 11:22:33:44:c000::/67{67,71} ]) = "[11:22:33:44:c000::/67{::2000:0:0:0}, 11:22:33:44:c000::/68{::1e00:0:0:0}, 11:22:33:44:d000::/68{::1e00:0:0:0}]");
 	bt_assert(format([ 11:22:33:44:c000::/67+ ]) = "[11:22:33:44:c000::/67{::2000:0:0:0}, 11:22:33:44:c000::/68{::1fff:ffff:ffff:ffff}, 11:22:33:44:d000::/68{::1fff:ffff:ffff:ffff}]");
diff --git a/lib/mempool.c b/lib/mempool.c
index 325b1ec..9d4404f 100644
--- a/lib/mempool.c
+++ b/lib/mempool.c
@@ -187,11 +187,18 @@ lp_flush(linpool *m)
 {
   struct lp_chunk *c;
 
-  /* Move ptr to the first chunk and free all large chunks */
+  /* Move ptr to the first chunk and free all other chunks */
   m->current = c = m->first;
   m->ptr = c ? c->data : NULL;
   m->end = c ? c->data + LP_DATA_SIZE : NULL;
 
+  while (c && c->next)
+  {
+    struct lp_chunk *d = c->next;
+    c->next = d->next;
+    free_page(d);
+  }
+
   while (c = m->first_large)
     {
       m->first_large = c->next;
@@ -233,9 +240,9 @@ 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 + LP_DATA_SIZE : NULL;
+  m->current = c = p->current ?: m->first;
+  m->ptr = p->ptr ?: (c ? c->data : NULL);
+  m->end = c ? (c->data + LP_DATA_SIZE) : NULL;
   m->total_large = p->total_large;
 
   while ((c = m->first_large) && (c != p->large))
diff --git a/lib/net.c b/lib/net.c
index 976ddbc..289a429 100644
--- a/lib/net.c
+++ b/lib/net.c
@@ -57,6 +57,19 @@ const u16 net_max_text_length[] = {
   [NET_MPLS]	= 7,	/* "1048575" */
 };
 
+/* There should be no implicit padding in net_addr structures */
+STATIC_ASSERT(sizeof(net_addr)		== 24);
+STATIC_ASSERT(sizeof(net_addr_ip4)	==  8);
+STATIC_ASSERT(sizeof(net_addr_ip6)	== 20);
+STATIC_ASSERT(sizeof(net_addr_vpn4)	== 16);
+STATIC_ASSERT(sizeof(net_addr_vpn6)	== 32);
+STATIC_ASSERT(sizeof(net_addr_roa4)	== 16);
+STATIC_ASSERT(sizeof(net_addr_roa6)	== 28);
+STATIC_ASSERT(sizeof(net_addr_flow4)	==  8);
+STATIC_ASSERT(sizeof(net_addr_flow6)	== 20);
+STATIC_ASSERT(sizeof(net_addr_ip6_sadr)	== 40);
+STATIC_ASSERT(sizeof(net_addr_mpls)	==  8);
+
 
 int
 rd_format(const u64 rd, char *buf, int buflen)
@@ -310,22 +323,3 @@ net_in_netX(const net_addr *a, const net_addr *n)
 
   return (net_pxlen(n) <= net_pxlen(a)) && ipa_in_netX(net_prefix(a), n);
 }
-
-#define CHECK_NET(T,S) \
-  ({ if (sizeof(T) != S) die("sizeof %s is %d/%d", #T, (int) sizeof(T), S); })
-
-void
-net_init(void)
-{
-  CHECK_NET(net_addr,		24);
-  CHECK_NET(net_addr_ip4,	 8);
-  CHECK_NET(net_addr_ip6,	20);
-  CHECK_NET(net_addr_vpn4,	16);
-  CHECK_NET(net_addr_vpn6,	32);
-  CHECK_NET(net_addr_roa4,	16);
-  CHECK_NET(net_addr_roa6,	28);
-  CHECK_NET(net_addr_flow4,	 8);
-  CHECK_NET(net_addr_flow6,	20);
-  CHECK_NET(net_addr_ip6_sadr,	40);
-  CHECK_NET(net_addr_mpls,	 8);
-}
diff --git a/lib/net.h b/lib/net.h
index 9f4a00a..da7254c 100644
--- a/lib/net.h
+++ b/lib/net.h
@@ -620,6 +620,4 @@ static inline int net_in_net_src_ip6_sadr(const net_addr_ip6_sadr *a, const net_
 int ipa_in_netX(const ip_addr A, const net_addr *N);
 int net_in_netX(const net_addr *A, const net_addr *N);
 
-void net_init(void);
-
 #endif
diff --git a/lib/printf_test.c b/lib/printf_test.c
index 47ea905..88ecf05 100644
--- a/lib/printf_test.c
+++ b/lib/printf_test.c
@@ -32,11 +32,14 @@ t_simple(void)
   BSPRINTF(1, "@", buf, "@", 64);
   BSPRINTF(1, "\xff", buf, "%c", 0xff);
 
-  errno = 5;
-  BSPRINTF(18, "Input/output error", buf, "%m");
+  const char *io_error_str = lp_strdup(tmp_linpool, strerror(EIO));
+  const int io_error_len = strlen(io_error_str);
+
+  errno = EIO;
+  BSPRINTF(io_error_len, io_error_str, buf, "%m");
   errno = 0;
 
-  BSPRINTF(18, "Input/output error", buf, "%M", 5);
+  BSPRINTF(io_error_len, io_error_str, buf, "%M", EIO);
 
   BSPRINTF(11, "TeSt%StRiNg", buf, "%s", "TeSt%StRiNg");
 
diff --git a/nest/Makefile b/nest/Makefile
index a2e30ee..163a119 100644
--- a/nest/Makefile
+++ b/nest/Makefile
@@ -3,10 +3,12 @@ obj := $(src-o-files)
 $(all-daemon)
 $(cf-local)
 
-$(objdir)/nest/proto-build.c: $(lastword $(MAKEFILE_LIST))
+$(o)proto-build.c: Makefile $(lastword $(MAKEFILE_LIST)) $(objdir)/.dir-stamp
 	$(E)echo GEN $@
 	$(Q)echo "$(patsubst %,void %_build(void); ,$(PROTO_BUILD)) void protos_build_gen(void) { $(patsubst %,  %_build(); ,$(PROTO_BUILD))}" > $@
 
+prepare: $(o)proto-build.c
+
 tests_src := a-set_test.c a-path_test.c
 tests_targets := $(tests_targets) $(tests-target-files)
 tests_objs := $(tests_objs) $(src-o-files)
diff --git a/nest/config.Y b/nest/config.Y
index b2aa090..e78350c 100644
--- a/nest/config.Y
+++ b/nest/config.Y
@@ -632,7 +632,7 @@ CF_CLI(SHOW INTERFACES SUMMARY,,, [[Show summary of network interfaces]])
 { if_show_summary(); } ;
 
 CF_CLI_HELP(SHOW ROUTE, ..., [[Show routing table]])
-CF_CLI(SHOW ROUTE, r_args, [[[<prefix>|for <prefix>|for <ip>] [table <t>] [(import|export) table <p>.<c>] [filter <f>|where <cond>] [all] [primary] [filtered] [(export|preexport|noexport) <p>] [protocol <p>] [stats|count]]], [[Show routing table]])
+CF_CLI(SHOW ROUTE, r_args, [[[<prefix>|for <prefix>|for <ip>|in <prefix>] [table <t>] [(import|export) table <p>.<c>] [filter <f>|where <cond>] [all] [primary] [filtered] [(export|preexport|noexport) <p>] [protocol <p>] [stats|count]]], [[Show routing table]])
 { rt_show($3); } ;
 
 r_args:
diff --git a/nest/protocol.h b/nest/protocol.h
index fcbf053..da6d434 100644
--- a/nest/protocol.h
+++ b/nest/protocol.h
@@ -42,6 +42,7 @@ enum protocol_class {
   PROTOCOL_BABEL,
   PROTOCOL_BFD,
   PROTOCOL_BGP,
+  PROTOCOL_BMP,
   PROTOCOL_DEVICE,
   PROTOCOL_DIRECT,
   PROTOCOL_KERNEL,
@@ -103,7 +104,7 @@ void protos_dump_all(void);
 extern struct protocol
   proto_device, proto_radv, proto_rip, proto_static, proto_mrt,
   proto_ospf, proto_perf,
-  proto_pipe, proto_bgp, proto_bfd, proto_babel, proto_rpki;
+  proto_pipe, proto_bgp, proto_bmp, proto_bfd, proto_babel, proto_rpki;
 
 /*
  *	Routing Protocol Instance
@@ -213,6 +214,7 @@ struct proto {
   void (*if_notify)(struct proto *, unsigned flags, struct iface *i);
   void (*ifa_notify)(struct proto *, unsigned flags, struct ifa *a);
   void (*rt_notify)(struct proto *, struct channel *, struct network *net, struct rte *new, struct rte *old);
+  void (*rte_update_in_notify)(struct channel *, const net_addr *, const struct rte *, const struct rte_src *);
   void (*neigh_notify)(struct neighbor *neigh);
   int (*preexport)(struct channel *, struct rte *rt);
   void (*reload_routes)(struct channel *);
diff --git a/nest/rt-table.c b/nest/rt-table.c
index e4b2781..2b06503 100644
--- a/nest/rt-table.c
+++ b/nest/rt-table.c
@@ -3095,6 +3095,10 @@ rte_update_in(struct channel *c, const net_addr *n, rte *new, struct rte_src *sr
 	if (old->flags & (REF_STALE | REF_DISCARD | REF_MODIFY))
 	{
 	  old->flags &= ~(REF_STALE | REF_DISCARD | REF_MODIFY);
+
+	  if (c->proto->rte_update_in_notify)
+	    c->proto->rte_update_in_notify(c, n, old, src);
+
 	  return 1;
 	}
 
@@ -3121,6 +3125,9 @@ rte_update_in(struct channel *c, const net_addr *n, rte *new, struct rte_src *sr
     if (!net->routes)
       fib_delete(&tab->fib, net);
 
+    if (c->proto->rte_update_in_notify)
+      c->proto->rte_update_in_notify(c, n, NULL, src);
+
     return 1;
   }
 
@@ -3149,6 +3156,10 @@ rte_update_in(struct channel *c, const net_addr *n, rte *new, struct rte_src *sr
   e->next = *pos;
   *pos = e;
   tab->rt_count++;
+
+  if (c->proto->rte_update_in_notify)
+    c->proto->rte_update_in_notify(c, n, e, src);
+
   return 1;
 
 drop_update:
diff --git a/proto/Doc b/proto/Doc
index ef573d2..9de9eee 100644
--- a/proto/Doc
+++ b/proto/Doc
@@ -2,6 +2,7 @@ H Protocols
 C babel
 C bfd
 C bgp
+C bmp
 C ospf
 C pipe
 C radv
diff --git a/proto/babel/babel.c b/proto/babel/babel.c
index ff8b6b5..9f33dd3 100644
--- a/proto/babel/babel.c
+++ b/proto/babel/babel.c
@@ -14,9 +14,8 @@
 /**
  * DOC: The Babel protocol
  *
- * Babel (RFC6126) is a loop-avoiding distance-vector routing protocol that is
- * robust and efficient both in ordinary wired networks and in wireless mesh
- * networks.
+ * The Babel is a loop-avoiding distance-vector routing protocol that is robust
+ * and efficient both in ordinary wired networks and in wireless mesh networks.
  *
  * The Babel protocol keeps state for each neighbour in a &babel_neighbor
  * struct, tracking received Hello and I Heard You (IHU) messages. A
@@ -33,6 +32,12 @@
  * an entry is updated by receiving updates from the network or when modified by
  * internal timers. The function selects from feasible and reachable routes the
  * one with the lowest metric to be announced to the core.
+ *
+ * Supported standards:
+ * RFC 8966 - The Babel Routing Protocol
+ * RFC 8967 - MAC Authentication for Babel
+ * RFC 9079 - Source Specific Routing for Babel
+ * RFC 9229 - IPv4 Routes with IPv6 Next Hop for Babel
  */
 
 #include <stdlib.h>
@@ -49,6 +54,10 @@
 static inline int ge_mod64k(uint a, uint b)
 { return (u16)(a - b) < 0x8000; }
 
+/* Strict inequality version of the above */
+static inline int gt_mod64k(uint a, uint b)
+{ return ge_mod64k(a, b) && a != b; }
+
 static void babel_expire_requests(struct babel_proto *p, struct babel_entry *e);
 static void babel_select_route(struct babel_proto *p, struct babel_entry *e, struct babel_route *mod);
 static inline void babel_announce_retraction(struct babel_proto *p, struct babel_entry *e);
@@ -101,7 +110,8 @@ babel_find_source(struct babel_entry *e, u64 router_id)
 }
 
 static struct babel_source *
-babel_get_source(struct babel_proto *p, struct babel_entry *e, u64 router_id)
+babel_get_source(struct babel_proto *p, struct babel_entry *e, u64 router_id,
+                 u16 initial_seqno)
 {
   struct babel_source *s = babel_find_source(e, router_id);
 
@@ -111,7 +121,7 @@ babel_get_source(struct babel_proto *p, struct babel_entry *e, u64 router_id)
   s = sl_allocz(p->source_slab);
   s->router_id = router_id;
   s->expires = current_time() + BABEL_GARBAGE_INTERVAL;
-  s->seqno = 0;
+  s->seqno = initial_seqno;
   s->metric = BABEL_INFINITY;
   add_tail(&e->sources, NODE s);
 
@@ -289,7 +299,7 @@ babel_expire_routes(struct babel_proto *p)
 }
 
 /*
- * Add seqno request to the table of pending requests (RFC 6216 3.2.6) and send
+ * Add seqno request to the table of pending requests (RFC 8966 3.2.6) and send
  * it to network. Do nothing if it is already in the table.
  */
 
@@ -559,7 +569,7 @@ babel_is_feasible(struct babel_source *s, u16 seqno, u16 metric)
 {
   return !s ||
     (metric == BABEL_INFINITY) ||
-    (seqno > s->seqno) ||
+    gt_mod64k(seqno, s->seqno) ||
     ((seqno == s->seqno) && (metric < s->metric));
 }
 
@@ -998,8 +1008,18 @@ babel_send_update_(struct babel_iface *ifa, btime changed, struct fib *rtable)
     msg.update.router_id = e->router_id;
     net_copy(&msg.update.net, e->n.addr);
 
-    msg.update.next_hop = ((e->n.addr->type == NET_IP4) ?
-			   ifa->next_hop_ip4 : ifa->next_hop_ip6);
+    if (e->n.addr->type == NET_IP4)
+    {
+      /* Always prefer IPv4 nexthop if set */
+      if (ipa_nonzero(ifa->next_hop_ip4))
+        msg.update.next_hop = ifa->next_hop_ip4;
+
+      /* Only send IPv6 nexthop if enabled */
+      else if (ifa->cf->ext_next_hop)
+        msg.update.next_hop = ifa->next_hop_ip6;
+    }
+    else
+      msg.update.next_hop = ifa->next_hop_ip6;
 
     /* Do not send route if next hop is unknown, e.g. no configured IPv4 address */
     if (ipa_zero(msg.update.next_hop))
@@ -1007,13 +1027,13 @@ babel_send_update_(struct babel_iface *ifa, btime changed, struct fib *rtable)
 
     babel_enqueue(&msg, ifa);
 
-    /* Update feasibility distance for redistributed routes */
+    /* RFC 8966 3.7.3 - update feasibility distance for redistributed routes */
     if (e->router_id != p->router_id)
     {
-      struct babel_source *s = babel_get_source(p, e, e->router_id);
+      struct babel_source *s = babel_get_source(p, e, e->router_id, msg.update.seqno);
       s->expires = current_time() + BABEL_GARBAGE_INTERVAL;
 
-      if ((msg.update.seqno > s->seqno) ||
+      if (gt_mod64k(msg.update.seqno, s->seqno) ||
 	  ((msg.update.seqno == s->seqno) && (msg.update.metric < s->metric)))
       {
 	s->seqno = msg.update.seqno;
@@ -1258,6 +1278,13 @@ babel_handle_update(union babel_msg *m, struct babel_iface *ifa)
     return;
   }
 
+  /* Reject IPv4 via IPv6 routes if disabled */
+  if ((msg->net.type == NET_IP4) && ipa_is_ip6(msg->next_hop) && !ifa->cf->ext_next_hop)
+  {
+    DBG("Babel: Ignoring disabled IPv4 via IPv6 route.\n");
+    return;
+  }
+
   /* Retraction */
   if (msg->metric == BABEL_INFINITY)
   {
@@ -1303,10 +1330,11 @@ babel_handle_update(union babel_msg *m, struct babel_iface *ifa)
   best = e->selected;
 
   /*
-   * RFC section 3.8.2.2 - Dealing with unfeasible updates. Generate a one-off
-   * (not retransmitted) unicast seqno request to the originator of this update
+   * RFC 8966 3.8.2.2 - dealing with unfeasible updates. Generate a one-off
+   * (not retransmitted) unicast seqno request to the originator of this update.
+   * Note: !feasible -> s exists, check for 's' is just for clarity / safety.
    */
-  if (!feasible && (metric != BABEL_INFINITY) &&
+  if (!feasible && s && (metric != BABEL_INFINITY) &&
       (!best || (r == best) || (metric < best->metric)))
     babel_generate_seqno_request(p, e, s->router_id, s->seqno + 1, nbr);
 
@@ -1347,7 +1375,7 @@ babel_handle_route_request(union babel_msg *m, struct babel_iface *ifa)
   struct babel_proto *p = ifa->proto;
   struct babel_msg_route_request *msg = &m->route_request;
 
-  /* RFC 6126 3.8.1.1 */
+  /* RFC 8966 3.8.1.1 */
 
   /* Wildcard request - full update on the interface */
   if (msg->full)
@@ -1405,7 +1433,7 @@ babel_handle_seqno_request(union babel_msg *m, struct babel_iface *ifa)
   struct babel_proto *p = ifa->proto;
   struct babel_msg_seqno_request *msg = &m->seqno_request;
 
-  /* RFC 6126 3.8.1.2 */
+  /* RFC 8966 3.8.1.2 */
 
   TRACE(D_PACKETS, "Handling seqno request for %N router-id %lR seqno %d hop count %d",
 	&msg->net, msg->router_id, msg->seqno, msg->hop_count);
@@ -1543,7 +1571,8 @@ babel_auth_check_pc(struct babel_iface *ifa, struct babel_msg_auth *msg)
     n->auth_index_len = msg->index_len;
     memcpy(n->auth_index, msg->index, msg->index_len);
 
-    n->auth_pc = msg->pc;
+    n->auth_pc_unicast = msg->pc;
+    n->auth_pc_multicast = msg->pc;
     n->auth_passed = 1;
 
     return 1;
@@ -1562,16 +1591,30 @@ babel_auth_check_pc(struct babel_iface *ifa, struct babel_msg_auth *msg)
     return 0;
   }
 
-  /* (6) Index matches; only accept if PC is greater than last */
-  if (n->auth_pc >= msg->pc)
+  /*
+   * (6) Index matches; only accept if PC is greater than last. We keep separate
+   * counters for unicast and multicast because multicast packets can be delayed
+   * significantly on wireless networks (enough to be received out of order).
+   * Separate counters are safe because the packet destination address is part
+   * of the MAC pseudo-header (so unicast packets can't be replayed as multicast
+   * and vice versa).
+   */
+  u32 auth_pc = msg->unicast ? n->auth_pc_unicast : n->auth_pc_multicast;
+  if (auth_pc >= msg->pc)
   {
     LOG_PKT_AUTH("Authentication failed for %I on %s - "
-		 "lower packet counter (rcv %u, old %u)",
-                 msg->sender, ifa->ifname, msg->pc, n->auth_pc);
+		 "lower %s packet counter (rcv %u, old %u)",
+                 msg->sender, ifa->ifname,
+		 msg->unicast ? "unicast" : "multicast",
+		 msg->pc, auth_pc);
     return 0;
   }
 
-  n->auth_pc = msg->pc;
+  if (msg->unicast)
+    n->auth_pc_unicast = msg->pc;
+  else
+    n->auth_pc_multicast = msg->pc;
+
   n->auth_passed = 1;
 
   return 1;
@@ -1723,7 +1766,7 @@ babel_iface_update_addr4(struct babel_iface *ifa)
   ip_addr addr4 = ifa->iface->addr4 ? ifa->iface->addr4->ip : IPA_NONE;
   ifa->next_hop_ip4 = ipa_nonzero(ifa->cf->next_hop_ip4) ? ifa->cf->next_hop_ip4 : addr4;
 
-  if (ipa_zero(ifa->next_hop_ip4) && p->ip4_channel)
+  if (ipa_zero(ifa->next_hop_ip4) && p->ip4_channel && !ifa->cf->ext_next_hop)
     log(L_WARN "%s: Missing IPv4 next hop address for %s", p->p.name, ifa->ifname);
 
   if (ifa->up)
@@ -1800,8 +1843,8 @@ babel_add_iface(struct babel_proto *p, struct iface *new, struct babel_iface_con
   ifa->next_hop_ip4 = ipa_nonzero(ic->next_hop_ip4) ? ic->next_hop_ip4 : addr4;
   ifa->next_hop_ip6 = ipa_nonzero(ic->next_hop_ip6) ? ic->next_hop_ip6 : ifa->addr;
 
-  if (ipa_zero(ifa->next_hop_ip4) && p->ip4_channel)
-    log(L_WARN "%s: Missing IPv4 next hop address for %s", p->p.name, new->name);
+  if (ipa_zero(ifa->next_hop_ip4) && p->ip4_channel && !ic->ext_next_hop)
+    log(L_WARN "%s: Missing IPv4 next hop address for %s", p->p.name, ifa->ifname);
 
   init_list(&ifa->neigh_list);
   ifa->hello_seqno = 1;
@@ -1921,7 +1964,7 @@ babel_reconfigure_iface(struct babel_proto *p, struct babel_iface *ifa, struct b
   if ((new->auth_type != BABEL_AUTH_NONE) && (new->auth_type != old->auth_type))
     babel_auth_reset_index(ifa);
 
-  if (ipa_zero(ifa->next_hop_ip4) && p->ip4_channel)
+  if (ipa_zero(ifa->next_hop_ip4) && p->ip4_channel && !new->ext_next_hop)
     log(L_WARN "%s: Missing IPv4 next hop address for %s", p->p.name, ifa->ifname);
 
   if (ifa->next_hello > (current_time() + new->hello_interval))
diff --git a/proto/babel/babel.h b/proto/babel/babel.h
index da8386b..dcd303e 100644
--- a/proto/babel/babel.h
+++ b/proto/babel/babel.h
@@ -114,6 +114,7 @@ enum babel_ae_type {
   BABEL_AE_IP4			= 1,
   BABEL_AE_IP6			= 2,
   BABEL_AE_IP6_LL		= 3,
+  BABEL_AE_IP4_VIA_IP6		= 4,
   BABEL_AE_MAX
 };
 
@@ -147,8 +148,9 @@ struct babel_iface_config {
 
   ip_addr next_hop_ip4;
   ip_addr next_hop_ip6;
+  u8 ext_next_hop;			/* Enable IPv4 via IPv6 */
 
-  u8 auth_type;			/* Authentication type (BABEL_AUTH_*) */
+  u8 auth_type;				/* Authentication type (BABEL_AUTH_*) */
   u8 auth_permissive;			/* Don't drop packets failing auth check */
   uint mac_num_keys;			/* Number of configured HMAC keys */
   uint mac_total_len;			/* Total digest length for all configured keys */
@@ -227,7 +229,8 @@ struct babel_neighbor {
   u16 next_hello_seqno;
   uint last_hello_int;
 
-  u32 auth_pc;
+  u32 auth_pc_unicast;
+  u32 auth_pc_multicast;
   u8 auth_passed;
   u8 auth_index_len;
   u8 auth_index[BABEL_AUTH_INDEX_LEN];
@@ -405,6 +408,7 @@ struct babel_msg_auth {
   u8 challenge_seen;
   u8 challenge_len;
   u8 challenge[BABEL_AUTH_MAX_NONCE_LEN];
+  u8 unicast;
 };
 
 static inline int babel_sadr_enabled(struct babel_proto *p)
diff --git a/proto/babel/config.Y b/proto/babel/config.Y
index 05210fa..1b4dc6f 100644
--- a/proto/babel/config.Y
+++ b/proto/babel/config.Y
@@ -25,7 +25,8 @@ CF_DECLS
 CF_KEYWORDS(BABEL, INTERFACE, METRIC, RXCOST, HELLO, UPDATE, INTERVAL, PORT,
 	TYPE, WIRED, WIRELESS, RX, TX, BUFFER, PRIORITY, LENGTH, CHECK, LINK,
 	NEXT, HOP, IPV4, IPV6, BABEL_METRIC, SHOW, INTERFACES, NEIGHBORS,
-	ENTRIES, RANDOMIZE, ROUTER, ID, AUTHENTICATION, NONE, MAC, PERMISSIVE)
+	ENTRIES, RANDOMIZE, ROUTER, ID, AUTHENTICATION, NONE, MAC, PERMISSIVE,
+	EXTENDED)
 
 CF_GRAMMAR
 
@@ -67,6 +68,7 @@ babel_iface_start:
   BABEL_IFACE->tx_tos = IP_PREC_INTERNET_CONTROL;
   BABEL_IFACE->tx_priority = sk_priority_control;
   BABEL_IFACE->check_link = 1;
+  BABEL_IFACE->ext_next_hop = 1;
 };
 
 
@@ -143,6 +145,7 @@ babel_iface_item:
  | CHECK LINK bool { BABEL_IFACE->check_link = $3; }
  | NEXT HOP IPV4 ipa { BABEL_IFACE->next_hop_ip4 = $4; if (!ipa_is_ip4($4)) cf_error("Must be an IPv4 address"); }
  | NEXT HOP IPV6 ipa { BABEL_IFACE->next_hop_ip6 = $4; if (!ipa_is_ip6($4)) cf_error("Must be an IPv6 address"); }
+ | EXTENDED NEXT HOP bool { BABEL_IFACE->ext_next_hop = $4; }
  | AUTHENTICATION NONE { BABEL_IFACE->auth_type = BABEL_AUTH_NONE; }
  | AUTHENTICATION MAC { BABEL_IFACE->auth_type = BABEL_AUTH_MAC; BABEL_IFACE->auth_permissive = 0; }
  | AUTHENTICATION MAC PERMISSIVE { BABEL_IFACE->auth_type = BABEL_AUTH_MAC; BABEL_IFACE->auth_permissive = 1; }
diff --git a/proto/babel/packets.c b/proto/babel/packets.c
index d4acc17..61c94cc 100644
--- a/proto/babel/packets.c
+++ b/proto/babel/packets.c
@@ -166,10 +166,12 @@ struct babel_parse_state {
   ip_addr next_hop_ip6;
   u64 router_id;		/* Router ID used in subsequent updates */
   u8 def_ip6_prefix[16];	/* Implicit IPv6 prefix in network order */
-  u8 def_ip4_prefix[4];		/* Implicit IPv4 prefix in network order */
+  u8 def_ip4_prefix[4];		/* Implicit IPv4 prefix (AE 1) in network order */
+  u8 def_ip4_via_ip6_prefix[4];	/* Implicit IPv4 prefix (AE 4) in network order */
   u8 router_id_seen;		/* router_id field 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_via_ip6_prefix_seen; /* def_ip4_via_ip6_prefix is valid */
   u8 current_tlv_endpos;	/* End of self-terminating TLVs (offset from start) */
   u8 sadr_enabled;
   u8 is_unicast;
@@ -515,9 +517,6 @@ babel_read_ihu(struct babel_tlv *hdr, union babel_msg *m,
   msg->addr = IPA_NONE;
   msg->sender = state->saddr;
 
-  if (msg->ae >= BABEL_AE_MAX)
-    return PARSE_IGNORE;
-
   /*
    * We only actually read link-local IPs. In every other case, the addr field
    * will be 0 but validation will succeed. The handler takes care of these
@@ -526,17 +525,20 @@ babel_read_ihu(struct babel_tlv *hdr, union babel_msg *m,
    */
   switch (msg->ae)
   {
+  case BABEL_AE_WILDCARD:
+    return PARSE_SUCCESS;
+
   case BABEL_AE_IP4:
     if (TLV_OPT_LENGTH(tlv) < 4)
       return PARSE_ERROR;
     state->current_tlv_endpos += 4;
-    break;
+    return PARSE_SUCCESS;
 
   case BABEL_AE_IP6:
     if (TLV_OPT_LENGTH(tlv) < 16)
       return PARSE_ERROR;
     state->current_tlv_endpos += 16;
-    break;
+    return PARSE_SUCCESS;
 
   case BABEL_AE_IP6_LL:
     if (TLV_OPT_LENGTH(tlv) < 8)
@@ -544,10 +546,17 @@ babel_read_ihu(struct babel_tlv *hdr, union babel_msg *m,
 
     msg->addr = ipa_from_ip6(get_ip6_ll(&tlv->addr));
     state->current_tlv_endpos += 8;
-    break;
+    return PARSE_SUCCESS;
+
+  /* RFC 9229 2.4 - IHU TLV MUST NOT carry the AE 4 (IPv4-via-IPv6) */
+  case BABEL_AE_IP4_VIA_IP6:
+    return PARSE_ERROR;
+
+  default:
+    return PARSE_IGNORE;
   }
 
-  return PARSE_SUCCESS;
+  return PARSE_IGNORE;
 }
 
 static uint
@@ -640,6 +649,10 @@ babel_read_next_hop(struct babel_tlv *hdr, union babel_msg *m UNUSED,
     state->current_tlv_endpos += 8;
     return PARSE_IGNORE;
 
+  /* RFC 9229 2.4 - Next Hop TLV MUST NOT carry the AE 4 (IPv4-via-IPv6) */
+  case BABEL_AE_IP4_VIA_IP6:
+    return PARSE_ERROR;
+
   default:
     return PARSE_IGNORE;
   }
@@ -692,6 +705,42 @@ babel_write_next_hop(struct babel_tlv *hdr, ip_addr addr,
   return 0;
 }
 
+/* This is called directly from babel_read_update() to handle
+   both BABEL_AE_IP4 and BABEL_AE_IP4_VIA_IP6 encodings */
+static int
+babel_read_ip4_prefix(struct babel_tlv_update *tlv, struct babel_msg_update *msg,
+		     u8 *def_prefix, u8 *def_prefix_seen, ip_addr next_hop, int len)
+{
+    if (tlv->plen > IP4_MAX_PREFIX_LENGTH)
+      return PARSE_ERROR;
+
+    /* Cannot omit data if there is no saved prefix */
+    if (tlv->omitted && !*def_prefix_seen)
+      return PARSE_ERROR;
+
+    /* Update must have next hop, unless it is retraction */
+    if (ipa_zero(next_hop) && msg->metric != BABEL_INFINITY)
+      return PARSE_ERROR;
+
+    /* Merge saved prefix and received prefix parts */
+    u8 buf[4] = {};
+    memcpy(buf, def_prefix, tlv->omitted);
+    memcpy(buf + tlv->omitted, tlv->addr, len);
+
+    ip4_addr prefix4 = get_ip4(buf);
+    net_fill_ip4(&msg->net, prefix4, tlv->plen);
+
+    if (tlv->flags & BABEL_UF_DEF_PREFIX)
+    {
+      put_ip4(def_prefix, prefix4);
+      *def_prefix_seen = 1;
+    }
+
+    msg->next_hop = next_hop;
+
+    return PARSE_SUCCESS;
+}
+
 static int
 babel_read_update(struct babel_tlv *hdr, union babel_msg *m,
                   struct babel_parse_state *state)
@@ -706,11 +755,11 @@ babel_read_update(struct babel_tlv *hdr, union babel_msg *m,
 
   /* Length of received prefix data without omitted part */
   int len = BYTES(tlv->plen) - (int) tlv->omitted;
-  u8 buf[16] = {};
 
   if ((len < 0) || ((uint) len > TLV_OPT_LENGTH(tlv)))
     return PARSE_ERROR;
 
+  int rc;
   switch (tlv->ae)
   {
   case BABEL_AE_WILDCARD:
@@ -724,31 +773,20 @@ babel_read_update(struct babel_tlv *hdr, union babel_msg *m,
     break;
 
   case BABEL_AE_IP4:
-    if (tlv->plen > IP4_MAX_PREFIX_LENGTH)
-      return PARSE_ERROR;
-
-    /* Cannot omit data if there is no saved prefix */
-    if (tlv->omitted && !state->def_ip4_prefix_seen)
-      return PARSE_ERROR;
-
-    /* Update must have next hop, unless it is retraction */
-    if (ipa_zero(state->next_hop_ip4) && (msg->metric != BABEL_INFINITY))
-      return PARSE_IGNORE;
-
-    /* Merge saved prefix and received prefix parts */
-    memcpy(buf, state->def_ip4_prefix, tlv->omitted);
-    memcpy(buf + tlv->omitted, tlv->addr, len);
+    rc = babel_read_ip4_prefix(tlv, msg, state->def_ip4_prefix,
+			       &state->def_ip4_prefix_seen,
+			       state->next_hop_ip4, len);
+    if (rc != PARSE_SUCCESS)
+      return rc;
 
-    ip4_addr prefix4 = get_ip4(buf);
-    net_fill_ip4(&msg->net, prefix4, tlv->plen);
-
-    if (tlv->flags & BABEL_UF_DEF_PREFIX)
-    {
-      put_ip4(state->def_ip4_prefix, prefix4);
-      state->def_ip4_prefix_seen = 1;
-    }
+    break;
 
-    msg->next_hop = state->next_hop_ip4;
+  case BABEL_AE_IP4_VIA_IP6:
+    rc = babel_read_ip4_prefix(tlv, msg, state->def_ip4_via_ip6_prefix,
+			       &state->def_ip4_via_ip6_prefix_seen,
+			       state->next_hop_ip6, len);
+    if (rc != PARSE_SUCCESS)
+      return rc;
 
     break;
 
@@ -761,6 +799,7 @@ babel_read_update(struct babel_tlv *hdr, union babel_msg *m,
       return PARSE_ERROR;
 
     /* Merge saved prefix and received prefix parts */
+    u8 buf[16] = {};
     memcpy(buf, state->def_ip6_prefix, tlv->omitted);
     memcpy(buf + tlv->omitted, tlv->addr, len);
 
@@ -863,7 +902,7 @@ babel_write_update(struct babel_tlv *hdr, union babel_msg *m,
   }
   else if (msg->net.type == NET_IP4)
   {
-    tlv->ae = BABEL_AE_IP4;
+    tlv->ae = ipa_is_ip4(msg->next_hop) ? BABEL_AE_IP4 : BABEL_AE_IP4_VIA_IP6;
     tlv->plen = net4_pxlen(&msg->net);
     put_ip4_px(tlv->addr, &msg->net);
   }
@@ -931,7 +970,12 @@ babel_read_route_request(struct babel_tlv *hdr, union babel_msg *m,
     msg->full = 1;
     return PARSE_SUCCESS;
 
+  /*
+   * RFC 9229 2.3 - When receiving requests, AE 1 (IPv4) and AE 4
+   * (IPv4-via-IPv6) MUST be treated in the same manner.
+   */
   case BABEL_AE_IP4:
+  case BABEL_AE_IP4_VIA_IP6:
     if (tlv->plen > IP4_MAX_PREFIX_LENGTH)
       return PARSE_ERROR;
 
@@ -1032,7 +1076,12 @@ babel_read_seqno_request(struct babel_tlv *hdr, union babel_msg *m,
   case BABEL_AE_WILDCARD:
     return PARSE_ERROR;
 
+  /*
+   * RFC 9229 2.3 - When receiving requests, AE 1 (IPv4) and AE 4
+   * (IPv4-via-IPv6) MUST be treated in the same manner.
+   */
   case BABEL_AE_IP4:
+  case BABEL_AE_IP4_VIA_IP6:
     if (tlv->plen > IP4_MAX_PREFIX_LENGTH)
       return PARSE_ERROR;
 
@@ -1655,6 +1704,7 @@ babel_read_pc(struct babel_tlv *hdr, union babel_msg *m UNUSED,
   state->auth.pc_seen = 1;
   state->auth.index_len = index_len;
   state->auth.index = tlv->index;
+  state->auth.unicast = state->is_unicast;
   state->current_tlv_endpos += index_len;
 
   return PARSE_SUCCESS;
diff --git a/proto/bgp/attrs.c b/proto/bgp/attrs.c
index 1e234b1..de45cae 100644
--- a/proto/bgp/attrs.c
+++ b/proto/bgp/attrs.c
@@ -1151,6 +1151,13 @@ bgp_attr_known(uint code)
   return (code < ARRAY_SIZE(bgp_attr_table)) && bgp_attr_table[code].name;
 }
 
+void bgp_fix_attr_flags(ea_list *attrs)
+{
+  for (u8 i = 0; i < attrs->count; i++)
+  {
+    attrs->attrs[i].flags = bgp_attr_table[EA_ID(attrs->attrs[i].id)].flags;
+  }
+}
 
 /*
  *	Attribute export
@@ -1834,7 +1841,7 @@ bgp_update_attrs(struct bgp_proto *p, struct bgp_channel *c, rte *e, ea_list *at
 
     /* MULTI_EXIT_DESC attribute - accept only if set in export filter */
     a = bgp_find_attr(attrs0, BA_MULTI_EXIT_DISC);
-    if (a && !(a->fresh))
+    if (a && !a->fresh && !p->cf->allow_med)
       bgp_unset_attr(&attrs, pool, BA_MULTI_EXIT_DISC);
   }
 
diff --git a/proto/bgp/bgp.c b/proto/bgp/bgp.c
index 2e442e1..c806765 100644
--- a/proto/bgp/bgp.c
+++ b/proto/bgp/bgp.c
@@ -101,9 +101,9 @@
  * RFC 8203 - BGP Administrative Shutdown Communication
  * RFC 8212 - Default EBGP Route Propagation Behavior without Policies
  * RFC 8654 - Extended Message Support for BGP
+ * RFC 9072 - Extended Optional Parameters Length for BGP OPEN Message
  * RFC 9117 - Revised Validation Procedure for BGP Flow Specifications
  * RFC 9234 - Route Leak Prevention and Detection Using Roles
- * draft-ietf-idr-ext-opt-param-07
  * draft-uttaro-idr-bgp-persistence-04
  * draft-walton-bgp-hostname-capability-02
  */
@@ -125,6 +125,7 @@
 #include "lib/string.h"
 
 #include "bgp.h"
+#include "proto/bmp/bmp.h"
 
 
 static list STATIC_LIST_INIT(bgp_sockets);		/* Global list of listening sockets */
@@ -866,7 +867,10 @@ bgp_graceful_restart_timeout(timer *t)
     }
   }
   else
+  {
     bgp_stop(p, 0, NULL, 0);
+    bmp_peer_down(p, BE_NONE, NULL, 0);
+  }
 }
 
 static void
@@ -990,7 +994,10 @@ bgp_sock_err(sock *sk, int err)
   if (err)
     BGP_TRACE(D_EVENTS, "Connection lost (%M)", err);
   else
+  {
     BGP_TRACE(D_EVENTS, "Connection closed");
+    bmp_peer_down(p, BE_SOCKET, NULL, 0);
+  }
 
   if ((conn->state == BS_ESTABLISHED) && p->gr_ready)
     bgp_handle_graceful_restart(p);
@@ -1109,6 +1116,7 @@ bgp_connect(struct bgp_proto *p)	/* Enter Connect state and start establishing c
   s->tos = IP_PREC_INTERNET_CONTROL;
   s->password = p->cf->password;
   s->tx_hook = bgp_connected;
+  s->flags = p->cf->free_bind ? SKF_FREEBIND : 0;
   BGP_TRACE(D_EVENTS, "Connecting to %I%J from local address %I%J",
 	    s->daddr, ipa_is_link_local(s->daddr) ? p->cf->iface : NULL,
 	    s->saddr, ipa_is_link_local(s->saddr) ? s->iface : NULL);
@@ -1314,6 +1322,7 @@ bgp_neigh_notify(neighbor *n)
       bgp_store_error(p, NULL, BE_MISC, BEM_NEIGHBOR_LOST);
       /* Perhaps also run bgp_update_startup_delay(p)? */
       bgp_stop(p, 0, NULL, 0);
+      bmp_peer_down(p, BE_MISC, NULL, 0);
     }
   }
   else if (p->cf->check_link && !(n->iface->flags & IF_LINK_UP))
@@ -1325,6 +1334,7 @@ bgp_neigh_notify(neighbor *n)
       if (ps == PS_UP)
 	bgp_update_startup_delay(p);
       bgp_stop(p, 0, NULL, 0);
+      bmp_peer_down(p, BE_MISC, NULL, 0);
     }
   }
   else
@@ -1366,6 +1376,7 @@ bgp_bfd_notify(struct bfd_request *req)
       if (ps == PS_UP)
 	bgp_update_startup_delay(p);
       bgp_stop(p, 0, NULL, 0);
+      bmp_peer_down(p, BE_MISC, NULL, 0);
     }
   }
 }
@@ -1694,6 +1705,10 @@ bgp_init(struct proto_config *CF)
   P->rte_modify = bgp_rte_modify_stale;
   P->rte_igp_metric = bgp_rte_igp_metric;
 
+#ifdef CONFIG_BMP
+  P->rte_update_in_notify = bgp_rte_update_in_notify;
+#endif
+
   p->cf = cf;
   p->is_internal = (cf->local_as == cf->remote_as);
   p->is_interior = p->is_internal || cf->confederation_member;
diff --git a/proto/bgp/bgp.h b/proto/bgp/bgp.h
index 2808d47..7c96e85 100644
--- a/proto/bgp/bgp.h
+++ b/proto/bgp/bgp.h
@@ -97,7 +97,7 @@ struct bgp_config {
   int capabilities;			/* Enable capability handshake [RFC 5492] */
   int enable_refresh;			/* Enable local support for route refresh [RFC 2918] */
   int enable_as4;			/* Enable local support for 4B AS numbers [RFC 6793] */
-  int enable_extended_messages;		/* Enable local support for extended messages [draft] */
+  int enable_extended_messages;		/* Enable local support for extended messages [RFC 8654] */
   int enable_hostname;			/* Enable local support for hostname [draft] */
   u32 rr_cluster_id;			/* Route reflector cluster ID, if different from local ID */
   int rr_client;			/* Whether neighbor is RR client of me */
@@ -108,6 +108,7 @@ struct bgp_config {
   int interpret_communities;		/* Hardwired handling of well-known communities */
   int allow_local_as;			/* Allow that number of local ASNs in incoming AS_PATHs */
   int allow_local_pref;			/* Allow LOCAL_PREF in EBGP sessions */
+  int allow_med;			/* Allow BGP_MED in EBGP sessions */
   int allow_as_sets;			/* Allow AS_SETs in incoming AS_PATHs */
   int enforce_first_as;			/* Enable check for neighbor AS as first AS in AS_PATH */
   int gr_mode;				/* Graceful restart mode (BGP_GR_*) */
@@ -239,7 +240,7 @@ struct bgp_caps {
   u32 as4_number;			/* Announced ASN */
 
   u8 as4_support;			/* Four-octet AS capability, RFC 6793 */
-  u8 ext_messages;			/* Extended message length,  RFC draft */
+  u8 ext_messages;			/* Extended message length,  RFC 8654 */
   u8 route_refresh;			/* Route refresh capability, RFC 2918 */
   u8 enhanced_refresh;			/* Enhanced route refresh,   RFC 7313 */
   u8 role;				/* BGP role capability,      RFC 9234 */
@@ -433,6 +434,7 @@ struct bgp_write_state {
   int as4_session;
   int add_path;
   int mpls;
+  int sham;
 
   eattr *mp_next_hop;
   const adata *mpls_labels;
@@ -495,6 +497,13 @@ struct bgp_parse_state {
 #define BGP_CF_WALK_CHANNELS(P,C) WALK_LIST(C, P->c.channels) if (C->c.channel == &channel_bgp)
 #define BGP_WALK_CHANNELS(P,C) WALK_LIST(C, P->p.channels) if (C->c.channel == &channel_bgp)
 
+#define BGP_MSG_HDR_MARKER_SIZE	16
+#define BGP_MSG_HDR_MARKER_POS	0
+#define BGP_MSG_HDR_LENGTH_SIZE	2
+#define BGP_MSG_HDR_LENGTH_POS	BGP_MSG_HDR_MARKER_SIZE
+#define BGP_MSG_HDR_TYPE_SIZE	1
+#define BGP_MSG_HDR_TYPE_POS	(BGP_MSG_HDR_MARKER_SIZE + BGP_MSG_HDR_LENGTH_SIZE)
+
 static inline int bgp_channel_is_ipv4(struct bgp_channel *c)
 { return BGP_AFI(c->afi) == BGP_AFI_IPV4; }
 
@@ -541,6 +550,8 @@ void bgp_store_error(struct bgp_proto *p, struct bgp_conn *c, u8 class, u32 code
 void bgp_stop(struct bgp_proto *p, int subcode, byte *data, uint len);
 const char *bgp_format_role_name(u8 role);
 
+void bgp_fix_attr_flags(ea_list *attrs);
+
 static inline int
 rte_resolvable(rte *rt)
 {
@@ -587,7 +598,9 @@ bgp_set_attr_data(ea_list **to, struct linpool *pool, uint code, uint flags, voi
   bgp_set_attr(to, pool, code, flags, (uintptr_t) a);
 }
 
-#define bgp_unset_attr(to, pool, code) ea_unset_attr(to, pool, 0, code)
+static inline void
+bgp_unset_attr(ea_list **to, struct linpool *pool, uint code)
+{ ea_unset_attr(to, pool, 0, EA_CODE(PROTOCOL_BGP, code)); }
 
 int bgp_encode_mp_reach_mrt(struct bgp_write_state *s, eattr *a, byte *buf, uint size);
 
@@ -612,6 +625,7 @@ struct rte *bgp_rte_modify_stale(struct rte *r, struct linpool *pool);
 u32 bgp_rte_igp_metric(struct rte *);
 void bgp_rt_notify(struct proto *P, struct channel *C, net *n, rte *new, rte *old);
 int bgp_preexport(struct channel *, struct rte *);
+void bgp_rte_update_in_notify(struct channel *C, const net_addr *n, const struct rte *new, const struct rte_src *src);
 int bgp_get_attr(const struct eattr *e, byte *buf, int buflen);
 void bgp_get_route_info(struct rte *, byte *buf);
 int bgp_total_aigp_metric_(rte *e, u64 *metric, const struct adata **ad);
@@ -645,6 +659,7 @@ void bgp_log_error(struct bgp_proto *p, u8 class, char *msg, unsigned code, unsi
 
 void bgp_update_next_hop(struct bgp_export_state *s, eattr *a, ea_list **to);
 
+byte * bgp_create_end_mark(struct bgp_channel *c, byte *buf);
 
 /* Packet types */
 
diff --git a/proto/bgp/config.Y b/proto/bgp/config.Y
index a2dfa74..013d14a 100644
--- a/proto/bgp/config.Y
+++ b/proto/bgp/config.Y
@@ -199,6 +199,7 @@ bgp_proto:
  | bgp_proto ALLOW LOCAL AS ';' { BGP_CFG->allow_local_as = -1; }
  | bgp_proto ALLOW LOCAL AS expr ';' { BGP_CFG->allow_local_as = $5; }
  | bgp_proto ALLOW BGP_LOCAL_PREF bool ';' { BGP_CFG->allow_local_pref = $4; }
+ | bgp_proto ALLOW BGP_MED bool ';' { BGP_CFG->allow_med = $4; }
  | bgp_proto ALLOW AS SETS bool ';' { BGP_CFG->allow_as_sets = $5; }
  | bgp_proto GRACEFUL RESTART bool ';' { BGP_CFG->gr_mode = $4; }
  | bgp_proto GRACEFUL RESTART AWARE ';' { BGP_CFG->gr_mode = BGP_GR_AWARE; }
diff --git a/proto/bgp/packets.c b/proto/bgp/packets.c
index 16818cf..6e6e41c 100644
--- a/proto/bgp/packets.c
+++ b/proto/bgp/packets.c
@@ -26,6 +26,7 @@
 #include "nest/cli.h"
 
 #include "bgp.h"
+#include "proto/bmp/bmp.h"
 
 
 #define BGP_RR_REQUEST		0
@@ -166,6 +167,7 @@ bgp_create_notification(struct bgp_conn *conn, byte *buf)
   buf[0] = conn->notify_code;
   buf[1] = conn->notify_subcode;
   memcpy(buf+2, conn->notify_data, conn->notify_size);
+  bmp_peer_down(p, BE_NONE, buf, conn->notify_size + 2);
   return buf + 2 + conn->notify_size;
 }
 
@@ -213,6 +215,13 @@ bgp_af_caps_cmp(const void *X, const void *Y)
   return (x->afi < y->afi) ? -1 : (x->afi > y->afi) ? 1 : 0;
 }
 
+struct bgp_caps *
+bgp_alloc_capabilities(struct bgp_proto *p, int n)
+{
+  struct bgp_caps *caps = mb_allocz(p->p.pool, sizeof(struct bgp_caps) + n * sizeof(struct bgp_af_caps));
+  caps->role = BGP_ROLE_UNDEFINED;
+  return caps;
+}
 
 void
 bgp_prepare_capabilities(struct bgp_conn *conn)
@@ -225,13 +234,13 @@ bgp_prepare_capabilities(struct bgp_conn *conn)
   if (!p->cf->capabilities)
   {
     /* Just prepare empty local_caps */
-    conn->local_caps = mb_allocz(p->p.pool, sizeof(struct bgp_caps));
+    conn->local_caps = bgp_alloc_capabilities(p, 0);
     return;
   }
 
   /* Prepare bgp_caps structure */
   int n = list_length(&p->p.channels);
-  caps = mb_allocz(p->p.pool, sizeof(struct bgp_caps) + n * sizeof(struct bgp_af_caps));
+  caps = bgp_alloc_capabilities(p, n);
   conn->local_caps = caps;
 
   caps->as4_support = p->cf->enable_as4;
@@ -462,10 +471,7 @@ bgp_read_capabilities(struct bgp_conn *conn, byte *pos, int len)
   u32 af;
 
   if (!conn->remote_caps)
-  {
-    caps = mb_allocz(p->p.pool, sizeof(struct bgp_caps) + sizeof(struct bgp_af_caps));
-    caps->role = BGP_ROLE_UNDEFINED;
-  }
+    caps = bgp_alloc_capabilities(p, 1);
   else
   {
     caps = conn->remote_caps;
@@ -518,7 +524,7 @@ bgp_read_capabilities(struct bgp_conn *conn, byte *pos, int len)
       }
       break;
 
-    case  6: /* Extended message length capability, RFC draft */
+    case  6: /* Extended message length capability, RFC 8654 */
       if (cl != 0)
 	goto err;
 
@@ -711,7 +717,7 @@ bgp_read_options(struct bgp_conn *conn, byte *pos, uint len, uint rest)
   struct bgp_proto *p = conn->bgp;
   int ext = 0;
 
-  /* Handle extended length (draft-ietf-idr-ext-opt-param-07) */
+  /* Handle extended length, RFC 9072 */
   if ((len > 0) && (rest > 0) && (pos[0] == 255))
   {
     if (rest < 3)
@@ -761,7 +767,7 @@ bgp_read_options(struct bgp_conn *conn, byte *pos, uint len, uint rest)
 
   /* Prepare empty caps if no capability option was announced */
   if (!conn->remote_caps)
-    conn->remote_caps = mb_allocz(p->p.pool, sizeof(struct bgp_caps));
+    conn->remote_caps = bgp_alloc_capabilities(p, 0);
 
   return 0;
 
@@ -796,7 +802,7 @@ bgp_create_open(struct bgp_conn *conn, byte *buf)
       buf[10] = 2;		/* Option 2: Capability list */
       buf[11] = len;		/* Option data length */
     }
-    else /* draft-ietf-idr-ext-opt-param-07 */
+    else /* Extended length, RFC 9072 */
     {
       /* Move capabilities 4 B forward */
       memmove(buf + 16, pos, len);
@@ -975,6 +981,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_conn_enter_openconfirm_state(conn);
+  bmp_put_recv_bgp_open_msg(p, pkt, len);
 }
 
 
@@ -1563,7 +1570,10 @@ bgp_encode_nlri_ip4(struct bgp_write_state *s, struct bgp_bucket *buck, byte *bu
     memcpy(pos, &a, b);
     ADVANCE(pos, size, b);
 
-    bgp_free_prefix(s->channel, px);
+    if (!s->sham)
+      bgp_free_prefix(s->channel, px);
+    else
+      rem_node(&px->buck_node);
   }
 
   return pos - buf;
@@ -1648,7 +1658,10 @@ bgp_encode_nlri_ip6(struct bgp_write_state *s, struct bgp_bucket *buck, byte *bu
     memcpy(pos, &a, b);
     ADVANCE(pos, size, b);
 
-    bgp_free_prefix(s->channel, px);
+    if (!s->sham)
+      bgp_free_prefix(s->channel, px);
+    else
+      rem_node(&px->buck_node);
   }
 
   return pos - buf;
@@ -1736,7 +1749,10 @@ bgp_encode_nlri_vpn4(struct bgp_write_state *s, struct bgp_bucket *buck, byte *b
     memcpy(pos, &a, b);
     ADVANCE(pos, size, b);
 
-    bgp_free_prefix(s->channel, px);
+    if (!s->sham)
+      bgp_free_prefix(s->channel, px);
+    else
+      rem_node(&px->buck_node);
   }
 
   return pos - buf;
@@ -1833,7 +1849,10 @@ bgp_encode_nlri_vpn6(struct bgp_write_state *s, struct bgp_bucket *buck, byte *b
     memcpy(pos, &a, b);
     ADVANCE(pos, size, b);
 
-    bgp_free_prefix(s->channel, px);
+    if (!s->sham)
+      bgp_free_prefix(s->channel, px);
+    else
+      rem_node(&px->buck_node);
   }
 
   return pos - buf;
@@ -1920,7 +1939,10 @@ bgp_encode_nlri_flow4(struct bgp_write_state *s, struct bgp_bucket *buck, byte *
     memcpy(pos, net->data, flen);
     ADVANCE(pos, size, flen);
 
-    bgp_free_prefix(s->channel, px);
+    if (!s->sham)
+      bgp_free_prefix(s->channel, px);
+    else
+      rem_node(&px->buck_node);
   }
 
   return pos - buf;
@@ -2012,7 +2034,10 @@ bgp_encode_nlri_flow6(struct bgp_write_state *s, struct bgp_bucket *buck, byte *
     memcpy(pos, net->data, flen);
     ADVANCE(pos, size, flen);
 
-    bgp_free_prefix(s->channel, px);
+    if (!s->sham)
+      bgp_free_prefix(s->channel, px);
+    else
+      rem_node(&px->buck_node);
   }
 
   return pos - buf;
@@ -2256,7 +2281,8 @@ bgp_create_ip_reach(struct bgp_write_state *s, struct bgp_bucket *buck, byte *bu
   if (la < 0)
   {
     /* Attribute list too long */
-    bgp_withdraw_bucket(s->channel, buck);
+    if (!s->sham)
+      bgp_withdraw_bucket(s->channel, buck);
     return NULL;
   }
 
@@ -2303,7 +2329,8 @@ bgp_create_mp_reach(struct bgp_write_state *s, struct bgp_bucket *buck, byte *bu
   if (la < 0)
   {
     /* Attribute list too long */
-    bgp_withdraw_bucket(s->channel, buck);
+    if (!s->sham)
+      bgp_withdraw_bucket(s->channel, buck);
     return NULL;
   }
 
@@ -2384,6 +2411,94 @@ bgp_create_mp_unreach(struct bgp_write_state *s, struct bgp_bucket *buck, byte *
   return buf+11+len;
 }
 
+
+#ifdef CONFIG_BMP
+
+static byte *
+bgp_create_update_bmp(struct bgp_channel *c, byte *buf, struct bgp_bucket *buck, bool update)
+{
+  struct bgp_proto *p = (void *) c->c.proto;
+  byte *end = buf + (BGP_MAX_EXT_MSG_LENGTH - BGP_HEADER_LENGTH);
+  /* FIXME: must be a bit shorter */
+
+  struct bgp_caps *peer = p->conn->remote_caps;
+  const struct bgp_af_caps *rem = bgp_find_af_caps(peer, c->afi);
+  struct bgp_write_state s = {
+    .proto = p,
+    .channel = c,
+    .pool = tmp_linpool,
+    .mp_reach = (c->afi != BGP_AF_IPV4) || rem->ext_next_hop,
+    .as4_session = 1,
+    .add_path = c->add_path_rx,
+    .mpls = c->desc->mpls,
+    .sham = 1,
+  };
+
+  if (!update)
+  {
+    return !s.mp_reach ?
+      bgp_create_ip_unreach(&s, buck, buf, end):
+      bgp_create_mp_unreach(&s, buck, buf, end);
+  }
+  else
+  {
+    return !s.mp_reach ?
+      bgp_create_ip_reach(&s, buck, buf, end):
+      bgp_create_mp_reach(&s, buck, buf, end);
+  }
+}
+
+static byte *
+bgp_bmp_prepare_bgp_hdr(byte *buf, const u16 msg_size, const u8 msg_type)
+{
+  memset(buf + BGP_MSG_HDR_MARKER_POS, 0xff, BGP_MSG_HDR_MARKER_SIZE);
+  put_u16(buf + BGP_MSG_HDR_LENGTH_POS, msg_size);
+  put_u8(buf + BGP_MSG_HDR_TYPE_POS, msg_type);
+
+  return buf + BGP_MSG_HDR_TYPE_POS + BGP_MSG_HDR_TYPE_SIZE;
+}
+
+void
+bgp_rte_update_in_notify(struct channel *C, const net_addr *n,
+			 const struct rte *new, const struct rte_src *src)
+{
+//  struct bgp_proto *p = (void *) C->proto;
+  struct bgp_channel *c = (void *) C;
+
+  byte buf[BGP_MAX_EXT_MSG_LENGTH];
+  byte *pkt = buf + BGP_HEADER_LENGTH;
+
+  ea_list *attrs = new ? new->attrs->eattrs : NULL;
+  uint ea_size = new ? (sizeof(ea_list) + attrs->count * sizeof(eattr)) : 0;
+  uint bucket_size = sizeof(struct bgp_bucket) + ea_size;
+  uint prefix_size = sizeof(struct bgp_prefix) + n->length;
+
+  /* Sham bucket */
+  struct bgp_bucket *b = alloca(bucket_size);
+  *b = (struct bgp_bucket) { };
+  init_list(&b->prefixes);
+
+  if (attrs)
+    memcpy(b->eattrs, attrs, ea_size);
+
+  /* Sham prefix */
+  struct bgp_prefix *px = alloca(prefix_size);
+  *px = (struct bgp_prefix) { };
+  px->path_id = src->private_id;
+  net_copy(px->net, n);
+  add_tail(&b->prefixes, &px->buck_node);
+
+  byte *end = bgp_create_update_bmp(c, pkt, b, !!new);
+  if (!end)
+    return;
+
+  bgp_bmp_prepare_bgp_hdr(buf, end - buf, PKT_UPDATE);
+  bmp_route_monitor_put_update_in_pre_msg(buf, end - buf);
+}
+
+#endif /* CONFIG_BMP */
+
+
 static byte *
 bgp_create_update(struct bgp_channel *c, byte *buf)
 {
@@ -2484,7 +2599,7 @@ bgp_create_mp_end_mark(struct bgp_channel *c, byte *buf)
   return buf+10;
 }
 
-static byte *
+byte *
 bgp_create_end_mark(struct bgp_channel *c, byte *buf)
 {
   struct bgp_proto *p = (void *) c->c.proto;
@@ -2635,6 +2750,7 @@ bgp_rx_update(struct bgp_conn *conn, byte *pkt, uint len)
   s.ip_reach_len = len - pos;
   s.ip_reach_nlri = pkt + pos;
 
+  bmp_route_monitor_update_in_pre_begin();
 
   if (s.attr_len)
     ea = bgp_decode_attrs(&s, s.attrs, s.attr_len);
@@ -2666,6 +2782,9 @@ bgp_rx_update(struct bgp_conn *conn, byte *pkt, uint len)
     bgp_decode_nlri(&s, s.mp_reach_af, s.mp_reach_nlri, s.mp_reach_len,
 		    ea, s.mp_next_hop_data, s.mp_next_hop_len);
 
+  bmp_route_monitor_update_in_pre_commit(p);
+  bmp_route_monitor_update_in_pre_end();
+
 done:
   rta_free(s.cached_rta);
   lp_restore(tmp_linpool, &tmpp);
@@ -2917,7 +3036,12 @@ bgp_fire_tx(struct bgp_conn *conn)
   {
     conn->packets_to_send &= ~(1 << PKT_OPEN);
     end = bgp_create_open(conn, pkt);
-    return bgp_send(conn, PKT_OPEN, end - buf);
+    int rv = bgp_send(conn, PKT_OPEN, end - buf);
+    if (rv >= 0)
+    {
+      bmp_put_sent_bgp_open_msg(p, pkt, end - buf);
+    }
+    return rv;
   }
   else if (s & (1 << PKT_KEEPALIVE))
   {
@@ -3216,6 +3340,8 @@ bgp_rx_notification(struct bgp_conn *conn, byte *pkt, uint len)
       p->p.disabled = 1;
     }
   }
+
+  bmp_peer_down(p, BE_NONE, pkt, len);
 }
 
 static void
diff --git a/proto/bmp/Doc b/proto/bmp/Doc
new file mode 100644
index 0000000..69b6e80
--- /dev/null
+++ b/proto/bmp/Doc
@@ -0,0 +1 @@
+S bmp.c
diff --git a/proto/bmp/LICENSE b/proto/bmp/LICENSE
new file mode 100644
index 0000000..1772f08
--- /dev/null
+++ b/proto/bmp/LICENSE
@@ -0,0 +1,2 @@
+The patch is provided under the terms of the GNU General Public License, either
+version 2, or any later version.
\ No newline at end of file
diff --git a/proto/bmp/Makefile b/proto/bmp/Makefile
new file mode 100644
index 0000000..d6fca1a
--- /dev/null
+++ b/proto/bmp/Makefile
@@ -0,0 +1,6 @@
+src := bmp.c buffer.c map.c
+obj := $(src-o-files)
+$(all-daemon)
+$(cf-local)
+
+tests_objs := $(tests_objs) $(src-o-files)
\ No newline at end of file
diff --git a/proto/bmp/README.txt b/proto/bmp/README.txt
new file mode 100644
index 0000000..386f402
--- /dev/null
+++ b/proto/bmp/README.txt
@@ -0,0 +1,6 @@
+ABOUT
+This package |proto/bmp/*| provide implementation of BGP Monitoring Protocol (BMP).
+It has been started by Akamai Technologies, Inc. as a pilot program for support BMP in BIRD.
+It provides only basic features of BMP specification which are needed by Akamai evaluation of 
+feasible BMP protocol.
+Content of this package has been provided as a patch for BIRD release v2.0.7.
\ No newline at end of file
diff --git a/proto/bmp/bmp.c b/proto/bmp/bmp.c
new file mode 100644
index 0000000..f04b59b
--- /dev/null
+++ b/proto/bmp/bmp.c
@@ -0,0 +1,1168 @@
+/*
+ *	BIRD -- The BGP Monitoring Protocol (BMP)
+ *
+ *	(c) 2020 Akamai Technologies, Inc. (Pawel Maslanka, pmaslank@akamai.com)
+ *
+ *	Can be freely distributed and used under the terms of the GNU GPL.
+ */
+
+/**
+ * DOC: BGP Monitoring Protocol (BMP)
+ *
+ * Supported standards:
+ * o RFC 7854 - BMP standard
+ *
+ * TODO:
+ * - Support Peer Distinguisher ID in Per-Peer Header
+ * - Support peer type as RD Instance in Peer Type field of Per-Peer Header.
+ *   Currently, there are supported Global and Local Instance Peer types
+ * - Support corresponding FSM event code during send PEER DOWN NOTIFICATION
+ * - Support DE_CONFIGURED PEER DOWN REASON code in PEER DOWN NOTIFICATION message
+ * - If connection with BMP collector will lost then we don't establish connection again
+ * - Set Peer Type by its a global and local-scope IP address
+ *
+ * The BMP session is managed by a simple state machine with three states: Idle
+ * (!started, !sk), Connect (!started, sk active), and Established (started). It
+ * has three events: connect successfull (Connect -> Established), socket error
+ * (any -> Idle), and connect timeout (Idle/Connect -> Connect, resetting the
+ * TCP socket).
+ */
+
+#include "proto/bmp/bmp.h"
+#include "proto/bmp/buffer.h"
+#include "proto/bmp/map.h"
+
+#include <sys/socket.h>
+#include <sys/time.h>
+#include <sys/types.h>
+#include <netinet/in.h>
+#include <netdb.h>
+#include <string.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <errno.h>
+#include <arpa/inet.h>
+#include <limits.h>
+
+#include "nest/cli.h"
+#include "filter/filter.h"
+#include "proto/bgp/bgp.h"
+#include "sysdep/unix/unix.h"
+#include "lib/event.h"
+#include "lib/ip.h"
+#include "lib/lists.h"
+#include "lib/resource.h"
+#include "lib/unaligned.h"
+#include "nest/iface.h"
+#include "nest/route.h"
+
+// We allow for single instance of BMP protocol
+static struct bmp_proto *g_bmp;
+
+/* BMP Common Header [RFC 7854 - Section 4.1] */
+enum bmp_version {
+  BMP_VER_UNUSED = 0, // Version 0 is reserved and MUST NOT be sent
+  BMP_VERSION_1 = 1,  // Version 1 was used by draft version of RFC 7854
+  BMP_VERSION_2 = 2,  // Version 2 was used by draft version of RFC 7854
+  BMP_VERSION_3 = 3   // Version 3 is used by all messages defined in RFC 7854
+};
+
+enum bmp_message_type {
+  BMP_ROUTE_MONITOR = 0,   // Route Monitoring
+  BMP_STATS_REPORT = 1,    // Statistics Report
+  BMP_PEER_DOWN_NOTIF = 2, // Peer Down Notification
+  BMP_PEER_UP_NOTIF = 3,   // Peer Up Notification
+  BMP_INIT_MSG = 4,        // Initiation Message
+  BMP_TERM_MSG = 5,        // Termination Message
+  BMP_ROUTE_MIRROR_MSG = 6 // Route Mirroring Message
+};
+
+// Total size of Common Header
+#define BMP_COMMON_HDR_SIZE 6
+// Defines size of padding when IPv4 address is going to be put into field
+// which can accept also IPv6 address
+#define BMP_PADDING_IP4_ADDR_SIZE 12
+
+/* BMP Per-Peer Header [RFC 7854 - Section 4.2] */
+// Total size of Per-Peer Header
+#define BMP_PER_PEER_HDR_SIZE 42
+
+enum bmp_peer_type {
+  BMP_PEER_TYPE_GLOBAL_INSTANCE = 0,
+  BMP_PEER_TYPE_RD_INSTANCE = 1,
+  BMP_PEER_TYPE_LOCAL_INSTANCE = 2
+};
+
+#define BMP_PEER_HDR_FLAG_V_SHIFT 7
+enum bmp_peer_flag_v_t {
+  // The Peer address is an IPv4 address
+  BMP_PEER_HDR_FLAG_V_IP4 = (0 << BMP_PEER_HDR_FLAG_V_SHIFT),
+  // The Peer address is an IPv6 address
+  BMP_PEER_HDR_FLAG_V_IP6 = (1 << BMP_PEER_HDR_FLAG_V_SHIFT)
+};
+
+#define BMP_PEER_HDR_FLAG_L_SHIFT 6
+enum bmp_peer_flag_l {
+  BMP_PEER_HDR_FLAG_L_PRE_POLICY_ADJ_RIB_IN = (0 << BMP_PEER_HDR_FLAG_L_SHIFT),
+  BMP_PEER_HDR_FLAG_L_POST_POLICY_ADJ_RIB_IN = (1 << BMP_PEER_HDR_FLAG_L_SHIFT)
+};
+
+#define BMP_PEER_HDR_FLAG_A_SHIFT 5
+enum bmp_peer_flag_a {
+  // The 4-byte AS_PATH format
+  BMP_PEER_HDR_FLAG_A_AS_PATH_4B = (0 << BMP_PEER_HDR_FLAG_A_SHIFT),
+  // The legacy 2-byte AS_PATH format
+  BMP_PEER_HDR_FLAG_A_AS_PATH_2B = (1 << BMP_PEER_HDR_FLAG_A_SHIFT)
+};
+
+#define BMP_PEER_HDR_FLAGS_INIT(flags) \
+  (flags) = 0
+#define BMP_PEER_HDR_FLAGS_SET(flags, bit_mask) \
+  (flags) |= (bit_mask)
+
+/* BMP Information TLV header [RFC 7854 - Section 4.4] */
+// Total size of Type and Length fields of Information TLV Header without
+// variable part
+#define BMP_INFO_TLV_FIX_SIZE 4
+
+enum bmp_info_tlv_type {
+  BMP_INFO_TLV_TYPE_STRING = 0,    // String
+  BMP_INFO_TLV_TYPE_SYS_DESCR = 1, // SysDescr
+  BMP_INFO_TLV_TYPE_SYS_NAME = 2   // SysName
+};
+
+/* BMP Peer Up Notification message header [RFC 7854 - Section 4.10] */
+// Total size of all fields of Peer Up Notification message except variable part
+#define BMP_PEER_UP_NOTIF_MSG_FIX_SIZE 20
+
+enum bmp_peer_down_notif_reason {
+  // The local system closed the session
+  BMP_PEER_DOWN_REASON_LOCAL_BGP_NOTIFICATION = 1,
+  // The local system closed the session
+  BMP_PEER_DOWN_REASON_LOCAL_NO_NOTIFICATION = 2,
+  // The remote system closed the session with a notification message
+  BMP_PEER_DOWN_REASON_REMOTE_BGP_NOTIFICATION = 3,
+  // The remote system closed the session without a notification message
+  BMP_PEER_DOWN_REASON_REMOTE_NO_NOTIFICATION = 4,
+  // Information for this peer will no longer be sent to the monitoring station
+  // for configuration reasons
+  BMP_PEER_DOWN_REASON_PEER_DE_CONFIGURED = 5
+};
+
+/* BMP Termination Message [RFC 7854 - Section 4.5] */
+#define BMP_TERM_INFO_TYPE_SIZE 2
+enum bmp_term_info_type {
+  BMP_TERM_INFO_STRING = 0, // The Information field contains string
+  BMP_TERM_INFO_REASON = 1, // The Information field contains 2-byte reason code
+};
+
+// 2-byte code in the Information field
+#define BMP_TERM_REASON_CODE_SIZE 2
+enum bmp_term_reason {
+  BMP_TERM_REASON_ADM = 0,  // Session administratively closed
+  BMP_TERM_REASON_UNK = 1,  // Unspecified reason
+  BMP_TERM_REASON_OOR = 2,  // Out of resources
+  BMP_TERM_REASON_DUP = 3,  // Redundant connection
+  BMP_TERM_REASON_PERM = 4, // Session permanently administratively closed
+};
+
+// Size of Information Length field in Termination Message header
+#define BMP_TERM_INFO_LEN_FIELD_SIZE 2
+
+// Default chunk size request when memory allocation
+#define DEFAULT_MEM_BLOCK_SIZE 4096
+
+// Initial delay for connection to the BMP collector
+#define CONNECT_INIT_TIME (200 MS)
+
+// Timeout for connection to the BMP collector retry
+#define CONNECT_RETRY_TIME (10 S)
+
+#define IP4_MAX_TTL 255
+
+
+#define IF_COND_TRUE_PRINT_ERR_MSG_AND_RETURN_OPT_VAL(expr, msg, rv...)     \
+  do {                                                                      \
+    if ((expr))                                                             \
+    {                                                                       \
+      log(L_WARN "[BMP] " msg);                                             \
+      return rv;                                                            \
+    }                                                                       \
+  } while (0)
+
+
+#define IF_PTR_IS_NULL_PRINT_ERR_MSG_AND_RETURN_OPT_VAL(p, msg, rv...)	\
+  do {									\
+    IF_COND_TRUE_PRINT_ERR_MSG_AND_RETURN_OPT_VAL(!(p), msg, rv);	\
+  } while (0)
+
+
+static void bmp_connected(struct birdsock *sk);
+static void bmp_sock_err(sock *sk, int err);
+static void bmp_close_socket(struct bmp_proto *p);
+
+static void
+bmp_send_peer_up_notif_msg(struct bmp_proto *p, const struct bgp_proto *bgp,
+  const byte* tx_data, const size_t tx_data_size,
+  const byte* rx_data, const size_t rx_data_size);
+
+// Stores necessary any data in list
+struct bmp_data_node {
+  node n;
+  byte *data;
+  size_t data_size;
+};
+
+static void
+bmp_route_monitor_pre_policy_table_in_snapshot(struct channel *C);
+
+static void
+bmp_common_hdr_serialize(buffer *stream, const enum bmp_message_type type, const u32 data_size)
+{
+  bmp_put_u8(stream, BMP_VERSION_3);
+  bmp_put_u32(stream, BMP_COMMON_HDR_SIZE + data_size);
+  bmp_put_u8(stream, type);
+}
+
+static void
+bmp_info_tlv_hdr_serialize(buffer *stream, const enum bmp_info_tlv_type type,
+  const char *str)
+{
+  size_t str_len = strlen(str);
+  str_len = MIN(str_len, 65535);
+
+  bmp_put_u16(stream, type);
+  bmp_put_u16(stream, str_len);
+  bmp_put_data(stream, str, str_len);
+}
+
+// Serializes BMP Initiation message header [RFC 7854 - Section 4.3]
+static void
+bmp_init_msg_serialize(buffer *stream, const char *sys_descr, const char *sys_name)
+{
+  const size_t sys_descr_len = strlen(sys_descr);
+  const size_t sys_name_len = strlen(sys_name);
+  // We include MIB-II sysDescr and sysName in BMP INIT MSG so that's why
+  // allocated 2x BMP_INFO_TLV_FIX_SIZE memory pool size
+  const size_t data_size = (2 * BMP_INFO_TLV_FIX_SIZE) + sys_descr_len + sys_name_len;
+  bmp_buffer_need(stream, BMP_COMMON_HDR_SIZE + data_size);
+  bmp_common_hdr_serialize(stream, BMP_INIT_MSG, data_size);
+  bmp_info_tlv_hdr_serialize(stream, BMP_INFO_TLV_TYPE_SYS_DESCR, sys_descr);
+  bmp_info_tlv_hdr_serialize(stream, BMP_INFO_TLV_TYPE_SYS_NAME, sys_name);
+}
+
+static void
+bmp_schedule_tx_packet(struct bmp_proto *p, const byte *payload, const size_t size)
+{
+  ASSERT(p->started);
+
+  struct bmp_data_node *tx_data = mb_alloc(p->tx_mem_pool, sizeof (struct bmp_data_node));
+  tx_data->data = mb_alloc(p->tx_mem_pool, size);
+  memcpy(tx_data->data, payload, size);
+  tx_data->data_size = size;
+  add_tail(&p->tx_queue, &tx_data->n);
+
+  if (sk_tx_buffer_empty(p->sk)
+      && !ev_active(p->tx_ev))
+  {
+    ev_schedule(p->tx_ev);
+  }
+}
+
+static void
+bmp_fire_tx(void *p_)
+{
+  struct bmp_proto *p = p_;
+  byte *buf = p->sk->tbuf;
+  IF_COND_TRUE_PRINT_ERR_MSG_AND_RETURN_OPT_VAL(
+    EMPTY_LIST(p->tx_queue),
+    "Called BMP TX event handler when there is not any data to send"
+  );
+
+  size_t cnt = 0; // Counts max packets which we want to send per TX slot
+  struct bmp_data_node *tx_data;
+  struct bmp_data_node *tx_data_next;
+  WALK_LIST_DELSAFE(tx_data, tx_data_next, p->tx_queue)
+  {
+    if (tx_data->data_size > p->sk->tbsize)
+    {
+      sk_set_tbsize(p->sk, tx_data->data_size);
+    }
+
+    size_t data_size = tx_data->data_size;
+    memcpy(buf, tx_data->data, tx_data->data_size);
+    mb_free(tx_data->data);
+    rem_node((node *) tx_data);
+    mb_free(tx_data);
+
+    if (sk_send(p->sk, data_size) <= 0)
+      return;
+
+    // BMP packets should be treat with lowest priority when scheduling sending
+    // packets to target. That's why we want to send max. 32 packets per event
+    // call
+    if (++cnt > 32)
+    {
+      if (!ev_active(p->tx_ev))
+      {
+        ev_schedule(p->tx_ev);
+      }
+
+      return;
+    }
+  }
+}
+
+static void
+bmp_tx(struct birdsock *sk)
+{
+  bmp_fire_tx(sk->data);
+}
+
+/* We need RX hook just to accept socket close events */
+static int
+bmp_rx(struct birdsock *sk UNUSED, uint size UNUSED)
+{
+  return 0;
+}
+
+
+static inline void
+bmp_put_ipa(buffer *stream, const ip_addr addr)
+{
+  bmp_put_ip6(stream, ipa_is_ip4(addr) ?
+	      ip6_build(0,0,0, ipa_to_u32(addr)) :
+	      ipa_to_ip6(addr));
+}
+
+static void
+bmp_set_initial_bgp_hdr(buffer *stream, const u16 msg_size, const u8 msg_type)
+{
+  byte marker[BGP_MSG_HDR_MARKER_SIZE];
+  memset(marker, 0xff, BGP_MSG_HDR_MARKER_SIZE);
+  bmp_put_data(stream, marker, BGP_MSG_HDR_MARKER_SIZE);
+  bmp_put_u16(stream, msg_size);
+  bmp_put_u8(stream, msg_type);
+}
+
+/**
+ * bmp_per_peer_hdr_serialize - serializes Per-Peer Header
+ *
+ * @is_pre_policy: indicate the message reflects the pre-policy Adj-RIB-In
+ * @peer_addr: the remote IP address associated with the TCP session
+ * @peer_as: the Autonomous System number of the peer
+ * @peer_bgp_id: the BGP Identifier of the peer
+ * @ts_sec: the time in seconds when the encapsulated routes were received
+ * @ts_usec: the time in microseconds when the encapsulated routes were received
+ */
+static void
+bmp_per_peer_hdr_serialize(buffer *stream, const bool is_global_instance_peer,
+  const bool is_pre_policy, const bool is_as_path_4bytes,
+  const ip_addr peer_addr, const u32 peer_as, const u32 peer_bgp_id,
+  const u32 ts_sec, const u32 ts_usec)
+{
+  // TODO: ATM we don't support BMP_PEER_TYPE_RD_INSTANCE
+  const enum bmp_peer_type peer_type = is_global_instance_peer
+                                      ? BMP_PEER_TYPE_GLOBAL_INSTANCE
+                                      : BMP_PEER_TYPE_LOCAL_INSTANCE;
+  const u8 peer_flag_v = ipa_is_ip4(peer_addr)
+                           ? BMP_PEER_HDR_FLAG_V_IP4
+                           : BMP_PEER_HDR_FLAG_V_IP6;
+  const u8 peer_flag_l = is_pre_policy
+                           ? BMP_PEER_HDR_FLAG_L_PRE_POLICY_ADJ_RIB_IN
+                           : BMP_PEER_HDR_FLAG_L_POST_POLICY_ADJ_RIB_IN;
+  const u8 peer_flag_a = is_as_path_4bytes
+                           ? BMP_PEER_HDR_FLAG_A_AS_PATH_4B
+                           : BMP_PEER_HDR_FLAG_A_AS_PATH_2B;
+  u8 peer_flags;
+  BMP_PEER_HDR_FLAGS_INIT(peer_flags);
+  BMP_PEER_HDR_FLAGS_SET(peer_flags, peer_flag_v);
+  BMP_PEER_HDR_FLAGS_SET(peer_flags, peer_flag_l);
+  BMP_PEER_HDR_FLAGS_SET(peer_flags, peer_flag_a);
+
+  bmp_put_u8(stream, peer_type);
+  bmp_put_u8(stream, peer_flags);
+  // TODO: Provide appropriate peer Route Distinguisher if applicable
+  bmp_put_u64(stream, 0x00); // 0x00 - Not supported peer distinguisher
+  bmp_put_ipa(stream, peer_addr);
+  bmp_put_u32(stream, peer_as);
+  bmp_put_u32(stream, peer_bgp_id);
+  bmp_put_u32(stream, ts_sec);
+  bmp_put_u32(stream, ts_usec);
+}
+
+/* [4.6] Route Monitoring */
+static void
+bmp_route_monitor_msg_serialize(buffer *stream, const bool is_peer_global,
+  const bool table_in_pre_policy, const u32 peer_as, const u32 peer_bgp_id,
+  const bool as4_support, const ip_addr remote_addr, const byte *update_msg,
+  const size_t update_msg_size, u32 ts_sec, u32 ts_usec)
+{
+  const size_t data_size = BMP_PER_PEER_HDR_SIZE + update_msg_size;
+  bmp_buffer_need(stream, BMP_COMMON_HDR_SIZE + data_size);
+  bmp_common_hdr_serialize(stream, BMP_ROUTE_MONITOR, data_size);
+  bmp_per_peer_hdr_serialize(stream, is_peer_global, table_in_pre_policy,
+    as4_support, remote_addr, peer_as, peer_bgp_id, ts_sec, ts_usec);
+  bmp_put_data(stream, update_msg, update_msg_size);
+}
+
+static void
+bmp_peer_up_notif_msg_serialize(buffer *stream, const bool is_peer_global,
+  const u32 peer_as, const u32 peer_bgp_id, const bool as4_support,
+  const ip_addr local_addr, const ip_addr remote_addr, const u16 local_port,
+  const u16 remote_port, const byte *sent_msg, const size_t sent_msg_size,
+  const byte *recv_msg, const size_t recv_msg_size)
+{
+  const size_t data_size = BMP_PER_PEER_HDR_SIZE + BMP_PEER_UP_NOTIF_MSG_FIX_SIZE
+                             + sent_msg_size + recv_msg_size;
+  bmp_buffer_need(stream, BMP_COMMON_HDR_SIZE + data_size);
+  bmp_common_hdr_serialize(stream, BMP_PEER_UP_NOTIF, data_size);
+  bmp_per_peer_hdr_serialize(stream, is_peer_global,
+    true /* TODO: Hardcoded pre-policy Adj-RIB-In */, as4_support, remote_addr,
+    peer_as, peer_bgp_id, 0, 0); // 0, 0 - No timestamp provided
+  bmp_put_ipa(stream, local_addr);
+  bmp_put_u16(stream, local_port);
+  bmp_put_u16(stream, remote_port);
+  bmp_set_initial_bgp_hdr(stream, sent_msg_size, PKT_OPEN);
+  const size_t missing_bgp_hdr_size = BGP_MSG_HDR_MARKER_SIZE
+                                        + BGP_MSG_HDR_LENGTH_SIZE
+                                        + BGP_MSG_HDR_TYPE_SIZE;
+  bmp_put_data(stream, sent_msg, sent_msg_size - missing_bgp_hdr_size);
+  bmp_put_data(stream, recv_msg, recv_msg_size);
+}
+
+static void
+bmp_peer_down_notif_msg_serialize(buffer *stream, const bool is_peer_global,
+  const u32 peer_as, const u32 peer_bgp_id, const bool as4_support,
+  const ip_addr remote_addr, const byte *data, const size_t data_size)
+{
+  const size_t payload_size = BMP_PER_PEER_HDR_SIZE + data_size;
+  bmp_buffer_need(stream, BMP_COMMON_HDR_SIZE + payload_size);
+  bmp_common_hdr_serialize(stream, BMP_PEER_DOWN_NOTIF, payload_size);
+  bmp_per_peer_hdr_serialize(stream, is_peer_global,
+    true /* TODO: Hardcoded pre-policy adj RIB IN */,  as4_support, remote_addr,
+    peer_as, peer_bgp_id, 0, 0); // 0, 0 - No timestamp provided
+  bmp_put_data(stream, data, data_size);
+}
+
+static void
+bmp_peer_map_walk_tx_open_msg_and_send_peer_up_notif(
+  const struct bmp_peer_map_key key, const byte *tx_msg,
+  const size_t tx_msg_size, void *bmp_)
+{
+  struct bmp_proto *p = bmp_;
+  ASSERT(p->started);
+
+  const struct bmp_peer_map_entry *map_rx_msg = bmp_peer_map_get(&p->peer_open_msg.rx_msg, key);
+  IF_PTR_IS_NULL_PRINT_ERR_MSG_AND_RETURN_OPT_VAL(
+    map_rx_msg,
+    "Processing TX BGP OPEN MSG but there is not corresponding received MSG"
+  );
+
+  const struct bmp_peer_map_entry *map_bgp_proto = bmp_peer_map_get(&p->bgp_peers, key);
+  IF_PTR_IS_NULL_PRINT_ERR_MSG_AND_RETURN_OPT_VAL(
+    map_bgp_proto,
+    "There is not BGP proto related with stored TX/RX OPEN MSG"
+  );
+
+  const struct bgp_proto *bgp;
+  memcpy(&bgp, map_bgp_proto->data.buf, sizeof (bgp));
+  if (bgp->p.proto_state == PS_UP)
+  {
+    bmp_send_peer_up_notif_msg(p, bgp, tx_msg, tx_msg_size,
+			       map_rx_msg->data.buf, map_rx_msg->data.buf_size);
+  }
+}
+
+static void
+bmp_peer_up(const struct bgp_proto *bgp)
+{
+  struct bgp_channel *c;
+  WALK_LIST(c, bgp->p.channels)
+  {
+    bmp_route_monitor_pre_policy_table_in_snapshot((struct channel *) c);
+  }
+}
+
+static const struct birdsock *
+bmp_get_birdsock(const struct bgp_proto *bgp)
+{
+  if (bgp->conn && bgp->conn->sk)
+  {
+    return bgp->conn->sk;
+  }
+
+  return NULL;
+}
+
+static const struct birdsock *
+bmp_get_birdsock_ext(const struct bgp_proto *bgp)
+{
+  const struct birdsock *sk = bmp_get_birdsock(bgp);
+
+  if (sk != NULL)
+  {
+    return sk;
+  }
+
+  if (bgp->incoming_conn.sk)
+  {
+    sk = bgp->incoming_conn.sk;
+  }
+  else if (bgp->outgoing_conn.sk)
+  {
+    sk = bgp->outgoing_conn.sk;
+  }
+
+  return sk;
+}
+
+static const struct bgp_caps *
+bmp_get_bgp_remote_caps(const struct bgp_proto *bgp)
+{
+  if (bgp->conn && bgp->conn->remote_caps)
+  {
+    return bgp->conn->remote_caps;
+  }
+
+  return NULL;
+}
+
+static const struct bgp_caps *
+bmp_get_bgp_remote_caps_ext(const struct bgp_proto *bgp)
+{
+  const struct bgp_caps *remote_caps = bmp_get_bgp_remote_caps(bgp);
+  if (remote_caps != NULL)
+  {
+    return remote_caps;
+  }
+
+  if (bgp->incoming_conn.remote_caps)
+  {
+    remote_caps = bgp->incoming_conn.remote_caps;
+  }
+  else if (bgp->outgoing_conn.remote_caps)
+  {
+    remote_caps = bgp->outgoing_conn.remote_caps;
+  }
+
+  return remote_caps;
+}
+
+static bool
+bmp_is_peer_global_instance(const struct bgp_proto *bgp)
+{
+  return (bgp->cf->peer_type != BGP_PT_EXTERNAL &&
+            bgp->cf->peer_type != BGP_PT_INTERNAL)
+              ? (bgp->local_as != bgp->remote_as)
+              : (bgp->cf->peer_type == BGP_PT_EXTERNAL);
+}
+
+static void
+bmp_send_peer_up_notif_msg(struct bmp_proto *p, const struct bgp_proto *bgp,
+  const byte* tx_data, const size_t tx_data_size,
+  const byte* rx_data, const size_t rx_data_size)
+{
+  ASSERT(p->started);
+
+  const struct birdsock *sk = bmp_get_birdsock_ext(bgp);
+  IF_PTR_IS_NULL_PRINT_ERR_MSG_AND_RETURN_OPT_VAL(
+    sk,
+    "[BMP] No BGP socket"
+  );
+
+  const bool is_global_instance_peer = bmp_is_peer_global_instance(bgp);
+  buffer payload = bmp_buffer_alloc(p->buffer_mpool, DEFAULT_MEM_BLOCK_SIZE);
+  bmp_peer_up_notif_msg_serialize(&payload, is_global_instance_peer,
+    bgp->remote_as, bgp->remote_id, 1,
+    sk->saddr, sk->daddr, sk->sport, sk->dport, tx_data, tx_data_size,
+    rx_data, rx_data_size);
+  bmp_schedule_tx_packet(p, bmp_buffer_data(&payload), bmp_buffer_pos(&payload));
+  bmp_buffer_free(&payload);
+
+  bmp_peer_up(bgp);
+}
+
+void
+bmp_put_sent_bgp_open_msg(const struct bgp_proto *bgp, const byte* pkt,
+  const size_t pkt_size)
+{
+  struct bmp_proto *p = g_bmp;
+
+  if (!p)
+  {
+    return;
+  }
+
+  struct bmp_peer_map_key key
+    = bmp_peer_map_key_create(bgp->remote_ip, bgp->remote_as);
+  const struct bmp_peer_map_entry *rx_msg
+    = bmp_peer_map_get(&p->peer_open_msg.rx_msg, key);
+
+  bmp_peer_map_insert(&p->peer_open_msg.tx_msg, key, pkt, pkt_size);
+
+  if (!rx_msg)
+    bmp_peer_map_insert(&p->bgp_peers, key, (const byte *) &bgp, sizeof (bgp));
+
+  if (rx_msg && p->started)
+    bmp_send_peer_up_notif_msg(p, bgp, pkt, pkt_size, rx_msg->data.buf,
+			       rx_msg->data.buf_size);
+}
+
+void
+bmp_put_recv_bgp_open_msg(const struct bgp_proto *bgp, const byte* pkt,
+  const size_t pkt_size)
+{
+  struct bmp_proto *p = g_bmp;
+
+  if (!p)
+  {
+    return;
+  }
+
+  struct bmp_peer_map_key key
+    = bmp_peer_map_key_create(bgp->remote_ip, bgp->remote_as);
+  const struct bmp_peer_map_entry *tx_msg
+    = bmp_peer_map_get(&p->peer_open_msg.tx_msg, key);
+
+  bmp_peer_map_insert(&p->peer_open_msg.rx_msg, key, pkt, pkt_size);
+
+  if (!tx_msg)
+    bmp_peer_map_insert(&p->bgp_peers, key, (const byte *) &bgp, sizeof (bgp));
+
+  if (tx_msg && p->started)
+    bmp_send_peer_up_notif_msg(p, bgp, tx_msg->data.buf, tx_msg->data.buf_size,
+			       pkt, pkt_size);
+}
+
+void
+bmp_route_monitor_update_in_pre_begin()
+{
+  struct bmp_proto *p = g_bmp;
+
+  if (!p || !p->started)
+  {
+    return;
+  }
+
+  if (p->monitoring_rib.in_pre_policy == false)
+  {
+    return;
+  }
+
+  IF_COND_TRUE_PRINT_ERR_MSG_AND_RETURN_OPT_VAL(
+    !EMPTY_LIST(p->rt_table_in_pre_policy.update_msg_queue),
+    "Previous BMP route monitoring update not finished yet"
+  );
+
+  gettimeofday(&p->rt_table_in_pre_policy.update_begin_time,NULL);
+  init_list(&p->rt_table_in_pre_policy.update_msg_queue);
+  p->rt_table_in_pre_policy.update_msg_size = 0;
+  p->rt_table_in_pre_policy.update_in_progress = true;
+}
+
+void
+bmp_route_monitor_put_update_in_pre_msg(const byte *data, const size_t data_size)
+{
+  struct bmp_proto *p = g_bmp;
+
+  if (!p || !p->started)
+  {
+    return;
+  }
+
+  if (p->monitoring_rib.in_pre_policy == false)
+  {
+    return;
+  }
+
+  IF_COND_TRUE_PRINT_ERR_MSG_AND_RETURN_OPT_VAL(
+    !p->rt_table_in_pre_policy.update_in_progress,
+    "BMP route monitoring update not started yet"
+  );
+
+  struct bmp_data_node *upd_msg = mb_alloc(p->update_msg_mem_pool,
+                               sizeof (struct bmp_data_node));
+  upd_msg->data = mb_alloc(p->update_msg_mem_pool, data_size);
+  memcpy(upd_msg->data, data, data_size);
+  upd_msg->data_size = data_size;
+  p->rt_table_in_pre_policy.update_msg_size += data_size;
+  add_tail(&p->rt_table_in_pre_policy.update_msg_queue, &upd_msg->n);
+}
+
+void
+bmp_route_monitor_update_in_pre_commit(const struct bgp_proto *bgp)
+{
+  struct bmp_proto *p = g_bmp;
+
+  if (!p || !p->started)
+  {
+    return;
+  }
+
+  if (p->monitoring_rib.in_pre_policy == false)
+  {
+    return;
+  }
+
+  const struct birdsock *sk = bmp_get_birdsock(bgp);
+  IF_PTR_IS_NULL_PRINT_ERR_MSG_AND_RETURN_OPT_VAL(
+    sk,
+    "Failed to get bird socket from BGP proto"
+  );
+
+  const struct bgp_caps *remote_caps = bmp_get_bgp_remote_caps(bgp);
+  IF_PTR_IS_NULL_PRINT_ERR_MSG_AND_RETURN_OPT_VAL(
+    remote_caps,
+    "Failed to get remote capabilities from BGP proto"
+  );
+
+  bool is_global_instance_peer = bmp_is_peer_global_instance(bgp);
+  buffer payload
+    = bmp_buffer_alloc(p->buffer_mpool,
+        p->rt_table_in_pre_policy.update_msg_size + DEFAULT_MEM_BLOCK_SIZE);
+
+  buffer update_msgs
+    = bmp_buffer_alloc(p->buffer_mpool,
+        p->rt_table_in_pre_policy.update_msg_size);
+
+  struct bmp_data_node *data;
+  WALK_LIST(data, p->rt_table_in_pre_policy.update_msg_queue)
+  {
+    bmp_put_data(&update_msgs, data->data, data->data_size);
+    bmp_route_monitor_msg_serialize(&payload,
+      is_global_instance_peer, true /* TODO: Hardcoded pre-policy Adj-Rib-In */,
+      bgp->conn->received_as, bgp->remote_id, remote_caps->as4_support,
+      sk->daddr, bmp_buffer_data(&update_msgs), bmp_buffer_pos(&update_msgs),
+      p->rt_table_in_pre_policy.update_begin_time.tv_sec,
+      p->rt_table_in_pre_policy.update_begin_time.tv_usec);
+
+    bmp_schedule_tx_packet(p, bmp_buffer_data(&payload), bmp_buffer_pos(&payload));
+
+    bmp_buffer_flush(&payload);
+    bmp_buffer_flush(&update_msgs);
+  }
+
+  bmp_buffer_free(&payload);
+  bmp_buffer_free(&update_msgs);
+}
+
+void
+bmp_route_monitor_update_in_pre_end()
+{
+  struct bmp_proto *p = g_bmp;
+
+  if (!p || !p->started)
+  {
+    return;
+  }
+
+  if (p->monitoring_rib.in_pre_policy == false)
+  {
+    return;
+  }
+
+  struct bmp_data_node *upd_msg;
+  struct bmp_data_node *upd_msg_next;
+  WALK_LIST_DELSAFE(upd_msg, upd_msg_next, p->rt_table_in_pre_policy.update_msg_queue)
+  {
+    mb_free(upd_msg->data);
+    rem_node((node *) upd_msg);
+    mb_free(upd_msg);
+  }
+
+  p->rt_table_in_pre_policy.update_in_progress = false;
+}
+
+static void
+bmp_route_monitor_pre_policy_table_in_snapshot(struct channel *C)
+{
+  struct bmp_proto *p = g_bmp;
+
+  if (p->monitoring_rib.in_pre_policy == false)
+  {
+    return;
+  }
+
+  struct rtable *tab = C->in_table;
+  if (!tab)
+  {
+    return;
+  }
+
+  size_t cnt = 0;
+  struct proto *P;
+  struct fib_iterator fit;
+  memset(&fit, 0x00, sizeof (fit));
+  FIB_ITERATE_INIT(&fit, &tab->fib);
+  FIB_ITERATE_START(&tab->fib, &fit, net, n)
+  {
+    P = n->routes->sender->proto;
+    if (P->proto->class != PROTOCOL_BGP)
+    {
+      continue;
+    }
+
+    bmp_route_monitor_update_in_pre_begin();
+
+    rte *e;
+    for (e = n->routes; e; e = e->next)
+    {
+      bgp_rte_update_in_notify(C, n->n.addr, e, e->src);
+    }
+
+    bmp_route_monitor_update_in_pre_commit((struct bgp_proto *) P);
+    bmp_route_monitor_update_in_pre_end();
+    ++cnt;
+  }
+  FIB_ITERATE_END;
+
+  if (cnt > 0)
+  {
+    bmp_route_monitor_update_in_pre_begin();
+    byte rx_end_payload[DEFAULT_MEM_BLOCK_SIZE];
+    byte *pos
+      = bgp_create_end_mark((struct bgp_channel *) C, rx_end_payload
+                                                        + BGP_HEADER_LENGTH);
+    memset(rx_end_payload + BGP_MSG_HDR_MARKER_POS, 0xff,
+             BGP_MSG_HDR_MARKER_SIZE); // BGP UPDATE MSG marker
+    put_u16(rx_end_payload + BGP_MSG_HDR_LENGTH_POS, pos - rx_end_payload);
+    put_u8(rx_end_payload + BGP_MSG_HDR_TYPE_POS, PKT_UPDATE);
+    bmp_route_monitor_put_update_in_pre_msg(rx_end_payload, pos - rx_end_payload);
+    bmp_route_monitor_update_in_pre_commit((struct bgp_proto *) C->proto);
+    bmp_route_monitor_update_in_pre_end();
+  }
+}
+
+static void
+bmp_send_peer_down_notif_msg(struct bmp_proto *p, const struct bgp_proto *bgp,
+  const byte* data, const size_t data_size)
+{
+  ASSERT(p->started);
+
+  const struct bgp_caps *remote_caps = bmp_get_bgp_remote_caps_ext(bgp);
+  bool is_global_instance_peer = bmp_is_peer_global_instance(bgp);
+  buffer payload
+    = bmp_buffer_alloc(p->buffer_mpool, DEFAULT_MEM_BLOCK_SIZE);
+  bmp_peer_down_notif_msg_serialize(&payload, is_global_instance_peer,
+    bgp->remote_as, bgp->remote_id,
+    remote_caps ? remote_caps->as4_support : bgp->as4_session,
+    bgp->remote_ip, data, data_size);
+  bmp_schedule_tx_packet(p, bmp_buffer_data(&payload), bmp_buffer_pos(&payload));
+
+  bmp_buffer_free(&payload);
+}
+
+void
+bmp_peer_down(const struct bgp_proto *bgp, const int err_class, const byte *pkt,
+  size_t pkt_size)
+{
+  struct bmp_proto *p = g_bmp;
+
+  if (!p)
+  {
+    return;
+  }
+
+  struct bmp_peer_map_key key
+    = bmp_peer_map_key_create(bgp->remote_ip, bgp->remote_as);
+
+  bmp_peer_map_remove(&p->peer_open_msg.tx_msg, key);
+  bmp_peer_map_remove(&p->peer_open_msg.rx_msg, key);
+  bmp_peer_map_remove(&p->bgp_peers, key);
+  const size_t missing_bgp_hdr_size = BGP_MSG_HDR_MARKER_SIZE
+                                        + BGP_MSG_HDR_LENGTH_SIZE
+                                        + BGP_MSG_HDR_TYPE_SIZE;
+
+  if (!p->started)
+    return;
+
+  buffer payload
+    = bmp_buffer_alloc(p->buffer_mpool, pkt_size + missing_bgp_hdr_size + 1);
+  if (pkt != NULL && pkt_size > 0)
+  {
+    byte marker[BGP_MSG_HDR_MARKER_SIZE];
+    memset(marker, 0xff, BGP_MSG_HDR_MARKER_SIZE); // NOTIF MSG marker
+    if (!memcmp(pkt, marker, BGP_MSG_HDR_MARKER_SIZE))
+    {
+      // So it is received BGP PDU
+      bmp_put_u8(&payload, BMP_PEER_DOWN_REASON_REMOTE_BGP_NOTIFICATION);
+      bmp_put_data(&payload, pkt, pkt_size);
+    }
+    else
+    {
+      bmp_put_u8(&payload, BMP_PEER_DOWN_REASON_LOCAL_BGP_NOTIFICATION);
+      bmp_put_data(&payload, marker, BGP_MSG_HDR_MARKER_SIZE);
+      bmp_put_u16(&payload, pkt_size);
+      bmp_put_u8(&payload, PKT_NOTIFICATION);
+      bmp_put_data(&payload, pkt, pkt_size);
+    }
+  }
+  else
+  {
+    // TODO: Handle De-configured Peer Down Reason Code
+    if (err_class == BE_SOCKET || err_class == BE_MISC)
+    {
+      bmp_put_u8(&payload, BMP_PEER_DOWN_REASON_REMOTE_NO_NOTIFICATION);
+    }
+    else
+    {
+      bmp_put_u8(&payload, BMP_PEER_DOWN_REASON_LOCAL_NO_NOTIFICATION);
+      // TODO: Fill in with appropriate FSM event code
+      bmp_put_u16(&payload, 0x00); // no relevant Event code is defined
+    }
+  }
+
+  bmp_send_peer_down_notif_msg(p, bgp, bmp_buffer_data(&payload), bmp_buffer_pos(&payload));
+
+  bmp_buffer_free(&payload);
+}
+
+static void
+bmp_send_termination_msg(struct bmp_proto *p,
+  const enum bmp_term_reason reason)
+{
+  const size_t term_msg_hdr_size = BMP_TERM_INFO_TYPE_SIZE
+                                     + BMP_TERM_INFO_LEN_FIELD_SIZE
+                                     + BMP_TERM_REASON_CODE_SIZE;
+  const size_t term_msg_size = BMP_COMMON_HDR_SIZE + term_msg_hdr_size;
+  buffer stream = bmp_buffer_alloc(p->buffer_mpool, term_msg_size);
+  bmp_common_hdr_serialize(&stream, BMP_TERM_MSG, term_msg_hdr_size);
+  bmp_put_u16(&stream, BMP_TERM_INFO_REASON);
+  bmp_put_u16(&stream, BMP_TERM_REASON_CODE_SIZE); // 2-byte code indication the reason
+  bmp_put_u16(&stream, reason);
+  memcpy(p->sk->tbuf, bmp_buffer_data(&stream), bmp_buffer_pos(&stream));
+  IF_COND_TRUE_PRINT_ERR_MSG_AND_RETURN_OPT_VAL(
+    sk_send(p->sk, bmp_buffer_pos(&stream)) < 0,
+    "Failed to send BMP termination message"
+    );
+
+  bmp_buffer_free(&stream);
+}
+
+/**
+ * bmp_startup - enter established state
+ * @p: BMP instance
+ *
+ * The bgp_startup() function is called when the BMP session is established.
+ * It sends initiation and peer up messagages.
+ */
+static void
+bmp_startup(struct bmp_proto *p)
+{
+  ASSERT(!p->started);
+  p->started = true;
+
+  TRACE(D_EVENTS, "BMP session established");
+
+  /* Send initiation message */
+  buffer payload = bmp_buffer_alloc(p->buffer_mpool, DEFAULT_MEM_BLOCK_SIZE);
+  bmp_init_msg_serialize(&payload, p->sys_descr, p->sys_name);
+  bmp_schedule_tx_packet(p, bmp_buffer_data(&payload), bmp_buffer_pos(&payload));
+  bmp_buffer_free(&payload);
+
+  /* Send Peer Up messages */
+  bmp_peer_map_walk(&p->peer_open_msg.tx_msg,
+		    bmp_peer_map_walk_tx_open_msg_and_send_peer_up_notif, p);
+
+  proto_notify_state(&p->p, PS_UP);
+}
+
+/**
+ * bmp_down - leave established state
+ * @p: BMP instance
+ *
+ * The bgp_down() function is called when the BMP session fails.
+ */
+static void
+bmp_down(struct bmp_proto *p)
+{
+  ASSERT(p->started);
+  p->started = false;
+
+  TRACE(D_EVENTS, "BMP session closed");
+
+  proto_notify_state(&p->p, PS_START);
+}
+
+/**
+ * bmp_connect - initiate an outgoing connection
+ * @p: BMP instance
+ *
+ * The bmp_connect() function creates the socket and initiates an outgoing TCP
+ * connection to the monitoring station. It is called to enter Connect state.
+ */
+static void
+bmp_connect(struct bmp_proto *p)
+{
+  ASSERT(!p->started);
+
+  sock *sk = sk_new(p->p.pool);
+  sk->type = SK_TCP_ACTIVE;
+  sk->daddr = p->station_ip;
+  sk->dport = p->station_port;
+  sk->ttl = IP4_MAX_TTL;
+  sk->tos = IP_PREC_INTERNET_CONTROL;
+  sk->tbsize = BGP_TX_BUFFER_EXT_SIZE;
+  sk->tx_hook = bmp_connected;
+  sk->err_hook = bmp_sock_err;
+
+  p->sk = sk;
+  sk->data = p;
+
+  int rc = sk_open(sk);
+
+  if (rc < 0)
+    sk_log_error(sk, p->p.name);
+
+  tm_start(p->connect_retry_timer, CONNECT_RETRY_TIME);
+}
+
+/* BMP connect successfull event - switch from Connect to Established state */
+static void
+bmp_connected(struct birdsock *sk)
+{
+  struct bmp_proto *p = (void *) sk->data;
+
+  sk->rx_hook = bmp_rx;
+  sk->tx_hook = bmp_tx;
+  tm_stop(p->connect_retry_timer);
+
+  bmp_startup(p);
+}
+
+/* BMP socket error event - switch from any state to Idle state */
+static void
+bmp_sock_err(sock *sk, int err)
+{
+  struct bmp_proto *p = sk->data;
+
+  if (err)
+    TRACE(D_EVENTS, "Connection lost (%M)", err);
+  else
+    TRACE(D_EVENTS, "Connection closed");
+
+  if (p->started)
+    bmp_down(p);
+
+  bmp_close_socket(p);
+  tm_start(p->connect_retry_timer, CONNECT_RETRY_TIME);
+}
+
+/* BMP connect timeout event - switch from Idle/Connect state to Connect state */
+static void
+bmp_connection_retry(timer *t)
+{
+  struct bmp_proto *p = t->data;
+
+  if (p->started)
+    return;
+
+  bmp_close_socket(p);
+  bmp_connect(p);
+}
+
+static void
+bmp_close_socket(struct bmp_proto *p)
+{
+  rfree(p->sk);
+  p->sk = NULL;
+}
+
+
+/** Configuration handle section **/
+static struct proto *
+bmp_init(struct proto_config *CF)
+{
+  struct proto *P = proto_new(CF);
+  struct bmp_proto *p = (void *) P;
+  struct bmp_config *cf = (void *) CF;
+
+  p->cf = cf;
+  p->station_ip = cf->station_ip;
+  p->station_port = cf->station_port;
+  strcpy(p->sys_descr, cf->sys_descr);
+  strcpy(p->sys_name, cf->sys_name);
+  p->monitoring_rib.in_pre_policy = cf->monitoring_rib_in_pre_policy;
+  p->monitoring_rib.in_post_policy = cf->monitoring_rib_in_post_policy;
+  p->monitoring_rib.local = cf->monitoring_rib_local;
+
+  return P;
+}
+
+static int
+bmp_start(struct proto *P)
+{
+  struct bmp_proto *p = (void *) P;
+
+  log(L_DEBUG "Init BMP");
+
+  p->buffer_mpool = rp_new(P->pool, "BMP Buffer");
+  p->map_mem_pool = rp_new(P->pool, "BMP Map");
+  p->tx_mem_pool = rp_new(P->pool, "BMP Tx");
+  p->update_msg_mem_pool = rp_new(P->pool, "BMP Update");
+  p->tx_ev = ev_new_init(p->tx_mem_pool, bmp_fire_tx, p);
+  p->connect_retry_timer = tm_new_init(p->p.pool, bmp_connection_retry, p, 0, 0);
+  p->sk = NULL;
+
+  bmp_peer_map_init(&p->peer_open_msg.tx_msg, p->map_mem_pool);
+  bmp_peer_map_init(&p->peer_open_msg.rx_msg, p->map_mem_pool);
+  bmp_peer_map_init(&p->bgp_peers, p->map_mem_pool);
+
+  init_list(&p->tx_queue);
+  init_list(&p->rt_table_in_pre_policy.update_msg_queue);
+  p->started = false;
+
+  tm_start(p->connect_retry_timer, CONNECT_INIT_TIME);
+
+  g_bmp = p;
+
+  return PS_START;
+}
+
+static int
+bmp_shutdown(struct proto *P)
+{
+  struct bmp_proto *p = (void *) P;
+
+  if (p->started)
+  {
+    bmp_send_termination_msg(p, BMP_TERM_REASON_ADM);
+    p->started = false;
+  }
+
+  g_bmp = NULL;
+
+  return PS_DOWN;
+}
+
+static int
+bmp_reconfigure(struct proto *P, struct proto_config *CF)
+{
+  struct bmp_proto *p = (void *) P;
+  const struct bmp_config *cf = (void *) CF;
+
+  log(L_WARN "Reconfiguring BMP is not supported");
+
+  p->cf = cf;
+
+  return 1;
+}
+
+struct protocol proto_bmp = {
+  .name = "BMP",
+  .template = "bmp%d",
+  .class = PROTOCOL_BMP,
+  .proto_size = sizeof(struct bmp_proto),
+  .config_size = sizeof(struct bmp_config),
+  .init = bmp_init,
+  .start = bmp_start,
+  .shutdown = bmp_shutdown,
+  .reconfigure = bmp_reconfigure,
+};
+
+void
+bmp_build(void)
+{
+  proto_build(&proto_bmp);
+}
diff --git a/proto/bmp/bmp.h b/proto/bmp/bmp.h
new file mode 100644
index 0000000..19623e3
--- /dev/null
+++ b/proto/bmp/bmp.h
@@ -0,0 +1,153 @@
+/*
+ *	BIRD -- The BGP Monitoring Protocol (BMP)
+ *
+ *	(c) 2020 Akamai Technologies, Inc. (Pawel Maslanka, pmaslank@akamai.com)
+ *
+ *	Can be freely distributed and used under the terms of the GNU GPL.
+ */
+
+#ifndef _BIRD_BMP_H_
+#define _BIRD_BMP_H_
+
+#include "nest/bird.h"
+#include "nest/protocol.h"
+#include "lib/lists.h"
+#include "nest/route.h"
+#include "lib/event.h"
+#include "lib/hash.h"
+#include "lib/socket.h"
+#include "proto/bmp/map.h"
+
+#include <stdbool.h>
+
+// Max length of MIB-II description object
+#define MIB_II_STR_LEN 255
+
+// The following fields of this structure controls whether there will be put
+// specific routes into Route Monitoring message and send to BMP collector
+struct monitoring_rib {
+  bool in_pre_policy;  // Monitoring pre-policy Adj-Rib-In
+  bool in_post_policy; // Monitoring post-policy Adj-Rib-In
+  bool local;          // Monitoring Local Rib
+};
+
+struct bmp_config {
+  struct proto_config c;
+  const char *sys_descr;              // sysDescr MIB-II [RFC1213] object
+  const char *sys_name;               // sysName MIB-II [RFC1213] object
+  ip_addr station_ip;                 // Monitoring station address
+  u16 station_port;                   // Monitoring station TCP port
+  bool monitoring_rib_in_pre_policy;  // Route monitoring pre-policy Adj-Rib-In
+  bool monitoring_rib_in_post_policy; // Route monitoring post-policy Adj-Rib-In
+  bool monitoring_rib_local;          // Route monitoring Local Rib
+};
+
+/* Forward declarations */
+struct bgp_proto;
+struct bmp_proto;
+
+// Stores sent and received BGP OPEN MSGs
+struct bmp_peer_open_msg {
+  struct bmp_peer_map tx_msg;
+  struct bmp_peer_map rx_msg;
+};
+
+// Keeps necessary information during composing BGP UPDATE MSG which is going
+// to be sent to the BMP collector
+struct rt_table_info {
+  list update_msg_queue;         // Stores all composed BGP UPDATE MSGs
+  size_t update_msg_size;        // Size of all BGP UPDATE MSGs
+  struct timeval update_begin_time; // Keeps timestamp of starting BGP UPDATE MSGs composing
+  bool update_in_progress;       // Holds information whether composing process is still in progress
+};
+
+struct bmp_proto {
+  struct proto p;                  // Parent proto
+  const struct bmp_config *cf;     // Shortcut to BMP configuration
+  sock *sk;                        // TCP connection
+  event *tx_ev;			   // TX event
+  char sys_descr[MIB_II_STR_LEN];  // sysDescr MIB-II [RFC1213] object
+  char sys_name[MIB_II_STR_LEN];   // sysName MIB-II [RFC1213] object
+  ip_addr station_ip;              // Monitoring station IP address
+  u16 station_port;                // Monitoring station TCP port
+  struct monitoring_rib monitoring_rib;
+  // Below fields are for internal use
+  struct bmp_peer_map bgp_peers;   // Stores 'bgp_proto' structure per BGP peer
+  struct bmp_peer_open_msg peer_open_msg; // Stores sent and received BGP OPEN MSG per BGP peer
+  pool *buffer_mpool;              // Memory pool used for BMP buffer allocations
+  pool *map_mem_pool;              // Memory pool used for BMP map allocations
+  pool *tx_mem_pool;               // Memory pool used for packet allocations designated to BMP collector
+  pool *update_msg_mem_pool;       // Memory pool used for BPG UPDATE MSG allocations
+  list tx_queue;                   // Stores queued packets going to be sent
+  timer *connect_retry_timer;      // Timer for retrying connection to the BMP collector
+  struct rt_table_info rt_table_in_pre_policy; // Pre-policy route import table
+  bool started;                    // Flag that stores running status of BMP instance
+};
+
+
+#ifdef CONFIG_BMP
+
+/**
+ * bmp_put_sent_bgp_open_msg - save sent BGP OPEN msg packet in BMP implementation.
+ * NOTE: If there has been passed sent and received BGP OPEN MSGs to the BMP
+ *       implementation, then there is going to be send BMP Peer Up Notification
+ *       message to the BMP collector.
+ */
+void
+bmp_put_sent_bgp_open_msg(const struct bgp_proto *bgp, const byte* pkt,
+  const size_t pkt_size);
+
+/**
+ * bmp_put_recv_bgp_open_msg - save received BGP OPEN msg packet in BMP implementation.
+ * NOTE: If there has been passed sent and received BGP OPEN MSGs to the BMP
+ *       implementation, then there is going to be send BMP Peer Up Notification
+ *       message to the BMP collector.
+ */
+void
+bmp_put_recv_bgp_open_msg(const struct bgp_proto *bgp, const byte* pkt,
+  const size_t pkt_size);
+
+/**
+ * The following 4 functions create BMP Route Monitoring message based on
+ * pre-policy Adj-RIB-In. Composing Route Monitoring message consist of few
+ * stages. First of all call bmp_route_monitor_update_in_pre_begin() in order
+ * to start composing message. As a second step, call
+ * bmp_route_monitor_put_update_in_pre_msg() in order to save BGP UPDATE msg.
+ * As a third step call bmp_route_monitor_update_in_pre_commit() in order to
+ * send BMP Route Monitoring message to the BMP collector. As a last step,
+ * call bmp_route_monitor_update_in_pre_end() in order to release resources.
+ */
+void
+bmp_route_monitor_update_in_pre_begin(void);
+
+void
+bmp_route_monitor_put_update_in_pre_msg(const byte *data, const size_t data_size);
+
+void
+bmp_route_monitor_update_in_pre_commit(const struct bgp_proto *bgp);
+
+void
+bmp_route_monitor_update_in_pre_end(void);
+
+/**
+ * bmp_peer_down - send notification that BGP peer connection is not in
+ * established state
+ */
+void
+bmp_peer_down(const struct bgp_proto *bgp, const int err_class, const byte *pkt,
+  size_t pkt_size);
+
+
+#else /* BMP build disabled */
+
+static inline void bmp_put_sent_bgp_open_msg(const struct bgp_proto *bgp UNUSED, const byte* pkt UNUSED, const size_t pkt_size UNUSED) { }
+static inline void bmp_put_recv_bgp_open_msg(const struct bgp_proto *bgp UNUSED, const byte* pkt UNUSED, const size_t pkt_size UNUSED) { }
+static inline void bmp_route_monitor_update_in_pre_begin(void) { }
+static inline void bmp_route_monitor_put_update_in_pre_msg(const byte *data UNUSED, const size_t data_size UNUSED) { }
+static inline void bmp_route_monitor_update_in_pre_commit(const struct bgp_proto *bgp UNUSED) { }
+static inline void bmp_route_monitor_update_in_pre_end(void) { }
+static inline void bmp_peer_down(const struct bgp_proto *bgp UNUSED, const int err_class UNUSED, const byte *pkt UNUSED, size_t pkt_size UNUSED) { }
+
+#endif /* CONFIG_BMP */
+
+#endif /* _BIRD_BMP_H_ */
diff --git a/proto/bmp/buffer.c b/proto/bmp/buffer.c
new file mode 100644
index 0000000..f471e08
--- /dev/null
+++ b/proto/bmp/buffer.c
@@ -0,0 +1,58 @@
+/*
+ *	BIRD -- The BGP Monitoring Protocol (BMP)
+ *
+ *	(c) 2020 Akamai Technologies, Inc. (Pawel Maslanka, pmaslank@akamai.com)
+ *
+ *	Can be freely distributed and used under the terms of the GNU GPL.
+ */
+
+#include "proto/bmp/buffer.h"
+
+buffer
+bmp_buffer_alloc(pool *ppool, const size_t n)
+{
+  buffer buf;
+  buf.start = mb_alloc(ppool, n);
+  buf.pos = buf.start;
+  buf.end = buf.start + n;
+
+  return buf;
+}
+
+void
+bmp_buffer_free(buffer *buf)
+{
+  mb_free(buf->start);
+  buf->start = buf->pos = buf->end = NULL;
+}
+
+static void
+bmp_buffer_grow(buffer *buf, const size_t n)
+{
+  const size_t pos = bmp_buffer_pos(buf);
+  buf->start = mb_realloc(buf->start, n);
+  buf->pos = buf->start + pos;
+  buf->end = buf->start + n;
+}
+
+void
+bmp_buffer_need(buffer *buf, const size_t n)
+{
+  if (bmp_buffer_avail(buf) < n)
+  {
+    bmp_buffer_grow(buf, n);
+  }
+}
+
+void
+bmp_put_data(buffer *buf, const void *src, const size_t n)
+{
+  if (!n)
+  {
+    return;
+  }
+
+  bmp_buffer_need(buf, n);
+  memcpy(buf->pos, src, n);
+  buf->pos += n;
+}
\ No newline at end of file
diff --git a/proto/bmp/buffer.h b/proto/bmp/buffer.h
new file mode 100644
index 0000000..f752cf5
--- /dev/null
+++ b/proto/bmp/buffer.h
@@ -0,0 +1,77 @@
+/*
+ *	BIRD -- The BGP Monitoring Protocol (BMP)
+ *
+ *	(c) 2020 Akamai Technologies, Inc. (Pawel Maslanka, pmaslank@akamai.com)
+ *
+ *	Can be freely distributed and used under the terms of the GNU GPL.
+ */
+
+#ifndef _BIRD_BMP_BUFFER_H_
+#define _BIRD_BMP_BUFFER_H_
+
+#include "proto/bmp/bmp.h"
+
+#include <stdlib.h>
+
+#include "lib/resource.h"
+
+buffer
+bmp_buffer_alloc(pool *ppool, const size_t n);
+
+void
+bmp_buffer_free(buffer *buf);
+
+static inline void
+bmp_buffer_flush(buffer *buf)
+{
+  buf->pos = buf->start;
+}
+
+static inline size_t
+bmp_buffer_size(const buffer *buf)
+{
+  return buf->end - buf->start;
+}
+
+static inline size_t
+bmp_buffer_avail(const buffer *buf)
+{
+  return buf->end - buf->pos;
+}
+
+static inline size_t
+bmp_buffer_pos(const buffer *buf)
+{
+  return buf->pos - buf->start;
+}
+
+static inline byte *
+bmp_buffer_data(const buffer *buf)
+{
+  return buf->start;
+}
+
+void
+bmp_buffer_need(buffer *buf, const size_t n);
+
+// Idea for following macros has been taken from |proto/mrt/mrt.c|
+#define BMP_DEFINE_PUT_FUNC(S, T)                               \
+  static inline void                                            \
+  bmp_put_##S(buffer *b, const T x)                             \
+  {                                                             \
+    bmp_buffer_need(b, sizeof(T));                              \
+    put_##S(b->pos, x);                                    \
+    b->pos += sizeof(T);                                   \
+  }
+
+BMP_DEFINE_PUT_FUNC(u8, u8)
+BMP_DEFINE_PUT_FUNC(u16, u16)
+BMP_DEFINE_PUT_FUNC(u32, u32)
+BMP_DEFINE_PUT_FUNC(u64, u64)
+BMP_DEFINE_PUT_FUNC(ip4, ip4_addr)
+BMP_DEFINE_PUT_FUNC(ip6, ip6_addr)
+
+void
+bmp_put_data(buffer *buf, const void *src, const size_t n);
+
+#endif /* _BIRD_BMP_BUFFER_H_ */
diff --git a/proto/bmp/config.Y b/proto/bmp/config.Y
new file mode 100644
index 0000000..776d7ec
--- /dev/null
+++ b/proto/bmp/config.Y
@@ -0,0 +1,83 @@
+/*
+ *	BIRD -- The BGP Monitoring Protocol (BMP)
+ *
+ *	(c) 2020 Akamai Technologies, Inc. (Pawel Maslanka, pmaslank@akamai.com)
+ *
+ *	Can be freely distributed and used under the terms of the GNU GPL.
+ */
+
+CF_HDR
+
+#include "proto/bmp/bmp.h"
+
+CF_DEFINES
+
+#define BMP_CFG ((struct bmp_config *) this_proto)
+
+CF_DECLS
+
+CF_KEYWORDS(BMP, DESCRIPTION, ENABLED, IN, IP, MONITORING, NAME, PORT,
+  PRE_POLICY, POST_POLICY, RIB, STATION, SYSTEM)
+
+CF_GRAMMAR
+
+proto: bmp_proto '}' ;
+
+bmp_proto_start: proto_start BMP {
+     this_proto = proto_config_new(&proto_bmp, $1);
+     BMP_CFG->station_ip = IPA_NONE4;
+     BMP_CFG->station_port = 0;
+     BMP_CFG->sys_descr = "Not defined";
+     BMP_CFG->sys_name = "Not defined";
+     BMP_CFG->monitoring_rib_in_pre_policy = false;
+     BMP_CFG->monitoring_rib_in_post_policy = false;
+     BMP_CFG->monitoring_rib_local = false;
+   }
+ ;
+
+bmp_station_address:
+   /* empty */
+ | bmp_station_address IP ipa {
+     if (ipa_zero($3))
+       cf_error("Invalid BMP monitoring station IP address");
+     BMP_CFG->station_ip = $3;
+   }
+ | bmp_station_address PORT expr {
+     if (($3 < 1) || ($3 > 65535))
+       cf_error("Invalid BMP monitoring station port number");
+     BMP_CFG->station_port = $3;
+   }
+ ;
+
+bmp_proto:
+   bmp_proto_start proto_name '{'
+ | bmp_proto proto_item ';'
+ | bmp_proto STATION ADDRESS bmp_station_address ';'
+ | bmp_proto SYSTEM DESCRIPTION text ';' {
+     if (!$4 || (strlen($4) == 0))
+       cf_error("String is empty");
+     else if (strlen($4) > 255)
+       cf_error("Invalid string length");
+     BMP_CFG->sys_descr = $4;
+   }
+ | bmp_proto SYSTEM NAME text ';' {
+     if (!$4 || (strlen($4) == 0))
+       cf_error("String is empty");
+     else if (strlen($4) > 255)
+       cf_error("Invalid string length");
+     BMP_CFG->sys_name = $4;
+   }
+ | bmp_proto MONITORING RIB IN PRE_POLICY bool ';' {
+     BMP_CFG->monitoring_rib_in_pre_policy = $6;
+   }
+ | bmp_proto MONITORING RIB IN POST_POLICY bool ';' {
+     BMP_CFG->monitoring_rib_in_post_policy = $6;
+   }
+ | bmp_proto MONITORING RIB LOCAL bool ';' {
+     BMP_CFG->monitoring_rib_local = $5;
+   }
+ ;
+
+CF_CODE
+
+CF_END
diff --git a/proto/bmp/map.c b/proto/bmp/map.c
new file mode 100644
index 0000000..16e714f
--- /dev/null
+++ b/proto/bmp/map.c
@@ -0,0 +1,119 @@
+/*
+ *	BIRD -- The BGP Monitoring Protocol (BMP)
+ *
+ *	(c) 2020 Akamai Technologies, Inc. (Pawel Maslanka, pmaslank@akamai.com)
+ *
+ *	Can be freely distributed and used under the terms of the GNU GPL.
+ */
+
+#include "proto/bmp/map.h"
+
+/* Peer Index Table */
+#define PEER_KEY(n) (n)->peer_as, (n)->peer_ip
+#define PEER_NEXT(n) (n)->next
+#define PEER_EQ(as1,ip1,as2,ip2) \
+  (as1) == (as2) && ipa_equal(ip1, ip2)
+#define PEER_FN(as,ip) ipa_hash(ip)
+
+#define PEER_REHASH bmp_peer_rehash
+#define PEER_PARAMS /8, *2, 2, 2, 6, 20
+
+HASH_DEFINE_REHASH_FN(PEER, struct bmp_peer_map_key)
+
+#define PEER_INIT_ORDER 6
+
+void
+bmp_peer_map_init(struct bmp_peer_map *map, pool *mpool)
+{
+  map->mpool = mpool;
+  HASH_INIT(map->peer_hash, map->mpool, PEER_INIT_ORDER);
+}
+
+struct bmp_peer_map_key
+bmp_peer_map_key_create(const ip_addr peer_ip, const u32 peer_as)
+{
+  struct bmp_peer_map_key key;
+  key.next = NULL;
+  key.peer_ip = peer_ip;
+  key.peer_as = peer_as;
+
+  return key;
+}
+
+void
+bmp_peer_map_flush(struct bmp_peer_map *map)
+{
+  struct bmp_peer_map_entry *entry;
+  HASH_WALK_DELSAFE(map->peer_hash, next, e)
+  {
+    entry = (struct bmp_peer_map_entry *) e;
+    mb_free(entry->data.buf);
+    HASH_DELETE(map->peer_hash, PEER, PEER_KEY(&entry->key));
+    mb_free(entry);
+  }
+  HASH_WALK_DELSAFE_END;
+
+  HASH_MAY_RESIZE_DOWN(map->peer_hash, PEER, map->mpool);
+}
+
+void
+bmp_peer_map_free(struct bmp_peer_map *map)
+{
+  bmp_peer_map_flush(map);
+  HASH_FREE(map->peer_hash);
+}
+
+void
+bmp_peer_map_insert(struct bmp_peer_map *map, const struct bmp_peer_map_key key,
+  const byte *data, const size_t data_size)
+{
+  struct bmp_peer_map_entry *entry
+    = (void *) HASH_FIND(map->peer_hash, PEER, PEER_KEY(&key));
+
+  if (entry)
+  {
+    mb_free(entry->data.buf);
+    entry->data.buf = mb_alloc(map->mpool, data_size);
+    memcpy(entry->data.buf, data, data_size);
+    entry->data.buf_size = data_size;
+    return;
+  }
+
+  entry = mb_alloc(map->mpool, sizeof (struct bmp_peer_map_entry));
+  entry->data.buf = mb_alloc(map->mpool, data_size);
+  memcpy(entry->data.buf, data, data_size);
+  entry->data.buf_size = data_size;
+  entry->key = key;
+  HASH_INSERT2(map->peer_hash, PEER, map->mpool, &entry->key);
+}
+
+void
+bmp_peer_map_remove(struct bmp_peer_map *map, const struct bmp_peer_map_key key)
+{
+  struct bmp_peer_map_entry *entry
+    = (void *) HASH_DELETE(map->peer_hash, PEER, PEER_KEY(&key));
+
+  if (!entry)
+    return;
+
+  mb_free(entry->data.buf);
+  mb_free(entry);
+}
+
+const struct bmp_peer_map_entry *
+bmp_peer_map_get(struct bmp_peer_map *map, const struct bmp_peer_map_key key)
+{
+  return (struct bmp_peer_map_entry *) HASH_FIND(map->peer_hash, PEER, PEER_KEY(&key));
+}
+
+void
+bmp_peer_map_walk(const struct bmp_peer_map *map, bmp_peer_map_walk_action action, void *arg)
+{
+  struct bmp_peer_map_entry *entry;
+  HASH_WALK_FILTER(map->peer_hash, next, e, _)
+  {
+    entry = (struct bmp_peer_map_entry *) e;
+    action(entry->key, entry->data.buf, entry->data.buf_size, arg);
+  }
+  HASH_WALK_FILTER_END;
+}
diff --git a/proto/bmp/map.h b/proto/bmp/map.h
new file mode 100644
index 0000000..8e7ea69
--- /dev/null
+++ b/proto/bmp/map.h
@@ -0,0 +1,68 @@
+/*
+ *	BIRD -- The BGP Monitoring Protocol (BMP)
+ *
+ *	(c) 2020 Akamai Technologies, Inc. (Pawel Maslanka, pmaslank@akamai.com)
+ *
+ *	Can be freely distributed and used under the terms of the GNU GPL.
+ */
+
+/**
+ * This map implementation binds peer IP address as container key with custom data.
+ */
+#ifndef _BIRD_BMP_MAP_H_
+#define _BIRD_BMP_MAP_H_
+
+#include "nest/bird.h"
+#include "lib/hash.h"
+#include "lib/resource.h"
+
+struct bmp_peer_map_key {
+  struct bmp_peer_map_key *next;
+  ip_addr peer_ip;
+  u32 peer_as;
+};
+
+struct bmp_peer_map_data {
+  void *buf;
+  size_t buf_size;
+};
+
+struct bmp_peer_map_entry {
+  struct bmp_peer_map_key key;
+  struct bmp_peer_map_data data;
+};
+
+struct bmp_peer_map {
+  pool *mpool;                             // Memory pool for peer entries in peer_hash
+  HASH(struct bmp_peer_map_key) peer_hash; // Hash for peers to find the index
+};
+
+void
+bmp_peer_map_init(struct bmp_peer_map *map, pool *mpool);
+
+struct bmp_peer_map_key
+bmp_peer_map_key_create(const ip_addr peer_ip, const u32 peer_as);
+
+void
+bmp_peer_map_free(struct bmp_peer_map *map);
+
+void
+bmp_peer_map_flush(struct bmp_peer_map *map);
+
+void
+bmp_peer_map_insert(struct bmp_peer_map *map, const struct bmp_peer_map_key key,
+  const byte *data, const size_t data_size);
+
+void
+bmp_peer_map_remove(struct bmp_peer_map *map, const struct bmp_peer_map_key key);
+
+const struct bmp_peer_map_entry *
+bmp_peer_map_get(struct bmp_peer_map *map, const struct bmp_peer_map_key key);
+
+typedef void (*bmp_peer_map_walk_action)(const struct bmp_peer_map_key key,
+					 const byte *data, const size_t data_size, void *arg);
+
+void
+bmp_peer_map_walk(const struct bmp_peer_map *map, bmp_peer_map_walk_action action, void *arg);
+
+#endif /* _BIRD_BMP_MAP_H_ */
diff --git a/sysdep/autoconf.h.in b/sysdep/autoconf.h.in
index 3db21c2..432cc68 100644
--- a/sysdep/autoconf.h.in
+++ b/sysdep/autoconf.h.in
@@ -12,6 +12,9 @@
 /* BGP protocol */
 #undef CONFIG_BGP
 
+/* BMP protocol */
+#undef CONFIG_BMP
+
 /* MRT protocol */
 #undef CONFIG_MRT
 
diff --git a/sysdep/bsd/krt-sock.c b/sysdep/bsd/krt-sock.c
index 1f79329..d13e20a 100644
--- a/sysdep/bsd/krt-sock.c
+++ b/sysdep/bsd/krt-sock.c
@@ -314,7 +314,7 @@ krt_send_route(struct krt_proto *p, int cmd, rte *e)
       if (ipa_is_link_local(gw))
 	_I0(gw) = 0xfe800000 | (i->index & 0x0000ffff);
 
-      sockaddr_fill(&gate, af, gw, NULL, 0);
+      sockaddr_fill(&gate, (ipa_is_ip4(gw) ? AF_INET : AF_INET6), gw, NULL, 0);
       msg.rtm.rtm_flags |= RTF_GATEWAY;
       msg.rtm.rtm_addrs |= RTA_GATEWAY;
       break;
@@ -469,7 +469,7 @@ krt_read_route(struct ks_msg *msg, struct krt_proto *p, int scan)
 
   idst  = ipa_from_sa(&dst);
   imask = ipa_from_sa(&mask);
-  igate = (gate.sa.sa_family == dst.sa.sa_family) ? ipa_from_sa(&gate) : IPA_NONE;
+  igate = ipa_from_sa(&gate);
 
 #ifdef KRT_SHARED_SOCKET
   if (!scan)
diff --git a/sysdep/config.h b/sysdep/config.h
index 861e52b..cf5787d 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.0.12"
+#define BIRD_VERSION "2.13.1"
 #endif
 
 /* Include parameters determined by configure script */
diff --git a/sysdep/unix/main.c b/sysdep/unix/main.c
index 627d7a4..0d3ec0c 100644
--- a/sysdep/unix/main.c
+++ b/sysdep/unix/main.c
@@ -889,7 +889,6 @@ main(int argc, char **argv)
   log_switch(1, NULL, NULL);
 
   random_init();
-  net_init();
   resource_init();
   timer_init();
   olock_init();
diff --git a/test/birdtest.h b/test/birdtest.h
index ad5f8f9..cfeebb9 100644
--- a/test/birdtest.h
+++ b/test/birdtest.h
@@ -40,7 +40,7 @@ static inline u64 bt_random(void)
 void bt_log_suite_result(int result, const char *fmt, ...);
 void bt_log_suite_case_result(int result, const char *fmt, ...);
 
-#define BT_TIMEOUT 			5	/* Default timeout in seconds */
+#define BT_TIMEOUT 			60	/* Default timeout in seconds */
 #define BT_FORKING 			1	/* Forking is enabled in default */
 
 #define BT_RANDOM_SEED 			0x5097d2bb

More details

Full run details

Historical runs