You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

342 lines
10 KiB

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