Filter: Update trie documentation

This commit is contained in:
Ondrej Zajicek (work) 2020-04-06 14:20:16 +02:00
parent 562a2b8c29
commit dd61278c9d

View file

@ -1,7 +1,8 @@
/* /*
* Filters: Trie for prefix sets * Filters: Trie for prefix sets
* *
* Copyright 2009 Ondrej Zajicek <santiago@crfreenet.org> * (c) 2009--2020 Ondrej Zajicek <santiago@crfreenet.org>
* (c) 2009--2020 CZ.NIC z.s.p.o.
* *
* Can be freely distributed and used under the terms of the GNU GPL. * Can be freely distributed and used under the terms of the GNU GPL.
*/ */
@ -9,53 +10,68 @@
/** /**
* DOC: Trie for prefix sets * DOC: Trie for prefix sets
* *
* We use a (compressed) trie to represent prefix sets. Every node * We use a (compressed) trie to represent prefix sets. Every node in the trie
* in the trie represents one prefix (&addr/&plen) and &plen also * represents one prefix (&addr/&plen) and &plen also indicates the index of
* indicates the index of the bit in the address that is used to * bits in the address that are used to branch at the node. Note that such
* branch at the node. If we need to represent just a set of * prefix is not necessary a member of the prefix set, it is just a canonical
* prefixes, it would be simple, but we have to represent a * prefix associated with a node. Prefix lengths of nodes are aligned to
* set of prefix patterns. Each prefix pattern consists of * multiples of &TRIE_STEP (4) and there is 16-way branching in each
* &ppaddr/&pplen and two integers: &low and &high, and a prefix * node. Therefore, we say that a node is associated with a range of prefix
* &paddr/&plen matches that pattern if the first MIN(&plen, &pplen) * lengths (&plen .. &plen + TRIE_STEP - 1).
* bits of &paddr and &ppaddr are the same and &low <= &plen <= &high.
* *
* We use a bitmask (&accept) to represent accepted prefix lengths * The prefix set is not just a set of prefixes, it is defined by a set of
* at a node. As there are 33 prefix lengths (0..32 for IPv4), but * prefix patterns. Each prefix pattern consists of &ppaddr/&pplen and two
* there is just one prefix of zero length in the whole trie so we * integers: &low and &high. The tested prefix &paddr/&plen matches that pattern
* have &zero flag in &f_trie (indicating whether the trie accepts * if the first MIN(&plen, &pplen) bits of &paddr and &ppaddr are the same and
* prefix 0.0.0.0/0) as a special case, and &accept bitmask * &low <= &plen <= &high.
*
* There are two ways to represent accepted prefixes for a node. First, there is
* a bitmask &local, which represents independently all 15 prefixes that extend
* the canonical prefix of the node and are within a range of prefix lengths
* associated with the node. E.g., for node 10.0.0.0/8 they are 10.0.0.0/8,
* 10.0.0.0/9, 10.128.0.0/9, .. 10.224.0.0/11. This order (first by length, then
* lexicographically) is used for indexing the bitmask &local, starting at
* position 1. I.e., index is 2^(plen - base) + offset within the same length,
* see function trie_local_mask6() for details.
*
* Second, we use a bitmask &accept to represent accepted prefix lengths at a
* node. The bit is set means that all prefixes of given length that are either
* subprefixes or superprefixes of the canonical prefix are accepted. As there
* are 33 prefix lengths (0..32 for IPv4), but there is just one prefix of zero
* length in the whole trie so we have &zero flag in &f_trie (indicating whether
* the trie accepts prefix 0.0.0.0/0) as a special case, and &accept bitmask
* represents accepted prefix lengths from 1 to 32. * represents accepted prefix lengths from 1 to 32.
* *
* There are two cases in prefix matching - a match when the length * One complication is handling of prefix patterns with unaligned prefix length.
* of the prefix is smaller that the length of the prefix pattern, * When such pattern is to be added, we add a primary node above (with rounded
* (&plen < &pplen) and otherwise. The second case is simple - we * down prefix length &nlen) and a set of secondary nodes below (with rounded up
* just walk through the trie and look at every visited node * prefix lengths &slen). Accepted prefix lengths of the original prefix pattern
* whether that prefix accepts our prefix length (&plen). The * are then represented in different places based on their lengths. For prefixes
* first case is tricky - we don't want to examine every descendant * shorter than &nlen, it is &accept bitmask of the primary node, for prefixes
* of a final node, so (when we create the trie) we have to propagate * between &nlen and &slen - 1 it is &local bitmask of the primary node, and for
* that information from nodes to their ascendants. * prefixes longer of equal &slen it is &accept bitmasks of secondary nodes.
* *
* Suppose that we have two masks (M1 and M2) for a node. Mask M1 * There are two cases in prefix matching - a match when the length of the
* represents accepted prefix lengths by just the node and mask M2 * prefix is smaller that the length of the prefix pattern, (&plen < &pplen) and
* represents accepted prefix lengths by the node or any of its * otherwise. The second case is simple - we just walk through the trie and look
* descendants. Therefore M2 is a bitwise or of M1 and children's * at every visited node whether that prefix accepts our prefix length (&plen).
* M2 and this is a maintained invariant during trie building. * The first case is tricky - we do not want to examine every descendant of a
* Basically, when we want to match a prefix, we walk through the trie, * final node, so (when we create the trie) we have to propagate that
* check mask M1 for our prefix length and when we came to * information from nodes to their ascendants.
* final node, we check mask M2.
* *
* There are two differences in the real implementation. First, * There are two kinds of propagations - propagation from child's &accept
* we use a compressed trie so there is a case that we skip our * bitmask to parent's &accept bitmask, and propagation from child's &accept
* final node (if it is not in the trie) and we came to node that * bitmask to parent's &local bitmask. The first kind is simple - as all
* is either extension of our prefix, or completely out of path * superprefixes of a parent are also all superprefixes of appropriate length of
* In the first case, we also have to check M2. * a child, then we can just add (by bitwise or) a child &accept mask masked by
* parent prefix length mask to the parent &accept mask. This handles prefixes
* shorter than node &plen.
* *
* Second, we really need not to maintain two separate bitmasks. * The second kind of propagation is necessary to handle superprefixes of a
* Checks for mask M1 are always larger than &applen and we need * child that are represented by parent &local mask - that are in the range of
* just the first &pplen bits of mask M2 (if trie compression * prefix lengths associated with the parent. For each accepted (by child
* hadn't been used it would suffice to know just $applen-th bit), * &accept mask) prefix length from that range, we need to set appropriate bit
* so we have to store them together in &accept mask - the first * in &local mask. See function trie_amask_to_local() for details.
* &pplen bits of mask M2 and then mask M1.
* *
* There are four cases when we walk through a trie: * There are four cases when we walk through a trie:
* *
@ -65,8 +81,7 @@
* - we are beyond the end of path (node length > &plen) * - we are beyond the end of path (node length > &plen)
* - we are still on path and keep walking (node length < &plen) * - we are still on path and keep walking (node length < &plen)
* *
* The walking code in trie_match_prefix() is structured according to * The walking code in trie_match_net() is structured according to these cases.
* these cases.
*/ */
#include "nest/bird.h" #include "nest/bird.h"
@ -166,6 +181,10 @@ attach_node(struct f_trie_node *parent, struct f_trie_node *child, int v4)
} }
/*
* Compute appropriate mask representing prefix px/plen in local bitmask of node
* with prefix length nlen. Assuming that nlen <= plen < (nlen + TRIE_STEP).
*/
static inline uint static inline uint
trie_local_mask4(ip4_addr px, uint plen, uint nlen) trie_local_mask4(ip4_addr px, uint plen, uint nlen)
{ {
@ -182,6 +201,12 @@ trie_local_mask6(ip6_addr px, uint plen, uint nlen)
return 1u << pos; return 1u << pos;
} }
/*
* Compute an appropriate local mask (for a node with prefix length nlen)
* representing prefixes of px that are accepted by amask and fall within the
* range associated with that node. Used for propagation of child accept mask
* to parent local mask.
*/
static inline uint static inline uint
trie_amask_to_local(ip_addr px, ip_addr amask, uint nlen) trie_amask_to_local(ip_addr px, ip_addr amask, uint nlen)
{ {