Bug#1143452: trixie-pu: package bird3/3.1.8-0+deb13u1

Marco d'Itri <[email protected]> Sun, 2 Aug 2026 06:07:12 +0200
Newsgroups gmane.linux.debian.devel.release
Message-ID <am7CcDPAIub1XA7z__19609.7072724783$1785643806$gmane$org@bongo.bofh.it>
--5OLJOChtBbiYU44U
Content-Type: text/plain; charset=us-ascii
Content-Disposition: inline
Content-Transfer-Encoding: quoted-printable

Package: release.debian.org
Severity: normal
Tags: trixie
X-Debbugs-Cc: [email protected]
Control: affects -1 + src:bird3
User: [email protected]
Usertags: pu

Upstream released a new bug fixes only release targeting stable.

It contains multiple bug fixes, detailed in the NEWS file, which solve
actual reliability issues.
Future upstream releases with security updates will be based on this=20
release.

git diff debian/3.1.7-0+deb13u1..debian/bird3/trixie ':(exclude).gitlab-ci.=
yml' ':(exclude)gitlab/'

diff --git a/NEWS b/NEWS
index 9564233bf..bc34ca601 100644
--- a/NEWS
+++ b/NEWS
@@ -1,3 +1,16 @@
+Version 3.1.8 (2026-07-30)
+  o BGP: Fix stack buffer overflow in Flowspec NLRI decoder
+  o BGP: Minor improvements in Flowspec parsing
+  o BGP: Fix minor issues with send hold timer
+  o Fix null byte handling in authentication keys
+  o Pipe, L3VPN: Fix hostentry stripping
+  o Filter: Fix zero arg handling
+  o Logging: Fix use-after-free on failed rotation
+  o CLI: Fix crashes in show route
+  o Allocator: Pre-fill hot pages when entering RCU critical section
+  o Fix obstacle cleanup
+  o Update bird-users mailing list links
+
 Version 3.1.7 (2026-06-09)
   o BGP: Fix parsing labelled NLRIs with no next hop
   o BGP: Fix cork behavior in collision with graceful restart
