/*
* BIRD Internet Routing Daemon -- Route aggregation
*
- * (c) 2023--2024 Igor Putovny <igor.putovny@nic.cz>
- * (c) 2024 CZ.NIC, z.s.p.o.
+ * (c) 2023--2025 Igor Putovny <igor.putovny@nic.cz>
+ * (c) 2025 CZ.NIC, z.s.p.o.
*
* Can be freely distributed and used under the terms of the GNU GPL.
*/
* Aggregation of routes for networks means that for each destination, routes
* with the same values of attributes will be aggregated into a single
* multi-path route. Aggregation is performed by inserting routes into a hash
- * table based on values of their attributes and egenrating new routes from
+ * table based on values of their attributes and generating new routes from
* the routes in th same bucket. Buckets are represented by @aggregator_bucket,
* which contains linked list of @aggregator_route.
*
* algorithm. This algorithm uses a binary tree representation of the routing
* table. An edge from the parent node to its left child represents bit 0, and
* an edge from the parent node to its right child represents bit 1 as the
- * prefix is traversed from the most to the least significant bit. Leaf node
+ * prefix is traversed from the most to the least significant bit. Last node
* of every prefix contains pointer to @aggregator_bucket where the route for
* this prefix belongs.
*
* are carried to the parent node. Otherwise, all of children's buckets are
* carried to the parent node.
*
- * The third pass moves down the tree, selecting a bucket for the prefix and
- * removing redundant routes. The node inherits a bucket from the closest
- * ancestor node that has a bucket (except for the root node). If the inherited
- * bucket is a member of the node's set of potential buckets, then the node
- * does not need a bucket. Otherwise, the node does need a bucket and any of
- * its potential buckets can be chosen. All leaves which have not been assigned
- * a bucket are removed.
+ * The third pass moves down the trie while deciding which prefixes will be
+ * exported to the FIB. The node inherits a bucket from the closest ancestor
+ * that has a bucket. If the inherited bucket is one of potential buckets of
+ * this node, then this node does not need a bucket and its prefix will not
+ * be in FIB. Otherwise the node does need a bucket and any of its potential
+ * buckets can be chosen. We always choose the bucket with the lowest ID, but
+ * other options are possible.
*
- * The algorithm works on the assumption that there is a default route, that is,
- * the null prefix at the root node has a bucket. This route is created before
- * the aggregation starts.
- *
- * Incorporation of incremental updates of routes has not been implemented yet.
- * The whole trie is rebuilt and aggregation runs all over again when enough
- * updates are collected. To achieve this, the aggregator uses a settle timer
- * configured with two intervals, @min and @max. User can specify these
- * intervals in the configuration file. After receiving an update, settle timer
- * is kicked. If no update is received for interval @min or if @max interval is
- * exceeded, timer triggers and refeed of the source channel is requested. When
- * the refeed ends, all prefixes are inserted into the trie and aggregation
- * algorithm proceeds.
+ * The algorithm works with the assumption that there is a default route, that is,
+ * the null prefix at the root node has a bucket.
*
* Memory for the aggregator is allocated from three linpools: one for buckets,
* one for routes and one for trie used in prefix aggregation. Obviously, trie
* after prefix aggregation is finished, thus destroying all data structures
* used.
*
+ * Aggregator is capable of processing incremental updates. After receiving
+ * an update, which can be either announce or withdraw, corresponding node
+ * is found in the trie and its original bucket is updated.
+ * The trie now needs to be recomputed to reflect this update. We go from
+ * updated node upwards until we find its closest IN_FIB ancestor.
+ * This is the prefix node that covers an address space which is affected
+ * by received update. The whole subtree rooted at this node is deaggregated,
+ * which means deleting all information computed by aggregation algorithm.
+ * This is followed by second pass which propagates potential buckets from
+ * the leaves upwards. Merging of sets of potential buckets continues upwards
+ * until the node's set is not changed by this operation. Then, third pass is
+ * run from this node, finishing the aggregation. During third pass, any change
+ * in prefix FIB status is detected and based on this, new route is exported
+ * or is removed from the routing table, respectively. All new routes are
+ * exported immmediately, however, all routes to be withdrawed are pushed on
+ * the stack and are removed after recomputing the trie.
+ *
+ * From a practical point of view, our implementation differs a little bit from
+ * the algorithm as it was described in the original paper.
+ * During first pass, the trie is normalized by adding new nodes so that every
+ * node has either zero or two children. These nodes are used only for
+ * computing of node's set of potential buckets from the sets of its children.
+ * We can therefore afford to not add these nodes to save both time and memory.
+ * Another change is performing the work previously done in the first pass
+ * during second pass, saving one trie traversal.
*/
#undef LOCAL_DEBUG
}
/*
- * Prepare route withdrawal for @prefix
+ * Prepare to withdraw route for @prefix
*/
static void
aggregator_prepare_rte_withdrawal(struct aggregator_proto *p, ip_addr prefix, u32 pxlen, struct aggregator_bucket *bucket)
*pxlen = len;
}
-
-
-
-
-
-
/*
* Second pass of Optimal Route Table Construction (ORTC) algorithm
*/
/*
* Keep information whether this prefix was original. If not, then its origin
- * is changed to aggregated, because it's going to FIB.
+ * is changed to aggregated, because algorithm decided it's going to FIB.
*/
node->px_origin = (ORIGINAL == node->px_origin) ? ORIGINAL : AGGREGATED;
node->status = IN_FIB;
* both time and memory.
* On the other hand, nodes may be removed from the trie during third pass,
* if they do not have bucket on their own and inherit one from their
- * ancestors (and thus are not needed in FIB).
- * Nodes do get bucket if the bucket inherited from their ancestors is NOT
+ * ancestors instead (and thus are not needed in FIB).
+ * Nodes get bucket if the bucket inherited from their ancestors is NOT
* one of their potential buckets. In this case, we need to add these nodes
* to the trie.
*/
/*
* Root node is initialized with NON_FIB status.
- * Default route will be created during third pass.
+ * Default route will be exported during first aggregation run.
*/
*p->root = (struct trie_node) {
.original_bucket = new_bucket,