IP sets

The functions in this section allow you to work with sets of IP addresses. IP sets work seamlessly with both IPv4 and IPv6 addresses.

Note

Before using any of the IP set functions, you must initialize this library using the following function.

int ipset_init_library(void)

Initializes the IP set library. This function must be called before any other function in the library. It is safe to call this function multiple times, and from multiple threads.

Creating and freeing sets

struct ip_set

A set of IP addresses. The fields of this struct are opaque; you should only use the public library functions to access the contents of the set.

There are two ways that you can work with IP sets. The first is that you can allocate the space for the ip_set instance yourself — for instance, directly on the stack. The second is to let the IP set library allocate the space for you. Your choice determines which of the following functions you will use to create and free your IP sets.

void ipset_init(struct ip_set \*set)
struct ip_set \*ipset_new(void)

Creates a new IP set. The init variant should be used if you’ve allocated space for the set yourself. The new variant should be used if you want the library to allocate the space for you. (The new variant never returns NULL; it will abort the program if the allocation fails.) In both cases, the set starts off empty.

void ipset_done(struct ip_set \*set)
void ipset_free(struct ip_set \*set)

Finalizes an IP set. The done variant must be used if you created the set using ipset_init(); the free variant must be used if you created the set using ipset_new().

Adding and removing elements

We provide a variety of functions for adding and removing addresses from an IP set.

bool ipset_ipv4_add(struct ip_set \*set, struct cork_ipv4 \*ip)
bool ipset_ipv6_add(struct ip_set \*set, struct cork_ipv6 \*ip)
bool ipset_ip_add(struct ip_set \*set, struct cork_ip \*ip)

Adds a single IP address to set. If the IP address was not already in the set, we return true; otherwise we return false. (In other words, we return whether the set changed as a result of this operation.)

bool ipset_ipv4_remove(struct ip_set \*set, struct cork_ipv4 \*ip)
bool ipset_ipv6_remove(struct ip_set \*set, struct cork_ipv6 \*ip)
bool ipset_ip_remove(struct ip_set \*set, struct cork_ip \*ip)

Removes a single IP removeress from set. If the IP address was previously in the set, we return true; otherwise we return false. (In other words, we return whether the set changed as a result of this operation.)

bool ipset_ipv4_add_network(struct ip_set \*set, struct cork_ipv4 \*ip, unsigned int cidr_prefix)
bool ipset_ipv6_add_network(struct ip_set \*set, struct cork_ipv6 \*ip, unsigned int cidr_prefix)
bool ipset_ip_add_network(struct ip_set \*set, struct cork_ip \*ip, unsigned int cidr_prefix)

Adds an entire CIDR network of IP addresses to set. ip is one of the addresses in the set; cidr_prefix is the number of bits in the network portion of each IP address in the CIDR network, as defined in RFC 4632. cidr_prefix must be in the range 0-32 (inclusive) if ip is an IPv4 address, and in the range 0-128 (inclusive) if it’s an IPv6 address.

We return whether the set changed as a result of this operation; if we return true, than at least one of the address in the CIDR network was not already present in set. We cannot currently distinguish whether all of the addresses were missing (and therefore added).

bool ipset_ipv4_remove_network(struct ip_set \*set, struct cork_ipv4 \*ip, unsigned int cidr_prefix)
bool ipset_ipv6_remove_network(struct ip_set \*set, struct cork_ipv6 \*ip, unsigned int cidr_prefix)
bool ipset_ip_remove_network(struct ip_set \*set, struct cork_ip \*ip, unsigned int cidr_prefix)

Removes an entire CIDR network of IP addresses from set. ip is one of the addresses in the set; cidr_prefix is the number of bits in the network portion of each IP address in the CIDR network, as defined in RFC 4632. cidr_prefix must be in the range 0-32 (inclusive) if ip is an IPv4 address, and in the range 0-128 (inclusive) if it’s an IPv6 address.

We return whether the set changed as a result of this operation; if we return true, than at least one of the address in the CIDR network was present in set. We cannot currently distinguish whether all of the addresses were present (and therefore removed).

Note