diff --git a/README b/README
index 75a4281f1..9edadb033 100644
--- a/README
+++ b/README
@@ -71,8 +71,8 @@ around to see what's going to develop, feel free to subsc=
ribe to the BIRD
 users mailing list [email protected], just send `subscribe' to
 [email protected].
=20
-Subscribe:	http://bird.network.cz/mailman/listinfo/bird-users/
-Archive:	http://bird.network.cz/pipermail/bird-users/
+Subscribe:	https://bird.nic.cz/lists/bird-users/
+Archive:	https://bird.nic.cz/lists/bird-users/archive/
=20
 Please don't send security issues to the mailing-list, contact us instead =
at
 [email protected] which is a private e-mail address where you also c=
an
diff --git a/VERSION b/VERSION
index 23887f6eb..c848fb9cb 100644
--- a/VERSION
+++ b/VERSION
@@ -1 +1 @@
-3.1.7
+3.1.8
diff --git a/conf/conf.c b/conf/conf.c
index aa54db60e..d1e408d76 100644
--- a/conf/conf.c
+++ b/conf/conf.c
@@ -81,10 +81,11 @@ int undo_available;			/* Undo was not requested from la=
st reconfiguration */
 /* Note that both shutting_down and undo_available are related to requests=
, not processing */
=20
 static void
-config_obstacles_cleared(struct callback *_ UNUSED)
+config_obstacles_cleared(struct callback *cb UNUSED)
 {
   ASSERT_DIE(birdloop_inside(&main_birdloop));
   ASSERT_DIE(configuring);
+
   config_done();
 }
=20
@@ -206,7 +207,9 @@ config_free(struct config *c)
     return;
=20
   synchronize_rcu();
+
   ASSERT_DIE(!obstacle_target_count(&c->obstacles));
+  obstacle_target_cleanup(&c->obstacles);
=20
   rp_free(c->pool);
 }
diff --git a/conf/flowspec.Y b/conf/flowspec.Y
index 0709a45b1..d073a1204 100644
--- a/conf/flowspec.Y
+++ b/conf/flowspec.Y
@@ -59,7 +59,7 @@ flow_num_type_:
  | ICMP CODE	{ $$ =3D FLOW_TYPE_ICMP_CODE; }
  | LENGTH	{ $$ =3D FLOW_TYPE_PACKET_LENGTH; }
  | DSCP		{ $$ =3D FLOW_TYPE_DSCP; }
- | LABEL	{ $$ =3D FLOW_TYPE_LABEL; }
+ | LABEL	{ $$ =3D FLOW_TYPE_LABEL; if (!this_flow->ipv6) cf_error("Flow la=
bel not valid in flow4"); }
  ;
=20
 flow_num_type: flow_num_type_{ flow_builder_set_type(this_flow, $1); };
@@ -73,11 +73,11 @@ flow_srcdst:
=20
 flow_num_opts:
    flow_num_op expr {
-     flow_check_cf_value_length(this_flow, $2);
+     flow_check_cf_numeric_arg(this_flow, $2);
      flow_builder_add_op_val(this_flow, $1, $2);
    }
  | flow_num_opts flow_logic_op flow_num_op expr {
-     flow_check_cf_value_length(this_flow, $4);
+     flow_check_cf_numeric_arg(this_flow, $4);
      flow_builder_add_op_val(this_flow, $2 | $3, $4);
    }
  | flow_num_opt_ext
@@ -86,12 +86,12 @@ flow_num_opts:
=20
 flow_num_opt_ext_expr:
    expr {
-     flow_check_cf_value_length(this_flow, $1);
+     flow_check_cf_numeric_arg(this_flow, $1);
      flow_builder_add_op_val(this_flow, FLOW_OP_EQ, $1);
    }
  | expr DDOT expr {
-     flow_check_cf_value_length(this_flow, $1);
-     flow_check_cf_value_length(this_flow, $3);
+     flow_check_cf_numeric_arg(this_flow, $1);
+     flow_check_cf_numeric_arg(this_flow, $3);
      flow_builder_add_op_val(this_flow, FLOW_OP_GEQ, $1);
      flow_builder_add_op_val(this_flow, FLOW_OP_AND | FLOW_OP_LEQ, $3);
    }
@@ -104,15 +104,15 @@ flow_num_opt_ext:
=20
 flow_bmk_opts:
    flow_neg expr '/' expr {
-     flow_check_cf_bmk_values(this_flow, $1, $2, $4);
+     flow_check_cf_bitmask_arg(this_flow, $1, $2, $4);
      flow_builder_add_val_mask(this_flow, $1, $2, $4);
    }
  | flow_bmk_opts flow_logic_op flow_neg expr '/' expr {
-     flow_check_cf_bmk_values(this_flow, $3, $4, $6);
+     flow_check_cf_bitmask_arg(this_flow, $3, $4, $6);
      flow_builder_add_val_mask(this_flow, $2 | $3, $4, $6);
    }
  | flow_bmk_opts ',' flow_neg expr '/' expr {
-     flow_check_cf_bmk_values(this_flow, $3, $4, $6);
+     flow_check_cf_bitmask_arg(this_flow, $3, $4, $6);
      flow_builder_add_val_mask(this_flow, 0x40 | $3, $4, $6); /* AND */
    }
  ;
@@ -123,7 +123,7 @@ flow_neg:
  ;
=20
 flow_frag_val:
-   DONT_FRAGMENT  { $$ =3D 1; }
+   DONT_FRAGMENT  { $$ =3D 1; if (this_flow->ipv6) cf_error("Flag dont_fra=
gment not valid in flow6"); }
  | IS_FRAGMENT	  { $$ =3D 2; }
  | FIRST_FRAGMENT { $$ =3D 4; }
  | LAST_FRAGMENT  { $$ =3D 8; }
diff --git a/debian/changelog b/debian/changelog
index 333ceb93c..decda435b 100644
--- a/debian/changelog
+++ b/debian/changelog
@@ -1,3 +1,9 @@
+bird3 (3.1.8-0+deb13u1) stable; urgency=3Dmedium
+
+  * New upstream release.
+
+ -- Marco d'Itri <[email protected]>  Sun, 02 Aug 2026 04:55:28 +0200
+
 bird3 (3.1.7-0+deb13u1) trixie-security; urgency=3Dmedium
=20
   * New upstream release.
diff --git a/doc/bird.sgml b/doc/bird.sgml
index 0a2b2bd14..b119965cd 100644
--- a/doc/bird.sgml
+++ b/doc/bird.sgml
@@ -6616,7 +6616,6 @@ Note that for negated matches, value must be either z=
ero or equal to bitmask
 <p><descrip>
 	<tag><label id=3D"flow-dst">dst <m/inet4/</tag>
 	Set a matching destination prefix (e.g. <cf>dst 192.168.0.0/16</cf>).
-	Only this option is mandatory in IPv4 Flowspec.
=20
 	<tag><label id=3D"flow-src">src <m/inet4/</tag>
 	Set a matching source prefix (e.g. <cf>src 10.0.0.0/8</cf>).
@@ -6684,7 +6683,6 @@ protocol static {
 	but with prefix <cf/offset/ <m/number/ too (e.g.
 	<cf>::1234:5678:9800:0000/101 offset 64</cf>). Offset means to don't
 	care of <m/number/ first bits.
-	<item>IPv6 Flowspec hasn't mandatory any flowspec component.
 	<item>In IPv6 packets, there is a matching the last next header value
 	for a matching IP protocol number (e.g. <cf>next header 6</cf>).
 	<item>It is not possible to set <cf>dont_fragment</cf> as a type of
@@ -6803,7 +6801,7 @@ versions of BIRD:
 (<HTMLURL URL=3D"mailto:[email protected]" name=3D"bird-users@network.=
cz">)
 where you can share your experiences with the other users and consult
 your problems with the authors. To subscribe to the list, visit
-<HTMLURL URL=3D"https://bird.network.cz/mailman/listinfo/bird-users" name=
=3D"https://bird.network.cz/mailman/listinfo/bird-users">.
+<HTMLURL URL=3D"https://bird.nic.cz/lists/bird-users/" name=3D"https://bir=
d.nic.cz/lists/bird-users/">.
 The home page of BIRD can be found at <HTMLURL URL=3D"http://bird.nic.cz/"=
 name=3D"http://bird.nic.cz/">.
=20
 <p>BIRD is a relatively young system and it probably contains some bugs. Y=
ou can
diff --git a/filter/filter.c b/filter/filter.c
index 1d7665888..2d22098fc 100644
--- a/filter/filter.c
+++ b/filter/filter.c
@@ -131,7 +131,7 @@ interpret(struct filter_state *fs, const struct f_line =
*line, uint argc, const s
=20
   /* Set the arguments and top-level variables */
   fstk->vcnt =3D line->vars + line->args;
-  memcpy(fstk->vstk, argv, sizeof(struct f_val) * line->args);
+  bmemcpy(fstk->vstk, argv, sizeof(struct f_val) * line->args);
   memset(fstk->vstk + argc, 0, sizeof(struct f_val) * line->vars);
=20
   /* The same as with the value stack. Not resetting the stack completely =
for performance reasons. */
diff --git a/lib/flowspec.c b/lib/flowspec.c
index a20cc6d2c..a393e9c95 100644
--- a/lib/flowspec.c
+++ b/lib/flowspec.c
@@ -170,16 +170,8 @@ flow_next_part(const byte *pos, const byte *end, int i=
pv6)
   case FLOW_TYPE_SRC_PREFIX:
   {
     uint pxlen =3D *pos++;
-    uint bytes =3D BYTES(pxlen);
-    if (ipv6)
-    {
-      uint offset =3D *pos++ / 8;
-      pos +=3D bytes - offset;
-    }
-    else
-    {
-      pos +=3D bytes;
-    }
+    uint offset =3D ipv6 ? *pos++ : 0;
+    pos +=3D BYTES(pxlen - offset);
     break;
   }
=20
@@ -332,11 +324,11 @@ static const char* flow_validated_state_str_[] =3D {
   [FLOW_ST_EXCEED_MAX_PREFIX_OFFSET]	=3D "Exceed maximal prefix offset",
   [FLOW_ST_EXCEED_MAX_VALUE_LENGTH]	=3D "Exceed maximal value length",
   [FLOW_ST_BAD_TYPE_ORDER] 		=3D "Bad component order",
-  [FLOW_ST_AND_BIT_SHOULD_BE_UNSET] 	=3D "The AND-bit should be unset",
-  [FLOW_ST_ZERO_BIT_SHOULD_BE_UNSED] 	=3D "The Zero-bit should be unset",
-  [FLOW_ST_DEST_PREFIX_REQUIRED] 	=3D "Destination prefix is missing",
-  [FLOW_ST_INVALID_TCP_FLAGS]		=3D "TCP flags exceeding 0xfff",
-  [FLOW_ST_CANNOT_USE_DONT_FRAGMENT]    =3D "Cannot use Don't fragment fla=
g in IPv6 flow"
+  [FLOW_ST_NONZERO_PADDING]		=3D "Nonzero prefix padding",
+  [FLOW_ST_FIRST_AND_BIT_SET] 		=3D "The first AND-bit is set",
+  [FLOW_ST_ZERO_BIT_SET] 		=3D "Zero-bit is set",
+  [FLOW_ST_INVALID_TCP_FLAGS]		=3D "Invalid TCP flags bitmask operand",
+  [FLOW_ST_INVALID_FRAGMENT]   		=3D "Invalid fragment bitmask operand",
 };
=20
 /**
@@ -351,45 +343,28 @@ flow_validated_state_str(enum flow_validated_state co=
de)
   return flow_validated_state_str_[code];
 }
=20
-static const u8 flow4_max_value_length[] =3D {
-  [FLOW_TYPE_DST_PREFIX]	=3D 0,
-  [FLOW_TYPE_SRC_PREFIX]	=3D 0,
-  [FLOW_TYPE_IP_PROTOCOL]	=3D 1,
-  [FLOW_TYPE_PORT]		=3D 2,
-  [FLOW_TYPE_DST_PORT]		=3D 2,
-  [FLOW_TYPE_SRC_PORT]		=3D 2,
-  [FLOW_TYPE_ICMP_TYPE]		=3D 1,
-  [FLOW_TYPE_ICMP_CODE]		=3D 1,
+/* Maximum length of value fields, as mandated by RFC 8955 */
+static const u8 flow_max_value_length[FLOW_TYPE_MAX] =3D {
   [FLOW_TYPE_TCP_FLAGS]		=3D 2,
-  [FLOW_TYPE_PACKET_LENGTH]	=3D 2,
   [FLOW_TYPE_DSCP]		=3D 1,
-  [FLOW_TYPE_FRAGMENT]		=3D 1	/* XXX */
+  [FLOW_TYPE_FRAGMENT]		=3D 1,
 };
=20
-static const u8 flow6_max_value_length[] =3D {
-  [FLOW_TYPE_DST_PREFIX]	=3D 0,
-  [FLOW_TYPE_SRC_PREFIX]	=3D 0,
-  [FLOW_TYPE_NEXT_HEADER]	=3D 1,
-  [FLOW_TYPE_PORT]		=3D 2,
-  [FLOW_TYPE_DST_PORT]		=3D 2,
-  [FLOW_TYPE_SRC_PORT]		=3D 2,
-  [FLOW_TYPE_ICMP_TYPE]		=3D 1,
-  [FLOW_TYPE_ICMP_CODE]		=3D 1,
-  [FLOW_TYPE_TCP_FLAGS]		=3D 2,
-  [FLOW_TYPE_PACKET_LENGTH]	=3D 2,
-  [FLOW_TYPE_DSCP]		=3D 1,
-  [FLOW_TYPE_FRAGMENT]		=3D 1,	/* XXX */
-  [FLOW_TYPE_LABEL]		=3D 4
+/* Maximum valid numeric values (in bits), semantically */
+static const u8 flow_max_valid_value[FLOW_TYPE_MAX] =3D {
+  [FLOW_TYPE_IP_PROTOCOL]	=3D 8,
+  [FLOW_TYPE_PORT]		=3D 16,
+  [FLOW_TYPE_DST_PORT]		=3D 16,
+  [FLOW_TYPE_SRC_PORT]		=3D 16,
+  [FLOW_TYPE_ICMP_TYPE]		=3D 8,
+  [FLOW_TYPE_ICMP_CODE]		=3D 8,
+  [FLOW_TYPE_PACKET_LENGTH]	=3D 16,
+  [FLOW_TYPE_DSCP]		=3D 6,
+  [FLOW_TYPE_LABEL]		=3D 20,
 };
=20
-static u8
-flow_max_value_length(enum flow_type type, int ipv6)
-{
-  return ipv6 ? flow6_max_value_length[type] : flow4_max_value_length[type=
];
-}
-
 /**
- * flow_check_cf_bmk_values - check value/bitmask part of flowspec compone=
nt
+ * flow_check_cf_bitmask_arg - check value/bitmask part of flowspec compon=
ent
  * @fb: flow builder instance
  * @neg: negation operand
  * @val: value from value/mask pair
@@ -400,61 +375,63 @@ flow_max_value_length(enum flow_type type, int ipv6)
  * to failing of validation.
  */
 void
-flow_check_cf_bmk_values(struct flow_builder *fb, u8 neg, u32 val, u32 mas=
k)
+flow_check_cf_bitmask_arg(struct flow_builder *fb, u8 neg, u32 val, u32 ma=
sk)
 {
-  flow_check_cf_value_length(fb, val);
-  flow_check_cf_value_length(fb, mask);
-
   if (neg && !(val =3D=3D 0 || val =3D=3D mask))
     cf_error("For negation, value must be zero or bitmask");
=20
-  if ((fb->this_type =3D=3D FLOW_TYPE_TCP_FLAGS) && (mask & 0xf000))
+  if ((fb->this_type =3D=3D FLOW_TYPE_TCP_FLAGS) && (mask & ~0xfff))
     cf_error("Invalid mask 0x%x, must not exceed 0xfff", mask);
=20
-  if ((fb->this_type =3D=3D FLOW_TYPE_FRAGMENT) && fb->ipv6 && (mask & 0x0=
1))
-    cf_error("Invalid mask 0x%x, bit 0 must be 0", mask);
+  u32 valid =3D fb->ipv6 ? 0x0e : 0x0f;
+  if ((fb->this_type =3D=3D FLOW_TYPE_FRAGMENT) && (mask & ~valid))
+    cf_error("Invalid mask 0x%x, must not exceed 0x%x", mask, valid);
=20
   if (val & ~mask)
     cf_error("Value 0x%x outside bitmask 0x%x", val, mask);
 }
=20
 /**
- * flow_check_cf_value_length - check value by flowspec component type
+ * flow_check_cf_numeric_arg - check numeric argument of flowspec component
  * @fb: flow builder instance
  * @val: value
  *
- * This function checks if the value is in range of component's type suppo=
rt.
- * If some problem will appear, the function calls cf_error() function with
- * a textual description of reason to failing of validation.
+ * This function checks the value of numeric argument to see whether it is=
 in
+ * the range of component's type.  If some problem will appear, the functi=
on
+ * calls cf_error() function with a textual description of reason to faili=
ng of
+ * validation.
  */
 void
-flow_check_cf_value_length(struct flow_builder *fb, u32 val)
+flow_check_cf_numeric_arg(struct flow_builder *fb, uint val)
 {
   enum flow_type t =3D fb->this_type;
-  u8 max =3D flow_max_value_length(t, fb->ipv6);
+  u64 max =3D (U64(1) << flow_max_valid_value[t]) - 1;
=20
-  if (t =3D=3D FLOW_TYPE_DSCP && val > 0x3f)
-    cf_error("%s value %u out of range (0-63)", flow_type_str(t, fb->ipv6)=
, val);
+  if (max && (val > max))
+    cf_error("%s value %u out of range (0-%lu)", flow_type_str(t, fb->ipv6=
), val, max);
+}
=20
-  if (max =3D=3D 1 && (val > 0xff))
-    cf_error("%s value %u out of range (0-255)", flow_type_str(t, fb->ipv6=
), val);
-
-  if (max =3D=3D 2 && (val > 0xffff))
-    cf_error("%s value %u out of range (0-65535)", flow_type_str(t, fb->ip=
v6), val);
+/* Bitmask of padding bits in last byte of prefix */
+static inline u8
+flow_padding(uint n)
+{
+  ASSUME(n % 8 !=3D 0);
+  return (1 << (8 - n % 8)) - 1;
 }
=20
 static enum flow_validated_state
-flow_validate(const byte *nlri, uint len, int ipv6)
+flow_decode(byte *nlri, uint length, bool ipv6)
 {
   enum flow_type type =3D 0;
-  const byte *pos =3D nlri;
-  const byte *end =3D nlri + len;
+  byte *pos =3D nlri;
+  byte *end =3D nlri + length;
=20
   while (pos < end)
   {
     /* Check increasing type ordering */
     if (*pos <=3D type)
       return FLOW_ST_BAD_TYPE_ORDER;
+
     type =3D *pos++;
=20
     switch (type)
@@ -462,19 +439,42 @@ flow_validate(const byte *nlri, uint len, int ipv6)
     case FLOW_TYPE_DST_PREFIX:
     case FLOW_TYPE_SRC_PREFIX:
     {
-      uint pxlen =3D *pos++;
-      if (pxlen > (ipv6 ? IP6_MAX_PREFIX_LENGTH : IP4_MAX_PREFIX_LENGTH))
-	return FLOW_ST_EXCEED_MAX_PREFIX_LENGTH;
+      uint pxlen, offset =3D 0;
=20
-      uint bytes =3D BYTES(pxlen);
-      if (ipv6)
+      if (!ipv6)
       {
-        uint pxoffset =3D *pos++;
-        if (pxoffset > IP6_MAX_PREFIX_LENGTH || pxoffset > pxlen)
-          return FLOW_ST_EXCEED_MAX_PREFIX_OFFSET;
-        bytes =3D BYTES(pxlen - pxoffset);
+	if (pos + 1 > end)
+	  return FLOW_ST_NOT_COMPLETE;
+
+	pxlen =3D *pos++;
+
+	if (pxlen > IP4_MAX_PREFIX_LENGTH)
+	  return FLOW_ST_EXCEED_MAX_PREFIX_LENGTH;
       }
-      pos +=3D bytes;
+      else
+      {
+	if (pos + 2 > end)
+	  return FLOW_ST_NOT_COMPLETE;
+
+	pxlen =3D *pos++;
+	offset =3D *pos++;
+
+	if (pxlen > IP6_MAX_PREFIX_LENGTH)
+	  return FLOW_ST_EXCEED_MAX_PREFIX_LENGTH;
+
+	if (offset > pxlen)
+	  return FLOW_ST_EXCEED_MAX_PREFIX_OFFSET;
+      }
+
+      uint bits =3D pxlen - offset;
+      pos +=3D BYTES(bits);
+
+      if (pos > end)
+	return FLOW_ST_NOT_COMPLETE;
+
+      /* Padding bits in the last byte MUST be 0 */
+      if (bits % 8)
+	pos[-1] &=3D ~flow_padding(bits);
=20
       break;
     }
@@ -483,6 +483,7 @@ flow_validate(const byte *nlri, uint len, int ipv6)
       if (!ipv6)
 	return FLOW_ST_UNKNOWN_COMPONENT;
       /* fall through */
+
     case FLOW_TYPE_IP_PROTOCOL: /* =3D=3D FLOW_TYPE_NEXT_HEADER */
     case FLOW_TYPE_PORT:
     case FLOW_TYPE_DST_PORT:
@@ -494,70 +495,236 @@ flow_validate(const byte *nlri, uint len, int ipv6)
     case FLOW_TYPE_DSCP:
     case FLOW_TYPE_FRAGMENT:
     {
-      uint last =3D 0;
-      uint first =3D 1;
+      /*
+       *    0   1   2   3   4   5   6   7       0   1   2   3   4   5   6 =
  7
+       *  +---+---+---+---+---+---+---+---+   +---+---+---+---+---+---+---=
+---+
+       *  | e | a |  len  | 0 |lt |gt |eq |   | e | a |  len  | 0 | 0 |not=
| m |
+       *  +---+---+---+---+---+---+---+---+   +---+---+---+---+---+---+---=
+---+
+       *
+       *           Numeric operator                    Bitmask operator
+       */
=20
+      bool first =3D true;
+      bool last =3D false;
       while (!last)
       {
-	/*
-	 *    0   1   2   3   4   5   6   7
-	 *  +---+---+---+---+---+---+---+---+
-	 *  | e | a |  len  | 0 |lt |gt |eq |
-	 *  +---+---+---+---+---+---+---+---+
-	 *
-	 *           Numeric operator
-	 */
+	if (pos + 1 > end)
+	  return FLOW_ST_NOT_COMPLETE;
=20
-	last =3D isset_end(pos);
-
-	/* The AND bit should in the first operator byte of a sequence */
-	if (first && isset_and(pos))
-	  return FLOW_ST_AND_BIT_SHOULD_BE_UNSET;
-
-	/* This bit should be zero */
-	if (*pos & 0x08)
-	  return FLOW_ST_ZERO_BIT_SHOULD_BE_UNSED;
-
-	if (type =3D=3D FLOW_TYPE_TCP_FLAGS || type =3D=3D FLOW_TYPE_FRAGMENT)
-	{
-	  /*
-	   *    0   1   2   3   4   5   6   7
-	   *  +---+---+---+---+---+---+---+---+
-	   *  | e | a |  len  | 0 | 0 |not| m |
-	   *  +---+---+---+---+---+---+---+---+
-	   *
-	   *           Bitmask operand
-	   */
-	  if (*pos & 0x04)
-	    return FLOW_ST_ZERO_BIT_SHOULD_BE_UNSED;
-	}
-
-	/* Value length of operator */
 	uint len =3D get_value_length(pos);
-	if (len > flow_max_value_length(type, ipv6))
+
+	/* Some component values MUST be encoded with limited length */
+	uint maxlen =3D flow_max_value_length[type];
+	if (maxlen && (len > maxlen))
 	  return FLOW_ST_EXCEED_MAX_VALUE_LENGTH;
=20
-	/* TCP Flags component must not check highest nibble (just 12 valid bits)=
 */
-	if ((type =3D=3D FLOW_TYPE_TCP_FLAGS) && (len =3D=3D 2) && (pos[1] & 0xf0=
))
-	  return FLOW_ST_INVALID_TCP_FLAGS;
+	if (pos + 1 + len > end)
+	  return FLOW_ST_NOT_COMPLETE;
=20
-	/* Bit-7 must be 0 [draft-ietf-idr-flow-spec-v6] */
-	if ((type =3D=3D FLOW_TYPE_FRAGMENT) && ipv6 && (pos[1] & 0x01))
-	  return FLOW_ST_CANNOT_USE_DONT_FRAGMENT;
-	/* XXX: Could be a fragment component encoded in 2-bytes? */
+	/* In first operator of sequence, AND bit ... MUST be ignored during deco=
ding */
+	if (first)
+	  pos[0] &=3D ~0x40;
=20
+	/* Zero field MUST be set to 0 on encoding and MUST be ignored during dec=
oding */
+	pos[0] &=3D ~0x08;
+
+	/* Bitmask operator has additional zero field */
+	if (type =3D=3D FLOW_TYPE_TCP_FLAGS || type =3D=3D FLOW_TYPE_FRAGMENT)
+	  pos[0] &=3D ~0x04;
+
+	/* Zero fields in highest nibble of TCP Flags bitmask operand */
+	if ((type =3D=3D FLOW_TYPE_TCP_FLAGS) && (len =3D=3D 2))
+	  pos[1] &=3D ~0xf0;
+
+	/* Zero fields in fragment bitmask operand */
+	if ((type =3D=3D FLOW_TYPE_FRAGMENT) && (len =3D=3D 1))
+	  pos[1] &=3D ipv6 ? ~0xf1 : ~0xf0;
+
+	/* Move to next operator */
+	first =3D false;
+	last =3D isset_end(pos);
 	pos +=3D 1+len;
-
-	if (pos > end && !last)
-	  return FLOW_ST_NOT_COMPLETE;
-
-	if (pos > (end+1))
-	  return FLOW_ST_NOT_COMPLETE;
-
-	first =3D 0;
       }
       break;
     }
+
+    default:
+      return FLOW_ST_UNKNOWN_COMPONENT;
+    }
+  }
+
+  if (pos !=3D end)
+    return FLOW_ST_NOT_COMPLETE;
+
+  return FLOW_ST_VALID;
+}
+
+/**
+ * flow4_decode - decode incoming BGP IPv4 flowspec data stream
+ * @nlri: flowspec data stream without length header
+ * @len: length of @nlri
+ *
+ * This function checks syntatic correctness of binary flowspec. It returns
+ * %FLOW_ST_VALID, or some other %FLOW_ST_xxx state if the NLRI is malform=
ed.
+ * If the NLRI is valid but not normalized (e.g. some zero bits are set), =
the
+ * flow4_decode() modify the datastream to normalize it.
+ */
+inline enum flow_validated_state
+flow4_decode(byte *nlri, uint len)
+{
+  return flow_decode(nlri, len, false);
+}
+
+/**
+ * flow6_decode - decode incoming BGP IPv6 flowspec data stream
+ * @nlri: flowspec data stream without length header
+ * @len: length of @nlri
+ *
+ * This function checks syntatic correctness of binary flowspec. It returns
+ * %FLOW_ST_VALID, or some other %FLOW_ST_xxx state if the NLRI is malform=
ed.
+ * If the NLRI is valid but not normalized (e.g. some zero bits are set), =
the
+ * flow6_decode() modify the datastream to normalize it.
+ */
+inline enum flow_validated_state
+flow6_decode(byte *nlri, uint len)
+{
+  return flow_decode(nlri, len, true);
+}
+
+static enum flow_validated_state
+flow_validate(const byte *nlri, uint len, bool ipv6)
+{
+  enum flow_type type =3D 0;
+  const byte *pos =3D nlri;
+  const byte *end =3D nlri + len;
+
+  while (pos < end)
+  {
+    /* Check increasing type ordering */
+    if (*pos <=3D type)
+      return FLOW_ST_BAD_TYPE_ORDER;
+
+    type =3D *pos++;
+
+    switch (type)
+    {
+    case FLOW_TYPE_DST_PREFIX:
+    case FLOW_TYPE_SRC_PREFIX:
+    {
+      uint pxlen, offset =3D 0;
+
+      if (!ipv6)
+      {
+	if (pos + 1 > end)
+	  return FLOW_ST_NOT_COMPLETE;
+
+	pxlen =3D *pos++;
+
+	if (pxlen > IP4_MAX_PREFIX_LENGTH)
+	  return FLOW_ST_EXCEED_MAX_PREFIX_LENGTH;
+      }
+      else
+      {
+	if (pos + 2 > end)
+	  return FLOW_ST_NOT_COMPLETE;
+
+	pxlen =3D *pos++;
+	offset =3D *pos++;
+
+	if (pxlen > IP6_MAX_PREFIX_LENGTH)
+	  return FLOW_ST_EXCEED_MAX_PREFIX_LENGTH;
+
+	if (offset > pxlen)
+	  return FLOW_ST_EXCEED_MAX_PREFIX_OFFSET;
+      }
+
+      uint bits =3D pxlen - offset;
+      pos +=3D BYTES(bits);
+
+      if (pos > end)
+	return FLOW_ST_NOT_COMPLETE;
+
+      /* Padding bits in the last byte MUST be 0 */
+      if (bits % 8)
+	if (pos[-1] & flow_padding(bits))
+	  return FLOW_ST_NONZERO_PADDING;
+
+      break;
+    }
+
+    case FLOW_TYPE_LABEL:
+      if (!ipv6)
+	return FLOW_ST_UNKNOWN_COMPONENT;
+      /* fall through */
+
+    case FLOW_TYPE_IP_PROTOCOL: /* =3D=3D FLOW_TYPE_NEXT_HEADER */
+    case FLOW_TYPE_PORT:
+    case FLOW_TYPE_DST_PORT:
+    case FLOW_TYPE_SRC_PORT:
+    case FLOW_TYPE_ICMP_TYPE:
+    case FLOW_TYPE_ICMP_CODE:
+    case FLOW_TYPE_TCP_FLAGS:
+    case FLOW_TYPE_PACKET_LENGTH:
+    case FLOW_TYPE_DSCP:
+    case FLOW_TYPE_FRAGMENT:
+    {
+      /*
+       *    0   1   2   3   4   5   6   7       0   1   2   3   4   5   6 =
  7
+       *  +---+---+---+---+---+---+---+---+   +---+---+---+---+---+---+---=
+---+
+       *  | e | a |  len  | 0 |lt |gt |eq |   | e | a |  len  | 0 | 0 |not=
| m |
+       *  +---+---+---+---+---+---+---+---+   +---+---+---+---+---+---+---=
+---+
+       *
+       *           Numeric operator                    Bitmask operator
+       */
+
+      bool first =3D true;
+      bool last =3D false;
+      while (!last)
+      {
+	if (pos + 1 > end)
+	  return FLOW_ST_NOT_COMPLETE;
+
+	uint len =3D get_value_length(pos);
+
+	/* Some component values MUST be encoded with limited length */
+	uint maxlen =3D flow_max_value_length[type];
+	if (maxlen && (len > maxlen))
+	  return FLOW_ST_EXCEED_MAX_VALUE_LENGTH;
+
+	if (pos + 1 + len > end)
+	  return FLOW_ST_NOT_COMPLETE;
+
+	/* In first operator of sequence, AND bit ... MUST be ignored during deco=
ding */
+	if (first && isset_and(pos))
+	  return FLOW_ST_FIRST_AND_BIT_SET;
+
+	/* Zero field MUST be set to 0 on encoding and MUST be ignored during dec=
oding */
+	if (pos[0] & 0x08)
+	  return FLOW_ST_ZERO_BIT_SET;
+
+	/* Bitmask operator has additional zero field */
+	if (type =3D=3D FLOW_TYPE_TCP_FLAGS || type =3D=3D FLOW_TYPE_FRAGMENT)
+	  if (pos[0] & 0x04)
+	    return FLOW_ST_ZERO_BIT_SET;
+
+	/* Zero fields in highest nibble of TCP Flags bitmask operand */
+	if ((type =3D=3D FLOW_TYPE_TCP_FLAGS) && (len =3D=3D 2))
+	  if (pos[1] & 0xf0)
+	    return FLOW_ST_INVALID_TCP_FLAGS;
+
+	/* Zero fields in fragment bitmask operand */
+	if ((type =3D=3D FLOW_TYPE_FRAGMENT) && (len =3D=3D 1))
+	  if (pos[1] & (ipv6 ? 0xf1 : 0xf0))
+	    return FLOW_ST_INVALID_FRAGMENT;
+
+	/* Move to next operator */
+	first =3D false;
+	last =3D isset_end(pos);
+	pos +=3D 1+len;
+      }
+      break;
+    }
+
     default:
       return FLOW_ST_UNKNOWN_COMPONENT;
     }
@@ -571,32 +738,32 @@ flow_validate(const byte *nlri, uint len, int ipv6)
=20
 /**
  * flow4_validate - check untrustworthy IPv4 flowspec data stream
- * @nlri: flowspec data stream without compressed encoded length value
+ * @nlri: flowspec data stream without length header
  * @len: length of @nlri
  *
- * This function checks meaningfulness of binary flowspec. It should return
- * %FLOW_ST_VALID or %FLOW_ST_UNKNOWN_COMPONENT. If some problem appears, =
it
- * returns some other %FLOW_ST_xxx state.
+ * This function checks syntactic correctness of binary flowspec. It retur=
ns
+ * %FLOW_ST_VALID, or some other %FLOW_ST_xxx state if the NLRI is malform=
ed
+ * or non-normalized.
  */
 inline enum flow_validated_state
 flow4_validate(const byte *nlri, uint len)
 {
-  return flow_validate(nlri, len, 0);
+  return flow_validate(nlri, len, false);
 }
=20
 /**
  * flow6_validate - check untrustworthy IPv6 flowspec data stream
- * @nlri: flowspec binary stream without encoded length value
+ * @nlri: flowspec binary stream without length header
  * @len: length of @nlri
  *
- * This function checks meaningfulness of binary flowspec. It should return
- * %FLOW_ST_VALID or %FLOW_ST_UNKNOWN_COMPONENT. If some problem appears, =
it
- * returns some other %FLOW_ST_xxx state.
+ * This function checks syntactic correctness of binary flowspec. It retur=
ns
+ * %FLOW_ST_VALID, or some other %FLOW_ST_xxx state if the NLRI is malform=
ed
+ * or non-normalized.
  */
 inline enum flow_validated_state
 flow6_validate(const byte *nlri, uint len)
 {
-  return flow_validate(nlri, len, 1);
+  return flow_validate(nlri, len, true);
 }
=20
 /**
@@ -790,7 +957,13 @@ flow_builder_add_op_val(struct flow_builder *fb, byte =
op, u32 value)
   /* Set the end-bit for operand-value pair of the component */
   op |=3D 0x80;
=20
-  if (value & 0xff00)
+  /* Label component values SHOULD be encoded as 4-octet quantities */
+  if ((value > 0xffff) || (fb->this_type =3D=3D FLOW_TYPE_LABEL))
+  {
+    BUFFER_PUSH(fb->data) =3D op | 0x20;
+    put_u32(BUFFER_INC(fb->data, 4), value);
+  }
+  else if (value > 0xff)
   {
     BUFFER_PUSH(fb->data) =3D op | 0x10;
     put_u16(BUFFER_INC(fb->data, 2), value);
diff --git a/lib/flowspec.h b/lib/flowspec.h
index 8ff968ed2..74b2288da 100644
--- a/lib/flowspec.h
+++ b/lib/flowspec.h
@@ -137,18 +137,21 @@ enum flow_validated_state {
   FLOW_ST_EXCEED_MAX_PREFIX_OFFSET,
   FLOW_ST_EXCEED_MAX_VALUE_LENGTH,
   FLOW_ST_BAD_TYPE_ORDER,
-  FLOW_ST_AND_BIT_SHOULD_BE_UNSET,
-  FLOW_ST_ZERO_BIT_SHOULD_BE_UNSED,
-  FLOW_ST_DEST_PREFIX_REQUIRED,
+  FLOW_ST_NONZERO_PADDING,
+  FLOW_ST_FIRST_AND_BIT_SET,
+  FLOW_ST_ZERO_BIT_SET,
   FLOW_ST_INVALID_TCP_FLAGS,
-  FLOW_ST_CANNOT_USE_DONT_FRAGMENT
+  FLOW_ST_INVALID_FRAGMENT,
 };
=20
 const char *flow_validated_state_str(enum flow_validated_state code);
+enum flow_validated_state flow4_decode(byte *nlri, uint len);
+enum flow_validated_state flow6_decode(byte *nlri, uint len);
 enum flow_validated_state flow4_validate(const byte *nlri, uint len);
 enum flow_validated_state flow6_validate(const byte *nlri, uint len);
-void flow_check_cf_value_length(struct flow_builder *fb, u32 expr);
-void flow_check_cf_bmk_values(struct flow_builder *fb, u8 neg, u32 val, u3=
2 mask);
+
+void flow_check_cf_bitmask_arg(struct flow_builder *fb, u8 neg, u32 val, u=
32 mask);
+void flow_check_cf_numeric_arg(struct flow_builder *fb, uint val);
 void flow4_validate_cf(net_addr_flow4 *f);
 void flow6_validate_cf(net_addr_flow6 *f);
=20
diff --git a/lib/flowspec_test.c b/lib/flowspec_test.c
index 1fd1a0b99..13f1468b2 100644
--- a/lib/flowspec_test.c
+++ b/lib/flowspec_test.c
@@ -281,6 +281,13 @@ t_validation4(void)
 	FLOW_TYPE_DST_PREFIX, 33, 5, 6, 7, 8, 9
       })
     ),
+    TS(
+      FLOW_ST_EXCEED_MAX_VALUE_LENGTH,
+      "DSCP of length 2",
+      ((byte []) {
+	FLOW_TYPE_DSCP, 0x91, 00, 63,
+      })
+    ),
     TS(
       FLOW_ST_BAD_TYPE_ORDER,
       "Bad flowspec component type order",
@@ -298,28 +305,65 @@ t_validation4(void)
       })
     ),
     TS(
-      FLOW_ST_AND_BIT_SHOULD_BE_UNSET,
+      FLOW_ST_UNKNOWN_COMPONENT,
+      "Unknown component of type number 14",
+      ((byte []) {
+	FLOW_TYPE_DST_PREFIX, 24, 5, 6, 7,
+	FLOW_TYPE_TCP_FLAGS, 0x80, 0x55,
+	14 /*something new*/, 0x80, 0x55,
+      })
+    ),
+    TS(
+      FLOW_ST_UNKNOWN_COMPONENT,
+      "Label component in IPv4",
+      ((byte []) {
+	FLOW_TYPE_DST_PREFIX, 24, 5, 6, 7,
+	FLOW_TYPE_LABEL, 0xa0, 0, 0, 0, 0x55,
+      })
+    ),
+    TS(
+      FLOW_ST_NONZERO_PADDING,
+      "Non-zero padding in prefix",
+      ((byte []) {
+	FLOW_TYPE_DST_PREFIX, 20, 5, 6, 7,
+      })
+    ),
+    TS(
+      FLOW_ST_FIRST_AND_BIT_SET,
       "The first numeric operator has set the AND bit",
       ((byte []) {
 	FLOW_TYPE_PORT, 0x43, 0x89, 0x45, 0x8b, 0x91, 0x1f, 0x90,
       })
     ),
     TS(
-      FLOW_ST_ZERO_BIT_SHOULD_BE_UNSED,
-      "Set zero bit in operand to one",
+      FLOW_ST_ZERO_BIT_SET,
+      "Set zero bit in operator to one (0x08)",
       ((byte []) {
 	FLOW_TYPE_IP_PROTOCOL, 0x89, 0x06,
       })
     ),
     TS(
-      FLOW_ST_UNKNOWN_COMPONENT,
-      "Unknown component of type number 13",
+      FLOW_ST_ZERO_BIT_SET,
+      "Set zero bit in operator to one (0x04)",
       ((byte []) {
-	FLOW_TYPE_DST_PREFIX, 24, 5, 6, 7,
-	FLOW_TYPE_TCP_FLAGS, 0x80, 0x55,
-	13 /*something new*/, 0x80, 0x55,
+	FLOW_TYPE_FRAGMENT, 0x84, 0x03,
       })
     ),
+    TS(
+      FLOW_ST_INVALID_TCP_FLAGS,
+      "Set zero bit in operand to one (TCP flags)",
+      ((byte []) {
+	FLOW_TYPE_TCP_FLAGS, 0x91, 0xff, 0x3a,
+      })
+    ),
+    TS(
+      FLOW_ST_INVALID_FRAGMENT,
+      "Set zero bit in operand to one (fragment)",
+      ((byte []) {
+	FLOW_TYPE_FRAGMENT, 0x81, 0xff,
+      })
+    ),
+
   };
 #undef TS
=20
@@ -338,7 +382,7 @@ t_validation6(void)
   enum flow_validated_state res;
=20
   byte nlri1[] =3D {
-    FLOW_TYPE_DST_PREFIX, 103, 61, 0x01, 0x12, 0x34, 0x56, 0x78, 0x98,
+    FLOW_TYPE_DST_PREFIX, 103, 61, 0x01, 0x12, 0x34, 0x56, 0x78, 0xc0,
     FLOW_TYPE_SRC_PREFIX, 8, 0, 0xc0,
     FLOW_TYPE_NEXT_HEADER, 0x81, 0x06,
     FLOW_TYPE_PORT, 0x03, 0x89, 0x45, 0x8b, 0x91, 0x1f, 0x90,
@@ -392,6 +436,13 @@ t_validation6(void)
 	FLOW_TYPE_DST_PREFIX, 48, 64, 0x40, 0x12, 0x34
       })
     ),
+    TS(
+      FLOW_ST_EXCEED_MAX_VALUE_LENGTH,
+      "Fragment of length 2",
+      ((byte []) {
+	FLOW_TYPE_FRAGMENT, 0x91, 00, 0x0f,
+      })
+    ),
     TS(
       FLOW_ST_BAD_TYPE_ORDER,
       "Bad flowspec component type order",
@@ -404,39 +455,67 @@ t_validation6(void)
       FLOW_ST_BAD_TYPE_ORDER,
       "Doubled destination prefix component",
       ((byte []) {
-	FLOW_TYPE_DST_PREFIX, 103, 61, 0x01, 0x12, 0x34, 0x56, 0x78, 0x98,
-	FLOW_TYPE_DST_PREFIX, 103, 61, 0x01, 0x12, 0x34, 0x56, 0x78, 0x98,
-      })
-    ),
-    TS(
-      FLOW_ST_AND_BIT_SHOULD_BE_UNSET,
-      "The first numeric operator has set the AND bit",
-      ((byte []) {
-	FLOW_TYPE_PORT, 0x43, 0x89, 0x45, 0x8b, 0x91, 0x1f, 0x90
-      })
-    ),
-    TS(
-      FLOW_ST_ZERO_BIT_SHOULD_BE_UNSED,
-      "Set zero bit in operand to one",
-      ((byte []) {
-	FLOW_TYPE_NEXT_HEADER, 0x89, 0x06
-      })
-    ),
-    TS(
-      FLOW_ST_VALID,
-      "Component of type number 13 (Label) is well-known in IPv6",
-      ((byte []) {
-	FLOW_TYPE_LABEL, 0x80, 0x55
+	FLOW_TYPE_DST_PREFIX, 103, 61, 0x01, 0x12, 0x34, 0x56, 0x78, 0xc0,
+	FLOW_TYPE_DST_PREFIX, 103, 61, 0x01, 0x12, 0x34, 0x56, 0x78, 0xc0,
       })
     ),
     TS(
       FLOW_ST_UNKNOWN_COMPONENT,
       "Unknown component of type number 14",
       ((byte []) {
-	FLOW_TYPE_LABEL, 0x80, 0x55,
+	FLOW_TYPE_TCP_FLAGS, 0x80, 0x55,
 	14 /*something new*/, 0x80, 0x55,
       })
-    )
+      ),
+    TS(
+      FLOW_ST_VALID,
+      "Label component is well-known in IPv6",
+      ((byte []) {
+	FLOW_TYPE_LABEL, 0xa0, 0, 0, 0, 0x55,
+      })
+    ),
+    TS(
+      FLOW_ST_NONZERO_PADDING,
+      "Non-zero padding in prefix",
+      ((byte []) {
+	FLOW_TYPE_DST_PREFIX, 44, 16, 0x40, 0x12, 0x34, 0x56,
+      })
+    ),
+    TS(
+      FLOW_ST_FIRST_AND_BIT_SET,
+      "The first numeric operator has set the AND bit",
+      ((byte []) {
+	FLOW_TYPE_PORT, 0x43, 0x89, 0x45, 0x8b, 0x91, 0x1f, 0x90
+      })
+    ),
+    TS(
+      FLOW_ST_ZERO_BIT_SET,
+      "Set zero bit in operator to one (0x08)",
+      ((byte []) {
+	FLOW_TYPE_NEXT_HEADER, 0x89, 0x06
+      })
+    ),
+    TS(
+      FLOW_ST_ZERO_BIT_SET,
+      "Set zero bit in operator to one (0x04)",
+      ((byte []) {
+	FLOW_TYPE_FRAGMENT, 0x84, 0x03,
+      })
+    ),
+    TS(
+      FLOW_ST_INVALID_TCP_FLAGS,
+      "Set zero bit in operand to one (TCP flags)",
+      ((byte []) {
+	FLOW_TYPE_TCP_FLAGS, 0x91, 0xff, 0x3a,
+      })
+    ),
+    TS(
+      FLOW_ST_INVALID_FRAGMENT,
+      "Set zero bit in operand to one (fragment)",
+      ((byte []) {
+	FLOW_TYPE_FRAGMENT, 0x81, 0xff,
+      })
+    ),
   };
 #undef TS
=20
@@ -449,6 +528,350 @@ t_validation6(void)
   return 1;
 }
=20
+static int
+t_decoding4(void)
+{
+  enum flow_validated_state res;
+
+  byte nlri1[] =3D {
+    FLOW_TYPE_DST_PREFIX, 24, 5, 6, 7,
+    FLOW_TYPE_SRC_PREFIX, 32, 10, 11, 12, 13,
+    FLOW_TYPE_IP_PROTOCOL, 0x81, 0x06,
+    FLOW_TYPE_PORT, 0x03, 0x89, 0x45, 0x8b, 0x91, 0x1f, 0x90,
+    FLOW_TYPE_TCP_FLAGS, 0x80, 0x55,
+  };
+
+  /* Empty NLRI */
+  res =3D flow4_decode(nlri1, 0);
+  bt_assert(res =3D=3D FLOW_ST_VALID);
+
+  /* Valid / Not Complete testing */
+  uint valid_sizes[] =3D {5, 11, 14, 22, 25, 0};
+  uint valid_idx =3D 0;
+  for (uint size =3D 1; size <=3D sizeof(nlri1); size++)
+  {
+    res =3D flow4_decode(nlri1, size);
+    bt_debug("size %u, result: %s\n", size, flow_validated_state_str(res));
+    if (size =3D=3D valid_sizes[valid_idx])
+    {
+      valid_idx++;
+      bt_assert(res =3D=3D FLOW_ST_VALID);
+    }
+    else
+    {
+      bt_assert(res =3D=3D FLOW_ST_NOT_COMPLETE);
+    }
+  }
+
+  /* Misc err tests */
+
+  struct tset {
+    enum flow_validated_state expect;
+    char *description;
+    u16 size;
+    byte *nlri;
+    byte *result;
+  };
+
+#define TS(type, msg, data, result) ((struct tset) {type, msg, sizeof(data=
), (data), (result)})
+  struct tset tset[] =3D {
+    TS(
+      FLOW_ST_EXCEED_MAX_PREFIX_LENGTH,
+      "33-length IPv4 prefix",
+      ((byte []) {
+	FLOW_TYPE_DST_PREFIX, 33, 5, 6, 7, 8, 9
+      }),
+      NULL
+    ),
+    TS(
+      FLOW_ST_EXCEED_MAX_VALUE_LENGTH,
+      "DSCP of length 2",
+      ((byte []) {
+	FLOW_TYPE_DSCP, 0x91, 00, 63,
+      }),
+      NULL
+    ),
+    TS(
+      FLOW_ST_BAD_TYPE_ORDER,
+      "Bad flowspec component type order",
+      ((byte []) {
+	FLOW_TYPE_SRC_PREFIX, 32, 10, 11, 12, 13,
+	FLOW_TYPE_DST_PREFIX, 24, 5, 6, 7,
+      }),
+      NULL
+    ),
+    TS(
+      FLOW_ST_BAD_TYPE_ORDER,
+      "Doubled destination prefix component",
+      ((byte []) {
+	FLOW_TYPE_DST_PREFIX, 24, 5, 6, 7,
+	FLOW_TYPE_DST_PREFIX, 24, 5, 6, 7,
+      }),
+      NULL
+    ),
+    TS(
+      FLOW_ST_UNKNOWN_COMPONENT,
+      "Unknown component of type number 14",
+      ((byte []) {
+	FLOW_TYPE_DST_PREFIX, 24, 5, 6, 7,
+	FLOW_TYPE_TCP_FLAGS, 0x80, 0x55,
+	14 /*something new*/, 0x80, 0x55,
+      }),
+      NULL
+    ),
+    TS(
+      FLOW_ST_UNKNOWN_COMPONENT,
+      "Label component in IPv4",
+      ((byte []) {
+	FLOW_TYPE_DST_PREFIX, 24, 5, 6, 7,
+	FLOW_TYPE_LABEL, 0xa0, 0, 0, 0, 0x55,
+      }),
+      NULL
+    ),
+    TS(
+      FLOW_ST_VALID,
+      "Non-zero padding in prefix",
+      ((byte []) {
+	FLOW_TYPE_DST_PREFIX, 21, 5, 6, 0x7f,
+	FLOW_TYPE_SRC_PREFIX, 24, 5, 6, 0x7f,
+      }),
+      ((byte []) {
+	FLOW_TYPE_DST_PREFIX, 21, 5, 6, 0x78,
+	FLOW_TYPE_SRC_PREFIX, 24, 5, 6, 0x7f,
+      })
+    ),
+    TS(
+      FLOW_ST_VALID,
+      "The first numeric operator has set the AND bit",
+      ((byte []) {
+	FLOW_TYPE_PORT, 0x43, 0x89, 0x45, 0x8b, 0x91, 0x1f, 0x90,
+      }),
+      ((byte []) {
+	FLOW_TYPE_PORT, 0x03, 0x89, 0x45, 0x8b, 0x91, 0x1f, 0x90,
+      })
+    ),
+    TS(
+      FLOW_ST_VALID,
+      "Set zero bit in operator to one",
+      ((byte []) {
+	FLOW_TYPE_IP_PROTOCOL, 0x89, 0x06,
+	FLOW_TYPE_FRAGMENT, 0x84, 0x03,
+      }),
+      ((byte []) {
+	FLOW_TYPE_IP_PROTOCOL, 0x81, 0x06,
+	FLOW_TYPE_FRAGMENT, 0x80, 0x03,
+      })
+    ),
+    TS(
+      FLOW_ST_VALID,
+      "Set zero bit in operand to one",
+      ((byte []) {
+	FLOW_TYPE_TCP_FLAGS, 0x91, 0xff, 0x3a,
+	FLOW_TYPE_FRAGMENT, 0x81, 0xff,
+      }),
+      ((byte []) {
+	FLOW_TYPE_TCP_FLAGS, 0x91, 0x0f, 0x3a,
+	FLOW_TYPE_FRAGMENT, 0x81, 0x0f,
+      })
+    ),
+  };
+#undef TS
+
+  for (uint tcase =3D 0; tcase < ARRAY_SIZE(tset); tcase++)
+  {
+    res =3D flow4_decode(tset[tcase].nlri, tset[tcase].size);
+    bt_assert_msg(res =3D=3D tset[tcase].expect, "Assertion (%s =3D=3D %s)=
 %s", flow_validated_state_str(res), flow_validated_state_str(tset[tcase].e=
xpect), tset[tcase].description);
+
+    if ((res =3D=3D FLOW_ST_VALID) && (tset[tcase].expect =3D=3D FLOW_ST_V=
ALID))
+    {
+      int pos;
+      for (pos =3D 0; pos < tset[tcase].size; pos++)
+	if (tset[tcase].nlri[pos] !=3D tset[tcase].result[pos])
+	  break;
+
+      bool ok =3D (pos =3D=3D tset[tcase].size);
+      bt_assert_msg(ok, "Assertion (pos %u: 0x%02x =3D=3D 0x%02x) %s", pos=
, (ok ? 0 : tset[tcase].nlri[pos]), (ok ? 0 : tset[tcase].result[pos]), tse=
t[tcase].description);
+    }
+  }
+
+  return 1;
+}
+
+static int
+t_decoding6(void)
+{
+  enum flow_validated_state res;
+
+  byte nlri1[] =3D {
+    FLOW_TYPE_DST_PREFIX, 103, 61, 0x01, 0x12, 0x34, 0x56, 0x78, 0xc0,
+    FLOW_TYPE_SRC_PREFIX, 8, 0, 0xc0,
+    FLOW_TYPE_NEXT_HEADER, 0x81, 0x06,
+    FLOW_TYPE_PORT, 0x03, 0x89, 0x45, 0x8b, 0x91, 0x1f, 0x90,
+    FLOW_TYPE_LABEL, 0x80, 0x55,
+  };
+
+  /* Isn't included destination prefix */
+  res =3D flow6_decode(nlri1, 0);
+  bt_assert(res =3D=3D FLOW_ST_VALID);
+
+  /* Valid / Not Complete testing */
+  uint valid_sizes[] =3D {0, 9, 13, 16, 24, 27, 0};
+  uint valid_idx =3D 0;
+  for (uint size =3D 0; size <=3D sizeof(nlri1); size++)
+  {
+    res =3D flow6_decode(nlri1, size);
+    bt_debug("size %u, result: %s\n", size, flow_validated_state_str(res));
+    if (size =3D=3D valid_sizes[valid_idx])
+    {
+      valid_idx++;
+      bt_assert(res =3D=3D FLOW_ST_VALID);
+    }
+    else
+    {
+      bt_assert(res =3D=3D FLOW_ST_NOT_COMPLETE);
+    }
+  }
+
+  /* Misc err tests */
+
+  struct tset {
+    enum flow_validated_state expect;
+    char *description;
+    u16 size;
+    byte *nlri;
+    byte *result;
+  };
+
+#define TS(type, msg, data, result) ((struct tset) {type, msg, sizeof(data=
), (data), (result)})
+  struct tset tset[] =3D {
+    TS(
+      FLOW_ST_EXCEED_MAX_PREFIX_LENGTH,
+      "129-length IPv6 prefix",
+      ((byte []) {
+	FLOW_TYPE_DST_PREFIX, 129, 64, 0x12, 0x34, 0x56, 0x78, 0x9a, 0xbc, 0xde, =
0xf0, 0x12
+      }),
+      NULL
+    ),
+    TS(
+      FLOW_ST_EXCEED_MAX_PREFIX_OFFSET,
+      "Prefix offset is higher than prefix length",
+      ((byte []) {
+	FLOW_TYPE_DST_PREFIX, 48, 64, 0x40, 0x12, 0x34
+      }),
+      NULL
+    ),
+    TS(
+      FLOW_ST_EXCEED_MAX_VALUE_LENGTH,
+      "Fragment of length 2",
+      ((byte []) {
+	FLOW_TYPE_FRAGMENT, 0x91, 00, 0x0f,
+      }),
+      NULL
+    ),
+    TS(
+      FLOW_ST_BAD_TYPE_ORDER,
+      "Bad flowspec component type order",
+      ((byte []) {
+	FLOW_TYPE_NEXT_HEADER, 0x81, 0x06,
+	FLOW_TYPE_SRC_PREFIX, 8, 0, 0xc0,
+      }),
+      NULL
+    ),
+    TS(
+      FLOW_ST_BAD_TYPE_ORDER,
+      "Doubled destination prefix component",
+      ((byte []) {
+	FLOW_TYPE_DST_PREFIX, 103, 61, 0x01, 0x12, 0x34, 0x56, 0x78, 0xc0,
+	FLOW_TYPE_DST_PREFIX, 103, 61, 0x01, 0x12, 0x34, 0x56, 0x78, 0xc0,
+      }),
+      NULL
+    ),
+    TS(
+      FLOW_ST_UNKNOWN_COMPONENT,
+      "Unknown component of type number 14",
+      ((byte []) {
+	FLOW_TYPE_TCP_FLAGS, 0x80, 0x55,
+	14 /*something new*/, 0x80, 0x55,
+      }),
+      NULL
+      ),
+    TS(
+      FLOW_ST_VALID,
+      "Label component is well-known in IPv6",
+      ((byte []) {
+	FLOW_TYPE_LABEL, 0xa0, 0, 0, 0, 0x55,
+      }),
+      ((byte []) {
+	FLOW_TYPE_LABEL, 0xa0, 0, 0, 0, 0x55,
+      })
+    ),
+    TS(
+      FLOW_ST_VALID,
+      "Non-zero padding in prefix",
+      ((byte []) {
+	FLOW_TYPE_DST_PREFIX, 43, 16, 0x40, 0x12, 0x34, 0x7c,
+      }),
+      ((byte []) {
+	FLOW_TYPE_DST_PREFIX, 43, 16, 0x40, 0x12, 0x34, 0x60,
+      })
+    ),
+    TS(
+      FLOW_ST_VALID,
+      "The first numeric operator has set the AND bit",
+      ((byte []) {
+	FLOW_TYPE_PORT, 0x43, 0x89, 0x45, 0x8b, 0x91, 0x1f, 0x90
+      }),
+      ((byte []) {
+	FLOW_TYPE_PORT, 0x03, 0x89, 0x45, 0x8b, 0x91, 0x1f, 0x90,
+      })
+    ),
+    TS(
+      FLOW_ST_VALID,
+      "Set zero bit in operator to one",
+      ((byte []) {
+	FLOW_TYPE_NEXT_HEADER, 0x89, 0x06,
+	FLOW_TYPE_FRAGMENT, 0x84, 0x02,
+      }),
+      ((byte []) {
+	FLOW_TYPE_IP_PROTOCOL, 0x81, 0x06,
+	FLOW_TYPE_FRAGMENT, 0x80, 0x02,
+      })
+    ),
+    TS(
+      FLOW_ST_VALID,
+      "Set zero bit in operand to one",
+      ((byte []) {
+	FLOW_TYPE_TCP_FLAGS, 0x91, 0xff, 0x3a,
+	FLOW_TYPE_FRAGMENT, 0x81, 0xff,
+      }),
+      ((byte []) {
+	FLOW_TYPE_TCP_FLAGS, 0x91, 0x0f, 0x3a,
+	FLOW_TYPE_FRAGMENT, 0x81, 0x0e,
+      })
+    ),
+  };
+#undef TS
+
+  for (uint tcase =3D 0; tcase < ARRAY_SIZE(tset); tcase++)
+  {
+    res =3D flow6_decode(tset[tcase].nlri, tset[tcase].size);
+    bt_assert_msg(res =3D=3D tset[tcase].expect, "Assertion (%s =3D=3D %s)=
 %s", flow_validated_state_str(res), flow_validated_state_str(tset[tcase].e=
xpect), tset[tcase].description);
+
+    if ((res =3D=3D FLOW_ST_VALID) && (tset[tcase].expect =3D=3D FLOW_ST_V=
ALID))
+    {
+      int pos;
+      for (pos =3D 0; pos < tset[tcase].size; pos++)
+	if (tset[tcase].nlri[pos] !=3D tset[tcase].result[pos])
+	  break;
+
+      bool ok =3D (pos =3D=3D tset[tcase].size);
+      bt_assert_msg(ok, "Assertion (pos %u: 0x%02x =3D=3D 0x%02x) %s", pos=
, (ok ? 0 : tset[tcase].nlri[pos]), (ok ? 0 : tset[tcase].result[pos]), tse=
t[tcase].description);
+    }
+  }
+
+  return 1;
+}
+
=20
=20
 /*
@@ -546,7 +969,7 @@ t_builder6(void)
     FLOW_TYPE_SRC_PREFIX, 8, 0, 0xc0,
     FLOW_TYPE_NEXT_HEADER, 0x80, 0x06,
     FLOW_TYPE_PORT, 0x03, 0x89, 0x45, 0x8b, 0x91, 0x1f, 0x90,
-    FLOW_TYPE_LABEL, 0x80, 0x55,
+    FLOW_TYPE_LABEL, 0xa0, 0x00, 0x00, 0x00, 0x55,
   );
=20
   /* Normal order */
@@ -729,6 +1152,8 @@ main(int argc, char *argv[])
   bt_test_suite(t_accessors6,   "Testing accessors (IPv6)");
   bt_test_suite(t_validation4,  "Testing validation (IPv4)");
   bt_test_suite(t_validation6,  "Testing validation (IPv6)");
+  bt_test_suite(t_decoding4,    "Testing decoding (IPv4)");
+  bt_test_suite(t_decoding6,    "Testing decoding (IPv6)");
   bt_test_suite(t_builder4,     "Inserting components into existing Flow S=
pecification (IPv4)");
   bt_test_suite(t_builder6,     "Inserting components into existing Flow S=
pecification (IPv6)");
   bt_test_suite(t_formatting4,  "Formatting Flow Specification (IPv4) into=
 text representation");
diff --git a/lib/lockfree.c b/lib/lockfree.c
index adfca04e9..81bee48de 100644
--- a/lib/lockfree.c
+++ b/lib/lockfree.c
@@ -10,7 +10,7 @@
 #include "lib/birdlib.h"
 #include "lib/lockfree.h"
=20
-#define LOCAL_DEBUG
+#undef LOCAL_DEBUG
=20
 void lfuc_unlock_deferred(struct deferred_call *dc)
 {
@@ -87,6 +87,10 @@ lfjour_push_prepare(struct lfjour *j)
   if (!block)
   {
     block =3D alloc_page();
+#ifdef DEBUGGING
+    memset(block, POISON_LFJOUR_NEXT, page_size);
+#endif
+
     lfjour_debug("lfjour(%p)_push_prepare: allocating block %p", j, block);
     *block =3D (struct lfjour_block) {};
     lfjour_block_add_tail(&j->pending, block);
@@ -534,11 +538,21 @@ lfjour_cleanup_hook(void *_j)
=20
   u64 orig_first_seq =3D first->seq;
=20
+  struct lfjour_item *first_to_poison =3D NULL, *last_to_poison =3D NULL;
+
   /* Now we do the actual cleanup */
   while (first && (first->seq <=3D min_seq))
   {
     j->item_done(j, first);
=20
+    /* Beware. We can't poison lfjour items here because there may still be
+     * readers of the "done" items. We'll poison them later after RCU sync=
hronizes. */
+    last_to_poison =3D first;
+
+    if (!first_to_poison)
+      first_to_poison =3D first;
+
+
     /* Find next journal item */
     struct lfjour_item *next =3D lfjour_get_next(j, first);
     if (PAGE_HEAD(next) !=3D PAGE_HEAD(first))
@@ -555,11 +569,14 @@ lfjour_cleanup_hook(void *_j)
       synchronize_rcu();
=20
       /* Now we can finally drop the block */
-#ifdef LOCAL_DEBUG
-      memset(block, 0xbe, page_size);
+#ifdef DEBUGGING
+      memset(block, POISON_LFJOUR_PAGE, page_size);
 #endif
       free_page(block);
=20
+      /* Do not poison items from a freed page */
+      first_to_poison =3D last_to_poison =3D NULL;
+
       /* If no more blocks are remaining, we shall reset
        * the sequence numbers */
=20
@@ -577,6 +594,18 @@ lfjour_cleanup_hook(void *_j)
     first =3D next;
   }
=20
+  /* Wait for possible pending readers of the partially done block */
+  synchronize_rcu();
+
+  /* Poison items from a page which is not yet done completely. */
+#ifdef DEBUGGING
+  if (first_to_poison)
+  {
+    ASSERT_DIE(PAGE_HEAD(first_to_poison) =3D=3D PAGE_HEAD(last_to_poison)=
);
+    memset(first_to_poison, POISON_LFJOUR_ITEM, (last_to_poison - first_to=
_poison) + j->item_size);
+  }
+#endif
+
   lfjour_cleanup_done(j);
=20
   CALL(j->cleanup_done, j, orig_first_seq, first ? first->seq : ~((u64) 0)=
);
diff --git a/lib/mempool.c b/lib/mempool.c
index b88bc1227..a6a9c28e3 100644
--- a/lib/mempool.c
+++ b/lib/mempool.c
@@ -124,6 +124,9 @@ lp_alloc_slow(linpool *m, uint size)
=20
 	  /* Need to allocate a new chunk */
 	  c =3D alloc_page();
+#ifdef DEBUGGING
+	  memset(c, POISON_LP_NEW_PAGE, page_size);
+#endif
=20
 	  m->total +=3D LP_DATA_SIZE;
 	  c->next =3D NULL;
@@ -254,6 +257,9 @@ lp_restore(linpool *m, lp_state *p)
   while (c =3D m->current->next)
     {
       m->current->next =3D c->next;
+#ifdef DEBUGGING
+      memset(c, POISON_LP_FREE_PAGE, page_size);
+#endif
       free_page(c);
     }
 }
diff --git a/lib/obstacle.h b/lib/obstacle.h
index 7d7bcac70..54040f8c0 100644
--- a/lib/obstacle.h
+++ b/lib/obstacle.h
@@ -73,6 +73,16 @@ obstacle_target_count(struct obstacle_target *t)
   return len;
 }
=20
+static inline void
+obstacle_target_cleanup(struct obstacle_target *t)
+{
+  /* Call this from the done callback */
+  ASSERT_DIE(!obstacle_target_count(t));
+
+  mb_free((void *) DOMAIN_NAME(resource, t->dom));
+  DOMAIN_FREE(resource, t->dom);
+}
+
 #define OBSREF(_type)	struct { _type *ref; struct obstacle o; }
=20
 #define OBSREF_SET(_ref, _val)	({	\
diff --git a/lib/rcu.h b/lib/rcu.h
index 6a771a3a5..2e6e3674b 100644
--- a/lib/rcu.h
+++ b/lib/rcu.h
@@ -29,8 +29,20 @@ struct rcu_thread {
=20
 extern _Thread_local struct rcu_thread this_rcu_thread;
=20
+static inline bool rcu_read_active(void)
+{
+  return !!(this_rcu_thread.local_ctl & RCU_NEST_MASK);
+}
+
+void page_fill_hot(void);
+
 static inline void rcu_read_lock(void)
 {
+  /* Be prepared for possible page allocations during critical sections.
+   * That is not nice but it happens anyway. */
+  if (!rcu_read_active())
+    page_fill_hot();
+
   /* Increment the nesting counter */
   atomic_store_explicit(&this_rcu_thread.ctl, (this_rcu_thread.local_ctl +=
=3D RCU_NEST_CNT), memory_order_release);
=20
@@ -54,11 +66,6 @@ static inline void rcu_read_unlock(void)
   this_rcu_thread.local_ctl--;
 }
=20
-static inline bool rcu_read_active(void)
-{
-  return !!(this_rcu_thread.local_ctl & RCU_NEST_MASK);
-}
-
 void synchronize_rcu(void);
=20
 /* Registering and unregistering a birdloop. To be called from birdloop im=
plementation */
diff --git a/lib/resource.c b/lib/resource.c
index a389403b0..56d08a5da 100644
--- a/lib/resource.c
+++ b/lib/resource.c
@@ -234,8 +234,11 @@ rfree(void *res)
   ASSERT_DIE(DG_IS_LOCKED(orig->domain));
   resource_rem_node(&orig->inside, r);
=20
+  uint rsz =3D r->class->size;
+
   r->class->free(r);
   r->class =3D NULL;
+  memset(r, POISON_RESOURCE_FREE, rsz);
   xfree(r);
 }
=20
@@ -522,6 +525,7 @@ mb_free(void *m)
     return;
=20
   SKIP_BACK_DECLARE(struct mblock, b, data, m);
+  memset(m, POISON_MB_FREE, b->size);
   rfree(&b->r);
 }
=20
diff --git a/lib/resource.h b/lib/resource.h
index 12b788510..e609a570f 100644
--- a/lib/resource.h
+++ b/lib/resource.h
@@ -203,5 +203,29 @@ void *xrealloc(void *, unsigned);
 void xfree(void *);
 #endif
=20
+/* Auxiliary memory poisoning enum for debug purposes.
+ * Keep spacing for cases where the memory gets modified by 1 before crash=
ing.
+ * Keep the values sorted by number and always explicit. */
+
+enum poison_byte {
+  POISON_LFJOUR_PAGE =3D 0x21,
+  POISON_LFJOUR_ITEM =3D 0x23,
+  POISON_LFJOUR_NEXT =3D 0x25,
+
+  POISON_THREAD_SHUTDOWN =3D 0x47,
+
+  POISON_RESOURCE_FREE =3D 0xaa,
+
+  POISON_LP_NEW_PAGE =3D 0xb3,
+  POISON_LP_FREE_PAGE =3D 0xb5,
+
+  POISON_MB_FREE =3D 0xbc,
+
+  POISON_SLAB_NEW_ITEM =3D 0xc1,
+  POISON_SLAB_NEW_PAGE =3D 0xc3,
+  POISON_SLAB_FREE_PAGE =3D 0xc5,
+  POISON_SLAB_FREE_ITEM =3D 0xc7,
+};
+
 #endif
=20
diff --git a/lib/slab.c b/lib/slab.c
index 9351a06dc..c77cd74b4 100644
--- a/lib/slab.c
+++ b/lib/slab.c
@@ -278,7 +278,7 @@ okay:
=20
       void *out =3D ((void *) h) + s->head_size + (i * 32 + pos) * s->obj_=
size;
 #ifdef POISON
-      memset(out, 0xcd, s->data_size);
+      memset(out, POISON_SLAB_NEW_ITEM, s->data_size);
 #endif
       return out;
     }
@@ -298,7 +298,7 @@ no_partial:
   ASSERT_DIE(SL_GET_HEAD(h) =3D=3D h);
=20
 #ifdef POISON
-  memset(h, 0xba, page_size);
+  memset(h, POISON_SLAB_NEW_PAGE, page_size);
 #endif
=20
   memset(h, 0, s->head_size);
@@ -339,7 +339,7 @@ sl_free(void *oo)
   ASSERT_DIE(DG_IS_LOCKED(resource_parent(&s->r)->domain));
=20
 #ifdef POISON
-  memset(oo, 0xdb, s->data_size);
+  memset(oo, POISON_SLAB_FREE_ITEM, s->data_size);
 #endif
=20
   uint offset =3D oo - ((void *) h) - s->head_size;
@@ -357,7 +357,7 @@ sl_free(void *oo)
       if (s->num_empty_heads >=3D MAX_EMPTY_HEADS)
       {
 #ifdef POISON
-	memset(h, 0xde, page_size);
+	memset(h, POISON_SLAB_FREE_PAGE, page_size);
 #endif
 	free_page(h);
       }
diff --git a/lib/string.h b/lib/string.h
index 7e0491951..4dc88c685 100644
--- a/lib/string.h
+++ b/lib/string.h
@@ -108,6 +108,14 @@ bmemcpy(void *dest, const void *src, size_t n)
     return dest;
 }
=20
+static inline void *
+memcpy0(void *dst, const void *src, size_t dst_len, size_t src_len)
+{
+  memset(dst, 0, dst_len);
+  memcpy(dst, src, MIN_(dst_len, src_len));
+  return dst;
+}
+
 #define ROUTER_ID_64_LENGTH 23
=20
 #endif
diff --git a/nest/iface.c b/nest/iface.c
index 06606d134..e316fa116 100644
--- a/nest/iface.c
+++ b/nest/iface.c
@@ -211,9 +211,6 @@ void ifa_unlink(struct ifa *a)
     return;
=20
   if_unlink(a->iface);
-#if DEBUGGING
-  memset(a, 0x5b, sizeof(struct ifa));
-#endif
   mb_free(a);
 }
=20
diff --git a/nest/password.h b/nest/password.h
index 335b9cd42..22fbaf6fe 100644
--- a/nest/password.h
+++ b/nest/password.h
@@ -28,10 +28,10 @@ struct password_item *password_find_by_id(list *l, uint=
 id);
 struct password_item *password_find_by_value(list *l, char *pass, uint siz=
e);
 void password_validate_length(const struct password_item *p);
=20
-static inline int password_verify(struct password_item *p1, char *p2, uint=
 size)
+static inline int password_verify(const struct password_item *p1, const ch=
ar *p2, uint size)
 {
   char buf[size];
-  strncpy(buf, p1->password, size);
+  memcpy0(buf, p1->password, size, p1->length);
   return !memcmp(buf, p2, size);
 }
=20
diff --git a/nest/proto.c b/nest/proto.c
index d1123c1f5..f361eef06 100644
--- a/nest/proto.c
+++ b/nest/proto.c
@@ -2133,6 +2133,8 @@ graceful_recovery_done(struct callback *_ UNUSED)
   ASSERT_DIE(birdloop_inside(&main_birdloop));
   ASSERT_DIE(_graceful_recovery_context.grc_state =3D=3D GRS_ACTIVE);
=20
+  obstacle_target_cleanup(&_graceful_recovery_context.obstacles);
+
   tm_stop(&_graceful_recovery_context.wait_timer);
   log(L_INFO "Graceful recovery done");
=20
diff --git a/nest/rt-table.c b/nest/rt-table.c
index e1310b085..af5991a3c 100644
--- a/nest/rt-table.c
+++ b/nest/rt-table.c
@@ -2722,8 +2722,13 @@ rt_net_feed_index(struct rtable_reading *tr, net *n,=
 struct bmap *seen, bool (*p
=20
   if (rcnt || ocnt || ecnt)
   {
-    if (!ecnt && prefilter && !prefilter(f, NET_READ_BEST_ROUTE(tr, n)->rt=
e.net))
-      return NULL;
+    if (!ecnt && prefilter)
+    {
+      struct rte_storage *best =3D NET_READ_BEST_ROUTE(tr, n);
+
+      if (best && !prefilter(f, best->rte.net))
+	return NULL;
+    }
=20
     feed =3D rt_alloc_feed(rcnt+ocnt, ecnt);
=20
diff --git a/proto/bfd/packets.c b/proto/bfd/packets.c
index 8bc08fe17..ce67de783 100644
--- a/proto/bfd/packets.c
+++ b/proto/bfd/packets.c
@@ -148,7 +148,7 @@ bfd_fill_authentication(struct bfd_proto *p, struct bfd=
_session *s, struct bfd_c
   {
     struct bfd_crypto_auth *auth =3D (void *) (pkt + 1);
     uint hash_alg =3D bfd_auth_type_to_hash_alg[cf->auth_type];
-    uint hash_len =3D mac_type_length(pass->alg);
+    uint hash_len =3D mac_type_length(hash_alg);
=20
     /* Increase CSN about one time per second */
     u32 new_time =3D (u64) current_time() >> 20;
@@ -169,7 +169,8 @@ bfd_fill_authentication(struct bfd_proto *p, struct bfd=
_session *s, struct bfd_c
     pkt->flags |=3D BFD_FLAG_AP;
     pkt->length +=3D auth->length;
=20
-    strncpy(auth->data, pass->password, hash_len);
+    memcpy0(auth->data, pass->password, hash_len, pass->length);
+
     mac_fill(hash_alg, NULL, 0, (byte *) pkt, pkt->length, auth->data);
     return;
   }
@@ -262,7 +263,7 @@ bfd_check_authentication(struct bfd_proto *p, struct bf=
d_session *s, struct bfd_
=20
     byte *auth_data =3D alloca(hash_len);
     memcpy(auth_data, auth->data, hash_len);
-    strncpy(auth->data, pass->password, hash_len);
+    memcpy0(auth->data, pass->password, hash_len, pass->length);
=20
     if (!mac_verify(hash_alg, NULL, 0, (byte *) pkt, pkt->length, auth_dat=
a))
       DROP("wrong authentication code", pass->id);
diff --git a/proto/bgp/bgp.c b/proto/bgp/bgp.c
index 4a3bdd90e..bb9f2418a 100644
--- a/proto/bgp/bgp.c
+++ b/proto/bgp/bgp.c
@@ -1338,15 +1338,15 @@ bgp_conn_leave_established_state(struct bgp_conn *c=
onn, struct bgp_proto *p)
   if (p->p.proto_state =3D=3D PS_UP)
     bgp_stop(p, 0, NULL, 0);
=20
-  uint adsz;
-  struct bgp_session_close_ad *bscad =3D alloca(adsz =3D sizeof *bscad + c=
onn->notify_size);
+  uint ad_size =3D sizeof(struct bgp_session_close_ad) + conn->notify_size;
+  struct bgp_session_close_ad *bscad =3D alloca(ad_size);
   *bscad =3D (struct bgp_session_close_ad) {
-    .ad.length =3D adsz - sizeof(adata),
+    .ad.length =3D ad_size - sizeof(adata),
     .last_error_class =3D p->last_error_class,
     .notify_code =3D conn->notify_code,
     .notify_subcode =3D conn->notify_subcode,
   };
-  memcpy(bscad->data, conn->notify_data, conn->notify_size);
+  bmemcpy(bscad->data, conn->notify_data, conn->notify_size);
=20
   ea_list *pes =3D p->p.ea_state;
   ea_set_attr(&pes, EA_LITERAL_DIRECT_ADATA(&ea_bgp_close_bmp, 0, &bscad->=
ad));
@@ -3009,6 +3009,9 @@ bgp_postconfig(struct proto_config *CF)
   if (cf->keepalive_time > (cf->hold_time / 2))
     log(L_WARN "Keepalive time should be at most 1/2 of hold time");
=20
+  if ((cf->send_hold_time > 0) && (cf->send_hold_time < cf->hold_time))
+    cf_error("Send hold time must be zero or at least hold time");
+
   if (cf->min_hold_time > cf->hold_time)
     cf_error("Min hold time (%u) exceeds hold time (%u)",
 	     cf->min_hold_time, cf->hold_time);
diff --git a/proto/bgp/packets.c b/proto/bgp/packets.c
index 899e68e3e..c20107d03 100644
--- a/proto/bgp/packets.c
+++ b/proto/bgp/packets.c
@@ -936,12 +936,14 @@ bgp_rx_open(struct bgp_conn *conn, byte *pkt, uint le=
n)
=20
   /* Compute effective hold and keepalive times */
   uint hold_time =3D MIN(hold, p->cf->hold_time);
+  uint base_hold_time =3D p->cf->hold_time ?: 1;
+
   uint keepalive_time =3D p->cf->keepalive_time ?
-    (p->cf->keepalive_time * hold_time / p->cf->hold_time) :
+    (p->cf->keepalive_time * hold_time / base_hold_time) :
     hold_time / 3;
=20
   uint send_hold_time =3D (p->cf->send_hold_time >=3D 0) ?
-    (p->cf->send_hold_time * hold_time / p->cf->hold_time) :
+    ((u64) p->cf->send_hold_time * hold_time / base_hold_time) :
     2 * hold_time;
=20
   /* Keepalive time might be rounded down to zero */
@@ -2101,8 +2103,8 @@ bgp_decode_nlri_flow4(struct bgp_parse_state *s, byte=
 *pos, uint len, ea_list *a
     if (len < flen)
       bgp_parse_error(s, 1);
=20
-    /* Validate flow data */
-    enum flow_validated_state r =3D flow4_validate(data, dlen);
+    /* Decode flow data */
+    enum flow_validated_state r =3D flow4_decode(data, dlen);
     if (r !=3D FLOW_ST_VALID)
     {
       log(L_REMOTE "%s: Invalid flow route: %s", s->proto->p.name, flow_va=
lidated_state_str(r));
@@ -2113,7 +2115,7 @@ bgp_decode_nlri_flow4(struct bgp_parse_state *s, byte=
 *pos, uint len, ea_list *a
     uint pxlen =3D 0;
=20
     /* Decode dst prefix */
-    if (data[0] =3D=3D FLOW_TYPE_DST_PREFIX)
+    if (dlen && (data[0] =3D=3D FLOW_TYPE_DST_PREFIX))
     {
       px =3D flow_read_ip4_part(data);
       pxlen =3D flow_read_pxlen(data);
@@ -2189,8 +2191,8 @@ bgp_decode_nlri_flow6(struct bgp_parse_state *s, byte=
 *pos, uint len, ea_list *a
     if (len < flen)
       bgp_parse_error(s, 1);
=20
-    /* Validate flow data */
-    enum flow_validated_state r =3D flow6_validate(data, dlen);
+    /* Decode flow data */
+    enum flow_validated_state r =3D flow6_decode(data, dlen);
     if (r !=3D FLOW_ST_VALID)
     {
       log(L_REMOTE "%s: Invalid flow route: %s", s->proto->p.name, flow_va=
lidated_state_str(r));
@@ -2201,7 +2203,7 @@ bgp_decode_nlri_flow6(struct bgp_parse_state *s, byte=
 *pos, uint len, ea_list *a
     uint pxlen =3D 0;
=20
     /* Decode dst prefix */
-    if (data[0] =3D=3D FLOW_TYPE_DST_PREFIX)
+    if (dlen && (data[0] =3D=3D FLOW_TYPE_DST_PREFIX))
     {
       px =3D flow_read_ip6_part(data);
       pxlen =3D flow_read_pxlen(data);
diff --git a/proto/l3vpn/l3vpn.c b/proto/l3vpn/l3vpn.c
index cb043e971..78f29558b 100644
--- a/proto/l3vpn/l3vpn.c
+++ b/proto/l3vpn/l3vpn.c
@@ -214,6 +214,7 @@ l3vpn_rt_notify(struct proto *P, struct channel *c0, co=
nst net_addr *n0, rte *ne
=20
     /* Hostentry also validn't */
     ea_unset_attr(&new->attrs, 0, &ea_gen_hostentry);
+    ea_unset_attr(&new->attrs, 0, &ea_gen_hostentry_version);
=20
     if (export)
     {
diff --git a/proto/ospf/packet.c b/proto/ospf/packet.c
index 1c9aafa4f..9104f9fc9 100644
--- a/proto/ospf/packet.c
+++ b/proto/ospf/packet.c
@@ -64,7 +64,8 @@ ospf_pkt_finalize2(struct ospf_iface *ifa, struct ospf_pa=
cket *pkt, uint *plen)
       log(L_ERR "No suitable password found for authentication");
       return;
     }
-    strncpy(auth->password, pass->password, sizeof(auth->password));
+    memcpy0(auth->password, pass->password, sizeof(auth->password), pass->=
length);
+
     /* fallthrough */
=20
   case OSPF_AUTH_NONE:
@@ -111,7 +112,7 @@ ospf_pkt_finalize2(struct ospf_iface *ifa, struct ospf_=
packet *pkt, uint *plen)
=20
     /* Append key for keyed hash, append padding for HMAC (RFC 5709 3.3) */
     if (pass->alg < ALG_HMAC)
-      strncpy(auth_tail, pass->password, auth_len);
+      memcpy0(auth_tail, pass->password, auth_len, pass->length);
     else
       memset32(auth_tail, HMAC_MAGIC, auth_len / 4);
=20
@@ -235,7 +236,7 @@ ospf_pkt_checkauth2(struct ospf_neighbor *n, struct osp=
f_iface *ifa, struct ospf
=20
     /* Append key for keyed hash, append padding for HMAC (RFC 5709 3.3) */
     if (pass->alg < ALG_HMAC)
-      strncpy(auth_tail, pass->password, auth_len);
+      memcpy0(auth_tail, pass->password, auth_len, pass->length);
     else
       memset32(auth_tail, HMAC_MAGIC, auth_len / 4);
=20
diff --git a/proto/pipe/pipe.c b/proto/pipe/pipe.c
index d6b5208f2..a2fbe72ed 100644
--- a/proto/pipe/pipe.c
+++ b/proto/pipe/pipe.c
@@ -62,6 +62,7 @@ pipe_rt_notify(struct proto *P, struct channel *src_ch, c=
onst net_addr *n, rte *
=20
       e0.generation =3D new->generation + 1;
       ea_unset_attr(&e0.attrs, 0, &ea_gen_hostentry);
+      ea_unset_attr(&e0.attrs, 0, &ea_gen_hostentry_version);
=20
       rte_update(dst, n, &e0, new->src);
     }
diff --git a/proto/rip/packets.c b/proto/rip/packets.c
index 70108ac35..dd627aa4a 100644
--- a/proto/rip/packets.c
+++ b/proto/rip/packets.c
@@ -274,7 +274,7 @@ rip_fill_authentication(struct rip_proto *p, struct rip=
_iface *ifa, struct rip_p
   case RIP_AUTH_PLAIN:
     auth->must_be_ffff =3D htons(0xffff);
     auth->auth_type =3D htons(RIP_AUTH_PLAIN);
-    strncpy(auth->password, pass->password, RIP_PASSWD_LENGTH);
+    memcpy0(auth->password, pass->password, sizeof(auth->password), pass->=
length);
     return;
=20
   case RIP_AUTH_CRYPTO:
@@ -313,7 +313,7 @@ rip_fill_authentication(struct rip_proto *p, struct rip=
_iface *ifa, struct rip_p
=20
     /* Append key for keyed hash, append padding for HMAC (RFC 4822 2.5) */
     if (pass->alg < ALG_HMAC)
-      strncpy(tail->auth_data, pass->password, auth_len);
+      memcpy0(tail->auth_data, pass->password, auth_len, pass->length);
     else
       memset32(tail->auth_data, HMAC_MAGIC, auth_len / 4);
=20
@@ -402,7 +402,7 @@ rip_check_authentication(struct rip_proto *p, struct ri=
p_iface *ifa, struct rip_
=20
     /* Append key for keyed hash, append padding for HMAC (RFC 4822 2.5) */
     if (pass->alg < ALG_HMAC)
-      strncpy(tail->auth_data, pass->password, auth_len);
+      memcpy0(tail->auth_data, pass->password, auth_len, pass->length);
     else
       memset32(tail->auth_data, HMAC_MAGIC, auth_len / 4);
=20
diff --git a/sysdep/unix/alloc.c b/sysdep/unix/alloc.c
index fcf855082..bf6e77985 100644
--- a/sysdep/unix/alloc.c
+++ b/sysdep/unix/alloc.c
@@ -203,6 +203,8 @@ alloc_hot_page(struct free_page *fp) {
   return NULL;
 }
=20
+static void *alloc_page_global(void);
+
 void *
 alloc_page(void)
 {
@@ -234,7 +236,14 @@ alloc_page(void)
=20
   ASSERT_DIE(pages_kept_here =3D=3D 0);
=20
+  return alloc_page_global();
+}
+
+static void *
+alloc_page_global(void)
+{
   /* If there is any free page kept hot in global storage, we use it. */
+  struct free_page *fp;
   if (fp =3D alloc_hot_page(fp))
     return fp;
=20
@@ -475,6 +484,14 @@ page_cleanup(void *_ UNUSED)
=20
   ajlog(NULL, NULL, 0, AJT_CLEANUP_END);
 }
+
+void
+page_fill_hot(void)
+{
+  while (pages_kept_here < KEEP_PAGES_MAX_LOCAL)
+    free_page(alloc_page_global());
+}
+
 #endif
=20
 void
diff --git a/sysdep/unix/io-loop.c b/sysdep/unix/io-loop.c
index 231ab6acf..395d05ac3 100644
--- a/sysdep/unix/io-loop.c
+++ b/sysdep/unix/io-loop.c
@@ -1256,7 +1256,7 @@ bird_thread_shutdown(void * _ UNUSED)
=20
   /* Last try to run the priority event list; ruin it then to be extra sur=
e */
   ev_run_list(&this_thread->priority_events);
-  memset(&this_thread->priority_events, 0xa5, sizeof(this_thread->priority=
_events));
+  memset(&this_thread->priority_events, POISON_THREAD_SHUTDOWN, sizeof(thi=
s_thread->priority_events));
=20
   /* Drop loops including the thread dropper itself */
   while (!EMPTY_TLIST(birdloop, &thr->loops))
diff --git a/sysdep/unix/log.c b/sysdep/unix/log.c
index 5cb425db0..a92733df3 100644
--- a/sysdep/unix/log.c
+++ b/sysdep/unix/log.c
@@ -164,26 +164,21 @@ lts_request(struct log_channel *lc_close, struct rfil=
e *rf_close, const char *na
   ev_send_loop(&main_birdloop, &lts->lts_event);
 }
=20
-static void
+static bool
 log_rotate(struct log_channel *lc)
 {
   struct rfile *old_rf =3D atomic_load_explicit(&lc->rf, memory_order_rela=
xed);
   lts_request(NULL, old_rf, "Log Rotate Close Old File");
=20
   if ((rename(lc->filename, lc->backup) < 0) && (unlink(lc->filename) < 0))
-  {
-    atomic_store_explicit(&lc->rf, NULL, memory_order_relaxed);
-    return lts_request(lc, NULL, "Log Rotate Failed");
-  }
+    return lts_request(lc, NULL, "Log Rotate Failed"), false;
=20
   struct rfile *rf =3D rf_open(log_pool, lc->filename, RF_APPEND, lc->limi=
t);
   if (!rf)
-  {
-    atomic_store_explicit(&lc->rf, NULL, memory_order_relaxed);
-    return lts_request(lc, NULL, "Log Rotate Failed");
-  }
+    return lts_request(lc, NULL, "Log Rotate Failed"), false;
=20
   atomic_store_explicit(&lc->rf, rf, memory_order_release);
+  return true;
 }
=20
 /**
@@ -231,10 +226,12 @@ log_commit(log_buffer *buf)
     glogs =3D &initial_stderr_log;
   }
=20
+  /* Log commit may cause the channel to get removed from the list.
+   * We need to be able to continue walking the list regardless. */
   for (
-      struct log_channel *l =3D glogs; l;
-      l =3D atomic_load_explicit(&l->next, memory_order_acquire)
-      )
+      struct log_channel * _Atomic *pprev =3D &global_logs, *l;
+      l =3D atomic_load_explicit(pprev, memory_order_acquire);
+      pprev =3D &l->next)
     {
       uint mask =3D atomic_load_explicit(&l->mask, memory_order_acquire);
       if (!(mask & (1 << buf->class)))
@@ -288,7 +285,14 @@ log_commit(log_buffer *buf)
 		break;
 	      }
=20
-	      log_rotate(l);
+	      if (!log_rotate(l))
+	      {
+		atomic_store_explicit(&l->rf, NULL, memory_order_relaxed);
+		atomic_store_explicit(pprev,
+		    atomic_load_explicit(&l->next, memory_order_acquire),
+		    memory_order_release);
+	      }
+
 	      log_unlock();
=20
 	      rf =3D atomic_load_explicit(&l->rf, memory_order_relaxed);

--=20
ciao,
Marco

--5OLJOChtBbiYU44U
Content-Type: application/pgp-signature; name=signature.asc

-----BEGIN PGP SIGNATURE-----

iHUEABYKAB0WIQQnKUXNg20437dCfobLPsM64d7XgQUCam7CcAAKCRDLPsM64d7X
gTF+AQCVaqdJgHFmc/i7iUL9EhLYS51BiFSEd/HN2+d3qBxvCAEA7prCR+eI1IAa
GdbOXIgISeEY7iVSZknC5gHWYG1hgA4=
=ps/P
-----END PGP SIGNATURE-----

--5OLJOChtBbiYU44U--