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; }
/** * 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; } }
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); }
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); }
/** * 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); }
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; }