In all of the _network functions, if you want to strictly adhere to RFC 4632, ip can only have non-zero bits in its cidr_prefix uppermost bits. All of the lower-order bits (i.e., in the host portion of the IP address) must be set to 0. We do not enforce this, however.

Querying a set

bool ipset_contains_ipv4(const struct ip_set \*set, struct cork_ipv4 \*ip)
bool ipset_contains_ipv6(const struct ip_set \*set, struct cork_ipv6 \*ip)
bool ipset_contains_ip(const struct ip_set \*set, struct cork_ip \*ip)

Returns whether set contains ip.

bool ipset_is_empty(const struct ip_set \*set)

Returns whether set is empty.

bool ipset_is_equal(const struct ip_set \*set1, const struct ip_set \*set2)

Returns whether set1 and set2 contain exactly the same addresses.

size_t ipset_memory_size(const struct ip_set \*set)

Returns the number of bytes of memory needed to store set. Note that adding together the storage needed for each set you use doesn’t necessarily give you the total memory requirements, since some storage can be shared between sets.

Iterating through a set

In addition to querying individual addresses, you can iterate through the entire contents of an IP set. There are two iterator functions; one that provides every individual IP address, and one that collapses addresses into CIDR networks as much as possible, and returns those networks.

Note

You should not modify an IP set while you’re actively iterating through its contents; if you do this, you’ll get undefined behavior.

struct ipset_iterator

An iterator object that lets you query all of the addresses in an IP set.

struct cork_ip addr

If iterating through individual addresses, this contains the address that the iterator currently points at. If iterating through CIDR networks, this is the representative address of the current network.

unsigned int cidr_prefix

If iterating through CIDR networks, this is the CIDR prefix of the current network. If iterating through individual IP addresses, this will always be 32 or 128, depending on whether addr contains an IPv4 or IPv6 address.

struct ipset_iterator \*ipset_iterate(struct ip_set \*set, bool desired_value)
struct ipset_iterator \*ipset_iterate_networks(struct ip_set \*set, bool desired_value)

If desired_value is true, then we return an iterator that will produce the IP addresses that are present in set. If it’s false, then the iterator will produce the IP addresses that are not in set.

The _networks variant will summarize the IP addresses into CIDR networks, to reduce the number of items that are reported by the iterator. (This can be especially useful (necessary?) if your set contains any /8 or /16 IPv4 networks, for instance; or even worse, a /64 IPv6 network.)

void ipset_iterator_advance(struct ipset_iterator \*iterator)

Advance iterator to the next IP address or network in its underlying set.

void ipset_iterator_free(struct ipset_iterator \*iterator)

Frees an IP set iterator.

Storing sets in files

The functions in this section allow you to store IP sets on disk, and reload them into another program at a later time. You don’t have to know the details of the file format to be able to use these functions; we guarantee that sets written with previous versions of the library will be readable by later versions of the library (but not vice versa). And we guarantee that the file format is platform-independent; sets written on any machine will be readable on any other machine.

(That said, if you do want to know the details of the file format, that’s documented in another section.)

int ipset_save(FILE \*stream, const struct ip_set \*set)

Saves an IP set into stream. You’re responsible for opening stream before calling this function, and for closing stream afterwards. If there are any errors writing the set, we return -1 and fill in a libcork error condition.

int ipset_save_to_stream(struct cork_stream_consumer \*stream, const struct ip_set \*set)

Saves an IP set into a libcork stream consumer. If there are any errors writing the set, we return -1 and fill in a libcork error condition.

struct ip_set \*ipset_load(FILE \*stream)

Loads an IP set from stream. You’re responsible for opening stream before calling this function, and for closing stream afterwards. If there are any errors reading the set, we return NULL and fill in a libcork error condition. You must use ipset_free() to free the set when you’re done with it.

int ipset_save_dot(FILE \*stream, const struct ip_set \*set)

Produces a GraphViz dot representation of the BDD graph used to store set, and writes this graph representation to stream. You’re responsible for opening stream before calling this function, and for closing stream afterwards. If there are any errors writing the set, we return -1 and fill in a libcork error condition.