]> git.ipfire.org Git - thirdparty/bird.git/commitdiff
Fixed flush condition when stale cycle valid/set indicators wrap around
authorMaria Matejka <mq@ucw.cz>
Sat, 20 Apr 2024 16:10:42 +0000 (18:10 +0200)
committerMaria Matejka <mq@ucw.cz>
Wed, 22 May 2024 09:34:34 +0000 (11:34 +0200)
nest/rt-table.c

index f0f8a7f38af4195b2d358f6dd12c8c342ab073ab..5868643735046710305517947f8f619bfc4dfee2 100644 (file)
@@ -2721,10 +2721,63 @@ rt_prune_net(struct rtable_private *tab, struct network *n)
   for (struct rte_storage *e = n->routes; e; e = e->next)
   {
     struct rt_import_hook *s = e->rte.sender;
-    if ((s->import_state == TIS_FLUSHING) ||
-       (e->rte.stale_cycle < s->stale_valid) ||
-       (e->rte.stale_cycle > s->stale_set))
+
+    _Bool stale = (s->import_state == TIS_FLUSHING);
+
+    if (!stale)
+    {
+
+    /*
+     * The range of 0..256 is split by s->stale_* like this:
+     *
+     *     pruned    pruning     valid      set
+     *       |          |          |         |
+     * 0     v          v          v         v       256
+     * |...........................+++++++++++........|
+     *
+     * We want to drop everything outside the marked range, thus
+     *     (e->rte.stale_cycle < s->stale_valid) ||
+     *     (e->rte.stale_cycle > s->stale_set))
+     *   looks right.
+     *
+     * But the pointers may wrap around, and in the following situation, all the routes get pruned:
+     *
+     *      set         pruned    pruning     valid
+     *       |            |          |          |
+     * 0     v            v          v          v    256
+     * |++++++..................................++++++|
+     *
+     * In that case, we want
+     *     (e->rte.stale_cycle > s->stale_valid) ||
+     *     (e->rte.stale_cycle < s->stale_set))
+     *
+     * Full logic table:
+     *
+     *    permutation   |  result  |  (S < V) + (S < SC) + (SC < V)
+     * -----------------+----------+---------------------------------
+     *   SC <   V <=  S  |   prune  |     0    +    0     +     1    =  1
+     *    S <  SC <   V  |   prune  |     1    +    1     +     1    =  3
+     *    V <=  S <  SC  |   prune  |     0    +    1     +     0    =  1
+     *   SC <=  S <   V  |    keep  |     1    +    0     +     1    =  2
+     *    V <= SC <=  S  |    keep  |     0    +    0     +     0    =  0
+     *    S <   V <= SC  |    keep  |     1    +    1     +     0    =  2
+     *
+     * Now the following code hopefully makes sense.
+     */
+
+      int sv = (s->stale_set < s->stale_valid);
+      int ssc = (s->stale_set < e->rte.stale_cycle);
+      int scv = (e->rte.stale_cycle < s->stale_valid);
+      stale = (sv + ssc + scv) & 1;
+    }
+
+    /* By the C standard, either the importer is flushing and stale_perm is 1,
+     * or by the table above, stale_perm is between 0 and 3, where even values
+     * say "keep" and odd values say "prune". */
+
+    if (stale)
     {
+      /* Announce withdrawal */
       struct netindex *i = RTE_GET_NETINDEX(&e->rte);
       rte_recalculate(tab, e->rte.sender, i, n, NULL, e->rte.src);
       return 1;