Beispiel #1
0
void
ipset_bdd_iterator_advance(struct ipset_bdd_iterator *iterator)
{
    /* If we're already at the end of the iterator, don't do anything. */
    if (CORK_UNLIKELY(iterator->finished)) {
        return;
    }

    /* We look at the last node in the stack.  If it's currently
     * assigned a false value, then we track down its true branch.  If
     * it's got a true branch, then we pop it off and check the next to
     * last node. */

    DEBUG("Advancing BDD iterator");

    while (cork_array_size(&iterator->stack) > 0) {
        ipset_node_id  last_node_id =
            cork_array_at
            (&iterator->stack, cork_array_size(&iterator->stack) - 1);

        struct ipset_node  *last_node =
            ipset_node_cache_get_nonterminal(iterator->cache, last_node_id);

        enum ipset_tribool  current_value =
            ipset_assignment_get(iterator->assignment, last_node->variable);

        /* The current value can't be EITHER, because we definitely
         * assign a TRUE or FALSE to the variables of the nodes that we
         * encounter. */
        if (current_value == IPSET_TRUE) {
            /* We've checked both outgoing edges for this node, so pop
             * it off and look at its parent. */
            iterator->stack.size--;

            /* Before continuing, reset this node's variable to
             * indeterminate in the assignment. */
            ipset_assignment_set
                (iterator->assignment, last_node->variable, IPSET_EITHER);
        } else {
            /* We've checked this node's low edge, but not its high
             * edge.  Set the variable to TRUE in the assignment, and
             * add the high edge's node to the node stack. */
            ipset_assignment_set
                (iterator->assignment, last_node->variable, IPSET_TRUE);
            add_node(iterator, last_node->high);
            return;
        }
    }

    /* If we fall through then we ran out of nodes to check.  That means
     * the iterator is done! */
    iterator->finished = true;
}
Beispiel #2
0
/**
 * Advance the BDD iterator, taking into account that some assignments
 * need to be expanded twice.
 */
static void
advance_assignment(struct ipset_iterator *iterator)
{
    /* Check the current state of the iterator to determine how to
     * advance. */

    /* In most cases, the assignment we just finished only needed to be
     * expanded once.  So we move on to the next assignment and process
     * it. */
    if (CORK_LIKELY(iterator->multiple_expansion_state ==
                    IPSET_ITERATOR_NORMAL))
    {
        ipset_bdd_iterator_advance(iterator->bdd_iterator);
        process_assignment(iterator);
        return;
    }

    /* If the assignment needs to be expanded twice, we'll do the IPv4
     * expansion first.  If that's what we've just finished, do the IPv6
     * expansion next. */

    if (iterator->multiple_expansion_state == IPSET_ITERATOR_MULTIPLE_IPV4) {
        DEBUG("Expanding IPv6 second");

        iterator->multiple_expansion_state = IPSET_ITERATOR_MULTIPLE_IPV6;
        ipset_assignment_set
            (iterator->bdd_iterator->assignment, 0, IPSET_FALSE);
        expand_ipv6(iterator);
        return;
    }

    /* If we've just finished the IPv6 expansion, then we've finished
     * with this assignment.  Before moving on to the next one, we have
     * to reset variable 0 to EITHER (which it was before we started
     * this whole mess). */

    if (iterator->multiple_expansion_state == IPSET_ITERATOR_MULTIPLE_IPV6) {
        DEBUG("Finished both expansions");

        ipset_assignment_set
            (iterator->bdd_iterator->assignment, 0, IPSET_EITHER);
        ipset_bdd_iterator_advance(iterator->bdd_iterator);
        process_assignment(iterator);
        return;
    }
}
Beispiel #3
0
END_TEST


START_TEST(test_bdd_assignment_cut_1)
{
    ipset_assignment_t  *a1;
    ipset_assignment_t  *a2;

    a1 = ipset_assignment_new();
    ipset_assignment_set(a1, 0, IPSET_TRUE);
    ipset_assignment_set(a1, 1, IPSET_FALSE);

    a2 = ipset_assignment_new();
    ipset_assignment_set(a2, 0, IPSET_TRUE);
    ipset_assignment_set(a2, 1, IPSET_FALSE);
    ipset_assignment_set(a2, 2, IPSET_TRUE);
    ipset_assignment_set(a2, 3, IPSET_TRUE);
    ipset_assignment_set(a2, 4, IPSET_FALSE);

    ipset_assignment_cut(a2, 2);

    fail_unless(ipset_assignment_equal(a1, a2),
                "Assignments should be equal");

    ipset_assignment_free(a1);
    ipset_assignment_free(a2);
}
Beispiel #4
0
END_TEST


