|
|
/* -*- coding: utf-8 -*-
* ---------------------------------------------------------------------- * Copyright © 2010-2012, RedJack, LLC. * All rights reserved. * * Please see the LICENSE.txt file in this distribution for license * details. * ---------------------------------------------------------------------- */
#include <string.h>
#include <libcork/core.h>
#include "ipset/bdd/nodes.h"
#include "ipset/bits.h"
#include "ipset/ipset.h"
#include "ipset/logging.h"
#define IPV4_BIT_SIZE 32
#define IPV6_BIT_SIZE 128
/* Forward declarations */
static void process_assignment(struct ipset_iterator *iterator);
static void expand_ipv6(struct ipset_iterator *iterator);
/**
* Find the highest non-EITHER bit in an assignment, starting from the * given bit index. */ static unsigned int find_last_non_either_bit(struct ipset_assignment *assignment, unsigned int starting_bit) { unsigned int i;
for (i = starting_bit; i >= 1; i--) { enum ipset_tribool value = ipset_assignment_get(assignment, i); if (value != IPSET_EITHER) { return i; } }
return 0; }
/**
* Create a generic IP address object from the current expanded * assignment. */ static void create_ip_address(struct ipset_iterator *iterator) { struct cork_ip *addr = &iterator->addr; struct ipset_expanded_assignment *exp = iterator->assignment_iterator;
/* Initialize the address to all 0 bits. */ memset(addr, 0, sizeof(struct cork_ip));
/* Check variable 0 to see if this is an IPv4 or IPv6 address. */ addr->version = IPSET_BIT_GET(exp->values.buf, 0)? 4: 6;
/* Copy bits from the expanded assignment. The number of bits to
* copy is given as the current netmask. We'll have calculated that * already based on the non-expanded assignment. */ unsigned int i; for (i = 0; i < iterator->cidr_prefix; i++) { IPSET_BIT_SET(&addr->ip, i, IPSET_BIT_GET(exp->values.buf, i+1)); }
#if IPSET_DEBUG
char buf[CORK_IP_STRING_LENGTH]; cork_ip_to_raw_string(addr, buf); DEBUG("Current IP address is %s/%u", buf, iterator->cidr_prefix); #endif
}
/**
* 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; } }
/**
* Process the current expanded assignment in the current BDD * assignment. */ static void process_expanded_assignment(struct ipset_iterator *iterator) { if (iterator->assignment_iterator->finished) { /* If there isn't anything in the expanded assignment, advance
* to the next BDD assignment. */
DEBUG("Expanded assignment is finished"); ipset_expanded_assignment_free(iterator->assignment_iterator); iterator->assignment_iterator = NULL; advance_assignment(iterator); } else { /* Otherwise, we've found a fully expanded assignment, so create
* an IP address for it and return. */ create_ip_address(iterator); } }
/**
* Expand the current assignment as IPv4 addresses. */ static void expand_ipv4(struct ipset_iterator *iterator) { unsigned int last_bit;
if (iterator->summarize) { last_bit = find_last_non_either_bit (iterator->bdd_iterator->assignment, IPV4_BIT_SIZE); DEBUG("Last non-either bit is %u", last_bit); } else { last_bit = IPV4_BIT_SIZE; }
iterator->assignment_iterator = ipset_assignment_expand (iterator->bdd_iterator->assignment, last_bit + 1); iterator->cidr_prefix = last_bit;
process_expanded_assignment(iterator); }
/**
* Expand the current assignment as IPv4 addresses. */ static void expand_ipv6(struct ipset_iterator *iterator) { unsigned int last_bit;
if (iterator->summarize) { last_bit = find_last_non_either_bit (iterator->bdd_iterator->assignment, IPV6_BIT_SIZE); DEBUG("Last non-either bit is %u", last_bit); } else { last_bit = IPV6_BIT_SIZE; }
iterator->assignment_iterator = ipset_assignment_expand (iterator->bdd_iterator->assignment, last_bit + 1); iterator->cidr_prefix = last_bit;
process_expanded_assignment(iterator); }
/**
* Process the current assignment in the BDD iterator. */
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; }
static struct ipset_iterator * create_iterator(struct ip_set *set, bool desired_value, bool summarize) { /* First allocate the iterator itself. */ struct ipset_iterator *iterator = cork_new(struct ipset_iterator); iterator->finished = false; iterator->assignment_iterator = NULL; iterator->desired_value = desired_value; iterator->summarize = summarize;
/* Then create the iterator that returns each BDD assignment. */ DEBUG("Iterating set"); iterator->bdd_iterator = ipset_node_iterate(set->cache, set->set_bdd);
/* Then drill down from the current BDD assignment, creating an
* expanded assignment for it. */ process_assignment(iterator); return iterator; }
struct ipset_iterator * ipset_iterate(struct ip_set *set, bool desired_value) { return create_iterator(set, desired_value, false); }
struct ipset_iterator * ipset_iterate_networks(struct ip_set *set, bool desired_value) { return create_iterator(set, desired_value, true); }
void ipset_iterator_free(struct ipset_iterator *iterator) { if (iterator->bdd_iterator != NULL) { ipset_bdd_iterator_free(iterator->bdd_iterator); } if (iterator->assignment_iterator != NULL) { ipset_expanded_assignment_free(iterator->assignment_iterator); } free(iterator); }
void ipset_iterator_advance(struct ipset_iterator *iterator) { /* If we're already at the end of the iterator, don't do anything. */
if (CORK_UNLIKELY(iterator->finished)) { return; }
/* Otherwise, advance the expanded assignment iterator to the next
* assignment, and then drill down into it. */
DEBUG("Advancing set iterator"); ipset_expanded_assignment_advance(iterator->assignment_iterator); process_expanded_assignment(iterator); }
|