START_TEST(test_bdd_assignment_expand_3)
{
    ipset_assignment_t  *a;

    a = ipset_assignment_new();
    ipset_assignment_set(a, 0, TRUE);
    ipset_assignment_set(a, 2, FALSE);

    ipset_expanded_assignment_t  *it;
    it = ipset_assignment_expand(a, 3);

    GByteArray  *ea = g_byte_array_sized_new(1);
    memset(ea->data, 0, 1);

    fail_if(it->finished,
            "Expanded assignment shouldn't be empty");
    IPSET_BIT_SET(ea->data, 0, TRUE);
    IPSET_BIT_SET(ea->data, 1, FALSE);
    IPSET_BIT_SET(ea->data, 2, FALSE);
    fail_unless(memcmp(ea->data, it->values->data, 1) == 0,
                "Expanded assignment 1 doesn't match");

    ipset_expanded_assignment_advance(it);
    fail_if(it->finished,
                "Expanded assignment should have at least 1 element");
    IPSET_BIT_SET(ea->data, 0, TRUE);
    IPSET_BIT_SET(ea->data, 1, TRUE);
    IPSET_BIT_SET(ea->data, 2, FALSE);
    fail_unless(memcmp(ea->data, it->values->data, 1) == 0,
                "Expanded assignment 2 doesn't match");

    ipset_expanded_assignment_advance(it);
    fail_unless(it->finished,
                "Expanded assignment should have 2 elements");

    g_byte_array_free(ea, TRUE);
    ipset_expanded_assignment_free(it);
    ipset_assignment_free(a);
}
Beispiel #5
0
/**
 * Add the given node ID to the node stack, and trace down from it
 * until we find a terminal node.  Assign values to the variables for
 * each nonterminal that encounter along the way.  We check low edges
 * first, so each new variable we encounter will be assigned FALSE.
 * (The high edges will be checked eventually by a call to the
 * ipset_bdd_iterator_advance() function.)
 */
static void
add_node(struct ipset_bdd_iterator *iterator, ipset_node_id node_id)
{
    /* Keep tracing down low edges until we reach a terminal. */
    while (ipset_node_get_type(node_id) == IPSET_NONTERMINAL_NODE) {
        /* Add this nonterminal node to the stack, and trace down
         * further into the tree.  We check low edges first, so set the
         * node's variable to FALSE in the assignment. */
        struct ipset_node  *node =
            ipset_node_cache_get_nonterminal(iterator->cache, node_id);

        cork_array_append(&iterator->stack, node_id);
        ipset_assignment_set(iterator->assignment, node->variable, false);

        node_id = node->low;
    }

    /* Once we find a terminal node, save it away in the iterator result
     * and return. */
    iterator->value = ipset_terminal_value(node_id);
}
Beispiel #6
0
static void
process_assignment(struct ipset_iterator *iterator)
{
    while (!iterator->bdd_iterator->finished) {
        if (iterator->bdd_iterator->value == iterator->desired_value) {
            /* If the BDD iterator hasn't finished, and the result of
             * the function with this assignment matches what the caller
             * wants, then we've found an assignment to generate IP
             * addresses from.
             *
             * Try to expand this assignment, and process the first
             * expanded assignment.  We want 32 + 1 variables if the
             * current address is IPv4; 128 + 1 if it's IPv6. */

            DEBUG("Got a matching BDD assignment");
            enum ipset_tribool  address_type = ipset_assignment_get
                (iterator->bdd_iterator->assignment, 0);

            if (address_type == IPSET_FALSE) {
                /* FALSE means IPv6*/
                DEBUG("Assignment is IPv6");
                iterator->multiple_expansion_state = IPSET_ITERATOR_NORMAL;
                expand_ipv6(iterator);
                return;
            } else if (address_type == IPSET_TRUE) {
                /* TRUE means IPv4*/
                DEBUG("Assignment is IPv4");
                iterator->multiple_expansion_state = IPSET_ITERATOR_NORMAL;
                expand_ipv4(iterator);
                return;
            } else {
                /* EITHER means that this assignment contains both IPv4
                 * and IPv6 addresses.  Expand it as IPv4 first. */
                DEBUG("Assignment is both IPv4 and IPv6");
                DEBUG("Expanding IPv4 first");
                iterator->multiple_expansion_state =
                    IPSET_ITERATOR_MULTIPLE_IPV4;
                ipset_assignment_set
                    (iterator->bdd_iterator->assignment, 0, IPSET_TRUE);
                expand_ipv4(iterator);
                return;
            }
        }

        /* The BDD iterator has a value, but it doesn't match the one we
         * want.  Advance the BDD iterator and try again. */
        DEBUG("Value is %d, skipping", iterator->bdd_iterator->value);
        ipset_bdd_iterator_advance(iterator->bdd_iterator);
    }

    /* If we fall through, then the BDD iterator has finished.  That
     * means there's nothing left for the set iterator. */

    DEBUG("Set iterator is finished");
    ipset_expanded_assignment_free(iterator->assignment_iterator);
    iterator->assignment_iterator = NULL;

    ipset_bdd_iterator_free(iterator->bdd_iterator);
    iterator->bdd_iterator = NULL;
    iterator->finished = true;
}