├── docs ├── _static │ ├── .keep │ ├── pygments.css │ └── docco-sphinx.css ├── _templates │ └── .keep ├── .gitattributes ├── index.rst ├── conf.py ├── CMakeLists.txt ├── commands.rst ├── maps.rst ├── file-format.rst └── sets.rst ├── tests ├── dot │ ├── empty │ │ ├── in │ │ ├── err │ │ ├── command │ │ └── out │ ├── universe │ │ ├── err │ │ ├── command │ │ ├── in │ │ └── out │ ├── 10.0.5.64-30 │ │ ├── err │ │ ├── command │ │ ├── in │ │ └── out │ └── command ├── errors │ ├── empty-cidr │ │ ├── out │ │ ├── in │ │ ├── command │ │ └── err │ ├── ipsetcat-no-input │ │ ├── in │ │ ├── out │ │ ├── command │ │ └── err │ ├── ipsetdot-no-input │ │ ├── in │ │ ├── out │ │ ├── command │ │ └── err │ ├── nonnumeric-cidr │ │ ├── out │ │ ├── in │ │ ├── command │ │ └── err │ ├── ipsetbuild-no-input │ │ ├── in │ │ ├── out │ │ ├── command │ │ └── err │ ├── ipsetbuild-no-output │ │ ├── in │ │ ├── out │ │ ├── command │ │ └── err │ ├── ipsetcat-empty-input │ │ ├── in │ │ ├── out │ │ ├── command │ │ └── err │ ├── ipsetdot-empty-input │ │ ├── in │ │ ├── out │ │ ├── command │ │ └── err │ ├── ipsetbuild-file-not-found │ │ ├── in │ │ ├── out │ │ ├── command │ │ └── err │ ├── ipsetcat-file-not-found │ │ ├── in │ │ ├── out │ │ ├── command │ │ └── err │ └── ipsetdot-file-not-found │ │ ├── in │ │ ├── out │ │ ├── command │ │ └── err ├── round-trip │ ├── empty │ │ ├── err │ │ ├── in │ │ ├── out │ │ └── command │ ├── 10.0.5.64-30 │ │ ├── err │ │ ├── command │ │ ├── out │ │ └── in │ ├── universe │ │ ├── err │ │ ├── command │ │ ├── in │ │ └── out │ ├── idempotent-01a │ │ ├── err │ │ ├── command │ │ ├── out │ │ └── in │ ├── idempotent-01b │ │ ├── err │ │ ├── command │ │ ├── out │ │ └── in │ ├── idempotent-02a │ │ ├── err │ │ ├── command │ │ ├── out │ │ └── in │ ├── idempotent-02b │ │ ├── err │ │ ├── command │ │ ├── out │ │ └── in │ └── command ├── .gitignore ├── .gitattributes ├── ccram ├── CMakeLists.txt ├── test-assignment.c └── test-iterator.c ├── examples ├── .gitignore ├── CMakeLists.txt └── ipv4-set-size.c ├── .buzzy ├── links.yaml └── package.yaml ├── .gitignore ├── AUTHORS ├── src ├── ipset.pc.in ├── libipset │ ├── general.c │ ├── set │ │ ├── allocation.c │ │ ├── ipv4_set.c │ │ ├── ipv6_set.c │ │ ├── inspection.c │ │ ├── storage.c │ │ ├── inspection-template.c.in │ │ └── iterator.c │ ├── map │ │ ├── allocation.c │ │ ├── ipv4_map.c │ │ ├── ipv6_map.c │ │ ├── inspection.c │ │ ├── inspection-template.c.in │ │ └── storage.c │ └── bdd │ │ ├── reachable.c │ │ ├── assignments.c │ │ ├── expanded.c │ │ ├── bdd-iterator.c │ │ └── read.c ├── CMakeLists.txt ├── ipsetdot │ └── ipsetdot.c └── ipsetcat │ └── ipsetcat.c ├── share └── CMakeLists.txt ├── include ├── CMakeLists.txt └── ipset │ ├── errors.h │ ├── logging.h │ ├── bits.h │ └── ipset.h ├── README.markdown ├── .travis.yml ├── INSTALL ├── run.sh ├── COPYING ├── cmake ├── FindParseArguments.cmake ├── FindPrereqs.cmake └── FindCTargets.cmake └── CMakeLists.txt /docs/_static/.keep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /tests/dot/empty/in: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /docs/_templates/.keep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /tests/dot/empty/err: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /tests/dot/universe/err: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /tests/dot/10.0.5.64-30/err: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /tests/errors/empty-cidr/out: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /tests/round-trip/empty/err: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /tests/round-trip/empty/in: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /tests/round-trip/empty/out: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /tests/dot/empty/command: -------------------------------------------------------------------------------- 1 | ../command -------------------------------------------------------------------------------- /tests/errors/ipsetcat-no-input/in: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /tests/errors/ipsetdot-no-input/in: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /tests/errors/nonnumeric-cidr/out: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /tests/round-trip/10.0.5.64-30/err: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /tests/round-trip/universe/err: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /examples/.gitignore: -------------------------------------------------------------------------------- 1 | ipv4-set-size 2 | -------------------------------------------------------------------------------- /tests/dot/universe/command: -------------------------------------------------------------------------------- 1 | ../command -------------------------------------------------------------------------------- /tests/errors/ipsetbuild-no-input/in: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /tests/errors/ipsetbuild-no-input/out: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /tests/errors/ipsetbuild-no-output/in: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /tests/errors/ipsetbuild-no-output/out: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /tests/errors/ipsetcat-empty-input/in: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /tests/errors/ipsetcat-empty-input/out: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /tests/errors/ipsetcat-no-input/out: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /tests/errors/ipsetdot-empty-input/in: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /tests/errors/ipsetdot-empty-input/out: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /tests/errors/ipsetdot-no-input/out: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /tests/round-trip/idempotent-01a/err: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /tests/round-trip/idempotent-01b/err: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /tests/round-trip/idempotent-02a/err: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /tests/round-trip/idempotent-02b/err: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /tests/dot/10.0.5.64-30/command: -------------------------------------------------------------------------------- 1 | ../command -------------------------------------------------------------------------------- /tests/errors/empty-cidr/in: -------------------------------------------------------------------------------- 1 | 10.0.0.0/ 2 | -------------------------------------------------------------------------------- /tests/errors/ipsetbuild-file-not-found/in: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /tests/errors/ipsetbuild-file-not-found/out: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /tests/errors/ipsetcat-file-not-found/in: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /tests/errors/ipsetcat-file-not-found/out: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /tests/errors/ipsetdot-file-not-found/in: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /tests/errors/ipsetdot-file-not-found/out: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /tests/round-trip/empty/command: -------------------------------------------------------------------------------- 1 | ../command -------------------------------------------------------------------------------- /tests/round-trip/universe/command: -------------------------------------------------------------------------------- 1 | ../command -------------------------------------------------------------------------------- /tests/.gitignore: -------------------------------------------------------------------------------- 1 | test-ipmap 2 | test-ipset 3 | -------------------------------------------------------------------------------- /tests/dot/universe/in: -------------------------------------------------------------------------------- 1 | 0.0.0.0/0 2 | ::/0 3 | -------------------------------------------------------------------------------- /tests/errors/nonnumeric-cidr/in: -------------------------------------------------------------------------------- 1 | 10.0.0.0/8o 2 | -------------------------------------------------------------------------------- /tests/round-trip/10.0.5.64-30/command: -------------------------------------------------------------------------------- 1 | ../command -------------------------------------------------------------------------------- /tests/dot/empty/out: -------------------------------------------------------------------------------- 1 | strict digraph bdd { 2 | } 3 | -------------------------------------------------------------------------------- /tests/round-trip/idempotent-01a/command: -------------------------------------------------------------------------------- 1 | ../command -------------------------------------------------------------------------------- /tests/round-trip/idempotent-01b/command: -------------------------------------------------------------------------------- 1 | ../command -------------------------------------------------------------------------------- /tests/round-trip/idempotent-02a/command: -------------------------------------------------------------------------------- 1 | ../command -------------------------------------------------------------------------------- /tests/round-trip/idempotent-02b/command: -------------------------------------------------------------------------------- 1 | ../command -------------------------------------------------------------------------------- /tests/round-trip/universe/in: -------------------------------------------------------------------------------- 1 | 0.0.0.0/0 2 | ::/0 3 | -------------------------------------------------------------------------------- /tests/round-trip/universe/out: -------------------------------------------------------------------------------- 1 | 0.0.0.0/0 2 | ::/0 3 | -------------------------------------------------------------------------------- /tests/errors/empty-cidr/command: -------------------------------------------------------------------------------- 1 | src/ipsetbuild - -o - 2 | -------------------------------------------------------------------------------- /tests/errors/ipsetcat-no-input/command: -------------------------------------------------------------------------------- 1 | src/ipsetcat 2 | -------------------------------------------------------------------------------- /tests/errors/ipsetdot-no-input/command: -------------------------------------------------------------------------------- 1 | src/ipsetdot 2 | -------------------------------------------------------------------------------- /tests/round-trip/idempotent-01a/out: -------------------------------------------------------------------------------- 1 | 192.168.0.0/23 2 | -------------------------------------------------------------------------------- /tests/round-trip/idempotent-01b/out: -------------------------------------------------------------------------------- 1 | 192.168.0.0/23 2 | -------------------------------------------------------------------------------- /tests/round-trip/idempotent-02a/out: -------------------------------------------------------------------------------- 1 | 192.168.0.0/24 2 | -------------------------------------------------------------------------------- /tests/round-trip/idempotent-02b/out: -------------------------------------------------------------------------------- 1 | 192.168.0.0/24 2 | -------------------------------------------------------------------------------- /tests/dot/command: -------------------------------------------------------------------------------- 1 | src/ipsetbuild -o - - | src/ipsetdot - 2 | -------------------------------------------------------------------------------- /tests/errors/ipsetbuild-no-input/command: -------------------------------------------------------------------------------- 1 | src/ipsetbuild 2 | -------------------------------------------------------------------------------- /tests/errors/ipsetbuild-no-output/command: -------------------------------------------------------------------------------- 1 | src/ipsetbuild - 2 | -------------------------------------------------------------------------------- /tests/errors/ipsetcat-empty-input/command: -------------------------------------------------------------------------------- 1 | src/ipsetcat - 2 | -------------------------------------------------------------------------------- /tests/errors/ipsetdot-empty-input/command: -------------------------------------------------------------------------------- 1 | src/ipsetdot - 2 | -------------------------------------------------------------------------------- /tests/errors/nonnumeric-cidr/command: -------------------------------------------------------------------------------- 1 | src/ipsetbuild - -o - 2 | -------------------------------------------------------------------------------- /tests/.gitattributes: -------------------------------------------------------------------------------- 1 | *.t -whitespace 2 | cram.py -diff 3 | -------------------------------------------------------------------------------- /tests/dot/10.0.5.64-30/in: -------------------------------------------------------------------------------- 1 | 10.0.5.64 2 | 10.0.5.66 3 | 10.0.5.67 4 | -------------------------------------------------------------------------------- /tests/round-trip/10.0.5.64-30/out: -------------------------------------------------------------------------------- 1 | 10.0.5.64 2 | 10.0.5.66/31 3 | -------------------------------------------------------------------------------- /tests/round-trip/command: -------------------------------------------------------------------------------- 1 | src/ipsetbuild -o - - | src/ipsetcat -n - 2 | -------------------------------------------------------------------------------- /tests/round-trip/idempotent-01a/in: -------------------------------------------------------------------------------- 1 | 192.168.0.0/24 2 | 192.168.1.0/24 3 | -------------------------------------------------------------------------------- /tests/round-trip/idempotent-01b/in: -------------------------------------------------------------------------------- 1 | 192.168.1.0/24 2 | 192.168.0.0/24 3 | -------------------------------------------------------------------------------- /tests/round-trip/idempotent-02a/in: -------------------------------------------------------------------------------- 1 | 192.168.0.0/23 2 | !192.168.1.0/24 3 | -------------------------------------------------------------------------------- /tests/round-trip/idempotent-02b/in: -------------------------------------------------------------------------------- 1 | !192.168.1.0/24 2 | 192.168.0.0/23 3 | -------------------------------------------------------------------------------- /tests/round-trip/10.0.5.64-30/in: -------------------------------------------------------------------------------- 1 | 10.0.5.64 2 | 10.0.5.66 3 | 10.0.5.67 4 | -------------------------------------------------------------------------------- /tests/errors/ipsetcat-file-not-found/command: -------------------------------------------------------------------------------- 1 | src/ipsetcat non-existing-file.set 2 | -------------------------------------------------------------------------------- /tests/errors/ipsetdot-file-not-found/command: -------------------------------------------------------------------------------- 1 | src/ipsetdot non-existing-file.set 2 | -------------------------------------------------------------------------------- /tests/dot/universe/out: -------------------------------------------------------------------------------- 1 | strict digraph bdd { 2 | t1 [shape=box, label=1]; 3 | } 4 | -------------------------------------------------------------------------------- /docs/.gitattributes: -------------------------------------------------------------------------------- 1 | *.graffle -diff -whitespace 2 | /*.[1-9] -diff -whitespace 3 | -------------------------------------------------------------------------------- /tests/errors/ipsetbuild-file-not-found/command: -------------------------------------------------------------------------------- 1 | src/ipsetbuild non-existing-file.set -o - 2 | -------------------------------------------------------------------------------- /tests/errors/ipsetcat-empty-input/err: -------------------------------------------------------------------------------- 1 | Error reading stdin: 2 | Unexpected end of file 3 | -------------------------------------------------------------------------------- /tests/errors/ipsetdot-empty-input/err: -------------------------------------------------------------------------------- 1 | Error reading stdin: 2 | Unexpected end of file 3 | -------------------------------------------------------------------------------- /.buzzy/links.yaml: -------------------------------------------------------------------------------- 1 | - !git-env 2 | url: git://github.com/dcreager/libcork.git 3 | commit: master 4 | -------------------------------------------------------------------------------- /tests/errors/empty-cidr/err: -------------------------------------------------------------------------------- 1 | Error: Line 1: Missing CIDR prefix 2 | The program halted on stdin with 1 input error. 3 | -------------------------------------------------------------------------------- /tests/errors/ipsetcat-file-not-found/err: -------------------------------------------------------------------------------- 1 | Cannot open file non-existing-file.set: 2 | No such file or directory 3 | -------------------------------------------------------------------------------- /tests/errors/ipsetdot-file-not-found/err: -------------------------------------------------------------------------------- 1 | Cannot open file non-existing-file.set: 2 | No such file or directory 3 | -------------------------------------------------------------------------------- /tests/errors/nonnumeric-cidr/err: -------------------------------------------------------------------------------- 1 | Error: Line 1: Invalid CIDR prefix "8o" 2 | The program halted on stdin with 1 input error. 3 | -------------------------------------------------------------------------------- /tests/errors/ipsetbuild-file-not-found/err: -------------------------------------------------------------------------------- 1 | ipsetbuild: Cannot open file non-existing-file.set: 2 | No such file or directory 3 | -------------------------------------------------------------------------------- /tests/errors/ipsetbuild-no-output/err: -------------------------------------------------------------------------------- 1 | ipsetbuild: You need to specify an output file. 2 | Usage: ipsetbuild [options] ... 3 | -------------------------------------------------------------------------------- /tests/errors/ipsetcat-no-input/err: -------------------------------------------------------------------------------- 1 | ipsetcat: You must specify exactly one input file. 2 | Usage: ipsetcat [options] 3 | -------------------------------------------------------------------------------- /tests/errors/ipsetdot-no-input/err: -------------------------------------------------------------------------------- 1 | ipsetdot: You must specify exactly one input file. 2 | Usage: ipsetdot [options] 3 | -------------------------------------------------------------------------------- /tests/errors/ipsetbuild-no-input/err: -------------------------------------------------------------------------------- 1 | ipsetbuild: You need to specify at least one input file. 2 | Usage: ipsetbuild [options] ... 3 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /build/ 2 | /.build/ 3 | 4 | /ipset*.tar.gz 5 | /ipset*.tar.bz2 6 | 7 | *.[oa] 8 | *.os 9 | *.so 10 | *.dylib 11 | 12 | !.travis.yml 13 | -------------------------------------------------------------------------------- /.buzzy/package.yaml: -------------------------------------------------------------------------------- 1 | name: ipset 2 | build_dependencies: 3 | - pkg-config 4 | - check >= 0.9.4 5 | - python-sphinx 6 | dependencies: 7 | - libcork >= 0.12.0 8 | license: BSD 9 | -------------------------------------------------------------------------------- /docs/index.rst: -------------------------------------------------------------------------------- 1 | .. _index: 2 | 3 | |project_name| documentation 4 | ============================ 5 | 6 | This is the documentation for |project_name| |release|, last updated 7 | |today|. 8 | 9 | 10 | Contents 11 | -------- 12 | 13 | .. toctree:: 14 | :maxdepth: 2 15 | 16 | sets 17 | maps 18 | commands 19 | file-format 20 | 21 | 22 | Indices and tables 23 | ------------------ 24 | 25 | * :ref:`genindex` 26 | * :ref:`search` 27 | -------------------------------------------------------------------------------- /AUTHORS: -------------------------------------------------------------------------------- 1 | # This is the list of libcorkipset authors for copyright purposes. 2 | # 3 | # This does not necessarily list everyone who has contributed code, since in 4 | # some cases, their employer may be the copyright holder. To see the full list 5 | # of contributors, see the revision history in source control. 6 | Gianfranco Costamagna 7 | Google Inc. 8 | Redjack, LLC 9 | Roger Shimizu 10 | -------------------------------------------------------------------------------- /src/ipset.pc.in: -------------------------------------------------------------------------------- 1 | prefix=@prefix@ 2 | exec_prefix=${prefix} 3 | libdir=${exec_prefix}/@CMAKE_INSTALL_LIBDIR@ 4 | includedir=${prefix}/@CMAKE_INSTALL_INCLUDEDIR@ 5 | sharedir=${prefix}/share 6 | sphinxdir=${sharedir}/doc/ipset/html 7 | 8 | Name: ipset 9 | Description: Library for storing IP sets and maps 10 | Version: @VERSION@ 11 | URL: http://github.com/dcreager/ipset/ 12 | Requires: libcork >= 0.12.0 13 | Libs: -L${libdir} -lipset 14 | Cflags: -I${includedir} 15 | -------------------------------------------------------------------------------- /share/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # ---------------------------------------------------------------------- 3 | # Copyright © 2012, libcorkipset authors 4 | # All rights reserved. 5 | # 6 | # Please see the COPYING file in this distribution for license details. 7 | # ---------------------------------------------------------------------- 8 | 9 | install(DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/valgrind 10 | DESTINATION share 11 | FILES_MATCHING PATTERN "*.supp") 12 | -------------------------------------------------------------------------------- /include/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # ---------------------------------------------------------------------- 3 | # Copyright © 2011, libcorkipset authors 4 | # All rights reserved. 5 | # 6 | # Please see the COPYING file in this distribution for license details. 7 | # ---------------------------------------------------------------------- 8 | 9 | install(DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/ 10 | DESTINATION ${CMAKE_INSTALL_INCLUDEDIR} 11 | FILES_MATCHING PATTERN "*.h") 12 | -------------------------------------------------------------------------------- /src/libipset/general.c: -------------------------------------------------------------------------------- 1 | /* -*- coding: utf-8 -*- 2 | * ---------------------------------------------------------------------- 3 | * Copyright © 2009, libcorkipset authors 4 | * All rights reserved. 5 | * 6 | * Please see the COPYING file in this distribution for license details. 7 | * ---------------------------------------------------------------------- 8 | */ 9 | 10 | #include 11 | 12 | #include "ipset/bdd/nodes.h" 13 | #include "ipset/ipset.h" 14 | 15 | 16 | int 17 | ipset_init_library() 18 | { 19 | return 0; 20 | } 21 | -------------------------------------------------------------------------------- /README.markdown: -------------------------------------------------------------------------------- 1 | # ipset 2 | 3 | [![Build Status](https://img.shields.io/travis/redjack/ipset/develop.svg)](https://travis-ci.org/redjack/ipset) 4 | 5 | The ipset library provides C data types for storing sets of IP 6 | addresses, and maps of IP addresses to integers. It supports both 7 | IPv4 and IPv6 addresses. It's implemented using [Binary Decision 8 | Diagrams](http://en.wikipedia.org/wiki/Binary_decision_diagram) 9 | (BDDs), which (we hypothesize) makes it space efficient for large 10 | sets. 11 | 12 | Please see the INSTALL file for installation instructions. 13 | -------------------------------------------------------------------------------- /tests/ccram: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | if [ "$1" = "--python" ]; then 4 | shift 5 | PYTHON="$1" 6 | shift 7 | else 8 | PYTHON=python 9 | fi 10 | 11 | if [ "$1" = "--root" ]; then 12 | shift 13 | ROOT="$1" 14 | shift 15 | else 16 | ROOT=$(dirname $PWD) 17 | fi 18 | 19 | if [ "$1" = "--tests" ]; then 20 | shift 21 | TESTS="$1" 22 | shift 23 | else 24 | TESTS=../tests 25 | fi 26 | 27 | export ROOT 28 | 29 | LD_LIBRARY_PATH="$PWD/src:$LD_LIBRARY_PATH" \ 30 | PATH="$PWD/src:$PATH" \ 31 | "$PYTHON" "$ROOT/tests/cram.py" "$@" "$TESTS" 32 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | dist: trusty 2 | sudo: required 3 | language: c 4 | compiler: 5 | - clang 6 | - gcc 7 | before_install: 8 | # Travis performs a shallow clone, which doesn't fetch tags. We need tags for 9 | # Buzzy to build/test the package. 10 | - git fetch 11 | # Download and install Buzzy 12 | - wget https://github.com/dcreager/buzzy/releases/download/0.6.0/ubuntu.trusty.buzzy_0.6.0_amd64.deb -O buzzy.deb 13 | - sudo dpkg -i buzzy.deb 14 | script: buzzy test 15 | 16 | # In addition to pull requests, always build these branches 17 | branches: 18 | only: 19 | - master 20 | -------------------------------------------------------------------------------- /examples/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # ---------------------------------------------------------------------- 3 | # Copyright © 2012, libcorkipset authors 4 | # All rights reserved. 5 | # 6 | # Please see the COPYING file in this distribution for license details. 7 | # ---------------------------------------------------------------------- 8 | 9 | #----------------------------------------------------------------------- 10 | # Examples 11 | 12 | add_c_executable( 13 | ipv4-set-size 14 | SKIP_INSTALL 15 | OUTPUT_NAME ipv4-set-size 16 | SOURCES ipv4-set-size.c 17 | LIBRARIES 18 | libcork 19 | LOCAL_LIBRARIES 20 | libipset 21 | ) 22 | -------------------------------------------------------------------------------- /include/ipset/errors.h: -------------------------------------------------------------------------------- 1 | /* -*- coding: utf-8 -*- 2 | * ---------------------------------------------------------------------- 3 | * Copyright © 2012, libcorkipset authors 4 | * All rights reserved. 5 | * 6 | * Please see the COPYING file in this distribution for license details. 7 | * ---------------------------------------------------------------------- 8 | */ 9 | 10 | #ifndef IPSET_ERRORS_H 11 | #define IPSET_ERRORS_H 12 | 13 | 14 | #include 15 | 16 | 17 | /*----------------------------------------------------------------------- 18 | * Error reporting 19 | */ 20 | 21 | /* Hash of "ipset.h" */ 22 | #define IPSET_ERROR 0xf2000181 23 | 24 | enum ipset_error { 25 | IPSET_IO_ERROR, 26 | IPSET_PARSE_ERROR 27 | }; 28 | 29 | 30 | #endif /* IPSET_ERRORS_H */ 31 | -------------------------------------------------------------------------------- /include/ipset/logging.h: -------------------------------------------------------------------------------- 1 | /* -*- coding: utf-8 -*- 2 | * ---------------------------------------------------------------------- 3 | * Copyright © 2010, libcorkipset authors 4 | * All rights reserved. 5 | * 6 | * Please see the COPYING file in this distribution for license details. 7 | * ---------------------------------------------------------------------- 8 | */ 9 | 10 | #ifndef IPSET_LOGGING_H 11 | #define IPSET_LOGGING_H 12 | 13 | 14 | #if !defined(IPSET_DEBUG) 15 | #define IPSET_DEBUG 0 16 | #endif 17 | 18 | #if IPSET_DEBUG 19 | #include 20 | #define DEBUG(...) \ 21 | do { \ 22 | fprintf(stderr, __VA_ARGS__); \ 23 | fprintf(stderr, "\n"); \ 24 | } while (0) 25 | #else 26 | #define DEBUG(...) /* no debug messages */ 27 | #endif 28 | 29 | 30 | #endif /* IPSET_LOGGING_H */ 31 | -------------------------------------------------------------------------------- /INSTALL: -------------------------------------------------------------------------------- 1 | Installation instructions 2 | ========================= 3 | 4 | The ipset library is written in ANSI C. It uses cmake as its build 5 | manager. 6 | 7 | 8 | Prerequisite libraries 9 | ---------------------- 10 | 11 | To build ipset, you need the following libraries installed on your 12 | system: 13 | 14 | * pkg-config 15 | * libcork (https://github.com/redjack/libcork) 16 | * check (http://check.sourceforge.net) 17 | 18 | 19 | Building from source 20 | -------------------- 21 | 22 | The ipset library uses cmake as its build manager. In most cases, you 23 | should be able to build the source code using the following: 24 | 25 | $ mkdir build 26 | $ cd build 27 | $ cmake .. -DCMAKE_INSTALL_PREFIX=$PREFIX 28 | $ make 29 | $ make test 30 | $ make install 31 | 32 | You might have to run the last command using sudo, if you need 33 | administrative privileges to write to the $PREFIX directory. 34 | -------------------------------------------------------------------------------- /run.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # Usage: run.sh [debug|release] program arguments 3 | # 4 | # Runs a program from one of the build directories, with 5 | # LD_LIBRARY_PATH set correctly so that it can find all of the shared 6 | # libraries before they're installed. 7 | 8 | 9 | # Check that there are enough command-line parameters. 10 | 11 | if [ $# -lt 1 ]; then 12 | echo "Usage: run.sh [debug|release] program arguments" 13 | exit 1 14 | fi 15 | 16 | 17 | # Verify that the user chose a valid build type. 18 | 19 | BUILD="$1" 20 | shift 21 | 22 | case "$BUILD" in 23 | debug) 24 | ;; 25 | release) 26 | ;; 27 | *) 28 | echo "Unknown build type $BUILD" 29 | exit 1 30 | ;; 31 | esac 32 | 33 | 34 | # Find all of the src subdirectories in the build directory, and use 35 | # those as the LD_LIBRARY_PATH. 36 | 37 | SRC_DIRS=$(find build/$BUILD -name src) 38 | JOINED=$(echo $SRC_DIRS | perl -ne 'print join(":", split)') 39 | 40 | 41 | # Run the desired program, and pass on any command-line arguments 42 | # as-is. 43 | 44 | LD_LIBRARY_PATH="$JOINED:$LD_LIBRARY_PATH" \ 45 | DYLD_LIBRARY_PATH="$JOINED:$DYLD_LIBRARY_PATH" \ 46 | "$@" 47 | -------------------------------------------------------------------------------- /src/libipset/set/allocation.c: -------------------------------------------------------------------------------- 1 | /* -*- coding: utf-8 -*- 2 | * ---------------------------------------------------------------------- 3 | * Copyright © 2009, libcorkipset authors 4 | * All rights reserved. 5 | * 6 | * Please see the COPYING file in this distribution for license details. 7 | * ---------------------------------------------------------------------- 8 | */ 9 | 10 | #include 11 | 12 | #include "ipset/bdd/nodes.h" 13 | #include "ipset/ipset.h" 14 | 15 | 16 | void 17 | ipset_init(struct ip_set *set) 18 | { 19 | /* The set starts empty, so every value assignment should yield 20 | * false. */ 21 | set->cache = ipset_node_cache_new(); 22 | set->set_bdd = ipset_terminal_node_id(false); 23 | } 24 | 25 | 26 | struct ip_set * 27 | ipset_new(void) 28 | { 29 | struct ip_set *result = cork_new(struct ip_set); 30 | ipset_init(result); 31 | return result; 32 | } 33 | 34 | 35 | void 36 | ipset_done(struct ip_set *set) 37 | { 38 | ipset_node_decref(set->cache, set->set_bdd); 39 | ipset_node_cache_free(set->cache); 40 | } 41 | 42 | 43 | void 44 | ipset_free(struct ip_set *set) 45 | { 46 | ipset_done(set); 47 | free(set); 48 | } 49 | -------------------------------------------------------------------------------- /src/libipset/map/allocation.c: -------------------------------------------------------------------------------- 1 | /* -*- coding: utf-8 -*- 2 | * ---------------------------------------------------------------------- 3 | * Copyright © 2009, libcorkipset authors 4 | * All rights reserved. 5 | * 6 | * Please see the COPYING file in this distribution for license details. 7 | * ---------------------------------------------------------------------- 8 | */ 9 | 10 | #include 11 | 12 | #include "ipset/bdd/nodes.h" 13 | #include "ipset/ipset.h" 14 | 15 | 16 | void 17 | ipmap_init(struct ip_map *map, int default_value) 18 | { 19 | /* The map starts empty, so every value assignment should yield the 20 | * default. */ 21 | map->cache = ipset_node_cache_new(); 22 | map->default_bdd = ipset_terminal_node_id(default_value); 23 | map->map_bdd = map->default_bdd; 24 | } 25 | 26 | 27 | struct ip_map * 28 | ipmap_new(int default_value) 29 | { 30 | struct ip_map *result = cork_new(struct ip_map); 31 | ipmap_init(result, default_value); 32 | return result; 33 | } 34 | 35 | 36 | void 37 | ipmap_done(struct ip_map *map) 38 | { 39 | ipset_node_decref(map->cache, map->map_bdd); 40 | ipset_node_cache_free(map->cache); 41 | } 42 | 43 | 44 | void 45 | ipmap_free(struct ip_map *map) 46 | { 47 | ipmap_done(map); 48 | free(map); 49 | } 50 | -------------------------------------------------------------------------------- /src/libipset/map/ipv4_map.c: -------------------------------------------------------------------------------- 1 | /* -*- coding: utf-8 -*- 2 | * ---------------------------------------------------------------------- 3 | * Copyright © 2009, libcorkipset authors 4 | * All rights reserved. 5 | * 6 | * Please see the COPYING file in this distribution for license details. 7 | * ---------------------------------------------------------------------- 8 | */ 9 | 10 | /* 11 | * The IPv4 and IPv6 map types are basically identical, except for the 12 | * names of the functions, and the size of the values that are being 13 | * stored. Rather than having two mostly duplicate definitions of each 14 | * function, we define “template functions” where anything that depends 15 | * on the size of the IP address is defined using the following macros. 16 | */ 17 | 18 | 19 | /* The name of the cork_ipvX type. */ 20 | #define CORK_IP struct cork_ipv4 21 | 22 | /* The number of bits in an IPvX address. */ 23 | #define IP_BIT_SIZE 32 24 | 25 | /* The value of the discriminator variable for an IPvX address. */ 26 | #define IP_DISCRIMINATOR_VALUE true 27 | 28 | /* Creates a identifier of the form “ipset_ipv4_”. */ 29 | #define IPSET_NAME(basename) ipset_ipv4_##basename 30 | 31 | /* Creates a identifier of the form “ipmap_ipv4_”. */ 32 | #define IPMAP_NAME(basename) ipmap_ipv4_##basename 33 | 34 | 35 | /* Now include all of the templates. */ 36 | #include "inspection-template.c.in" 37 | -------------------------------------------------------------------------------- /src/libipset/map/ipv6_map.c: -------------------------------------------------------------------------------- 1 | /* -*- coding: utf-8 -*- 2 | * ---------------------------------------------------------------------- 3 | * Copyright © 2009, libcorkipset authors 4 | * All rights reserved. 5 | * 6 | * Please see the COPYING file in this distribution for license details. 7 | * ---------------------------------------------------------------------- 8 | */ 9 | 10 | /* 11 | * The IPv4 and IPv6 map types are basically identical, except for the 12 | * names of the functions, and the size of the values that are being 13 | * stored. Rather than having two mostly duplicate definitions of each 14 | * function, we define “template functions” where anything that depends 15 | * on the size of the IP address is defined using the following macros. 16 | */ 17 | 18 | 19 | /* The name of the cork_ipvX type. */ 20 | #define CORK_IP struct cork_ipv6 21 | 22 | /* The number of bits in an IPvX address. */ 23 | #define IP_BIT_SIZE 128 24 | 25 | /* The value of the discriminator variable for an IPvX address. */ 26 | #define IP_DISCRIMINATOR_VALUE false 27 | 28 | /* Creates a identifier of the form “ipset_ipv6_”. */ 29 | #define IPSET_NAME(basename) ipset_ipv6_##basename 30 | 31 | /* Creates a identifier of the form “ipmap_ipv6_”. */ 32 | #define IPMAP_NAME(basename) ipmap_ipv6_##basename 33 | 34 | 35 | /* Now include all of the templates. */ 36 | #include "inspection-template.c.in" 37 | -------------------------------------------------------------------------------- /src/libipset/set/ipv4_set.c: -------------------------------------------------------------------------------- 1 | /* -*- coding: utf-8 -*- 2 | * ---------------------------------------------------------------------- 3 | * Copyright © 2009, libcorkipset authors 4 | * All rights reserved. 5 | * 6 | * Please see the COPYING file in this distribution for license details. 7 | * ---------------------------------------------------------------------- 8 | */ 9 | 10 | /* 11 | * The IPv4 and IPv6 set types are basically identical, except for the 12 | * names of the functions, and the size of the values that are being 13 | * stored. Rather than having two mostly duplicate definitions of each 14 | * function, we define “template functions” where anything that depends 15 | * on the size of the IP address is defined using the following macros. 16 | */ 17 | 18 | 19 | /* The name of the cork_ipvX type. */ 20 | #define CORK_IP struct cork_ipv4 21 | 22 | /* The number of bits in an IPvX address. */ 23 | #define IP_BIT_SIZE 32 24 | 25 | /* The value of the discriminator variable for an IPvX address. */ 26 | #define IP_DISCRIMINATOR_VALUE true 27 | 28 | /* Creates a identifier of the form “ipset_ipv4_”. */ 29 | #define IPSET_NAME(basename) ipset_ipv4_##basename 30 | 31 | /* Creates a identifier of the form “ipset__ipv4”. */ 32 | #define IPSET_PRENAME(basename) ipset_##basename##_ipv4 33 | 34 | 35 | /* Now include all of the templates. */ 36 | #include "inspection-template.c.in" 37 | -------------------------------------------------------------------------------- /src/libipset/set/ipv6_set.c: -------------------------------------------------------------------------------- 1 | /* -*- coding: utf-8 -*- 2 | * ---------------------------------------------------------------------- 3 | * Copyright © 2009, libcorkipset authors 4 | * All rights reserved. 5 | * 6 | * Please see the COPYING file in this distribution for license details. 7 | * ---------------------------------------------------------------------- 8 | */ 9 | 10 | /* 11 | * The IPv4 and IPv6 set types are basically identical, except for the 12 | * names of the functions, and the size of the values that are being 13 | * stored. Rather than having two mostly duplicate definitions of 14 | * each function, we define “template functions” where anything that 15 | * depends on the size of the IP address is defined using the 16 | * following macros. 17 | */ 18 | 19 | 20 | /* The name of the cork_ipvX type. */ 21 | #define CORK_IP struct cork_ipv6 22 | 23 | /* The number of bits in an IPvX address. */ 24 | #define IP_BIT_SIZE 128 25 | 26 | /* The value of the discriminator variable for an IPvX address. */ 27 | #define IP_DISCRIMINATOR_VALUE false 28 | 29 | /* Creates a identifier of the form “ipset_ipv6_”. */ 30 | #define IPSET_NAME(basename) ipset_ipv6_##basename 31 | 32 | /* Creates a identifier of the form “ipset__ipv6”. */ 33 | #define IPSET_PRENAME(basename) ipset_##basename##_ipv6 34 | 35 | 36 | /* Now include all of the templates. */ 37 | #include "inspection-template.c.in" 38 | -------------------------------------------------------------------------------- /tests/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # ---------------------------------------------------------------------- 3 | # Copyright © 2011, libcorkipset authors 4 | # All rights reserved. 5 | # 6 | # Please see the COPYING file in this distribution for license details. 7 | # ---------------------------------------------------------------------- 8 | 9 | #----------------------------------------------------------------------- 10 | # Build the test cases 11 | 12 | add_c_test(test-assignment) 13 | add_c_test(test-bdd) 14 | add_c_test(test-ipmap) 15 | add_c_test(test-ipset) 16 | add_c_test(test-iterator) 17 | 18 | #----------------------------------------------------------------------- 19 | # Command-line tests 20 | 21 | find_package(PythonInterp) 22 | 23 | if (PYTHON_EXECUTABLE) 24 | configure_file(ccram ${CMAKE_BINARY_DIR}/ccram COPYONLY) 25 | file(GLOB_RECURSE TESTS "${CMAKE_CURRENT_SOURCE_DIR}/*.t") 26 | foreach(TEST ${TESTS}) 27 | get_filename_component(TEST_NAME "${TEST}" NAME_WE) 28 | add_test( 29 | ${TEST_NAME} 30 | ${CMAKE_COMMAND} -E chdir ${CMAKE_BINARY_DIR} 31 | ${CMAKE_BINARY_DIR}/ccram 32 | --python ${PYTHON_EXECUTABLE} 33 | --root ${CMAKE_SOURCE_DIR} 34 | --tests ${TEST} 35 | ) 36 | endforeach(TEST) 37 | else (PYTHON_EXECUTABLE) 38 | message(WARNING "Unable to find Python; skipping cram tests.") 39 | endif (PYTHON_EXECUTABLE) 40 | -------------------------------------------------------------------------------- /COPYING: -------------------------------------------------------------------------------- 1 | Copyright © 2009-2017, libcorkipset authors 2 | All rights reserved. 3 | 4 | Redistribution and use in source and binary forms, with or without 5 | modification, are permitted provided that the following conditions are 6 | met: 7 | 8 | • Redistributions of source code must retain the above copyright 9 | notice, this list of conditions and the following disclaimer. 10 | 11 | • Redistributions in binary form must reproduce the above copyright 12 | notice, this list of conditions and the following disclaimer in 13 | the documentation and/or other materials provided with the 14 | distribution. 15 | 16 | • Neither the name of the libcorkipset project nor the names of its 17 | contributors may be used to endorse or promote products derived 18 | from this software without specific prior written permission. 19 | 20 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 21 | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 22 | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 23 | A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 24 | HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 25 | SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 26 | LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 27 | DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 28 | THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 29 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 30 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 31 | -------------------------------------------------------------------------------- /include/ipset/bits.h: -------------------------------------------------------------------------------- 1 | /* -*- coding: utf-8 -*- 2 | * ---------------------------------------------------------------------- 3 | * Copyright © 2012, libcorkipset authors 4 | * All rights reserved. 5 | * 6 | * Please see the COPYING file in this distribution for license details. 7 | * ---------------------------------------------------------------------- 8 | */ 9 | 10 | #ifndef IPSET_BITS_H 11 | #define IPSET_BITS_H 12 | 13 | #include 14 | 15 | /*----------------------------------------------------------------------- 16 | * Bit arrays 17 | */ 18 | 19 | /** 20 | * Extract the byte that contains a particular bit in an array. 21 | */ 22 | #define IPSET_BIT_GET_BYTE(array, i) \ 23 | (((uint8_t *) (array))[(i) / 8]) 24 | 25 | /** 26 | * Create a bit mask that extracts a particular bit from the byte that 27 | * it lives in. 28 | */ 29 | #define IPSET_BIT_ON_MASK(i) \ 30 | (0x80 >> ((i) % 8)) 31 | 32 | /** 33 | * Create a bit mask that extracts everything except for a particular 34 | * bit from the byte that it lives in. 35 | */ 36 | #define IPSET_BIT_NEG_MASK(i) \ 37 | (~IPSET_BIT_ON_MASK(i)) 38 | 39 | /** 40 | * Return whether a particular bit is set in a byte array. Bits are 41 | * numbered from 0, in a big-endian order. 42 | */ 43 | #define IPSET_BIT_GET(array, i) \ 44 | ((IPSET_BIT_GET_BYTE(array, i) & \ 45 | IPSET_BIT_ON_MASK(i)) != 0) 46 | 47 | /** 48 | * Set (or unset) a particular bit is set in a byte array. Bits are 49 | * numbered from 0, in a big-endian order. 50 | */ 51 | #define IPSET_BIT_SET(array, i, val) \ 52 | (IPSET_BIT_GET_BYTE(array, i) = \ 53 | (IPSET_BIT_GET_BYTE(array, i) & IPSET_BIT_NEG_MASK(i)) \ 54 | | ((val)? IPSET_BIT_ON_MASK(i): 0)) 55 | 56 | 57 | #endif /* IPSET_BITS_H */ 58 | -------------------------------------------------------------------------------- /cmake/FindParseArguments.cmake: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # ---------------------------------------------------------------------- 3 | # Copyright © 2015, libcorkipset authors 4 | # All rights reserved. 5 | # 6 | # Please see the COPYING file in this distribution for license details. 7 | # ---------------------------------------------------------------------- 8 | 9 | 10 | # CMake 2.8.4 and higher gives us cmake_parse_arguments out of the box. For 11 | # earlier versions (RHEL5!) we have to define it ourselves. (The definition 12 | # comes from .) 13 | 14 | if (CMAKE_VERSION VERSION_LESS "2.8.4") 15 | 16 | MACRO(CMAKE_PARSE_ARGUMENTS prefix arg_names option_names) 17 | SET(DEFAULT_ARGS) 18 | FOREACH(arg_name ${arg_names}) 19 | SET(${prefix}_${arg_name}) 20 | ENDFOREACH(arg_name) 21 | FOREACH(option ${option_names}) 22 | SET(${prefix}_${option} FALSE) 23 | ENDFOREACH(option) 24 | 25 | SET(current_arg_name DEFAULT_ARGS) 26 | SET(current_arg_list) 27 | FOREACH(arg ${ARGN}) 28 | SET(larg_names ${arg_names}) 29 | LIST(FIND larg_names "${arg}" is_arg_name) 30 | IF (is_arg_name GREATER -1) 31 | SET(${prefix}_${current_arg_name} ${current_arg_list}) 32 | SET(current_arg_name ${arg}) 33 | SET(current_arg_list) 34 | ELSE (is_arg_name GREATER -1) 35 | SET(loption_names ${option_names}) 36 | LIST(FIND loption_names "${arg}" is_option) 37 | IF (is_option GREATER -1) 38 | SET(${prefix}_${arg} TRUE) 39 | ELSE (is_option GREATER -1) 40 | SET(current_arg_list ${current_arg_list} ${arg}) 41 | ENDIF (is_option GREATER -1) 42 | ENDIF (is_arg_name GREATER -1) 43 | ENDFOREACH(arg) 44 | SET(${prefix}_${current_arg_name} ${current_arg_list}) 45 | ENDMACRO(CMAKE_PARSE_ARGUMENTS) 46 | 47 | else (CMAKE_VERSION VERSION_LESS "2.8.4") 48 | 49 | include(CMakeParseArguments) 50 | 51 | endif (CMAKE_VERSION VERSION_LESS "2.8.4") 52 | -------------------------------------------------------------------------------- /src/libipset/map/inspection.c: -------------------------------------------------------------------------------- 1 | /* -*- coding: utf-8 -*- 2 | * ---------------------------------------------------------------------- 3 | * Copyright © 2009, libcorkipset authors 4 | * All rights reserved. 5 | * 6 | * Please see the COPYING file in this distribution for license details. 7 | * ---------------------------------------------------------------------- 8 | */ 9 | 10 | #include 11 | 12 | #include "ipset/bdd/nodes.h" 13 | #include "ipset/ipset.h" 14 | 15 | bool 16 | ipmap_is_empty(const struct ip_map *map) 17 | { 18 | /* Since BDDs are unique, any map that maps all addresses to the 19 | * default value is “empty”. */ 20 | return (map->map_bdd == map->default_bdd); 21 | } 22 | 23 | bool 24 | ipmap_is_equal(const struct ip_map *map1, const struct ip_map *map2) 25 | { 26 | return ipset_node_cache_nodes_equal 27 | (map1->cache, map1->map_bdd, map2->cache, map2->map_bdd); 28 | } 29 | 30 | size_t 31 | ipmap_memory_size(const struct ip_map *map) 32 | { 33 | return ipset_node_memory_size(map->cache, map->map_bdd); 34 | } 35 | 36 | 37 | void 38 | ipmap_ip_set(struct ip_map *map, struct cork_ip *addr, int value) 39 | { 40 | if (addr->version == 4) { 41 | ipmap_ipv4_set(map, &addr->ip.v4, value); 42 | } else { 43 | ipmap_ipv6_set(map, &addr->ip.v6, value); 44 | } 45 | } 46 | 47 | 48 | void 49 | ipmap_ip_set_network(struct ip_map *map, struct cork_ip *addr, 50 | unsigned int cidr_prefix, int value) 51 | { 52 | if (addr->version == 4) { 53 | ipmap_ipv4_set_network(map, &addr->ip.v4, cidr_prefix, value); 54 | } else { 55 | ipmap_ipv6_set_network(map, &addr->ip.v6, cidr_prefix, value); 56 | } 57 | } 58 | 59 | 60 | int 61 | ipmap_ip_get(struct ip_map *map, struct cork_ip *addr) 62 | { 63 | if (addr->version == 4) { 64 | return ipmap_ipv4_get(map, &addr->ip.v4); 65 | } else { 66 | return ipmap_ipv6_get(map, &addr->ip.v6); 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /docs/conf.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | import sys, os 4 | 5 | extensions = ['sphinx.ext.intersphinx'] 6 | source_suffix = '.rst' 7 | master_doc = 'index' 8 | project_name = u'libcorkipset' 9 | project_slug = u'libcorkipset' 10 | company = u'libcorkipset authors' 11 | copyright_years = u'2009-2017' 12 | 13 | default_role = 'c:func' 14 | primary_domain = 'c' 15 | 16 | rst_epilog = """ 17 | .. |project_name| replace:: """ + project_name + """ 18 | """ 19 | 20 | # Intersphinx stuff 21 | 22 | intersphinx_mapping = { 23 | 'libcork': ('http://libcork.readthedocs.org/en/latest/', None), 24 | } 25 | 26 | # Our CMake build scripts will insert overrides below if the prereq 27 | # libraries have installed their Sphinx documentation locally. DO NOT 28 | # uncomment out the last line of this block; we need it commented so 29 | # that this conf.py file still works if CMake doesn't do its 30 | # substitution thing. 31 | # @INTERSPHINX_OVERRIDES@ 32 | 33 | #---------------------------------------------------------------------- 34 | # Everything below here shouldn't need to be changed. 35 | 36 | release = None 37 | version = None 38 | 39 | # Give CMake a chance to insert a version number 40 | # @VERSION_FOR_CONF_PY@ 41 | 42 | # Otherwise grab version from git 43 | if version is None: 44 | import re 45 | import subprocess 46 | release = subprocess.check_output(["git", "describe"]).rstrip() 47 | version = re.sub(r"-dev.*$", "-dev", release) 48 | 49 | # Project details 50 | 51 | project = project_name 52 | copyright = copyright_years+u', '+company 53 | templates_path = ['_templates'] 54 | exclude_patterns = ['_build'] 55 | pygments_style = 'sphinx' 56 | 57 | html_theme = 'default' 58 | html_style = 'docco-sphinx.css' 59 | html_static_path = ['_static'] 60 | htmlhelp_basename = project_slug+'-doc' 61 | 62 | 63 | latex_documents = [ 64 | ('index', project_slug+'.tex', project_name+u' Documentation', 65 | company, 'manual'), 66 | ] 67 | 68 | man_pages = [ 69 | ('index', project_slug, project_name+u' Documentation', 70 | [company], 1) 71 | ] 72 | 73 | texinfo_documents = [ 74 | ('index', project_slug, project_name+u' Documentation', 75 | company, project_name, 'One line description of project.', 76 | 'Miscellaneous'), 77 | ] 78 | -------------------------------------------------------------------------------- /examples/ipv4-set-size.c: -------------------------------------------------------------------------------- 1 | /* -*- coding: utf-8 -*- 2 | * ---------------------------------------------------------------------- 3 | * Copyright © 2009, libcorkipset authors 4 | * All rights reserved. 5 | * 6 | * Please see the COPYING file in this distribution for license details. 7 | * ---------------------------------------------------------------------- 8 | */ 9 | 10 | #include 11 | #include 12 | #include 13 | #include 14 | 15 | #include 16 | #include 17 | 18 | 19 | static inline void 20 | random_ip(struct cork_ipv4 *ip) 21 | { 22 | int i; 23 | 24 | for (i = 0; i < sizeof(struct cork_ipv4); i++) { 25 | uint8_t random_byte = random() & 0xff; 26 | ip->_.u8[i] = random_byte; 27 | } 28 | } 29 | 30 | 31 | static void 32 | one_test(long num_elements) 33 | { 34 | struct ip_set set; 35 | long i; 36 | size_t size; 37 | double size_per_element; 38 | clock_t start, end; 39 | double cpu_time_used; 40 | 41 | start = clock(); 42 | ipset_init(&set); 43 | for (i = 0; i < num_elements; i++) { 44 | struct cork_ipv4 ip; 45 | random_ip(&ip); 46 | ipset_ipv4_add(&set, &ip); 47 | } 48 | end = clock(); 49 | 50 | size = ipset_memory_size(&set); 51 | size_per_element = ((double) size) / num_elements; 52 | cpu_time_used = ((double) (end - start)) / CLOCKS_PER_SEC; 53 | 54 | fprintf(stdout, "%9lu%15zu%12.3lf%18.6lf\n", 55 | num_elements, size, size_per_element, cpu_time_used); 56 | 57 | ipset_done(&set); 58 | } 59 | 60 | 61 | int 62 | main(int argc, const char **argv) 63 | { 64 | long num_tests; 65 | long num_elements; 66 | long i; 67 | 68 | if (argc != 3) { 69 | fprintf(stderr, "Usage: ipv4-set-size [# tests] [# elements]\n"); 70 | return -1; 71 | } 72 | 73 | num_tests = atol(argv[1]); 74 | num_elements = atol(argv[2]); 75 | 76 | fprintf(stderr, "Creating %lu sets with %lu elements each.\n", 77 | num_tests, num_elements); 78 | 79 | ipset_init_library(); 80 | srandom(time(NULL)); 81 | 82 | fprintf(stdout, "%9s%15s%12s%18s\n", 83 | "elements", "bytes", "bytes_per", "cpu_time"); 84 | for (i = 0; i < num_tests; i++) { 85 | one_test(num_elements); 86 | } 87 | 88 | return 0; 89 | } 90 | -------------------------------------------------------------------------------- /src/libipset/set/inspection.c: -------------------------------------------------------------------------------- 1 | /* -*- coding: utf-8 -*- 2 | * ---------------------------------------------------------------------- 3 | * Copyright © 2009, libcorkipset authors 4 | * All rights reserved. 5 | * 6 | * Please see the COPYING file in this distribution for license details. 7 | * ---------------------------------------------------------------------- 8 | */ 9 | 10 | #include 11 | 12 | #include "ipset/bdd/nodes.h" 13 | #include "ipset/ipset.h" 14 | 15 | bool 16 | ipset_is_empty(const struct ip_set *set) 17 | { 18 | /* Since BDDs are unique, the only empty set is the “false” BDD. */ 19 | return (set->set_bdd == ipset_terminal_node_id(false)); 20 | } 21 | 22 | bool 23 | ipset_is_equal(const struct ip_set *set1, const struct ip_set *set2) 24 | { 25 | return ipset_node_cache_nodes_equal 26 | (set1->cache, set1->set_bdd, set2->cache, set2->set_bdd); 27 | } 28 | 29 | size_t 30 | ipset_memory_size(const struct ip_set *set) 31 | { 32 | return ipset_node_memory_size(set->cache, set->set_bdd); 33 | } 34 | 35 | 36 | bool 37 | ipset_ip_add(struct ip_set *set, struct cork_ip *addr) 38 | { 39 | if (addr->version == 4) { 40 | return ipset_ipv4_add(set, &addr->ip.v4); 41 | } else { 42 | return ipset_ipv6_add(set, &addr->ip.v6); 43 | } 44 | } 45 | 46 | 47 | bool 48 | ipset_ip_add_network(struct ip_set *set, struct cork_ip *addr, 49 | unsigned int cidr_prefix) 50 | { 51 | if (addr->version == 4) { 52 | return ipset_ipv4_add_network(set, &addr->ip.v4, cidr_prefix); 53 | } else { 54 | return ipset_ipv6_add_network(set, &addr->ip.v6, cidr_prefix); 55 | } 56 | } 57 | 58 | 59 | bool 60 | ipset_ip_remove(struct ip_set *set, struct cork_ip *addr) 61 | { 62 | if (addr->version == 4) { 63 | return ipset_ipv4_remove(set, &addr->ip.v4); 64 | } else { 65 | return ipset_ipv6_remove(set, &addr->ip.v6); 66 | } 67 | } 68 | 69 | 70 | bool 71 | ipset_ip_remove_network(struct ip_set *set, struct cork_ip *addr, 72 | unsigned int cidr_prefix) 73 | { 74 | if (addr->version == 4) { 75 | return ipset_ipv4_remove_network(set, &addr->ip.v4, cidr_prefix); 76 | } else { 77 | return ipset_ipv6_remove_network(set, &addr->ip.v6, cidr_prefix); 78 | } 79 | } 80 | 81 | 82 | bool 83 | ipset_contains_ip(const struct ip_set *set, struct cork_ip *addr) 84 | { 85 | if (addr->version == 4) { 86 | return ipset_contains_ipv4(set, &addr->ip.v4); 87 | } else { 88 | return ipset_contains_ipv6(set, &addr->ip.v6); 89 | } 90 | } 91 | -------------------------------------------------------------------------------- /src/libipset/map/inspection-template.c.in: -------------------------------------------------------------------------------- 1 | /* -*- coding: utf-8 -*- 2 | * ---------------------------------------------------------------------- 3 | * Copyright © 2009, libcorkipset authors 4 | * All rights reserved. 5 | * 6 | * Please see the COPYING file in this distribution for license details. 7 | * ---------------------------------------------------------------------- 8 | */ 9 | 10 | #include 11 | 12 | #include "ipset/bdd/nodes.h" 13 | #include "ipset/bits.h" 14 | #include "ipset/errors.h" 15 | #include "ipset/ipset.h" 16 | 17 | 18 | /** 19 | * Given a BDD variable number, return the index of the corresponding 20 | * bit in an IP address. IPv4 addresses use variables 1-32; IPv6 21 | * addresses use 1-128. (Variable 0 is used to identify the kind of 22 | * address — TRUE for IPv4, FALSE for IPv6.) 23 | */ 24 | 25 | static unsigned int 26 | IPMAP_NAME(bit_for_var)(ipset_variable var) 27 | { 28 | return (var - 1); 29 | } 30 | 31 | 32 | /** 33 | * An assignment function that can be used to evaluate an IP map BDD. 34 | */ 35 | 36 | static bool 37 | IPMAP_NAME(assignment)(const void *addr, ipset_variable var) 38 | { 39 | if (var == 0) { 40 | return IP_DISCRIMINATOR_VALUE; 41 | } else { 42 | unsigned int bit = IPMAP_NAME(bit_for_var)(var); 43 | return IPSET_BIT_GET(addr, bit); 44 | } 45 | } 46 | 47 | 48 | int 49 | IPMAP_NAME(get)(struct ip_map *map, CORK_IP *elem) 50 | { 51 | return ipset_node_evaluate 52 | (map->cache, map->map_bdd, IPMAP_NAME(assignment), elem); 53 | } 54 | 55 | 56 | void 57 | IPMAP_NAME(set_network)(struct ip_map *map, CORK_IP *elem, 58 | unsigned int cidr_prefix, int value) 59 | { 60 | /* Special case — the BDD for a netmask that's out of range never 61 | * evaluates to true. */ 62 | if (cidr_prefix > IP_BIT_SIZE) { 63 | cork_error_set 64 | (IPSET_ERROR, IPSET_PARSE_ERROR, 65 | "CIDR block %u out of range [0..%u]", cidr_prefix, IP_BIT_SIZE); 66 | return; 67 | } 68 | 69 | ipset_node_id new_bdd = 70 | ipset_node_insert 71 | (map->cache, map->map_bdd, 72 | IPMAP_NAME(assignment), elem, cidr_prefix + 1, value); 73 | ipset_node_decref(map->cache, map->map_bdd); 74 | map->map_bdd = new_bdd; 75 | } 76 | 77 | 78 | void 79 | IPMAP_NAME(set)(struct ip_map *map, CORK_IP *elem, int value) 80 | { 81 | ipset_node_id new_bdd = 82 | ipset_node_insert 83 | (map->cache, map->map_bdd, 84 | IPMAP_NAME(assignment), elem, IP_BIT_SIZE + 1, value); 85 | ipset_node_decref(map->cache, map->map_bdd); 86 | map->map_bdd = new_bdd; 87 | } 88 | -------------------------------------------------------------------------------- /docs/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # ---------------------------------------------------------------------- 3 | # Copyright © 2011, libcorkipset authors 4 | # All rights reserved. 5 | # 6 | # Please see the COPYING file in this distribution for license details. 7 | # ---------------------------------------------------------------------- 8 | 9 | find_program( 10 | SPHINX_EXECUTABLE 11 | NAMES sphinx-build 12 | HINTS ENV SPHINX_DIR 13 | PATH_SUFFIXES bin 14 | DOC "Sphinx documentation generator" 15 | ) 16 | 17 | set(GENERATE_DOC TRUE) 18 | 19 | if (NOT SPHINX_EXECUTABLE) 20 | message(WARNING "Unable to find Sphinx documentation generator") 21 | set(GENERATE_DOC FALSE) 22 | endif (NOT SPHINX_EXECUTABLE) 23 | 24 | set(INTERSPHINX_OVERRIDES "") 25 | 26 | macro(find_prereq_doc LIB_NAME) 27 | execute_process( 28 | COMMAND ${PKG_CONFIG_EXECUTABLE} --variable=sphinxdir ${LIB_NAME} 29 | OUTPUT_VARIABLE LIB_SPHINX_DIR 30 | OUTPUT_STRIP_TRAILING_WHITESPACE 31 | ) 32 | 33 | if (LIB_SPHINX_DIR) 34 | message(STATUS "Using ${LIB_NAME} docs in ${LIB_SPHINX_DIR}") 35 | set( 36 | INTERSPHINX_OVERRIDES 37 | "${INTERSPHINX_OVERRIDES}\nintersphinx_mapping['${LIB_NAME}'] = ('${LIB_SPHINX_DIR}', None)" 38 | ) 39 | endif (LIB_SPHINX_DIR) 40 | endmacro(find_prereq_doc) 41 | 42 | if (GENERATE_DOC) 43 | set(SPHINX_SRC_CONF_FILE "${CMAKE_CURRENT_SOURCE_DIR}/conf.py") 44 | set(SPHINX_CONF_FILE "${CMAKE_CURRENT_BINARY_DIR}/conf.py") 45 | set(SPHINX_CACHE_DIR "${CMAKE_CURRENT_BINARY_DIR}/_doctrees") 46 | set(SPHINX_HTML_DIR "${CMAKE_CURRENT_BINARY_DIR}/html") 47 | 48 | # If your Sphinx documentation references the Sphinx documentation of 49 | # any of your prerequisite libraries, add some calls to 50 | # find_prereq_doc here: 51 | find_prereq_doc(libcork) 52 | 53 | set( 54 | VERSION_FOR_CONF_PY 55 | "\nrelease=\"${VERSION}\"\nversion=\"${BASE_VERSION}\"" 56 | ) 57 | configure_file(${SPHINX_SRC_CONF_FILE} ${SPHINX_CONF_FILE} @ONLY) 58 | file(COPY _static DESTINATION ${CMAKE_CURRENT_BINARY_DIR}) 59 | file(COPY _templates DESTINATION ${CMAKE_CURRENT_BINARY_DIR}) 60 | 61 | add_custom_target(doc ALL 62 | ${SPHINX_EXECUTABLE} 63 | -b html 64 | -d "${SPHINX_CACHE_DIR}" 65 | -c "${CMAKE_CURRENT_BINARY_DIR}" 66 | "${CMAKE_CURRENT_SOURCE_DIR}" 67 | "${SPHINX_HTML_DIR}" 68 | COMMENT "Building HTML documentation with Sphinx" 69 | ) 70 | 71 | list(APPEND CLEAN_FILES "${SPHINX_CACHE_DIR}" "${SPHINX_HTML_DIR}") 72 | set_directory_properties( 73 | PROPERTIES 74 | ADDITIONAL_MAKE_CLEAN_FILES "${CLEAN_FILES}" 75 | ) 76 | 77 | install( 78 | DIRECTORY "${SPHINX_HTML_DIR}" 79 | DESTINATION "share/doc/${PROJECT_NAME}" 80 | ) 81 | endif (GENERATE_DOC) 82 | -------------------------------------------------------------------------------- /src/libipset/map/storage.c: -------------------------------------------------------------------------------- 1 | /* -*- coding: utf-8 -*- 2 | * ---------------------------------------------------------------------- 3 | * Copyright © 2009, libcorkipset authors 4 | * All rights reserved. 5 | * 6 | * Please see the COPYING file in this distribution for license details. 7 | * ---------------------------------------------------------------------- 8 | */ 9 | 10 | #include 11 | #include 12 | #include 13 | #include 14 | 15 | #include 16 | 17 | #include "ipset/bdd/nodes.h" 18 | #include "ipset/errors.h" 19 | #include "ipset/ipset.h" 20 | 21 | 22 | static void 23 | create_errno_error(FILE *stream) 24 | { 25 | if (ferror(stream)) { 26 | cork_error_set(IPSET_ERROR, IPSET_IO_ERROR, "%s", strerror(errno)); 27 | } else { 28 | cork_unknown_error(); 29 | } 30 | } 31 | 32 | struct file_consumer { 33 | /* file_consumer is a subclass of cork_stream_consumer */ 34 | struct cork_stream_consumer parent; 35 | /* the file to write the data into */ 36 | FILE *fp; 37 | }; 38 | 39 | static int 40 | file_consumer_data(struct cork_stream_consumer *vself, 41 | const void *buf, size_t size, bool is_first) 42 | { 43 | struct file_consumer *self = 44 | cork_container_of(vself, struct file_consumer, parent); 45 | size_t bytes_written = fwrite(buf, 1, size, self->fp); 46 | /* If there was an error writing to the file, then signal this to 47 | * the producer */ 48 | if (bytes_written == size) { 49 | return 0; 50 | } else { 51 | create_errno_error(self->fp); 52 | return -1; 53 | } 54 | } 55 | 56 | static int 57 | file_consumer_eof(struct cork_stream_consumer *vself) 58 | { 59 | /* We don't close the file, so there's nothing special to do at 60 | * end-of-stream. */ 61 | return 0; 62 | } 63 | 64 | 65 | int 66 | ipmap_save_to_stream(struct cork_stream_consumer *stream, 67 | const struct ip_map *map) 68 | { 69 | return ipset_node_cache_save(stream, map->cache, map->map_bdd); 70 | } 71 | 72 | int 73 | ipmap_save(FILE *fp, const struct ip_map *map) 74 | { 75 | struct file_consumer stream = { 76 | { file_consumer_data, file_consumer_eof, NULL }, fp 77 | }; 78 | return ipmap_save_to_stream(&stream.parent, map); 79 | } 80 | 81 | 82 | struct ip_map * 83 | ipmap_load(FILE *stream) 84 | { 85 | struct ip_map *map; 86 | ipset_node_id new_bdd; 87 | 88 | /* It doesn't matter what default value we use here, because we're 89 | * going to replace it with the default BDD we load in from the 90 | * file. */ 91 | map = ipmap_new(0); 92 | new_bdd = ipset_node_cache_load(stream, map->cache); 93 | if (cork_error_occurred()) { 94 | ipmap_free(map); 95 | return NULL; 96 | } 97 | 98 | map->map_bdd = new_bdd; 99 | return map; 100 | } 101 | -------------------------------------------------------------------------------- /src/libipset/bdd/reachable.c: -------------------------------------------------------------------------------- 1 | /* -*- coding: utf-8 -*- 2 | * ---------------------------------------------------------------------- 3 | * Copyright © 2010, libcorkipset authors 4 | * All rights reserved. 5 | * 6 | * Please see the COPYING file in this distribution for license details. 7 | * ---------------------------------------------------------------------- 8 | */ 9 | 10 | #include 11 | #include 12 | 13 | #include "ipset/bdd/nodes.h" 14 | #include "ipset/logging.h" 15 | 16 | 17 | size_t 18 | ipset_node_reachable_count(const struct ipset_node_cache *cache, 19 | ipset_node_id node) 20 | { 21 | /* Create a set to track when we've visited a given node. */ 22 | struct cork_hash_table *visited = cork_pointer_hash_table_new(0, 0); 23 | 24 | /* And a queue of nodes to check. */ 25 | cork_array(ipset_node_id) queue; 26 | cork_array_init(&queue); 27 | 28 | if (ipset_node_get_type(node) == IPSET_NONTERMINAL_NODE) { 29 | DEBUG("Adding node %u to queue", node); 30 | cork_array_append(&queue, node); 31 | } 32 | 33 | /* And somewhere to store the result. */ 34 | size_t node_count = 0; 35 | 36 | /* Check each node in turn. */ 37 | while (!cork_array_is_empty(&queue)) { 38 | ipset_node_id curr = cork_array_at(&queue, --queue.size); 39 | 40 | /* We don't have to do anything if this node is already in the 41 | * visited set. */ 42 | if (cork_hash_table_get(visited, (void *) (uintptr_t) curr) == NULL) { 43 | DEBUG("Visiting node %u for the first time", curr); 44 | 45 | /* Add the node to the visited set. */ 46 | cork_hash_table_put 47 | (visited, (void *) (uintptr_t) curr, 48 | (void *) (uintptr_t) true, NULL, NULL, NULL); 49 | 50 | /* Increase the node count. */ 51 | node_count++; 52 | 53 | /* And add the node's nonterminal children to the visit 54 | * queue. */ 55 | struct ipset_node *node = 56 | ipset_node_cache_get_nonterminal(cache, curr); 57 | 58 | if (ipset_node_get_type(node->low) == IPSET_NONTERMINAL_NODE) { 59 | DEBUG("Adding node %u to queue", node->low); 60 | cork_array_append(&queue, node->low); 61 | } 62 | 63 | if (ipset_node_get_type(node->high) == IPSET_NONTERMINAL_NODE) { 64 | DEBUG("Adding node %u to queue", node->high); 65 | cork_array_append(&queue, node->high); 66 | } 67 | } 68 | } 69 | 70 | /* Return the result, freeing everything before we go. */ 71 | cork_hash_table_free(visited); 72 | cork_array_done(&queue); 73 | return node_count; 74 | } 75 | 76 | 77 | size_t 78 | ipset_node_memory_size(const struct ipset_node_cache *cache, 79 | ipset_node_id node) 80 | { 81 | return ipset_node_reachable_count(cache, node) * sizeof(struct ipset_node); 82 | } 83 | -------------------------------------------------------------------------------- /src/libipset/set/storage.c: -------------------------------------------------------------------------------- 1 | /* -*- coding: utf-8 -*- 2 | * ---------------------------------------------------------------------- 3 | * Copyright © 2010, libcorkipset authors 4 | * All rights reserved. 5 | * 6 | * Please see the COPYING file in this distribution for license details. 7 | * ---------------------------------------------------------------------- 8 | */ 9 | 10 | #include 11 | #include 12 | #include 13 | #include 14 | 15 | #include 16 | 17 | #include "ipset/bdd/nodes.h" 18 | #include "ipset/errors.h" 19 | #include "ipset/ipset.h" 20 | 21 | 22 | static void 23 | create_errno_error(FILE *stream) 24 | { 25 | if (ferror(stream)) { 26 | cork_error_set(IPSET_ERROR, IPSET_IO_ERROR, "%s", strerror(errno)); 27 | } else { 28 | cork_unknown_error(); 29 | } 30 | } 31 | 32 | struct file_consumer { 33 | /* file_consumer is a subclass of cork_stream_consumer */ 34 | struct cork_stream_consumer parent; 35 | /* the file to write the data into */ 36 | FILE *fp; 37 | }; 38 | 39 | static int 40 | file_consumer_data(struct cork_stream_consumer *vself, 41 | const void *buf, size_t size, bool is_first) 42 | { 43 | struct file_consumer *self = 44 | cork_container_of(vself, struct file_consumer, parent); 45 | size_t bytes_written = fwrite(buf, 1, size, self->fp); 46 | /* If there was an error writing to the file, then signal this to 47 | * the producer */ 48 | if (bytes_written == size) { 49 | return 0; 50 | } else { 51 | create_errno_error(self->fp); 52 | return -1; 53 | } 54 | } 55 | 56 | static int 57 | file_consumer_eof(struct cork_stream_consumer *vself) 58 | { 59 | /* We don't close the file, so there's nothing special to do at 60 | * end-of-stream. */ 61 | return 0; 62 | } 63 | 64 | int 65 | ipset_save_to_stream(struct cork_stream_consumer *stream, 66 | const struct ip_set *set) 67 | { 68 | return ipset_node_cache_save(stream, set->cache, set->set_bdd); 69 | } 70 | 71 | int 72 | ipset_save(FILE *fp, const struct ip_set *set) 73 | { 74 | struct file_consumer stream = { 75 | { file_consumer_data, file_consumer_eof, NULL }, fp 76 | }; 77 | return ipset_save_to_stream(&stream.parent, set); 78 | } 79 | 80 | 81 | int 82 | ipset_save_dot(FILE *fp, const struct ip_set *set) 83 | { 84 | struct file_consumer stream = { 85 | { file_consumer_data, file_consumer_eof, NULL }, fp 86 | }; 87 | return ipset_node_cache_save_dot(&stream.parent, set->cache, set->set_bdd); 88 | } 89 | 90 | 91 | struct ip_set * 92 | ipset_load(FILE *stream) 93 | { 94 | struct ip_set *set; 95 | ipset_node_id new_bdd; 96 | 97 | set = ipset_new(); 98 | new_bdd = ipset_node_cache_load(stream, set->cache); 99 | if (cork_error_occurred()) { 100 | ipset_free(set); 101 | return NULL; 102 | } 103 | 104 | set->set_bdd = new_bdd; 105 | return set; 106 | } 107 | -------------------------------------------------------------------------------- /src/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # ---------------------------------------------------------------------- 3 | # Copyright © 2011, libcorkipset authors 4 | # All rights reserved. 5 | # 6 | # Please see the COPYING file in this distribution for license details. 7 | # ---------------------------------------------------------------------- 8 | 9 | #----------------------------------------------------------------------- 10 | # Build the library 11 | 12 | # Update the VERSION_INFO property below according to the following rules (taken 13 | # from [1]): 14 | # 15 | # VERSION_INFO = current:revision:age 16 | # 17 | # 1. Start with a VERSION of `0:0:0` for each shared library. 18 | # 2. Update VERSION_INFO only immediately before a public release of your 19 | # software. More frequent updates are unnecessary, and only guarantee that 20 | # the current interface number gets larger faster. 21 | # 3. If the library source code has changed at all since the last update, then 22 | # increment `revision` (`c:r:a` becomes `c:r+1:a`). 23 | # 4. If any interfaces have been added, removed, or changed since the last 24 | # update, increment `current`, and set `revision` to 0. 25 | # 5. If any interfaces have been added since the last public release, then 26 | # increment `age`. 27 | # 6. If any interfaces have been removed or changed since the last public 28 | # release, then set `age` to 0. 29 | # 30 | # Note that changing `current` and setting `age` to 0 means that you are 31 | # releasing a new backwards-incompatible version of the library. This has 32 | # implications on packaging, so once an API has stabilized, this should be a 33 | # rare occurrence. 34 | # 35 | # [1] http://www.gnu.org/software/libtool/manual/html_node/Updating-version-info.html#Updating-version-info 36 | 37 | add_c_library( 38 | libipset 39 | OUTPUT_NAME ipset 40 | PKGCONFIG_NAME ipset 41 | VERSION_INFO 2:0:1 42 | SOURCES 43 | libipset/general.c 44 | libipset/bdd/assignments.c 45 | libipset/bdd/basics.c 46 | libipset/bdd/bdd-iterator.c 47 | libipset/bdd/expanded.c 48 | libipset/bdd/reachable.c 49 | libipset/bdd/read.c 50 | libipset/bdd/write.c 51 | libipset/map/allocation.c 52 | libipset/map/inspection.c 53 | libipset/map/ipv4_map.c 54 | libipset/map/ipv6_map.c 55 | libipset/map/storage.c 56 | libipset/set/allocation.c 57 | libipset/set/inspection.c 58 | libipset/set/ipv4_set.c 59 | libipset/set/ipv6_set.c 60 | libipset/set/iterator.c 61 | libipset/set/storage.c 62 | LIBRARIES 63 | libcork 64 | ) 65 | 66 | #----------------------------------------------------------------------- 67 | # Utility commands 68 | 69 | add_c_executable( 70 | ipsetbuild 71 | OUTPUT_NAME ipsetbuild 72 | SOURCES ipsetbuild/ipsetbuild.c 73 | LOCAL_LIBRARIES 74 | libipset 75 | ) 76 | 77 | add_c_executable( 78 | ipsetcat 79 | OUTPUT_NAME ipsetcat 80 | SOURCES ipsetcat/ipsetcat.c 81 | LOCAL_LIBRARIES 82 | libipset 83 | ) 84 | 85 | add_c_executable( 86 | ipsetdot 87 | OUTPUT_NAME ipsetdot 88 | SOURCES ipsetdot/ipsetdot.c 89 | LOCAL_LIBRARIES 90 | libipset 91 | ) 92 | -------------------------------------------------------------------------------- /cmake/FindPrereqs.cmake: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # ---------------------------------------------------------------------- 3 | # Copyright © 2015, libcorkipset authors 4 | # All rights reserved. 5 | # 6 | # Please see the COPYING file in this distribution for license details. 7 | # ---------------------------------------------------------------------- 8 | 9 | 10 | #----------------------------------------------------------------------- 11 | # Configuration options that control all of the below 12 | 13 | set(PKG_CONFIG_PATH CACHE STRING "pkg-config search path") 14 | if (PKG_CONFIG_PATH) 15 | set(ENV{PKG_CONFIG_PATH} "${PKG_CONFIG_PATH}:$ENV{PKG_CONFIG_PATH}") 16 | endif (PKG_CONFIG_PATH) 17 | 18 | 19 | #----------------------------------------------------------------------- 20 | # pkg-config prerequisites 21 | 22 | find_package(PkgConfig) 23 | 24 | function(pkgconfig_prereq DEP) 25 | set(options OPTIONAL) 26 | set(one_args) 27 | set(multi_args) 28 | cmake_parse_arguments(_ "${options}" "${one_args}" "${multi_args}" ${ARGN}) 29 | 30 | string(REGEX REPLACE "[<>=].*" "" SHORT_NAME "${DEP}") 31 | string(REPLACE "-" "_" SHORT_NAME "${SHORT_NAME}") 32 | string(TOUPPER ${SHORT_NAME} UPPER_SHORT_NAME) 33 | string(TOLOWER ${SHORT_NAME} LOWER_SHORT_NAME) 34 | 35 | set(USE_CUSTOM_${UPPER_SHORT_NAME} NO CACHE BOOL 36 | "Whether you want to provide custom details for ${LOWER_SHORT_NAME}") 37 | 38 | if (NOT USE_CUSTOM_${UPPER_SHORT_NAME}) 39 | set(PKG_CHECK_ARGS) 40 | if (NOT __OPTIONAL) 41 | list(APPEND PKG_CHECK_ARGS REQUIRED) 42 | endif (NOT __OPTIONAL) 43 | list(APPEND PKG_CHECK_ARGS ${DEP}) 44 | 45 | pkg_check_modules(${UPPER_SHORT_NAME} ${PKG_CHECK_ARGS}) 46 | endif (NOT USE_CUSTOM_${UPPER_SHORT_NAME}) 47 | 48 | include_directories(${${UPPER_SHORT_NAME}_INCLUDE_DIRS}) 49 | link_directories(${${UPPER_SHORT_NAME}_LIBRARY_DIRS}) 50 | endfunction(pkgconfig_prereq) 51 | 52 | 53 | #----------------------------------------------------------------------- 54 | # find_library prerequisites 55 | 56 | function(library_prereq LIB_NAME) 57 | set(options OPTIONAL) 58 | set(one_args) 59 | set(multi_args) 60 | cmake_parse_arguments(_ "${options}" "${one_args}" "${multi_args}" ${ARGN}) 61 | 62 | string(REPLACE "-" "_" SHORT_NAME "${LIB_NAME}") 63 | string(TOUPPER ${SHORT_NAME} UPPER_SHORT_NAME) 64 | string(TOLOWER ${SHORT_NAME} LOWER_SHORT_NAME) 65 | 66 | set(USE_CUSTOM_${UPPER_SHORT_NAME} NO CACHE BOOL 67 | "Whether you want to provide custom details for ${LOWER_SHORT_NAME}") 68 | 69 | if (USE_CUSTOM_${UPPER_SHORT_NAME}) 70 | include_directories(${${UPPER_SHORT_NAME}_INCLUDE_DIRS}) 71 | link_directories(${${UPPER_SHORT_NAME}_LIBRARY_DIRS}) 72 | if (NOT ${UPPER_SHORT_NAME}_STATIC_LDFLAGS) 73 | set(${UPPER_SHORT_NAME}_STATIC_LDFLAGS 74 | ${${UPPER_SHORT_NAME}_LDFLAGS} 75 | PARENT_SCOPE) 76 | endif (NOT ${UPPER_SHORT_NAME}_STATIC_LDFLAGS) 77 | else (USE_CUSTOM_${UPPER_SHORT_NAME}) 78 | find_library(${UPPER_SHORT_NAME}_LDFLAGS ${LIB_NAME}) 79 | set(${UPPER_SHORT_NAME}_STATIC_LDFLAGS 80 | ${${UPPER_SHORT_NAME}_LDFLAGS} 81 | PARENT_SCOPE) 82 | endif (USE_CUSTOM_${UPPER_SHORT_NAME}) 83 | 84 | endfunction(library_prereq) 85 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # ---------------------------------------------------------------------- 3 | # Copyright © 2011, libcorkipset authors 4 | # All rights reserved. 5 | # 6 | # Please see the COPYING file in this distribution for license details. 7 | # ---------------------------------------------------------------------- 8 | 9 | cmake_minimum_required(VERSION 2.6) 10 | set(PROJECT_NAME ipset) 11 | set(RELEASE_DATE 2013-12-11) 12 | project(${PROJECT_NAME}) 13 | enable_testing() 14 | 15 | set(CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/cmake") 16 | find_package(ParseArguments) 17 | find_package(Prereqs) 18 | find_package(CTargets) 19 | include(GNUInstallDirs) 20 | 21 | #----------------------------------------------------------------------- 22 | # Retrieve the current version number 23 | 24 | execute_process( 25 | COMMAND git describe 26 | WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} 27 | RESULT_VARIABLE VERSION_RESULT 28 | OUTPUT_VARIABLE VERSION 29 | OUTPUT_STRIP_TRAILING_WHITESPACE 30 | ) 31 | if(VERSION_RESULT) 32 | message(FATAL_ERROR 33 | "Cannot determine version number: " ${VERSION_RESULT}) 34 | endif(VERSION_RESULT) 35 | message(STATUS "Current version: " ${VERSION}) 36 | 37 | string(REGEX REPLACE "-.*" "-dev" BASE_VERSION "${VERSION}") 38 | 39 | if(BASE_VERSION MATCHES "^([0-9]+)\\.([0-9]+)\\.([0-9]+)(-dev)?$") 40 | set(VERSION_MAJOR "${CMAKE_MATCH_1}") 41 | set(VERSION_MINOR "${CMAKE_MATCH_2}") 42 | set(VERSION_PATCH "${CMAKE_MATCH_3}") 43 | else(BASE_VERSION MATCHES "^([0-9]+)\\.([0-9]+)\\.([0-9]+)(-dev)?$") 44 | message(FATAL_ERROR "Invalid version number: ${VERSION}") 45 | endif(BASE_VERSION MATCHES "^([0-9]+)\\.([0-9]+)\\.([0-9]+)(-dev)?$") 46 | 47 | execute_process( 48 | COMMAND git rev-parse HEAD 49 | WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} 50 | RESULT_VARIABLE GIT_SHA1_RESULT 51 | OUTPUT_VARIABLE GIT_SHA1 52 | OUTPUT_STRIP_TRAILING_WHITESPACE 53 | ) 54 | if(GIT_SHA1_RESULT) 55 | message(FATAL_ERROR 56 | "Cannot determine git commit: " ${GIT_SHA1_RESULT}) 57 | endif(GIT_SHA1_RESULT) 58 | message(STATUS "Current revision: " ${GIT_SHA1}) 59 | 60 | #----------------------------------------------------------------------- 61 | # Set some options 62 | 63 | if(APPLE) 64 | if (NOT CMAKE_INSTALL_NAME_DIR) 65 | set(CMAKE_INSTALL_NAME_DIR "${CMAKE_INSTALL_PREFIX}/lib") 66 | endif (NOT CMAKE_INSTALL_NAME_DIR) 67 | endif(APPLE) 68 | 69 | if(NOT CMAKE_BUILD_TYPE) 70 | set(CMAKE_BUILD_TYPE Release CACHE STRING 71 | "Choose the type of build, options are: None Debug Release RelWithDebInfo MinSizeRel." 72 | FORCE) 73 | endif(NOT CMAKE_BUILD_TYPE) 74 | 75 | if(NOT CMAKE_INSTALL_LIBDIR) 76 | set(CMAKE_INSTALL_LIBDIR lib CACHE STRING 77 | "The base name of the installation directory for libraries") 78 | endif(NOT CMAKE_INSTALL_LIBDIR) 79 | 80 | if(CMAKE_C_COMPILER_ID STREQUAL "GNU") 81 | add_definitions(-Wall -Werror) 82 | elseif(CMAKE_C_COMPILER_ID STREQUAL "Clang") 83 | add_definitions(-Wall -Werror) 84 | elseif(CMAKE_C_COMPILER_ID STREQUAL "Intel") 85 | add_definitions(-Wall -Werror) 86 | endif(CMAKE_C_COMPILER_ID STREQUAL "GNU") 87 | 88 | #----------------------------------------------------------------------- 89 | # Check for prerequisite libraries 90 | 91 | pkgconfig_prereq(libcork>=0.12.0) 92 | 93 | #----------------------------------------------------------------------- 94 | # Include our subdirectories 95 | 96 | add_subdirectory(docs) 97 | add_subdirectory(include) 98 | add_subdirectory(share) 99 | add_subdirectory(src) 100 | add_subdirectory(examples) 101 | add_subdirectory(tests) 102 | -------------------------------------------------------------------------------- /src/libipset/set/inspection-template.c.in: -------------------------------------------------------------------------------- 1 | /* -*- coding: utf-8 -*- 2 | * ---------------------------------------------------------------------- 3 | * Copyright © 2012, libcorkipset authors 4 | * All rights reserved. 5 | * 6 | * Please see the COPYING file in this distribution for license details. 7 | * ---------------------------------------------------------------------- 8 | */ 9 | 10 | #include 11 | 12 | #include "ipset/bdd/nodes.h" 13 | #include "ipset/bits.h" 14 | #include "ipset/errors.h" 15 | #include "ipset/ipset.h" 16 | 17 | 18 | /** 19 | * Given a BDD variable number, return the index of the corresponding 20 | * bit in an IP address. IPv4 addresses use variables 1-32; IPv6 21 | * addresses use 1-128. (Variable 0 is used to identify the kind of 22 | * address — TRUE for IPv4, FALSE for IPv6.) 23 | */ 24 | 25 | static unsigned int 26 | IPSET_NAME(bit_for_var)(ipset_variable var) 27 | { 28 | return (var - 1); 29 | } 30 | 31 | 32 | /** 33 | * An assignment function that can be used to evaluate an IP set BDD. 34 | */ 35 | 36 | static bool 37 | IPSET_NAME(assignment)(const void *addr, ipset_variable var) 38 | { 39 | if (var == 0) { 40 | return IP_DISCRIMINATOR_VALUE; 41 | } else { 42 | unsigned int bit = IPSET_NAME(bit_for_var)(var); 43 | return IPSET_BIT_GET(addr, bit); 44 | } 45 | } 46 | 47 | 48 | bool 49 | IPSET_PRENAME(contains)(const struct ip_set *set, CORK_IP *elem) 50 | { 51 | return ipset_node_evaluate 52 | (set->cache, set->set_bdd, IPSET_NAME(assignment), elem); 53 | } 54 | 55 | 56 | bool 57 | IPSET_NAME(add_network)(struct ip_set *set, CORK_IP *elem, 58 | unsigned int cidr_prefix) 59 | { 60 | /* Special case — the BDD for a netmask that's out of range never 61 | * evaluates to true. */ 62 | if (cidr_prefix > IP_BIT_SIZE) { 63 | cork_error_set 64 | (IPSET_ERROR, IPSET_PARSE_ERROR, 65 | "CIDR block %u out of range [0..%u]", cidr_prefix, IP_BIT_SIZE); 66 | return false; 67 | } 68 | 69 | ipset_node_id new_bdd = 70 | ipset_node_insert 71 | (set->cache, set->set_bdd, 72 | IPSET_NAME(assignment), elem, cidr_prefix + 1, 1); 73 | bool result = (new_bdd == set->set_bdd); 74 | ipset_node_decref(set->cache, set->set_bdd); 75 | set->set_bdd = new_bdd; 76 | return result; 77 | } 78 | 79 | 80 | bool 81 | IPSET_NAME(add)(struct ip_set *set, CORK_IP *elem) 82 | { 83 | ipset_node_id new_bdd = 84 | ipset_node_insert 85 | (set->cache, set->set_bdd, 86 | IPSET_NAME(assignment), elem, IP_BIT_SIZE + 1, 1); 87 | bool result = (new_bdd == set->set_bdd); 88 | ipset_node_decref(set->cache, set->set_bdd); 89 | set->set_bdd = new_bdd; 90 | return result; 91 | } 92 | 93 | 94 | bool 95 | IPSET_NAME(remove)(struct ip_set *set, CORK_IP *elem) 96 | { 97 | ipset_node_id new_bdd = 98 | ipset_node_insert 99 | (set->cache, set->set_bdd, 100 | IPSET_NAME(assignment), elem, IP_BIT_SIZE + 1, 0); 101 | bool result = (new_bdd == set->set_bdd); 102 | ipset_node_decref(set->cache, set->set_bdd); 103 | set->set_bdd = new_bdd; 104 | return result; 105 | } 106 | 107 | 108 | bool 109 | IPSET_NAME(remove_network)(struct ip_set *set, CORK_IP *elem, 110 | unsigned int cidr_prefix) 111 | { 112 | /* Special case — the BDD for a netmask that's out of range never 113 | * evaluates to true. */ 114 | if (cidr_prefix > IP_BIT_SIZE) { 115 | cork_error_set 116 | (IPSET_ERROR, IPSET_PARSE_ERROR, 117 | "CIDR block %u out of range [0..%u]", cidr_prefix, IP_BIT_SIZE); 118 | return false; 119 | } 120 | 121 | ipset_node_id new_bdd = 122 | ipset_node_insert 123 | (set->cache, set->set_bdd, 124 | IPSET_NAME(assignment), elem, cidr_prefix + 1, 0); 125 | bool result = (new_bdd == set->set_bdd); 126 | ipset_node_decref(set->cache, set->set_bdd); 127 | set->set_bdd = new_bdd; 128 | return result; 129 | } 130 | -------------------------------------------------------------------------------- /src/libipset/bdd/assignments.c: -------------------------------------------------------------------------------- 1 | /* -*- coding: utf-8 -*- 2 | * ---------------------------------------------------------------------- 3 | * Copyright © 2010, libcorkipset authors 4 | * All rights reserved. 5 | * 6 | * Please see the COPYING file in this distribution for license details. 7 | * ---------------------------------------------------------------------- 8 | */ 9 | 10 | #include 11 | 12 | #include "ipset/bdd/nodes.h" 13 | 14 | 15 | struct ipset_assignment * 16 | ipset_assignment_new() 17 | { 18 | struct ipset_assignment *assignment = cork_new(struct ipset_assignment); 19 | cork_array_init(&assignment->values); 20 | return assignment; 21 | } 22 | 23 | 24 | void 25 | ipset_assignment_free(struct ipset_assignment *assignment) 26 | { 27 | cork_array_done(&assignment->values); 28 | free(assignment); 29 | } 30 | 31 | 32 | bool 33 | ipset_assignment_equal(const struct ipset_assignment *assignment1, 34 | const struct ipset_assignment *assignment2) 35 | { 36 | /* Identical pointers are trivially equal. */ 37 | if (assignment1 == assignment2) { 38 | return true; 39 | } 40 | 41 | /* Otherwise we compare the assignments piecewise up through the end 42 | * of the smaller vector. */ 43 | unsigned int size1 = cork_array_size(&assignment1->values); 44 | unsigned int size2 = cork_array_size(&assignment2->values); 45 | unsigned int smaller_size = (size1 < size2)? size1: size2; 46 | 47 | unsigned int i; 48 | for (i = 0; i < smaller_size; i++) { 49 | if (cork_array_at(&assignment1->values, i) != 50 | cork_array_at(&assignment2->values, i)) { 51 | return false; 52 | } 53 | } 54 | 55 | /* If one of the assignment vectors is longer, any remaining 56 | * elements must be indeterminate. */ 57 | if (size1 > smaller_size) { 58 | for (i = smaller_size; i < size1; i++) { 59 | if (cork_array_at(&assignment1->values, i) != IPSET_EITHER) { 60 | return false; 61 | } 62 | } 63 | } 64 | 65 | if (size2 > smaller_size) { 66 | for (i = smaller_size; i < size2; i++) { 67 | if (cork_array_at(&assignment2->values, i) != IPSET_EITHER) { 68 | return false; 69 | } 70 | } 71 | } 72 | 73 | /* If we make it through all of that, the two assignments are equal. */ 74 | return true; 75 | } 76 | 77 | 78 | void 79 | ipset_assignment_cut(struct ipset_assignment *assignment, 80 | ipset_variable var) 81 | { 82 | if (var < cork_array_size(&assignment->values)) { 83 | assignment->values.size = var; 84 | } 85 | } 86 | 87 | 88 | void 89 | ipset_assignment_clear(struct ipset_assignment *assignment) 90 | { 91 | ipset_assignment_cut(assignment, 0); 92 | } 93 | 94 | 95 | enum ipset_tribool 96 | ipset_assignment_get(struct ipset_assignment *assignment, ipset_variable var) 97 | { 98 | if (var < cork_array_size(&assignment->values)) { 99 | /* If the requested variable is in the range of the values 100 | * array, return whatever is stored there. */ 101 | return cork_array_at(&assignment->values, var); 102 | } else { 103 | /* Variables htat aren't in the values array are always EITHER. */ 104 | return IPSET_EITHER; 105 | } 106 | } 107 | 108 | 109 | void 110 | ipset_assignment_set(struct ipset_assignment *assignment, 111 | ipset_variable var, enum ipset_tribool value) 112 | { 113 | /* Ensure that the vector is big enough to hold this variable 114 | * assignment, inserting new EITHERs if needed. */ 115 | if (var >= cork_array_size(&assignment->values)) { 116 | unsigned int old_len = cork_array_size(&assignment->values); 117 | 118 | /* Expand the array. */ 119 | cork_array_ensure_size(&assignment->values, var+1); 120 | assignment->values.size = var+1; 121 | 122 | /* Fill in EITHERs in the newly allocated elements. */ 123 | if (var != old_len) { 124 | unsigned int i; 125 | for (i = old_len; i < var; i++) { 126 | cork_array_at(&assignment->values, i) = IPSET_EITHER; 127 | } 128 | } 129 | } 130 | 131 | /* Assign the desired value. */ 132 | cork_array_at(&assignment->values, var) = value; 133 | } 134 | -------------------------------------------------------------------------------- /docs/_static/pygments.css: -------------------------------------------------------------------------------- 1 | .highlight .hll { background-color: #ffffcc } 2 | .highlight .c { color: #408080; font-style: italic } /* Comment */ 3 | .highlight .err { border: 1px solid #FF0000 } /* Error */ 4 | .highlight .k { color: #954121 } /* Keyword */ 5 | .highlight .o { color: #666666 } /* Operator */ 6 | .highlight .cm { color: #408080; font-style: italic } /* Comment.Multiline */ 7 | .highlight .cp { color: #BC7A00 } /* Comment.Preproc */ 8 | .highlight .c1 { color: #408080; font-style: italic } /* Comment.Single */ 9 | .highlight .cs { color: #408080; font-style: italic } /* Comment.Special */ 10 | .highlight .gd { color: #A00000 } /* Generic.Deleted */ 11 | .highlight .ge { font-style: italic } /* Generic.Emph */ 12 | .highlight .gr { color: #FF0000 } /* Generic.Error */ 13 | .highlight .gh { color: #000080; font-weight: bold } /* Generic.Heading */ 14 | .highlight .gi { color: #00A000 } /* Generic.Inserted */ 15 | .highlight .go { color: #808080 } /* Generic.Output */ 16 | .highlight .gp { color: #000080; font-weight: bold } /* Generic.Prompt */ 17 | .highlight .gs { font-weight: bold } /* Generic.Strong */ 18 | .highlight .gu { color: #800080; font-weight: bold } /* Generic.Subheading */ 19 | .highlight .gt { color: #0040D0 } /* Generic.Traceback */ 20 | .highlight .kc { color: #954121 } /* Keyword.Constant */ 21 | .highlight .kd { color: #954121; font-weight: bold } /* Keyword.Declaration */ 22 | .highlight .kn { color: #954121; font-weight: bold } /* Keyword.Namespace */ 23 | .highlight .kp { color: #954121 } /* Keyword.Pseudo */ 24 | .highlight .kr { color: #954121; font-weight: bold } /* Keyword.Reserved */ 25 | .highlight .kt { color: #B00040 } /* Keyword.Type */ 26 | .highlight .m { color: #666666 } /* Literal.Number */ 27 | .highlight .s { color: #219161 } /* Literal.String */ 28 | .highlight .na { color: #7D9029 } /* Name.Attribute */ 29 | .highlight .nb { color: #954121 } /* Name.Builtin */ 30 | .highlight .nc { color: #0000FF; font-weight: bold } /* Name.Class */ 31 | .highlight .no { color: #880000 } /* Name.Constant */ 32 | .highlight .nd { color: #AA22FF } /* Name.Decorator */ 33 | .highlight .ni { color: #999999; font-weight: bold } /* Name.Entity */ 34 | .highlight .ne { color: #D2413A; font-weight: bold } /* Name.Exception */ 35 | .highlight .nf { color: #0000FF } /* Name.Function */ 36 | .highlight .nl { color: #A0A000 } /* Name.Label */ 37 | .highlight .nn { color: #0000FF; font-weight: bold } /* Name.Namespace */ 38 | .highlight .nt { color: #954121; font-weight: bold } /* Name.Tag */ 39 | .highlight .nv { color: #19469D } /* Name.Variable */ 40 | .highlight .ow { color: #AA22FF; font-weight: bold } /* Operator.Word */ 41 | .highlight .w { color: #bbbbbb } /* Text.Whitespace */ 42 | .highlight .mf { color: #666666 } /* Literal.Number.Float */ 43 | .highlight .mh { color: #666666 } /* Literal.Number.Hex */ 44 | .highlight .mi { color: #666666 } /* Literal.Number.Integer */ 45 | .highlight .mo { color: #666666 } /* Literal.Number.Oct */ 46 | .highlight .sb { color: #219161 } /* Literal.String.Backtick */ 47 | .highlight .sc { color: #219161 } /* Literal.String.Char */ 48 | .highlight .sd { color: #219161; font-style: italic } /* Literal.String.Doc */ 49 | .highlight .s2 { color: #219161 } /* Literal.String.Double */ 50 | .highlight .se { color: #BB6622; font-weight: bold } /* Literal.String.Escape */ 51 | .highlight .sh { color: #219161 } /* Literal.String.Heredoc */ 52 | .highlight .si { color: #BB6688; font-weight: bold } /* Literal.String.Interpol */ 53 | .highlight .sx { color: #954121 } /* Literal.String.Other */ 54 | .highlight .sr { color: #BB6688 } /* Literal.String.Regex */ 55 | .highlight .s1 { color: #219161 } /* Literal.String.Single */ 56 | .highlight .ss { color: #19469D } /* Literal.String.Symbol */ 57 | .highlight .bp { color: #954121 } /* Name.Builtin.Pseudo */ 58 | .highlight .vc { color: #19469D } /* Name.Variable.Class */ 59 | .highlight .vg { color: #19469D } /* Name.Variable.Global */ 60 | .highlight .vi { color: #19469D } /* Name.Variable.Instance */ 61 | .highlight .il { color: #666666 } /* Literal.Number.Integer.Long */ 62 | -------------------------------------------------------------------------------- /src/libipset/bdd/expanded.c: -------------------------------------------------------------------------------- 1 | /* -*- coding: utf-8 -*- 2 | * ---------------------------------------------------------------------- 3 | * Copyright © 2010, libcorkipset authors 4 | * All rights reserved. 5 | * 6 | * Please see the COPYING file in this distribution for license details. 7 | * ---------------------------------------------------------------------- 8 | */ 9 | 10 | #include 11 | 12 | #include 13 | 14 | #include "ipset/bdd/nodes.h" 15 | #include "ipset/bits.h" 16 | #include "ipset/logging.h" 17 | 18 | 19 | static void 20 | initialize(struct ipset_expanded_assignment *exp, 21 | const struct ipset_assignment *assignment, 22 | ipset_variable var_count) 23 | { 24 | /* First loop through all of the variables in the assignment vector, 25 | * making sure not to go further than the caller requested. */ 26 | 27 | ipset_variable last_assignment = cork_array_size(&assignment->values); 28 | if (var_count < last_assignment) { 29 | last_assignment = var_count; 30 | } 31 | 32 | ipset_variable var; 33 | for (var = 0; var < last_assignment; var++) { 34 | enum ipset_tribool curr_value = 35 | cork_array_at(&assignment->values, var); 36 | 37 | if (curr_value == IPSET_EITHER) { 38 | /* If this variable is EITHER, start it off as FALSE, and 39 | * add it to the eithers list. */ 40 | DEBUG("Variable %u is EITHER", var); 41 | 42 | IPSET_BIT_SET(exp->values.buf, var, false); 43 | cork_array_append(&exp->eithers, var); 44 | } else { 45 | /* Otherwise set the variable to the same value in the 46 | * expanded assignment as it is in the non-expanded one. */ 47 | 48 | DEBUG("Variable %u is %s", var, curr_value? "true": "false"); 49 | IPSET_BIT_SET(exp->values.buf, var, curr_value); 50 | } 51 | } 52 | 53 | /* If the caller requested more variables than there are in the 54 | * assignment vector, add them to the eithers list. */ 55 | for (var = last_assignment; var < var_count; var++) { 56 | DEBUG("Variable %u is implicitly EITHER", var); 57 | cork_array_append(&exp->eithers, var); 58 | } 59 | } 60 | 61 | 62 | struct ipset_expanded_assignment * 63 | ipset_assignment_expand(const struct ipset_assignment *assignment, 64 | ipset_variable var_count) 65 | { 66 | /* First allocate the iterator itself, and all of its contained 67 | * fields. */ 68 | 69 | struct ipset_expanded_assignment *exp; 70 | unsigned int values_size = (var_count / 8) + ((var_count % 8) != 0); 71 | 72 | exp = cork_new(struct ipset_expanded_assignment); 73 | exp->finished = false; 74 | cork_buffer_init(&exp->values); 75 | cork_buffer_ensure_size(&exp->values, values_size); 76 | memset(exp->values.buf, 0, values_size); 77 | cork_array_init(&exp->eithers); 78 | 79 | /* Then initialize the values and eithers fields. */ 80 | initialize(exp, assignment, var_count); 81 | return exp; 82 | } 83 | 84 | 85 | void 86 | ipset_expanded_assignment_free(struct ipset_expanded_assignment *exp) 87 | { 88 | if (exp == NULL) { 89 | return; 90 | } 91 | 92 | cork_buffer_done(&exp->values); 93 | cork_array_done(&exp->eithers); 94 | free(exp); 95 | } 96 | 97 | 98 | void 99 | ipset_expanded_assignment_advance(struct ipset_expanded_assignment *exp) 100 | { 101 | /* If we're already at the end of the iterator, don't do anything. */ 102 | if (CORK_UNLIKELY(exp->finished)) { 103 | return; 104 | } 105 | 106 | DEBUG("Advancing iterator"); 107 | 108 | /* Look at the last EITHER bit in the assignment. If it's 0, then 109 | * set it to 1 and return. Otherwise we set it to 0 and carry up to 110 | * the previous indeterminate bit. */ 111 | 112 | size_t i; 113 | for (i = cork_array_size(&exp->eithers); i > 0; i--) { 114 | size_t idx = i - 1; 115 | ipset_variable either_var = cork_array_at(&exp->eithers, idx); 116 | DEBUG("Checking EITHER variable %u", either_var); 117 | 118 | if (IPSET_BIT_GET(exp->values.buf, either_var)) { 119 | /* This variable is currently true, so set it back to false 120 | * and carry. */ 121 | DEBUG(" Variable %u is true, changing to false and carrying", 122 | either_var); 123 | IPSET_BIT_SET(exp->values.buf, either_var, false); 124 | } else { 125 | /* This variable is currently false, so set it to true and 126 | * return. */ 127 | DEBUG(" Variable %u is false, changing to true", 128 | either_var); 129 | IPSET_BIT_SET(exp->values.buf, either_var, true); 130 | return; 131 | } 132 | } 133 | 134 | /* If we fall through then we've made it through all of the expanded 135 | * assignments. */ 136 | exp->finished = true; 137 | } 138 | -------------------------------------------------------------------------------- /src/libipset/bdd/bdd-iterator.c: -------------------------------------------------------------------------------- 1 | /* -*- coding: utf-8 -*- 2 | * ---------------------------------------------------------------------- 3 | * Copyright © 2010, libcorkipset authors 4 | * All rights reserved. 5 | * 6 | * Please see the COPYING file in this distribution for license details. 7 | * ---------------------------------------------------------------------- 8 | */ 9 | 10 | #include 11 | 12 | #include "ipset/bdd/nodes.h" 13 | #include "ipset/logging.h" 14 | 15 | 16 | /** 17 | * Add the given node ID to the node stack, and trace down from it 18 | * until we find a terminal node. Assign values to the variables for 19 | * each nonterminal that encounter along the way. We check low edges 20 | * first, so each new variable we encounter will be assigned FALSE. 21 | * (The high edges will be checked eventually by a call to the 22 | * ipset_bdd_iterator_advance() function.) 23 | */ 24 | static void 25 | add_node(struct ipset_bdd_iterator *iterator, ipset_node_id node_id) 26 | { 27 | /* Keep tracing down low edges until we reach a terminal. */ 28 | while (ipset_node_get_type(node_id) == IPSET_NONTERMINAL_NODE) { 29 | /* Add this nonterminal node to the stack, and trace down 30 | * further into the tree. We check low edges first, so set the 31 | * node's variable to FALSE in the assignment. */ 32 | struct ipset_node *node = 33 | ipset_node_cache_get_nonterminal(iterator->cache, node_id); 34 | 35 | cork_array_append(&iterator->stack, node_id); 36 | ipset_assignment_set(iterator->assignment, node->variable, false); 37 | 38 | node_id = node->low; 39 | } 40 | 41 | /* Once we find a terminal node, save it away in the iterator result 42 | * and return. */ 43 | iterator->value = ipset_terminal_value(node_id); 44 | } 45 | 46 | 47 | struct ipset_bdd_iterator * 48 | ipset_node_iterate(struct ipset_node_cache *cache, ipset_node_id root) 49 | { 50 | /* First allocate the iterator itself, and all of its contained 51 | * fields. */ 52 | 53 | struct ipset_bdd_iterator *iterator = 54 | cork_new(struct ipset_bdd_iterator); 55 | iterator->finished = false; 56 | iterator->cache = cache; 57 | cork_array_init(&iterator->stack); 58 | iterator->assignment = ipset_assignment_new(); 59 | 60 | /* Then add the root node to the iterator, tracing down until we 61 | * find the first terminal node. */ 62 | add_node(iterator, root); 63 | return iterator; 64 | } 65 | 66 | 67 | void 68 | ipset_bdd_iterator_free(struct ipset_bdd_iterator *iterator) 69 | { 70 | cork_array_done(&iterator->stack); 71 | ipset_assignment_free(iterator->assignment); 72 | free(iterator); 73 | } 74 | 75 | 76 | void 77 | ipset_bdd_iterator_advance(struct ipset_bdd_iterator *iterator) 78 | { 79 | /* If we're already at the end of the iterator, don't do anything. */ 80 | if (CORK_UNLIKELY(iterator->finished)) { 81 | return; 82 | } 83 | 84 | /* We look at the last node in the stack. If it's currently 85 | * assigned a false value, then we track down its true branch. If 86 | * it's got a true branch, then we pop it off and check the next to 87 | * last node. */ 88 | 89 | DEBUG("Advancing BDD iterator"); 90 | 91 | while (cork_array_size(&iterator->stack) > 0) { 92 | ipset_node_id last_node_id = 93 | cork_array_at 94 | (&iterator->stack, cork_array_size(&iterator->stack) - 1); 95 | 96 | struct ipset_node *last_node = 97 | ipset_node_cache_get_nonterminal(iterator->cache, last_node_id); 98 | 99 | enum ipset_tribool current_value = 100 | ipset_assignment_get(iterator->assignment, last_node->variable); 101 | 102 | /* The current value can't be EITHER, because we definitely 103 | * assign a TRUE or FALSE to the variables of the nodes that we 104 | * encounter. */ 105 | if (current_value == IPSET_TRUE) { 106 | /* We've checked both outgoing edges for this node, so pop 107 | * it off and look at its parent. */ 108 | iterator->stack.size--; 109 | 110 | /* Before continuing, reset this node's variable to 111 | * indeterminate in the assignment. */ 112 | ipset_assignment_set 113 | (iterator->assignment, last_node->variable, IPSET_EITHER); 114 | } else { 115 | /* We've checked this node's low edge, but not its high 116 | * edge. Set the variable to TRUE in the assignment, and 117 | * add the high edge's node to the node stack. */ 118 | ipset_assignment_set 119 | (iterator->assignment, last_node->variable, IPSET_TRUE); 120 | add_node(iterator, last_node->high); 121 | return; 122 | } 123 | } 124 | 125 | /* If we fall through then we ran out of nodes to check. That means 126 | * the iterator is done! */ 127 | iterator->finished = true; 128 | } 129 | -------------------------------------------------------------------------------- /src/ipsetdot/ipsetdot.c: -------------------------------------------------------------------------------- 1 | /* -*- coding: utf-8 -*- 2 | * ---------------------------------------------------------------------- 3 | * Copyright © 2010, libcorkipset authors 4 | * All rights reserved. 5 | * 6 | * Please see the COPYING file in this distribution for license details. 7 | * ---------------------------------------------------------------------- 8 | */ 9 | 10 | 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | 18 | #include 19 | 20 | #include "ipset/ipset.h" 21 | 22 | 23 | static char *input_filename = NULL; 24 | static char *output_filename = "-"; 25 | static bool verbose = false; 26 | 27 | 28 | static struct option longopts[] = { 29 | { "help", no_argument, NULL, 'h' }, 30 | { "output", required_argument, NULL, 'o' }, 31 | { "verbose", 0, NULL, 'v' }, 32 | { NULL, 0, NULL, 0 } 33 | }; 34 | 35 | #define USAGE \ 36 | "Usage: ipsetdot [options] \n" 37 | 38 | #define FULL_USAGE \ 39 | USAGE \ 40 | "\n" \ 41 | "Creates a GraphViz file showing the BDD structure of an IP set.\n" \ 42 | "\n" \ 43 | "Options:\n" \ 44 | " \n" \ 45 | " The binary set file to read. To read from stdin, use \"-\" as the\n" \ 46 | " filename.\n" \ 47 | " --output=, -o \n" \ 48 | " Writes the GraphViz representation of the binary IP set file to\n" \ 49 | " . If this option isn't given, then the contents will be\n" \ 50 | " written to standard output.\n" \ 51 | " --verbose, -v\n" \ 52 | " Show progress information about the files being read and written. If\n" \ 53 | " this option is not given, the only output will be any error messages\n" \ 54 | " that occur.\n" \ 55 | " --help\n" \ 56 | " Display this help and exit.\n" \ 57 | "\n" \ 58 | "Output format:\n" \ 59 | " Internally, IP sets are represented by a binary-decision diagram (BDD).\n" \ 60 | " The ipsetdot program can be used to produce a GraphViz file that describes\n" \ 61 | " the internal BDD structure for an IP set. The GraphViz representation can\n" \ 62 | " then be passed in to GraphViz's \"dot\" program, for instance, to generate\n" \ 63 | " an image of the BDD's graph structure.\n" 64 | 65 | 66 | int 67 | main(int argc, char **argv) 68 | { 69 | ipset_init_library(); 70 | 71 | /* Parse the command-line options. */ 72 | 73 | int ch; 74 | while ((ch = getopt_long(argc, argv, "ho:", longopts, NULL)) != -1) { 75 | switch (ch) { 76 | case 'h': 77 | fprintf(stdout, FULL_USAGE); 78 | exit(0); 79 | 80 | case 'o': 81 | output_filename = optarg; 82 | break; 83 | 84 | case 'v': 85 | verbose = true; 86 | break; 87 | 88 | default: 89 | fprintf(stderr, USAGE); 90 | exit(1); 91 | } 92 | } 93 | 94 | argc -= optind; 95 | argv += optind; 96 | 97 | if (argc != 1) { 98 | fprintf(stderr, "ipsetdot: You must specify exactly one input file.\n"); 99 | fprintf(stderr, USAGE); 100 | exit(1); 101 | } 102 | 103 | input_filename = argv[0]; 104 | 105 | /* Read in the IP set files specified on the command line. */ 106 | struct ip_set *set = NULL; 107 | FILE *stream; 108 | bool close_stream; 109 | 110 | /* Create a FILE object for the file. */ 111 | if (strcmp(input_filename, "-") == 0) { 112 | if (verbose) { 113 | fprintf(stderr, "Opening stdin...\n"); 114 | } 115 | input_filename = "stdin"; 116 | stream = stdin; 117 | close_stream = false; 118 | } else { 119 | if (verbose) { 120 | fprintf(stderr, "Opening file %s...\n", input_filename); 121 | } 122 | stream = fopen(input_filename, "rb"); 123 | if (stream == NULL) { 124 | fprintf(stderr, "Cannot open file %s:\n %s\n", 125 | input_filename, strerror(errno)); 126 | exit(1); 127 | } 128 | close_stream = true; 129 | } 130 | 131 | /* Read in the IP set from the specified file. */ 132 | set = ipset_load(stream); 133 | if (set == NULL) { 134 | fprintf(stderr, "Error reading %s:\n %s\n", 135 | input_filename, cork_error_message()); 136 | exit(1); 137 | } 138 | 139 | if (close_stream) { 140 | fclose(stream); 141 | } 142 | 143 | /* Generate a GraphViz dot file for the set. */ 144 | 145 | FILE *ostream; 146 | bool close_ostream; 147 | 148 | if ((output_filename == NULL) || (strcmp(output_filename, "-") == 0)) { 149 | if (verbose) { 150 | fprintf(stderr, "Writing to stdout...\n"); 151 | } 152 | ostream = stdout; 153 | output_filename = "stdout"; 154 | close_ostream = false; 155 | } else { 156 | if (verbose) { 157 | fprintf(stderr, "Writing to file %s...\n", output_filename); 158 | } 159 | ostream = fopen(output_filename, "wb"); 160 | if (ostream == NULL) { 161 | fprintf(stderr, "Cannot open file %s:\n %s\n", 162 | output_filename, strerror(errno)); 163 | exit(1); 164 | } 165 | close_ostream = true; 166 | } 167 | 168 | if (ipset_save_dot(ostream, set) != 0) { 169 | fprintf(stderr, "Error saving IP set:\n %s\n", 170 | cork_error_message()); 171 | exit(1); 172 | } 173 | 174 | ipset_free(set); 175 | 176 | /* Close the output stream for exiting. */ 177 | if (close_ostream) { 178 | fclose(ostream); 179 | } 180 | 181 | return 0; 182 | } 183 | -------------------------------------------------------------------------------- /docs/commands.rst: -------------------------------------------------------------------------------- 1 | .. _commands: 2 | 3 | Command-line utilities 4 | ====================== 5 | 6 | The IP set library comes with several command-line utilities for building and 7 | interacting with IP sets. 8 | 9 | 10 | ipsetbuild 11 | ---------- 12 | 13 | .. program:: ipsetbuild 14 | 15 | The ``ipsetbuild`` command is used to an build binary IP set file from a text 16 | file containing a list of IP addresses and CIDR networks. 17 | 18 | :: 19 | 20 | $ ipsetbuild [options] ... 21 | 22 | .. option:: --output , -o 23 | 24 | Writes the binary IP set file to *filename*. If this option isn't given, 25 | then the binary set will be written to standard output. 26 | 27 | .. option:: --loose-cidr, -l 28 | 29 | Be more lenient about the address portion of any CIDR network blocks found in 30 | the input file. (This is described in more detail below.) 31 | 32 | .. option:: --verbose, -v 33 | 34 | Show summary information about the IP set that's built, as well as progress 35 | information about the files being read and written. If this option is not 36 | given, the only output will be any error, alert, or warning messages that 37 | occur. 38 | 39 | .. option:: --quiet, -q 40 | 41 | Show only error message for malformed input. All warnings, alerts, and 42 | summary information about the IP set is suppressed. 43 | 44 | .. option:: --help 45 | 46 | Display some help text and exit. 47 | 48 | Each input file must contain one IP address or network per line. Lines 49 | beginning with a ``#`` are considered comments and are ignored. Each IP address 50 | must have one of the following formats:: 51 | 52 | x.x.x.x 53 | x.x.x.x/cidr 54 | xxxx:xxxx:xxxx:xxxx:xxxx:xxxx:xxxx:xxxx 55 | xxxx:xxxx:xxxx:xxxx:xxxx:xxxx:xxxx:xxxx/cidr 56 | 57 | The first two are for IPv4 addresses and networks; the second two for IPv6 58 | addresses and networks. For IPv6 addresses, you can use the ``::`` shorthand 59 | notation to collapse consecutive ``0`` portions. 60 | 61 | If an address contains a ``/cidr`` suffix, then the entire CIDR network of 62 | addresses will be added to the set. You must ensure that the low- order bits of 63 | the address are set to 0; if not, we'll raise an error. (If you pass in the 64 | :option:`--loose-cidr ` option, we won't perform this 65 | sanity check.) 66 | 67 | You can also prefix any input line with an exclamation point (``!``). This 68 | causes the given address or network to be *removed* from the output set. This 69 | notation can be useful to define a set that contains most of the addresses in a 70 | large CIDR block, except for addresses at certain "holes". 71 | 72 | The order of the addresses and networks given to ipsetbuild does not matter. If 73 | a particular address is added to the set more than once, or removed from the set 74 | more than once, whether on its own or via a CIDR network, then you will get a 75 | warning message. (You can silence these warnings with the :option:`--quiet 76 | ` option.) If an address is both added to and removed from 77 | the set, then the removal takes precedence, regardless of where the relevant 78 | lines appear in the input file. 79 | 80 | 81 | ipsetcat 82 | -------- 83 | 84 | .. program:: ipsetcat 85 | 86 | The ``ipsetcat`` command is used to print out the (non-sorted) contents of a 87 | binary IP set file. 88 | 89 | :: 90 | 91 | $ ipsetcat [options] 92 | 93 | To read from stdin, use ``-`` as the filename. 94 | 95 | .. option:: --output , -o 96 | 97 | Writes the contents of the binary IP set file to *filename*. If this option 98 | isn't given, then the contents will be written to standard output. 99 | 100 | .. option:: --networks, -n 101 | 102 | Where possible, group the IP addresses in the set into CIDR network blocks. 103 | For dense sets, this can greatly reduce the amount of output that's 104 | generated. 105 | 106 | .. option:: --verbose, -v 107 | 108 | Show progress information about the files being read and written. If this 109 | option is not given, the only output will be any error messages that occur. 110 | 111 | .. option:: --help 112 | 113 | Display some help text and exit. 114 | 115 | The output will contain one IP address or network per line. If you give the 116 | :option:`--networks ` option, then we will collapse 117 | addresses into CIDR networks where possible. CIDR network blocks will have one 118 | of the following formats:: 119 | 120 | x.x.x.x/cidr 121 | xxxx:xxxx:xxxx:xxxx:xxxx:xxxx:xxxx:xxxx/cidr 122 | 123 | Individual IP addresses will have one of the following formats:: 124 | 125 | x.x.x.x 126 | xxxx:xxxx:xxxx:xxxx:xxxx:xxxx:xxxx:xxxx 127 | 128 | Note that we never include a /32 or /128 suffix for individual addresses, even 129 | if you've requested CIDR networks via the :option:`--networks ` option. 131 | 132 | .. note:: 133 | 134 | Please note that the output is *unsorted*. There are no guarantees made 135 | about the order of the IP addresses and networks in the output. 136 | 137 | 138 | ipsetdot 139 | -------- 140 | 141 | .. program:: ipsetdot 142 | 143 | The ``ipsetdot`` command is used to create a GraphViz_ file showing the BDD 144 | structure of an IP set. 145 | 146 | .. _GraphViz: http://www.graphviz.org/ 147 | 148 | :: 149 | 150 | $ ipsetdot [options] 151 | 152 | To read from stdin, use ``-`` as the filename. 153 | 154 | .. option:: --output , -o 155 | 156 | Writes the GraphViz representation of the binary IP set file to *filename*. 157 | If this option isn't given, then the contents will be written to standard 158 | output. 159 | 160 | .. option:: --verbose, -v 161 | 162 | Show progress information about the files being read and written. If this 163 | option is not given, the only output will be any error messages that occur. 164 | 165 | .. option:: --help 166 | 167 | Display some help text and exit. 168 | 169 | Internally, IP sets are represented by a binary-decision diagram (BDD). The 170 | ``ipsetdot`` program can be used to produce a GraphViz file that describes the 171 | internal BDD structure for an IP set. The GraphViz representation can then be 172 | passed in to GraphViz's ``dot`` program, for instance, to generate an image of 173 | the BDD's graph structure. 174 | -------------------------------------------------------------------------------- /docs/_static/docco-sphinx.css: -------------------------------------------------------------------------------- 1 | @import url("basic.css"); 2 | 3 | /* -- page layout ----------------------------------------------------------- */ 4 | 5 | body { 6 | font-family: 'Palatino Linotype', Palatino, 'URW Palladio L', 'Book Antiqua', FreeSerif, serif; 7 | /*font-size: 95%;*/ 8 | font-size: 95%; 9 | color: #252519; 10 | margin: 0; 11 | padding: 0; 12 | } 13 | 14 | div.document { 15 | } 16 | 17 | div.documentwrapper { 18 | float: left; 19 | width: 100%; 20 | } 21 | 22 | div.bodywrapper { 23 | margin: 0 0 0 19em; 24 | background-color: #ffffff; 25 | } 26 | 27 | div.body { 28 | color: #252519; 29 | padding: 30px 0px 30px 60px; 30 | width: 40em; 31 | } 32 | 33 | div.footer { 34 | width: 100%; 35 | padding: 9px 0 9px 0; 36 | text-align: center; 37 | font-size: 75%; 38 | } 39 | 40 | div.footer a { 41 | color: #261a3b; 42 | text-decoration: underline; 43 | } 44 | 45 | div.related { 46 | background-color: #eee; 47 | border: 1px solid #ccc; 48 | line-height: 30px; 49 | } 50 | 51 | div.related a { 52 | color: #261a3b; 53 | } 54 | 55 | div.sphinxsidebar { 56 | padding: 30px 0px 0 20px; 57 | width: 19em; 58 | color: #555; 59 | } 60 | 61 | div.sphinxsidebar a { 62 | color: #555; 63 | } 64 | 65 | div.sphinxsidebar h3 { 66 | font-size: 1.4em; 67 | margin: 0; 68 | padding: 0; 69 | } 70 | 71 | div.sphinxsidebar h4 { 72 | font-size: 1.3em; 73 | margin: 5px 0 0 0; 74 | padding: 0; 75 | } 76 | 77 | div.sphinxsidebar p { 78 | } 79 | 80 | div.sphinxsidebar p.topless { 81 | margin: 5px 10px 10px 10px; 82 | } 83 | 84 | div.sphinxsidebar ul { 85 | margin: 10px; 86 | padding: 0; 87 | color: #ffffff; 88 | } 89 | 90 | div.sphinxsidebar li { 91 | padding-top: 5px; 92 | line-height: 115%; 93 | } 94 | 95 | div.sphinxsidebar input { 96 | border: 1px solid #ccc; 97 | font-family: sans-serif; 98 | font-size: 1em; 99 | } 100 | 101 | 102 | 103 | /* -- hyperlink styles ------------------------------------------------------ */ 104 | 105 | a { 106 | color: #306060; 107 | text-decoration: none; 108 | } 109 | 110 | a:visited { 111 | color: #306060; 112 | text-decoration: none; 113 | } 114 | 115 | a:hover { 116 | text-decoration: underline; 117 | } 118 | 119 | 120 | 121 | /* -- body styles ----------------------------------------------------------- */ 122 | 123 | div.body h1, 124 | div.body h2 { 125 | border-top: 1px solid #ccc; 126 | margin: 40px -20px 10px -20px; 127 | padding: 3px 0 3px 0; 128 | } 129 | 130 | div.body h3, 131 | div.body h4, 132 | div.body h5, 133 | div.body h6 { 134 | margin: 20px 0px -10px 0px; 135 | } 136 | 137 | div.body h1 { margin-top: 0; font-size: 200%; border: 0px; } 138 | div.body h2 { font-size: 160%; } 139 | div.body h3 { font-size: 140%; } 140 | div.body h4 { font-size: 120%; } 141 | div.body h5 { font-size: 110%; } 142 | div.body h6 { font-size: 100%; } 143 | 144 | a.headerlink { 145 | color: #c60f0f; 146 | font-size: 0.8em; 147 | padding: 0 4px 0 4px; 148 | text-decoration: none; 149 | } 150 | 151 | a.headerlink:hover { 152 | background-color: #c60f0f; 153 | color: white; 154 | } 155 | 156 | div.body p, div.body dd, div.body li { 157 | line-height: 130%; 158 | } 159 | 160 | p.admonition-title { 161 | margin-right: 0.3em; 162 | } 163 | 164 | div.admonition p.admonition-title + p { 165 | display: inline; 166 | } 167 | 168 | div.admonition p { 169 | margin-bottom: 5px; 170 | } 171 | 172 | div.admonition pre { 173 | margin-bottom: 5px; 174 | } 175 | 176 | div.admonition ul, div.admonition ol { 177 | margin-bottom: 5px; 178 | } 179 | 180 | div.note { 181 | background-color: #eee; 182 | border: 1px solid #ccc; 183 | } 184 | 185 | div.seealso { 186 | background-color: #ffc; 187 | border: 1px solid #ff6; 188 | } 189 | 190 | div.topic { 191 | background-color: #eee; 192 | } 193 | 194 | div.warning { 195 | background-color: #ffe4e4; 196 | border: 1px solid #f66; 197 | } 198 | 199 | p.admonition-title { 200 | display: inline; 201 | } 202 | 203 | p.admonition-title:after { 204 | content: ":"; 205 | } 206 | 207 | pre { 208 | font-family: Menlo, Monaco, Consolas, "Lucida Console", monospace; 209 | font-size: 80%; 210 | padding: 5px; 211 | background-color: #f5f5ff; 212 | color: #333333; 213 | line-height: 130%; 214 | border: 1px solid #e5e5ee; 215 | border-left: none; 216 | border-right: none; 217 | } 218 | 219 | tt { 220 | font-family: Menlo, Monaco, Consolas, "Lucida Console", monospace; 221 | background-color: #f8f8ff; 222 | border: 1px solid #dedede; 223 | padding: 0 0.2em; 224 | font-size: 85%; 225 | } 226 | 227 | a tt { 228 | background-color: transparent; 229 | border: 0px; 230 | } 231 | 232 | dt { 233 | font-family: Menlo, Monaco, Consolas, "Lucida Console", monospace; 234 | font-size: 90%; 235 | line-height: 130%; 236 | } 237 | 238 | table.indextable dt { 239 | font-family: 'Palatino Linotype', Palatino, 'URW Palladio L', 'Book Antiqua', FreeSerif, serif; 240 | font-size: 100%; 241 | } 242 | 243 | em.property { 244 | font-size: 90%; 245 | } 246 | 247 | tt.descclassname { 248 | background-color: transparent; 249 | border: 0px; 250 | padding: 0px; 251 | } 252 | 253 | tt.descname { 254 | background-color: transparent; 255 | border: 0px; 256 | font-size: 100%; 257 | padding: 0px; 258 | } 259 | 260 | dt big { 261 | font-family: 'Palatino Linotype', Palatino, 'URW Palladio L', 'Book Antiqua', FreeSerif, serif; 262 | font-size: 140%; 263 | padding: 0 2px; 264 | } 265 | 266 | dt big.param_start_brace { 267 | padding: 0 6px; 268 | } 269 | 270 | dt big.param_end_brace { 271 | padding: 0 6px; 272 | } 273 | 274 | dt span.optional { 275 | font-family: 'Palatino Linotype', Palatino, 'URW Palladio L', 'Book Antiqua', FreeSerif, serif; 276 | font-size: 140%; 277 | padding: 0 2px; 278 | } 279 | 280 | th { 281 | background-color: #ede; 282 | } 283 | 284 | .warning tt { 285 | background: #efc2c2; 286 | } 287 | 288 | .note tt { 289 | background: #d6d6d6; 290 | } 291 | 292 | .viewcode-back { 293 | font-family: sans-serif; 294 | } 295 | 296 | div.viewcode-block:target { 297 | background-color: #f4debf; 298 | border-top: 1px solid #ac9; 299 | border-bottom: 1px solid #ac9; 300 | } 301 | -------------------------------------------------------------------------------- /tests/dot/10.0.5.64-30/out: -------------------------------------------------------------------------------- 1 | strict digraph bdd { 2 | t1 [shape=box, label=1]; 3 | n1 [shape=circle,label=32]; 4 | n1 -> t1 [style=dashed,color=red] 5 | high1 [shape=circle,fixedsize=true,height=0.25,width=0.25,label=""] 6 | n1 -> high1 [style=solid,color=black] 7 | n2 [shape=circle,label=31]; 8 | n2 -> n1 [style=dashed,color=red] 9 | n2 -> t1 [style=solid,color=black] 10 | n3 [shape=circle,label=30]; 11 | n3 -> n2 [style=dashed,color=red] 12 | high3 [shape=circle,fixedsize=true,height=0.25,width=0.25,label=""] 13 | n3 -> high3 [style=solid,color=black] 14 | n4 [shape=circle,label=29]; 15 | n4 -> n3 [style=dashed,color=red] 16 | high4 [shape=circle,fixedsize=true,height=0.25,width=0.25,label=""] 17 | n4 -> high4 [style=solid,color=black] 18 | n5 [shape=circle,label=28]; 19 | n5 -> n4 [style=dashed,color=red] 20 | high5 [shape=circle,fixedsize=true,height=0.25,width=0.25,label=""] 21 | n5 -> high5 [style=solid,color=black] 22 | n6 [shape=circle,label=27]; 23 | n6 -> n5 [style=dashed,color=red] 24 | high6 [shape=circle,fixedsize=true,height=0.25,width=0.25,label=""] 25 | n6 -> high6 [style=solid,color=black] 26 | n7 [shape=circle,label=26]; 27 | low7 [shape=circle,label=""] 28 | n7 -> low7 [style=dashed,color=red] 29 | n7 -> n6 [style=solid,color=black] 30 | n8 [shape=circle,label=25]; 31 | n8 -> n7 [style=dashed,color=red] 32 | high8 [shape=circle,fixedsize=true,height=0.25,width=0.25,label=""] 33 | n8 -> high8 [style=solid,color=black] 34 | n9 [shape=circle,label=24]; 35 | low9 [shape=circle,label=""] 36 | n9 -> low9 [style=dashed,color=red] 37 | n9 -> n8 [style=solid,color=black] 38 | n10 [shape=circle,label=23]; 39 | n10 -> n9 [style=dashed,color=red] 40 | high10 [shape=circle,fixedsize=true,height=0.25,width=0.25,label=""] 41 | n10 -> high10 [style=solid,color=black] 42 | n11 [shape=circle,label=22]; 43 | low11 [shape=circle,label=""] 44 | n11 -> low11 [style=dashed,color=red] 45 | n11 -> n10 [style=solid,color=black] 46 | n12 [shape=circle,label=21]; 47 | n12 -> n11 [style=dashed,color=red] 48 | high12 [shape=circle,fixedsize=true,height=0.25,width=0.25,label=""] 49 | n12 -> high12 [style=solid,color=black] 50 | n13 [shape=circle,label=20]; 51 | n13 -> n12 [style=dashed,color=red] 52 | high13 [shape=circle,fixedsize=true,height=0.25,width=0.25,label=""] 53 | n13 -> high13 [style=solid,color=black] 54 | n14 [shape=circle,label=19]; 55 | n14 -> n13 [style=dashed,color=red] 56 | high14 [shape=circle,fixedsize=true,height=0.25,width=0.25,label=""] 57 | n14 -> high14 [style=solid,color=black] 58 | n15 [shape=circle,label=18]; 59 | n15 -> n14 [style=dashed,color=red] 60 | high15 [shape=circle,fixedsize=true,height=0.25,width=0.25,label=""] 61 | n15 -> high15 [style=solid,color=black] 62 | n16 [shape=circle,label=17]; 63 | n16 -> n15 [style=dashed,color=red] 64 | high16 [shape=circle,fixedsize=true,height=0.25,width=0.25,label=""] 65 | n16 -> high16 [style=solid,color=black] 66 | n17 [shape=circle,label=16]; 67 | n17 -> n16 [style=dashed,color=red] 68 | high17 [shape=circle,fixedsize=true,height=0.25,width=0.25,label=""] 69 | n17 -> high17 [style=solid,color=black] 70 | n18 [shape=circle,label=15]; 71 | n18 -> n17 [style=dashed,color=red] 72 | high18 [shape=circle,fixedsize=true,height=0.25,width=0.25,label=""] 73 | n18 -> high18 [style=solid,color=black] 74 | n19 [shape=circle,label=14]; 75 | n19 -> n18 [style=dashed,color=red] 76 | high19 [shape=circle,fixedsize=true,height=0.25,width=0.25,label=""] 77 | n19 -> high19 [style=solid,color=black] 78 | n20 [shape=circle,label=13]; 79 | n20 -> n19 [style=dashed,color=red] 80 | high20 [shape=circle,fixedsize=true,height=0.25,width=0.25,label=""] 81 | n20 -> high20 [style=solid,color=black] 82 | n21 [shape=circle,label=12]; 83 | n21 -> n20 [style=dashed,color=red] 84 | high21 [shape=circle,fixedsize=true,height=0.25,width=0.25,label=""] 85 | n21 -> high21 [style=solid,color=black] 86 | n22 [shape=circle,label=11]; 87 | n22 -> n21 [style=dashed,color=red] 88 | high22 [shape=circle,fixedsize=true,height=0.25,width=0.25,label=""] 89 | n22 -> high22 [style=solid,color=black] 90 | n23 [shape=circle,label=10]; 91 | n23 -> n22 [style=dashed,color=red] 92 | high23 [shape=circle,fixedsize=true,height=0.25,width=0.25,label=""] 93 | n23 -> high23 [style=solid,color=black] 94 | n24 [shape=circle,label=9]; 95 | n24 -> n23 [style=dashed,color=red] 96 | high24 [shape=circle,fixedsize=true,height=0.25,width=0.25,label=""] 97 | n24 -> high24 [style=solid,color=black] 98 | n25 [shape=circle,label=8]; 99 | n25 -> n24 [style=dashed,color=red] 100 | high25 [shape=circle,fixedsize=true,height=0.25,width=0.25,label=""] 101 | n25 -> high25 [style=solid,color=black] 102 | n26 [shape=circle,label=7]; 103 | low26 [shape=circle,label=""] 104 | n26 -> low26 [style=dashed,color=red] 105 | n26 -> n25 [style=solid,color=black] 106 | n27 [shape=circle,label=6]; 107 | n27 -> n26 [style=dashed,color=red] 108 | high27 [shape=circle,fixedsize=true,height=0.25,width=0.25,label=""] 109 | n27 -> high27 [style=solid,color=black] 110 | n28 [shape=circle,label=5]; 111 | low28 [shape=circle,label=""] 112 | n28 -> low28 [style=dashed,color=red] 113 | n28 -> n27 [style=solid,color=black] 114 | n29 [shape=circle,label=4]; 115 | n29 -> n28 [style=dashed,color=red] 116 | high29 [shape=circle,fixedsize=true,height=0.25,width=0.25,label=""] 117 | n29 -> high29 [style=solid,color=black] 118 | n30 [shape=circle,label=3]; 119 | n30 -> n29 [style=dashed,color=red] 120 | high30 [shape=circle,fixedsize=true,height=0.25,width=0.25,label=""] 121 | n30 -> high30 [style=solid,color=black] 122 | n31 [shape=circle,label=2]; 123 | n31 -> n30 [style=dashed,color=red] 124 | high31 [shape=circle,fixedsize=true,height=0.25,width=0.25,label=""] 125 | n31 -> high31 [style=solid,color=black] 126 | n32 [shape=circle,label=1]; 127 | n32 -> n31 [style=dashed,color=red] 128 | high32 [shape=circle,fixedsize=true,height=0.25,width=0.25,label=""] 129 | n32 -> high32 [style=solid,color=black] 130 | n33 [shape=circle,label=0]; 131 | low33 [shape=circle,label=""] 132 | n33 -> low33 [style=dashed,color=red] 133 | n33 -> n32 [style=solid,color=black] 134 | } 135 | -------------------------------------------------------------------------------- /docs/maps.rst: -------------------------------------------------------------------------------- 1 | .. _maps: 2 | 3 | IP maps 4 | ======= 5 | 6 | The functions in this section allow you to work with *IP maps*, which map IP 7 | addresses to integer values. IP maps work seamlessly with both IPv4 and IPv6 8 | addresses. Every IP address is mapped to some value; each IP map has a *default 9 | value* that will be used for any addresses that aren't explicitly mapped to 10 | something else. (This is the map equivalent of an address not appearing in a 11 | set; "removing" an address from a map is the same as setting it to the map's 12 | default value.) 13 | 14 | .. note:: 15 | 16 | With this definition of a map, an IP set is just an IP map that is restricted 17 | to the values 0 and 1. This is actually how sets are implemented internally. 18 | 19 | .. note:: 20 | 21 | Before using any of the IP map functions, you must initialize this library 22 | using :c:func:`ipset_init_library`. 23 | 24 | 25 | Creating and freeing maps 26 | ------------------------- 27 | 28 | .. type:: struct ip_map 29 | 30 | Maps IP addresses to integer values. The fields of this ``struct`` are 31 | opaque; you should only use the public library functions to access the 32 | contents of the map. 33 | 34 | There are two ways that you can work with IP maps. The first is that you can 35 | allocate the space for the :c:type:`ip_map` instance yourself --- for instance, 36 | directly on the stack. The second is to let the IP map library allocate the 37 | space for you. Your choice determines which of the following functions you will 38 | use to create and free your IP maps. 39 | 40 | .. function:: void ipmap_init(struct ip_map \*map, int default_value) 41 | struct ip_map \*ipmap_new(int default_value) 42 | 43 | Creates a new IP map. The map will use *default_value* as the default value 44 | for any addresses that aren't explicitly mapped to something else. 45 | 46 | The ``init`` variant should be used if you've allocated space for the map 47 | yourself. The ``new`` variant should be used if you want the library to 48 | allocate the space for you. (The ``new`` variant never returns ``NULL``; it 49 | will abort the program if the allocation fails.) In both cases, the map 50 | starts off empty. 51 | 52 | .. function:: void ipmap_done(struct ip_map \*map) 53 | void ipmap_free(struct ip_map \*map) 54 | 55 | Finalizes an IP map. The ``done`` variant must be used if you created the 56 | map using :c:func:`ipmap_init`; the ``free`` variant must be used if you 57 | created the map using :c:func:`ipmap_new`. 58 | 59 | 60 | Adding and removing elements 61 | ---------------------------- 62 | 63 | We provide a variety of functions for adding and removing addresses from an IP 64 | map. 65 | 66 | .. function:: void ipmap_ipv4_set(struct ip_map \*map, struct cork_ipv4 \*ip, int value) 67 | void ipmap_ipv6_set(struct ip_map \*map, struct cork_ipv6 \*ip, int value) 68 | void ipmap_ip_set(struct ip_map \*map, struct cork_ip \*ip, int value) 69 | 70 | Updates *map* so that *ip* is mapped to *value*. 71 | 72 | .. function:: void ipmap_ipv4_set_network(struct ip_map \*map, struct cork_ipv4 \*ip, unsigned int cidr_prefix, int value) 73 | void ipmap_ipv6_set_network(struct ip_map \*map, struct cork_ipv6 \*ip, unsigned int cidr_prefix, int value) 74 | void ipmap_ip_set_network(struct ip_map \*map, struct cork_ip \*ip, unsigned int cidr_prefix, int value) 75 | 76 | Updates *map* so that all of the addresses in a CIDR network are mapped to 77 | *value*. *ip* is one of the addresses in the CIDR network; *cidr_prefix* is 78 | the number of bits in the network portion of each IP address in the CIDR 79 | network, as defined in `RFC 4632`_. *cidr_prefix* must be in the range 0-32 80 | (inclusive) if *ip* is an IPv4 address, and in the range 0-128 (inclusive) if 81 | it's an IPv6 address. 82 | 83 | .. _RFC 4632: http://tools.ietf.org/html/rfc4632 84 | 85 | .. note:: 86 | 87 | In all of the ``_network`` functions, if you want to strictly adhere to RFC 88 | 4632, *ip* can only have non-zero bits in its *cidr_prefix* uppermost bits. 89 | All of the lower-order bits (i.e., in the host portion of the IP address) 90 | must be map to 0. We do not enforce this, however. 91 | 92 | 93 | Querying a map 94 | -------------- 95 | 96 | .. function:: int ipmap_get_ipv4(const struct ip_map \*map, struct cork_ipv4 \*ip) 97 | int ipmap_get_ipv6(const struct ip_map \*map, struct cork_ipv6 \*ip) 98 | int ipmap_get_ip(const struct ip_map \*map, struct cork_ip \*ip) 99 | 100 | Returns the value that *ip* is mapped to in *map*. 101 | 102 | .. function:: bool ipmap_is_empty(const struct ip_map \*map) 103 | 104 | Returns whether *map* is empty. A map is considered empty is every IP 105 | address is mapped to the default value. 106 | 107 | .. function:: bool ipmap_is_equal(const struct ip_map \*map1, const struct ip_map \*map2) 108 | 109 | Returns whether every IP address is mapped to the same value in *map1* and 110 | *map2*. 111 | 112 | .. function:: size_t ipmap_memory_size(const struct ip_map \*map) 113 | 114 | Returns the number of bytes of memory needed to store *map*. Note that 115 | adding together the storage needed for each map you use doesn't necessarily 116 | give you the total memory requirements, since some storage can be shared 117 | between maps. 118 | 119 | 120 | Storing maps in files 121 | --------------------- 122 | 123 | The functions in this section allow you to store IP maps on disk, and reload 124 | them into another program at a later time. You don't have to know the details 125 | of the file format to be able to use these functions; we guarantee that maps 126 | written with previous versions of the library will be readable by later versions 127 | of the library (but not vice versa). And we guarantee that the file format is 128 | platform-independent; maps written on any machine will be readable on any other 129 | machine. 130 | 131 | (That said, if you do want to know the details of the file format, that's 132 | documented in :ref:`another section `.) 133 | 134 | .. function:: int ipmap_save(FILE \*stream, const struct ip_map \*map) 135 | 136 | Saves an IP map into *stream*. You're responsible for opening *stream* 137 | before calling this function, and for closing *stream* afterwards. If there 138 | are any errors writing the map, we return ``-1`` and fill in a libcork 139 | :ref:`error condition `. 140 | 141 | .. function:: int ipmap_save_to_stream(struct cork_stream_consumer \*stream, const struct ip_map \*map) 142 | 143 | Saves an IP map into a libcork :ref:`stream consumer `. If 144 | there are any errors writing the map, we return ``-1`` and fill in a libcork 145 | :ref:`error condition `. 146 | 147 | .. function:: struct ip_map \*ipmap_load(FILE \*stream) 148 | 149 | Loads an IP map from *stream*. You're responsible for opening *stream* 150 | before calling this function, and for closing *stream* afterwards. If there 151 | are any errors reading the map, we return ``NULL`` and fill in a libcork 152 | :ref:`error condition `. You must use :c:func:`ipmap_free` 153 | to free the map when you're done with it. 154 | -------------------------------------------------------------------------------- /docs/file-format.rst: -------------------------------------------------------------------------------- 1 | .. _file-format: 2 | 3 | File format reference 4 | ===================== 5 | 6 | This section describes the current file format used to encode IP sets on disk. 7 | 8 | Overview 9 | -------- 10 | 11 | An IP set is stored internally using a `Binary Decision Diagram`_ (BDD). This 12 | structure is reflected in the on-disk format, too. Taking an IPv4 address as an 13 | example, we can create a Boolean variable for each of the 32 bits in the 14 | address. A set can then be thought of as a Boolean formula. To see if a 15 | particular IP address is in the set, you assign each variable a ``TRUE`` or 16 | ``FALSE`` value depending on whether that bit is set in the IP address. You 17 | then plug those variables into the formula: if the result is ``TRUE``, the 18 | address is in the set; if it's ``FALSE``, it's not in the set. 19 | 20 | .. _Binary Decision Diagram: http://en.wikipedia.org/wiki/Binary_decision_diagram) 21 | 22 | To handle both IPv4 and IPv6 addresses, we use an initial variable (variable 0) 23 | that indicates whether a particular address is IPv4 or IPv6; we use ``TRUE`` for 24 | IPv4 and ``FALSE`` for IPv6. IPv4 addresses then use variables 1-32 for each 25 | bit in the address, while IPv6 addresses use variables 1-128. (We can reuse the 26 | variables like this since we've got variable 0 discriminating the two types of 27 | address.) 28 | 29 | A BDD encodes a Boolean formula as a binary search tree. There are two kinds of 30 | nodes: *terminal* nodes, which are the leaves of the tree, and *nonterminal* 31 | nodes. A terminal node is labeled with a value, representing the result of the 32 | Boolean formula; for a set, this is either ``TRUE`` or ``FALSE``. A nonterminal 33 | node is labeled with a variable number, and has **low** and **high** connections 34 | to other nodes. 35 | 36 | To evaluate a BDD with a particular assignment of variables, you start at the 37 | BDD's root node. At each nonterminal, you look up the specified variable. If 38 | the variable is ``TRUE``, you follow the **high** pointer; if it's ``FALSE``, 39 | you follow the **low** pointer. When you reach a terminal node, you have the 40 | result of the formula. 41 | 42 | BDDs have certain restrictions placed on them for efficiency reasons. They must 43 | be *reduced*, which means that the **low** and **high** connections of a 44 | nonterminal node must be different, and that you can't have two nonterminal 45 | nodes with the same contents. They must be *ordered*, which means that the 46 | variable number of a nonterminal node must be less than the variable numbers of 47 | its children. 48 | 49 | All of this has ramifications on the storage format, since we're storing the BDD 50 | structure of a set. To store a BDD, we only need two basic low-level storage 51 | elements: a *variable index*, and a *node ID*. Since an IP set can store both 52 | IPv4 and IPv6 addresses, we need 129 total variables: variable 0 to determine 53 | which kind of address, and variables 1-128 to store the bits of the address. 54 | Node IDs are references to the nodes in the BDD. 55 | 56 | A terminal node has an ID ≥ 0. We don't explicitly store terminal nodes in the 57 | storage stream; instead, the node ID of a terminal node is the value associated 58 | with that terminal node. For sets, node 0 is the ``FALSE`` node, and node 1 is 59 | the ``TRUE`` node. 60 | 61 | A nonterminal node has an ID < 0. Each nonterminal node has its own entry in 62 | the storage stream. The entries are stored as a flat list, starting with 63 | nonterminal -1, followed by -2, etc. Because of the tree structure of a BDD, we 64 | can ensure that all references to a node appear after that node in the list. 65 | 66 | Note that the BDD for an empty set has no nonterminal nodes — the formula always 67 | evaluates to ``FALSE``, regardless of what values the variables have, so the BDD 68 | is just the ``FALSE`` terminal node. Similarly, the BDD for a set that contains 69 | all IP addresses will just be the ``TRUE`` terminal. 70 | 71 | 72 | On-disk syntax 73 | -------------- 74 | 75 | With those preliminaries out of the way, we can define the actual on-disk 76 | structure. Note that all integers are big-endian. 77 | 78 | Header 79 | ~~~~~~ 80 | 81 | All IP sets start with a 20-byte header. This header starts with a six-byte 82 | magic number, which corresponds to the ASCII string ``IP set``:: 83 | 84 | +----+----+----+----+----+----+ 85 | | 49 | 50 | 20 | 73 | 65 | 74 | 86 | +----+----+----+----+----+----+ 87 | 88 | Next comes a 16-bit version field that tells us which version of the IP set file 89 | format is in use. The current version is 1:: 90 | 91 | +----+----+ 92 | | 00 | 01 | 93 | +----+----+ 94 | 95 | Next comes a 64-bit length field. This gives us the length of the *entire* 96 | serialized IP set, including the magic number and other header fields. 97 | 98 | :: 99 | 100 | +----+----+----+----+----+----+----+----+ 101 | | Length | 102 | +----+----+----+----+----+----+----+----+ 103 | 104 | The last header field is a 32-bit integer giving the number of nonterminal nodes 105 | in the set. 106 | 107 | :: 108 | 109 | +----+----+----+----+ 110 | | Nonterminal count | 111 | +----+----+----+----+ 112 | 113 | Terminal node 114 | ~~~~~~~~~~~~~ 115 | 116 | As mentioned above, it's possible for there to be no nonterminals in the set's 117 | BDD. If there aren't any nonterminals, there must exactly one terminal in the 118 | BDD. This indicates that the set is empty (if it's the ``FALSE`` terminal), or 119 | contains every IP address (if it's the ``TRUE`` terminal). In this case, the 120 | header is followed by a 32-bit integer that encodes the value of the BDD's 121 | terminal node. 122 | 123 | :: 124 | 125 | +----+----+----+----+ 126 | | Terminal value | 127 | +----+----+----+----+ 128 | 129 | Nonterminal nodes 130 | ~~~~~~~~~~~~~~~~~ 131 | 132 | If there are any nonterminal nodes in the BDD, the header is followed by a list 133 | of *node structures*, one for each nonterminal. Each node structure starts with 134 | an 8-bit variable index:: 135 | 136 | +----+ 137 | | VI | 138 | +----+ 139 | 140 | Variable 0 determines whether a particular address is IPv4 or IPv6. Variables 141 | 1-32 represent the bits of an IPv4 address; variables 1-128 represent the bits 142 | of an IPv6 address. (The bits of an address are numbered in big-endian byte 143 | order, and from MSB to LSB within each byte.) 144 | 145 | Next comes the **low** pointer and **high** pointer. Each is encoded as a 146 | 32-bit node ID. Node IDs ≥ 0 point to terminal nodes; in this case, the node ID 147 | is the value of the terminal node. Node IDs < 0 point to nonterminal nodes; 148 | node -1 is the first node in the list, node -2 is second, etc. 149 | 150 | :: 151 | 152 | +----+----+----+----+ 153 | | Low pointer | 154 | +----+----+----+----+ 155 | | High pointer | 156 | +----+----+----+----+ 157 | 158 | We use a depth-first search when writing the nodes to disk. This ensures any 159 | nonterminal node reference points to a node earlier in the node list. 160 | Therefore, when you read in an IP set, you can make a single pass through the 161 | node list; whenever you encounter a node reference, you can assume that the node 162 | it points to has already been read in. 163 | -------------------------------------------------------------------------------- /src/ipsetcat/ipsetcat.c: -------------------------------------------------------------------------------- 1 | /* -*- coding: utf-8 -*- 2 | * ---------------------------------------------------------------------- 3 | * Copyright © 2010, libcorkipset authors 4 | * All rights reserved. 5 | * 6 | * Please see the COPYING file in this distribution for license details. 7 | * ---------------------------------------------------------------------- 8 | */ 9 | 10 | 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | 18 | #include 19 | 20 | #include "ipset/ipset.h" 21 | 22 | 23 | static char *input_filename = NULL; 24 | static char *output_filename = "-"; 25 | static bool verbose = false; 26 | static bool want_networks = false; 27 | 28 | 29 | static struct option longopts[] = { 30 | { "help", no_argument, NULL, 'h' }, 31 | { "output", required_argument, NULL, 'o' }, 32 | { "networks", no_argument, NULL, 'n' }, 33 | { "verbose", 0, NULL, 'v' }, 34 | { NULL, 0, NULL, 0 } 35 | }; 36 | 37 | #define USAGE \ 38 | "Usage: ipsetcat [options] \n" 39 | 40 | #define FULL_USAGE \ 41 | USAGE \ 42 | "\n" \ 43 | "Prints out the (non-sorted) contents of a binary IP set file.\n" \ 44 | "\n" \ 45 | "Options:\n" \ 46 | " \n" \ 47 | " The binary set file to read. To read from stdin, use \"-\" as the\n" \ 48 | " filename.\n" \ 49 | " --output=, -o \n" \ 50 | " Writes the contents of the binary IP set file to . If this\n" \ 51 | " option isn't given, then the contents will be written to standard\n" \ 52 | " output.\n" \ 53 | " --networks, -n\n" \ 54 | " Where possible, we group the IP addresses in the set into CIDR network\n" \ 55 | " blocks. For dense sets, this can greatly reduce the amount of output\n" \ 56 | " that's generated.\n" \ 57 | " --verbose, -v\n" \ 58 | " Show progress information about the files being read and written. If\n" \ 59 | " this option is not given, the only output will be any error messages\n" \ 60 | " that occur.\n" \ 61 | " --help\n" \ 62 | " Display this help and exit.\n" \ 63 | "\n" \ 64 | "Output format:\n" \ 65 | " The output will contain one IP address or network per line. If you give\n" \ 66 | " the \"--networks\" option, then we will collapse addresses into CIDR\n" \ 67 | " networks where possible. CIDR network blocks will have one of the\n" \ 68 | " following formats:\n" \ 69 | "\n" \ 70 | " x.x.x.x/cidr\n" \ 71 | " xxxx:xxxx:xxxx:xxxx:xxxx:xxxx:xxxx:xxxx/cidr\n" \ 72 | "\n" \ 73 | " Individual IP addresses will have one of the following formats:\n" \ 74 | "\n" \ 75 | " x.x.x.x\n" \ 76 | " xxxx:xxxx:xxxx:xxxx:xxxx:xxxx:xxxx:xxxx\n" \ 77 | "\n" \ 78 | " Note that we never include a /32 or /128 suffix for individual addresses,\n" \ 79 | " even if you've requested CIDR networks via the \"--networks\" option.\n" \ 80 | "\n" \ 81 | " Please note that the output is UNSORTED. There are no guarantees made\n" \ 82 | " about the order of the IP addresses and networks in the output.\n" 83 | 84 | int 85 | main(int argc, char **argv) 86 | { 87 | ipset_init_library(); 88 | 89 | /* Parse the command-line options. */ 90 | 91 | int ch; 92 | while ((ch = getopt_long(argc, argv, "hno:", longopts, NULL)) != -1) { 93 | switch (ch) { 94 | case 'h': 95 | fprintf(stdout, FULL_USAGE); 96 | exit(0); 97 | 98 | case 'n': 99 | want_networks = true; 100 | break; 101 | 102 | case 'o': 103 | output_filename = optarg; 104 | break; 105 | 106 | case 'v': 107 | verbose = true; 108 | break; 109 | 110 | default: 111 | fprintf(stderr, USAGE); 112 | exit(1); 113 | } 114 | } 115 | 116 | argc -= optind; 117 | argv += optind; 118 | 119 | if (argc != 1) { 120 | fprintf(stderr, "ipsetcat: You must specify exactly one input file.\n"); 121 | fprintf(stderr, USAGE); 122 | exit(1); 123 | } 124 | 125 | input_filename = argv[0]; 126 | 127 | /* Read in the IP set files specified on the command line. */ 128 | struct ip_set *set = NULL; 129 | FILE *stream; 130 | bool close_stream; 131 | 132 | /* Create a FILE object for the file. */ 133 | if (strcmp(input_filename, "-") == 0) { 134 | if (verbose) { 135 | fprintf(stderr, "Opening stdin...\n"); 136 | } 137 | input_filename = "stdin"; 138 | stream = stdin; 139 | close_stream = false; 140 | } else { 141 | if (verbose) { 142 | fprintf(stderr, "Opening file %s...\n", input_filename); 143 | } 144 | stream = fopen(input_filename, "rb"); 145 | if (stream == NULL) { 146 | fprintf(stderr, "Cannot open file %s:\n %s\n", 147 | input_filename, strerror(errno)); 148 | exit(1); 149 | } 150 | close_stream = true; 151 | } 152 | 153 | /* Read in the IP set from the specified file. */ 154 | set = ipset_load(stream); 155 | if (set == NULL) { 156 | fprintf(stderr, "Error reading %s:\n %s\n", 157 | input_filename, cork_error_message()); 158 | exit(1); 159 | } 160 | 161 | if (close_stream) { 162 | fclose(stream); 163 | } 164 | 165 | /* Print out the IP addresses in the set. */ 166 | FILE *ostream; 167 | bool close_ostream; 168 | if ((output_filename == NULL) || (strcmp(output_filename, "-") == 0)) { 169 | if (verbose) { 170 | fprintf(stderr, "Writing to stdout...\n"); 171 | } 172 | ostream = stdout; 173 | output_filename = "stdout"; 174 | close_ostream = false; 175 | } else { 176 | if (verbose) { 177 | fprintf(stderr, "Writing to file %s...\n", output_filename); 178 | } 179 | ostream = fopen(output_filename, "wb"); 180 | if (ostream == NULL) { 181 | fprintf(stderr, "Cannot open file %s:\n %s\n", 182 | output_filename, strerror(errno)); 183 | exit(1); 184 | } 185 | close_ostream = true; 186 | } 187 | 188 | char ip_buf[CORK_IP_STRING_LENGTH]; 189 | struct cork_buffer buf = CORK_BUFFER_INIT(); 190 | 191 | struct ipset_iterator *it; 192 | if (want_networks) { 193 | /* If requested, iterate through network blocks instead of 194 | * individual IP addresses. */ 195 | it = ipset_iterate_networks(set, true); 196 | } else { 197 | /* The user wants individual IP addresses. Hope they know what 198 | * they're doing! */ 199 | it = ipset_iterate(set, true); 200 | } 201 | 202 | for (/* nothing */; !it->finished; ipset_iterator_advance(it)) { 203 | cork_ip_to_raw_string(&it->addr, ip_buf); 204 | if ((it->addr.version == 4 && it->cidr_prefix == 32) || 205 | (it->addr.version == 6 && it->cidr_prefix == 128)) { 206 | cork_buffer_printf(&buf, "%s\n", ip_buf); 207 | } else { 208 | cork_buffer_printf(&buf, "%s/%u\n", ip_buf, it->cidr_prefix); 209 | } 210 | 211 | if (fputs(buf.buf, ostream) == EOF) { 212 | fprintf(stderr, "Cannot write to file %s:\n %s\n", 213 | output_filename, strerror(errno)); 214 | exit(1); 215 | } 216 | } 217 | 218 | cork_buffer_done(&buf); 219 | ipset_free(set); 220 | 221 | /* Close the output stream for exiting. */ 222 | if (close_ostream) { 223 | fclose(ostream); 224 | } 225 | 226 | return 0; 227 | } 228 | -------------------------------------------------------------------------------- /include/ipset/ipset.h: -------------------------------------------------------------------------------- 1 | /* -*- coding: utf-8 -*- 2 | * ---------------------------------------------------------------------- 3 | * Copyright © 2009, libcorkipset authors 4 | * All rights reserved. 5 | * 6 | * Please see the COPYING file in this distribution for license details. 7 | * ---------------------------------------------------------------------- 8 | */ 9 | 10 | #ifndef IPSET_IPSET_H 11 | #define IPSET_IPSET_H 12 | 13 | #include 14 | 15 | #include 16 | #include 17 | 18 | #include 19 | 20 | 21 | struct ip_set { 22 | struct ipset_node_cache *cache; 23 | ipset_node_id set_bdd; 24 | }; 25 | 26 | 27 | struct ip_map { 28 | struct ipset_node_cache *cache; 29 | ipset_node_id map_bdd; 30 | ipset_node_id default_bdd; 31 | }; 32 | 33 | 34 | /*--------------------------------------------------------------------- 35 | * General functions 36 | */ 37 | 38 | int 39 | ipset_init_library(void); 40 | 41 | 42 | /*--------------------------------------------------------------------- 43 | * IP set functions 44 | */ 45 | 46 | void 47 | ipset_init(struct ip_set *set); 48 | 49 | void 50 | ipset_done(struct ip_set *set); 51 | 52 | struct ip_set * 53 | ipset_new(void); 54 | 55 | void 56 | ipset_free(struct ip_set *set); 57 | 58 | bool 59 | ipset_is_empty(const struct ip_set *set); 60 | 61 | bool 62 | ipset_is_equal(const struct ip_set *set1, const struct ip_set *set2); 63 | 64 | size_t 65 | ipset_memory_size(const struct ip_set *set); 66 | 67 | int 68 | ipset_save(FILE *stream, const struct ip_set *set); 69 | 70 | int 71 | ipset_save_to_stream(struct cork_stream_consumer *stream, 72 | const struct ip_set *set); 73 | 74 | int 75 | ipset_save_dot(FILE *stream, const struct ip_set *set); 76 | 77 | struct ip_set * 78 | ipset_load(FILE *stream); 79 | 80 | bool 81 | ipset_ipv4_add(struct ip_set *set, struct cork_ipv4 *elem); 82 | 83 | bool 84 | ipset_ipv4_add_network(struct ip_set *set, struct cork_ipv4 *elem, 85 | unsigned int cidr_prefix); 86 | 87 | bool 88 | ipset_ipv4_remove(struct ip_set *set, struct cork_ipv4 *elem); 89 | 90 | bool 91 | ipset_ipv4_remove_network(struct ip_set *set, struct cork_ipv4 *elem, 92 | unsigned int cidr_prefix); 93 | 94 | bool 95 | ipset_contains_ipv4(const struct ip_set *set, struct cork_ipv4 *elem); 96 | 97 | bool 98 | ipset_ipv6_add(struct ip_set *set, struct cork_ipv6 *elem); 99 | 100 | bool 101 | ipset_ipv6_add_network(struct ip_set *set, struct cork_ipv6 *elem, 102 | unsigned int cidr_prefix); 103 | 104 | bool 105 | ipset_ipv6_remove(struct ip_set *set, struct cork_ipv6 *elem); 106 | 107 | bool 108 | ipset_ipv6_remove_network(struct ip_set *set, struct cork_ipv6 *elem, 109 | unsigned int cidr_prefix); 110 | 111 | bool 112 | ipset_contains_ipv6(const struct ip_set *set, struct cork_ipv6 *elem); 113 | 114 | bool 115 | ipset_ip_add(struct ip_set *set, struct cork_ip *addr); 116 | 117 | bool 118 | ipset_ip_add_network(struct ip_set *set, struct cork_ip *addr, 119 | unsigned int cidr_prefix); 120 | 121 | bool 122 | ipset_ip_remove(struct ip_set *set, struct cork_ip *addr); 123 | 124 | bool 125 | ipset_ip_remove_network(struct ip_set *set, struct cork_ip *addr, 126 | unsigned int cidr_prefix); 127 | 128 | bool 129 | ipset_contains_ip(const struct ip_set *set, struct cork_ip *elem); 130 | 131 | 132 | /* An internal state type used by the ipset_iterator_multiple_expansion_state 133 | * field. */ 134 | enum ipset_iterator_state { 135 | IPSET_ITERATOR_NORMAL = 0, 136 | IPSET_ITERATOR_MULTIPLE_IPV4, 137 | IPSET_ITERATOR_MULTIPLE_IPV6 138 | }; 139 | 140 | 141 | /* An iterator that returns all of the IP addresses that have a given value in 142 | * an IP set or map. */ 143 | struct ipset_iterator { 144 | /* The address of the current IP network in the iterator. */ 145 | struct cork_ip addr; 146 | 147 | /* The netmask of the current IP network in the iterator, given as a 148 | * CIDR prefix. For a single IP address, this will be 32 or 128. */ 149 | unsigned int cidr_prefix; 150 | 151 | /* Whether the current assignment needs to be expanded a second 152 | * time. 153 | * 154 | * We have to expand IPv4 and IPv6 assignments separately, since the 155 | * set of variables to turn into address bits is different. 156 | * Unfortunately, a BDD assignment can contain both IPv4 and IPv6 157 | * addresses, if variable 0 is EITHER. (This is trivially true for 158 | * the empty set, for instance.) In this case, we have to 159 | * explicitly set variable 0 to TRUE, expand it as IPv4, and then 160 | * set it to FALSE, and expand it as IPv6. This variable tells us 161 | * whether we're in an assignment that needs to be expanded twice, 162 | * and if so, which expansion we're currently in. 163 | */ 164 | enum ipset_iterator_state multiple_expansion_state; 165 | 166 | /* An iterator for retrieving each assignment in the set's BDD. */ 167 | struct ipset_bdd_iterator *bdd_iterator; 168 | 169 | /* An iterator for expanding each assignment into individual IP 170 | * addresses. */ 171 | struct ipset_expanded_assignment *assignment_iterator; 172 | 173 | /* Whether there are any more IP addresses in this iterator. */ 174 | bool finished; 175 | 176 | /* The desired value for each IP address. */ 177 | bool desired_value; 178 | 179 | /* Whether to summarize the contents of the IP set as networks, 180 | * where possible. */ 181 | bool summarize; 182 | }; 183 | 184 | 185 | struct ipset_iterator * 186 | ipset_iterate(struct ip_set *set, bool desired_value); 187 | 188 | struct ipset_iterator * 189 | ipset_iterate_networks(struct ip_set *set, bool desired_value); 190 | 191 | void 192 | ipset_iterator_free(struct ipset_iterator *iterator); 193 | 194 | void 195 | ipset_iterator_advance(struct ipset_iterator *iterator); 196 | 197 | 198 | /*--------------------------------------------------------------------- 199 | * IP map functions 200 | */ 201 | 202 | void 203 | ipmap_init(struct ip_map *map, int default_value); 204 | 205 | void 206 | ipmap_done(struct ip_map *map); 207 | 208 | struct ip_map * 209 | ipmap_new(int default_value); 210 | 211 | void 212 | ipmap_free(struct ip_map *map); 213 | 214 | bool 215 | ipmap_is_empty(const struct ip_map *map); 216 | 217 | bool 218 | ipmap_is_equal(const struct ip_map *map1, const struct ip_map *map2); 219 | 220 | size_t 221 | ipmap_memory_size(const struct ip_map *map); 222 | 223 | int 224 | ipmap_save(FILE *stream, const struct ip_map *map); 225 | 226 | int 227 | ipmap_save_to_stream(struct cork_stream_consumer *stream, 228 | const struct ip_map *map); 229 | 230 | struct ip_map * 231 | ipmap_load(FILE *stream); 232 | 233 | void 234 | ipmap_ipv4_set(struct ip_map *map, struct cork_ipv4 *elem, int value); 235 | 236 | void 237 | ipmap_ipv4_set_network(struct ip_map *map, struct cork_ipv4 *elem, 238 | unsigned int cidr_prefix, int value); 239 | 240 | int 241 | ipmap_ipv4_get(struct ip_map *map, struct cork_ipv4 *elem); 242 | 243 | void 244 | ipmap_ipv6_set(struct ip_map *map, struct cork_ipv6 *elem, int value); 245 | 246 | void 247 | ipmap_ipv6_set_network(struct ip_map *map, struct cork_ipv6 *elem, 248 | unsigned int cidr_prefix, int value); 249 | 250 | int 251 | ipmap_ipv6_get(struct ip_map *map, struct cork_ipv6 *elem); 252 | 253 | void 254 | ipmap_ip_set(struct ip_map *map, struct cork_ip *addr, int value); 255 | 256 | void 257 | ipmap_ip_set_network(struct ip_map *map, struct cork_ip *addr, 258 | unsigned int cidr_prefix, int value); 259 | 260 | int 261 | ipmap_ip_get(struct ip_map *map, struct cork_ip *addr); 262 | 263 | 264 | #endif /* IPSET_IPSET_H */ 265 | -------------------------------------------------------------------------------- /tests/test-assignment.c: -------------------------------------------------------------------------------- 1 | /* -*- coding: utf-8 -*- 2 | * ---------------------------------------------------------------------- 3 | * Copyright © 2009, libcorkipset authors 4 | * All rights reserved. 5 | * 6 | * Please see the COPYING file in this distribution for license details. 7 | * ---------------------------------------------------------------------- 8 | */ 9 | 10 | #include 11 | #include 12 | 13 | #include 14 | #include 15 | 16 | #include "ipset/bdd/nodes.h" 17 | #include "ipset/bits.h" 18 | 19 | 20 | /*----------------------------------------------------------------------- 21 | * Assignments 22 | */ 23 | 24 | START_TEST(test_bdd_assignment_empty_equal) 25 | { 26 | struct ipset_assignment *a1; 27 | struct ipset_assignment *a2; 28 | 29 | a1 = ipset_assignment_new(); 30 | a2 = ipset_assignment_new(); 31 | 32 | fail_unless(ipset_assignment_equal(a1, a2), 33 | "Assignments should be equal"); 34 | 35 | ipset_assignment_free(a1); 36 | ipset_assignment_free(a2); 37 | } 38 | END_TEST 39 | 40 | 41 | START_TEST(test_bdd_assignment_equal_1) 42 | { 43 | struct ipset_assignment *a1; 44 | struct ipset_assignment *a2; 45 | 46 | a1 = ipset_assignment_new(); 47 | ipset_assignment_set(a1, 0, IPSET_TRUE); 48 | ipset_assignment_set(a1, 1, IPSET_FALSE); 49 | 50 | a2 = ipset_assignment_new(); 51 | ipset_assignment_set(a2, 0, IPSET_TRUE); 52 | ipset_assignment_set(a2, 1, IPSET_FALSE); 53 | 54 | fail_unless(ipset_assignment_equal(a1, a2), 55 | "Assignments should be equal"); 56 | 57 | ipset_assignment_free(a1); 58 | ipset_assignment_free(a2); 59 | } 60 | END_TEST 61 | 62 | 63 | START_TEST(test_bdd_assignment_equal_2) 64 | { 65 | struct ipset_assignment *a1; 66 | struct ipset_assignment *a2; 67 | 68 | a1 = ipset_assignment_new(); 69 | ipset_assignment_set(a1, 0, IPSET_TRUE); 70 | ipset_assignment_set(a1, 1, IPSET_FALSE); 71 | 72 | a2 = ipset_assignment_new(); 73 | ipset_assignment_set(a2, 0, IPSET_TRUE); 74 | ipset_assignment_set(a2, 1, IPSET_FALSE); 75 | ipset_assignment_set(a2, 4, IPSET_EITHER); 76 | 77 | fail_unless(ipset_assignment_equal(a1, a2), 78 | "Assignments should be equal"); 79 | 80 | ipset_assignment_free(a1); 81 | ipset_assignment_free(a2); 82 | } 83 | END_TEST 84 | 85 | 86 | START_TEST(test_bdd_assignment_cut_1) 87 | { 88 | struct ipset_assignment *a1; 89 | struct ipset_assignment *a2; 90 | 91 | a1 = ipset_assignment_new(); 92 | ipset_assignment_set(a1, 0, IPSET_TRUE); 93 | ipset_assignment_set(a1, 1, IPSET_FALSE); 94 | 95 | a2 = ipset_assignment_new(); 96 | ipset_assignment_set(a2, 0, IPSET_TRUE); 97 | ipset_assignment_set(a2, 1, IPSET_FALSE); 98 | ipset_assignment_set(a2, 2, IPSET_TRUE); 99 | ipset_assignment_set(a2, 3, IPSET_TRUE); 100 | ipset_assignment_set(a2, 4, IPSET_FALSE); 101 | 102 | ipset_assignment_cut(a2, 2); 103 | 104 | fail_unless(ipset_assignment_equal(a1, a2), 105 | "Assignments should be equal"); 106 | 107 | ipset_assignment_free(a1); 108 | ipset_assignment_free(a2); 109 | } 110 | END_TEST 111 | 112 | 113 | /*----------------------------------------------------------------------- 114 | * Expanded assignments 115 | */ 116 | 117 | START_TEST(test_bdd_assignment_expand_1) 118 | { 119 | struct ipset_assignment *a; 120 | 121 | a = ipset_assignment_new(); 122 | ipset_assignment_set(a, 0, true); 123 | ipset_assignment_set(a, 1, false); 124 | 125 | struct ipset_expanded_assignment *it; 126 | it = ipset_assignment_expand(a, 2); 127 | 128 | uint8_t ea[1]; 129 | memset(ea, 0, 1); 130 | 131 | fail_if(it->finished, 132 | "Expanded assignment shouldn't be empty"); 133 | IPSET_BIT_SET(ea, 0, true); 134 | IPSET_BIT_SET(ea, 1, false); 135 | fail_unless(memcmp(ea, it->values.buf, 1) == 0, 136 | "Expanded assignment doesn't match"); 137 | 138 | ipset_expanded_assignment_advance(it); 139 | fail_unless(it->finished, 140 | "Expanded assignment should have 1 element"); 141 | 142 | ipset_expanded_assignment_free(it); 143 | ipset_assignment_free(a); 144 | } 145 | END_TEST 146 | 147 | 148 | START_TEST(test_bdd_assignment_expand_2) 149 | { 150 | struct ipset_assignment *a; 151 | 152 | a = ipset_assignment_new(); 153 | ipset_assignment_set(a, 0, true); 154 | ipset_assignment_set(a, 1, false); 155 | 156 | struct ipset_expanded_assignment *it; 157 | it = ipset_assignment_expand(a, 3); 158 | 159 | uint8_t ea[1]; 160 | memset(ea, 0, 1); 161 | 162 | fail_if(it->finished, 163 | "Expanded assignment shouldn't be empty"); 164 | IPSET_BIT_SET(ea, 0, true); 165 | IPSET_BIT_SET(ea, 1, false); 166 | IPSET_BIT_SET(ea, 2, false); 167 | fail_unless(memcmp(ea, it->values.buf, 1) == 0, 168 | "Expanded assignment 1 doesn't match"); 169 | 170 | ipset_expanded_assignment_advance(it); 171 | fail_if(it->finished, 172 | "Expanded assignment should have at least 1 element"); 173 | IPSET_BIT_SET(ea, 0, true); 174 | IPSET_BIT_SET(ea, 1, false); 175 | IPSET_BIT_SET(ea, 2, true); 176 | fail_unless(memcmp(ea, it->values.buf, 1) == 0, 177 | "Expanded assignment 2 doesn't match"); 178 | 179 | ipset_expanded_assignment_advance(it); 180 | fail_unless(it->finished, 181 | "Expanded assignment should have 2 elements"); 182 | 183 | ipset_expanded_assignment_free(it); 184 | ipset_assignment_free(a); 185 | } 186 | END_TEST 187 | 188 | 189 | START_TEST(test_bdd_assignment_expand_3) 190 | { 191 | struct ipset_assignment *a; 192 | 193 | a = ipset_assignment_new(); 194 | ipset_assignment_set(a, 0, true); 195 | ipset_assignment_set(a, 2, false); 196 | 197 | struct ipset_expanded_assignment *it; 198 | it = ipset_assignment_expand(a, 3); 199 | 200 | uint8_t ea[1]; 201 | memset(ea, 0, 1); 202 | 203 | fail_if(it->finished, 204 | "Expanded assignment shouldn't be empty"); 205 | IPSET_BIT_SET(ea, 0, true); 206 | IPSET_BIT_SET(ea, 1, false); 207 | IPSET_BIT_SET(ea, 2, false); 208 | fail_unless(memcmp(ea, it->values.buf, 1) == 0, 209 | "Expanded assignment 1 doesn't match"); 210 | 211 | ipset_expanded_assignment_advance(it); 212 | fail_if(it->finished, 213 | "Expanded assignment should have at least 1 element"); 214 | IPSET_BIT_SET(ea, 0, true); 215 | IPSET_BIT_SET(ea, 1, true); 216 | IPSET_BIT_SET(ea, 2, false); 217 | fail_unless(memcmp(ea, it->values.buf, 1) == 0, 218 | "Expanded assignment 2 doesn't match"); 219 | 220 | ipset_expanded_assignment_advance(it); 221 | fail_unless(it->finished, 222 | "Expanded assignment should have 2 elements"); 223 | 224 | ipset_expanded_assignment_free(it); 225 | ipset_assignment_free(a); 226 | } 227 | END_TEST 228 | 229 | 230 | /*----------------------------------------------------------------------- 231 | * Testing harness 232 | */ 233 | 234 | static Suite * 235 | test_suite() 236 | { 237 | Suite *s = suite_create("assignment"); 238 | 239 | TCase *tc_assignments = tcase_create("assignments"); 240 | tcase_add_test(tc_assignments, test_bdd_assignment_empty_equal); 241 | tcase_add_test(tc_assignments, test_bdd_assignment_equal_1); 242 | tcase_add_test(tc_assignments, test_bdd_assignment_equal_2); 243 | tcase_add_test(tc_assignments, test_bdd_assignment_cut_1); 244 | suite_add_tcase(s, tc_assignments); 245 | 246 | TCase *tc_expanded = tcase_create("expanded"); 247 | tcase_add_test(tc_expanded, test_bdd_assignment_expand_1); 248 | tcase_add_test(tc_expanded, test_bdd_assignment_expand_2); 249 | tcase_add_test(tc_expanded, test_bdd_assignment_expand_3); 250 | suite_add_tcase(s, tc_expanded); 251 | 252 | return s; 253 | } 254 | 255 | 256 | int 257 | main(int argc, const char **argv) 258 | { 259 | int number_failed; 260 | Suite *suite = test_suite(); 261 | SRunner *runner = srunner_create(suite); 262 | 263 | srunner_run_all(runner, CK_NORMAL); 264 | number_failed = srunner_ntests_failed(runner); 265 | srunner_free(runner); 266 | 267 | return (number_failed == 0)? EXIT_SUCCESS: EXIT_FAILURE; 268 | } 269 | -------------------------------------------------------------------------------- /cmake/FindCTargets.cmake: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # ---------------------------------------------------------------------- 3 | # Copyright © 2015, libcorkipset authors 4 | # All rights reserved. 5 | # 6 | # Please see the COPYING file in this distribution for license details. 7 | # ---------------------------------------------------------------------- 8 | 9 | 10 | #----------------------------------------------------------------------- 11 | # Configuration options that control all of the below 12 | 13 | set(ENABLE_SHARED YES CACHE BOOL "Whether to build a shared library") 14 | set(ENABLE_SHARED_EXECUTABLES YES CACHE BOOL 15 | "Whether to link executables using shared libraries") 16 | set(ENABLE_STATIC YES CACHE BOOL "Whether to build a static library") 17 | 18 | 19 | #----------------------------------------------------------------------- 20 | # Library, with options to build both shared and static versions 21 | 22 | function(target_add_shared_libraries TARGET_NAME LIBRARIES LOCAL_LIBRARIES) 23 | foreach(lib ${LIBRARIES}) 24 | string(REPLACE "-" "_" lib ${lib}) 25 | string(TOUPPER ${lib} upperlib) 26 | target_link_libraries( 27 | ${TARGET_NAME} 28 | ${${upperlib}_LDFLAGS} 29 | ) 30 | endforeach(lib) 31 | foreach(lib ${LOCAL_LIBRARIES}) 32 | target_link_libraries(${TARGET_NAME} ${lib}-shared) 33 | endforeach(lib) 34 | endfunction(target_add_shared_libraries) 35 | 36 | function(target_add_static_libraries TARGET_NAME LIBRARIES LOCAL_LIBRARIES) 37 | foreach(lib ${LIBRARIES}) 38 | string(REPLACE "-" "_" lib ${lib}) 39 | string(TOUPPER ${lib} upperlib) 40 | target_link_libraries( 41 | ${TARGET_NAME} 42 | ${${upperlib}_STATIC_LDFLAGS} 43 | ) 44 | endforeach(lib) 45 | foreach(lib ${LOCAL_LIBRARIES}) 46 | target_link_libraries(${TARGET_NAME} ${lib}-static) 47 | endforeach(lib) 48 | endfunction(target_add_static_libraries) 49 | 50 | set_property(GLOBAL PROPERTY ALL_LOCAL_LIBRARIES "") 51 | 52 | function(add_c_library __TARGET_NAME) 53 | set(options) 54 | set(one_args OUTPUT_NAME PKGCONFIG_NAME VERSION_INFO) 55 | set(multi_args LIBRARIES LOCAL_LIBRARIES SOURCES) 56 | cmake_parse_arguments(_ "${options}" "${one_args}" "${multi_args}" ${ARGN}) 57 | 58 | if (__VERSION_INFO MATCHES "^([0-9]+):([0-9]+):([0-9]+)(-dev)?$") 59 | set(__VERSION_CURRENT "${CMAKE_MATCH_1}") 60 | set(__VERSION_REVISION "${CMAKE_MATCH_2}") 61 | set(__VERSION_AGE "${CMAKE_MATCH_3}") 62 | else (__VERSION_INFO MATCHES "^([0-9]+):([0-9]+):([0-9]+)(-dev)?$") 63 | message(FATAL_ERROR "Invalid library version info: ${__VERSION_INFO}") 64 | endif (__VERSION_INFO MATCHES "^([0-9]+):([0-9]+):([0-9]+)(-dev)?$") 65 | 66 | # Mimic libtool's behavior in calculating SONAME and VERSION from 67 | # version-info. 68 | # http://git.savannah.gnu.org/cgit/libtool.git/tree/build-aux/ltmain.in?id=722b6af0fad19b3d9f21924ae5aa6dfae5957378#n7042 69 | math(EXPR __SOVERSION "${__VERSION_CURRENT} - ${__VERSION_AGE}") 70 | set(__VERSION "${__SOVERSION}.${__VERSION_AGE}.${__VERSION_REVISION}") 71 | 72 | get_property(ALL_LOCAL_LIBRARIES GLOBAL PROPERTY ALL_LOCAL_LIBRARIES) 73 | list(APPEND ALL_LOCAL_LIBRARIES ${__TARGET_NAME}) 74 | set_property(GLOBAL PROPERTY ALL_LOCAL_LIBRARIES "${ALL_LOCAL_LIBRARIES}") 75 | 76 | if (ENABLE_SHARED OR ENABLE_SHARED_EXECUTABLES) 77 | add_library(${__TARGET_NAME}-shared SHARED ${__SOURCES}) 78 | set_target_properties( 79 | ${__TARGET_NAME}-shared PROPERTIES 80 | OUTPUT_NAME ${__OUTPUT_NAME} 81 | CLEAN_DIRECT_OUTPUT 1 82 | VERSION ${__VERSION} 83 | SOVERSION ${__SOVERSION} 84 | ) 85 | 86 | if (CMAKE_VERSION VERSION_GREATER "2.8.11") 87 | target_include_directories( 88 | ${__TARGET_NAME}-shared PUBLIC 89 | ${CMAKE_SOURCE_DIR}/include 90 | ${CMAKE_BINARY_DIR}/include 91 | ) 92 | else (CMAKE_VERSION VERSION_GREATER "2.8.11") 93 | include_directories( 94 | ${CMAKE_SOURCE_DIR}/include 95 | ${CMAKE_BINARY_DIR}/include 96 | ) 97 | endif (CMAKE_VERSION VERSION_GREATER "2.8.11") 98 | 99 | target_add_shared_libraries( 100 | ${__TARGET_NAME}-shared 101 | "${__LIBRARIES}" 102 | "${__LOCAL_LIBRARIES}" 103 | ) 104 | 105 | # We have to install the shared library if the user asked us to, or if 106 | # the user asked us to link our programs with the shared library. 107 | install(TARGETS ${__TARGET_NAME}-shared 108 | LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}) 109 | endif (ENABLE_SHARED OR ENABLE_SHARED_EXECUTABLES) 110 | 111 | if (ENABLE_STATIC OR NOT ENABLE_SHARED_EXECUTABLES) 112 | add_library(${__TARGET_NAME}-static STATIC ${__SOURCES}) 113 | set_target_properties( 114 | ${__TARGET_NAME}-static PROPERTIES 115 | OUTPUT_NAME ${__OUTPUT_NAME} 116 | CLEAN_DIRECT_OUTPUT 1 117 | ) 118 | 119 | if (CMAKE_VERSION VERSION_GREATER "2.8.11") 120 | target_include_directories( 121 | ${__TARGET_NAME}-static PUBLIC 122 | ${CMAKE_SOURCE_DIR}/include 123 | ${CMAKE_BINARY_DIR}/include 124 | ) 125 | else (CMAKE_VERSION VERSION_GREATER "2.8.11") 126 | include_directories( 127 | ${CMAKE_SOURCE_DIR}/include 128 | ${CMAKE_BINARY_DIR}/include 129 | ) 130 | endif (CMAKE_VERSION VERSION_GREATER "2.8.11") 131 | 132 | target_add_static_libraries( 133 | ${__TARGET_NAME}-static 134 | "${__LIBRARIES}" 135 | "${__LOCAL_LIBRARIES}" 136 | ) 137 | endif (ENABLE_STATIC OR NOT ENABLE_SHARED_EXECUTABLES) 138 | 139 | if (ENABLE_STATIC) 140 | # We DON'T have to install the static library if the user asked us to 141 | # link our programs statically. 142 | install(TARGETS ${__TARGET_NAME}-static 143 | ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}) 144 | endif (ENABLE_STATIC) 145 | 146 | set(prefix ${CMAKE_INSTALL_PREFIX}) 147 | configure_file( 148 | ${CMAKE_CURRENT_SOURCE_DIR}/${__PKGCONFIG_NAME}.pc.in 149 | ${CMAKE_CURRENT_BINARY_DIR}/${__PKGCONFIG_NAME}.pc 150 | @ONLY 151 | ) 152 | install( 153 | FILES ${CMAKE_CURRENT_BINARY_DIR}/${__PKGCONFIG_NAME}.pc 154 | DESTINATION ${CMAKE_INSTALL_LIBDIR}/pkgconfig 155 | ) 156 | endfunction(add_c_library) 157 | 158 | 159 | #----------------------------------------------------------------------- 160 | # Executable 161 | 162 | function(add_c_executable __TARGET_NAME) 163 | set(options SKIP_INSTALL) 164 | set(one_args OUTPUT_NAME) 165 | set(multi_args LIBRARIES LOCAL_LIBRARIES SOURCES) 166 | cmake_parse_arguments(_ "${options}" "${one_args}" "${multi_args}" ${ARGN}) 167 | 168 | add_executable(${__TARGET_NAME} ${__SOURCES}) 169 | 170 | if (CMAKE_VERSION VERSION_GREATER "2.8.11") 171 | target_include_directories( 172 | ${__TARGET_NAME} PUBLIC 173 | ${CMAKE_SOURCE_DIR}/include 174 | ${CMAKE_BINARY_DIR}/include 175 | ) 176 | else (CMAKE_VERSION VERSION_GREATER "2.8.11") 177 | include_directories( 178 | ${CMAKE_SOURCE_DIR}/include 179 | ${CMAKE_BINARY_DIR}/include 180 | ) 181 | endif (CMAKE_VERSION VERSION_GREATER "2.8.11") 182 | 183 | if (ENABLE_SHARED_EXECUTABLES) 184 | target_add_shared_libraries( 185 | ${__TARGET_NAME} 186 | "${__LIBRARIES}" 187 | "${__LOCAL_LIBRARIES}" 188 | ) 189 | else (ENABLE_SHARED_EXECUTABLES) 190 | target_add_static_libraries( 191 | ${__TARGET_NAME} 192 | "${__LIBRARIES}" 193 | "${__LOCAL_LIBRARIES}" 194 | ) 195 | endif (ENABLE_SHARED_EXECUTABLES) 196 | 197 | if (NOT __SKIP_INSTALL) 198 | install(TARGETS ${__TARGET_NAME} RUNTIME DESTINATION bin) 199 | endif (NOT __SKIP_INSTALL) 200 | endfunction(add_c_executable) 201 | 202 | 203 | #----------------------------------------------------------------------- 204 | # Test case 205 | 206 | pkgconfig_prereq(check OPTIONAL) 207 | 208 | function(add_c_test TEST_NAME) 209 | get_property(ALL_LOCAL_LIBRARIES GLOBAL PROPERTY ALL_LOCAL_LIBRARIES) 210 | add_c_executable( 211 | ${TEST_NAME} 212 | SKIP_INSTALL 213 | OUTPUT_NAME ${TEST_NAME} 214 | SOURCES ${TEST_NAME}.c 215 | LIBRARIES check 216 | LOCAL_LIBRARIES ${ALL_LOCAL_LIBRARIES} 217 | ) 218 | add_test(${TEST_NAME} ${TEST_NAME}) 219 | endfunction(add_c_test) 220 | -------------------------------------------------------------------------------- /docs/sets.rst: -------------------------------------------------------------------------------- 1 | .. _sets: 2 | 3 | IP sets 4 | ======= 5 | 6 | The functions in this section allow you to work with sets of IP addresses. IP 7 | sets work seamlessly with both IPv4 and IPv6 addresses. 8 | 9 | .. note:: 10 | 11 | Before using any of the IP set functions, you must initialize this library 12 | using the following function. 13 | 14 | .. function:: int ipset_init_library(void) 15 | 16 | Initializes the IP set library. This function **must** be called before any 17 | other function in the library. It is safe to call this function multiple 18 | times, and from multiple threads. 19 | 20 | 21 | Creating and freeing sets 22 | ------------------------- 23 | 24 | .. type:: struct ip_set 25 | 26 | A set of IP addresses. The fields of this ``struct`` are opaque; you should 27 | only use the public library functions to access the contents of the set. 28 | 29 | There are two ways that you can work with IP sets. The first is that you can 30 | allocate the space for the :c:type:`ip_set` instance yourself --- for instance, 31 | directly on the stack. The second is to let the IP set library allocate the 32 | space for you. Your choice determines which of the following functions you will 33 | use to create and free your IP sets. 34 | 35 | .. function:: void ipset_init(struct ip_set \*set) 36 | struct ip_set \*ipset_new(void) 37 | 38 | Creates a new IP set. The ``init`` variant should be used if you've 39 | allocated space for the set yourself. The ``new`` variant should be used if 40 | you want the library to allocate the space for you. (The ``new`` variant 41 | never returns ``NULL``; it will abort the program if the allocation fails.) 42 | In both cases, the set starts off empty. 43 | 44 | .. function:: void ipset_done(struct ip_set \*set) 45 | void ipset_free(struct ip_set \*set) 46 | 47 | Finalizes an IP set. The ``done`` variant must be used if you created the 48 | set using :c:func:`ipset_init`; the ``free`` variant must be used if you 49 | created the set using :c:func:`ipset_new`. 50 | 51 | 52 | Adding and removing elements 53 | ---------------------------- 54 | 55 | We provide a variety of functions for adding and removing addresses from an IP 56 | set. 57 | 58 | .. function:: bool ipset_ipv4_add(struct ip_set \*set, struct cork_ipv4 \*ip) 59 | bool ipset_ipv6_add(struct ip_set \*set, struct cork_ipv6 \*ip) 60 | bool ipset_ip_add(struct ip_set \*set, struct cork_ip \*ip) 61 | 62 | Adds a single IP address to *set*. If the IP address was not already in the 63 | set, we return ``true``; otherwise we return ``false``. (In other words, we 64 | return whether the set changed as a result of this operation.) 65 | 66 | .. function:: bool ipset_ipv4_remove(struct ip_set \*set, struct cork_ipv4 \*ip) 67 | bool ipset_ipv6_remove(struct ip_set \*set, struct cork_ipv6 \*ip) 68 | bool ipset_ip_remove(struct ip_set \*set, struct cork_ip \*ip) 69 | 70 | Removes a single IP removeress from *set*. If the IP address was previously 71 | in the set, we return ``true``; otherwise we return ``false``. (In other 72 | words, we return whether the set changed as a result of this operation.) 73 | 74 | .. function:: bool ipset_ipv4_add_network(struct ip_set \*set, struct cork_ipv4 \*ip, unsigned int cidr_prefix) 75 | bool ipset_ipv6_add_network(struct ip_set \*set, struct cork_ipv6 \*ip, unsigned int cidr_prefix) 76 | bool ipset_ip_add_network(struct ip_set \*set, struct cork_ip \*ip, unsigned int cidr_prefix) 77 | 78 | Adds an entire CIDR network of IP addresses to *set*. *ip* is one of the 79 | addresses in the set; *cidr_prefix* is the number of bits in the network 80 | portion of each IP address in the CIDR network, as defined in `RFC 4632`_. 81 | *cidr_prefix* must be in the range 0-32 (inclusive) if *ip* is an IPv4 82 | address, and in the range 0-128 (inclusive) if it's an IPv6 address. 83 | 84 | We return whether the set changed as a result of this operation; if we return 85 | ``true``, than at least one of the address in the CIDR network was not 86 | already present in *set*. We cannot currently distinguish whether *all* of 87 | the addresses were missing (and therefore added). 88 | 89 | .. function:: bool ipset_ipv4_remove_network(struct ip_set \*set, struct cork_ipv4 \*ip, unsigned int cidr_prefix) 90 | bool ipset_ipv6_remove_network(struct ip_set \*set, struct cork_ipv6 \*ip, unsigned int cidr_prefix) 91 | bool ipset_ip_remove_network(struct ip_set \*set, struct cork_ip \*ip, unsigned int cidr_prefix) 92 | 93 | Removes an entire CIDR network of IP addresses from *set*. *ip* is one of 94 | the addresses in the set; *cidr_prefix* is the number of bits in the network 95 | portion of each IP address in the CIDR network, as defined in `RFC 4632`_. 96 | *cidr_prefix* must be in the range 0-32 (inclusive) if *ip* is an IPv4 97 | address, and in the range 0-128 (inclusive) if it's an IPv6 address. 98 | 99 | We return whether the set changed as a result of this operation; if we return 100 | ``true``, than at least one of the address in the CIDR network was present in 101 | *set*. We cannot currently distinguish whether *all* of the addresses were 102 | present (and therefore removed). 103 | 104 | .. _RFC 4632: http://tools.ietf.org/html/rfc4632 105 | 106 | .. note:: 107 | 108 | In all of the ``_network`` functions, if you want to strictly adhere to RFC 109 | 4632, *ip* can only have non-zero bits in its *cidr_prefix* uppermost bits. 110 | All of the lower-order bits (i.e., in the host portion of the IP address) 111 | must be set to 0. We do not enforce this, however. 112 | 113 | 114 | Querying a set 115 | -------------- 116 | 117 | .. function:: bool ipset_contains_ipv4(const struct ip_set \*set, struct cork_ipv4 \*ip) 118 | bool ipset_contains_ipv6(const struct ip_set \*set, struct cork_ipv6 \*ip) 119 | bool ipset_contains_ip(const struct ip_set \*set, struct cork_ip \*ip) 120 | 121 | Returns whether *set* contains *ip*. 122 | 123 | .. function:: bool ipset_is_empty(const struct ip_set \*set) 124 | 125 | Returns whether *set* is empty. 126 | 127 | .. function:: bool ipset_is_equal(const struct ip_set \*set1, const struct ip_set \*set2) 128 | 129 | Returns whether *set1* and *set2* contain exactly the same addresses. 130 | 131 | .. function:: size_t ipset_memory_size(const struct ip_set \*set) 132 | 133 | Returns the number of bytes of memory needed to store *set*. Note that 134 | adding together the storage needed for each set you use doesn't necessarily 135 | give you the total memory requirements, since some storage can be shared 136 | between sets. 137 | 138 | 139 | Iterating through a set 140 | ----------------------- 141 | 142 | In addition to querying individual addresses, you can iterate through the entire 143 | contents of an IP set. There are two iterator functions; one that provides 144 | every individual IP address, and one that collapses addresses into CIDR networks 145 | as much as possible, and returns those networks. 146 | 147 | .. note:: 148 | 149 | You should not modify an IP set while you're actively iterating through its 150 | contents; if you do this, you'll get undefined behavior. 151 | 152 | 153 | .. type:: struct ipset_iterator 154 | 155 | An iterator object that lets you query all of the addresses in an IP set. 156 | 157 | .. member:: struct cork_ip addr 158 | 159 | If iterating through individual addresses, this contains the address that 160 | the iterator currently points at. If iterating through CIDR networks, 161 | this is the representative address of the current network. 162 | 163 | .. member:: unsigned int cidr_prefix 164 | 165 | If iterating through CIDR networks, this is the CIDR prefix of the current 166 | network. If iterating through individual IP addresses, this will always 167 | be ``32`` or ``128``, depending on whether *addr* contains an IPv4 or IPv6 168 | address. 169 | 170 | .. function:: struct ipset_iterator \*ipset_iterate(struct ip_set \*set, bool desired_value) 171 | struct ipset_iterator \*ipset_iterate_networks(struct ip_set \*set, bool desired_value) 172 | 173 | If *desired_value* is ``true``, then we return an iterator that will produce 174 | the IP addresses that are present in *set*. If it's ``false``, then the 175 | iterator will produce the IP addresses that are *not* in *set*. 176 | 177 | The ``_networks`` variant will summarize the IP addresses into CIDR networks, 178 | to reduce the number of items that are reported by the iterator. (This can 179 | be especially useful (necessary?) if your set contains any /8 or /16 IPv4 180 | networks, for instance; or even worse, a /64 IPv6 network.) 181 | 182 | .. function:: void ipset_iterator_advance(struct ipset_iterator \*iterator) 183 | 184 | Advance *iterator* to the next IP address or network in its underlying set. 185 | 186 | .. function:: void ipset_iterator_free(struct ipset_iterator \*iterator) 187 | 188 | Frees an IP set iterator. 189 | 190 | 191 | Storing sets in files 192 | --------------------- 193 | 194 | The functions in this section allow you to store IP sets on disk, and reload 195 | them into another program at a later time. You don't have to know the details 196 | of the file format to be able to use these functions; we guarantee that sets 197 | written with previous versions of the library will be readable by later versions 198 | of the library (but not vice versa). And we guarantee that the file format is 199 | platform-independent; sets written on any machine will be readable on any other 200 | machine. 201 | 202 | (That said, if you do want to know the details of the file format, that's 203 | documented in :ref:`another section `.) 204 | 205 | .. function:: int ipset_save(FILE \*stream, const struct ip_set \*set) 206 | 207 | Saves an IP set into *stream*. You're responsible for opening *stream* 208 | before calling this function, and for closing *stream* afterwards. If there 209 | are any errors writing the set, we return ``-1`` and fill in a libcork 210 | :ref:`error condition `. 211 | 212 | .. function:: int ipset_save_to_stream(struct cork_stream_consumer \*stream, const struct ip_set \*set) 213 | 214 | Saves an IP set into a libcork :ref:`stream consumer `. If 215 | there are any errors writing the set, we return ``-1`` and fill in a libcork 216 | :ref:`error condition `. 217 | 218 | .. function:: struct ip_set \*ipset_load(FILE \*stream) 219 | 220 | Loads an IP set from *stream*. You're responsible for opening *stream* 221 | before calling this function, and for closing *stream* afterwards. If there 222 | are any errors reading the set, we return ``NULL`` and fill in a libcork 223 | :ref:`error condition `. You must use :c:func:`ipset_free` 224 | to free the set when you're done with it. 225 | 226 | .. function:: int ipset_save_dot(FILE \*stream, const struct ip_set \*set) 227 | 228 | Produces a GraphViz_ ``dot`` representation of the BDD graph used to store 229 | *set*, and writes this graph representation to *stream*. You're responsible 230 | for opening *stream* before calling this function, and for closing *stream* 231 | afterwards. If there are any errors writing the set, we return ``-1`` and 232 | fill in a libcork :ref:`error condition `. 233 | 234 | .. _GraphViz: http://www.graphviz.org/ 235 | -------------------------------------------------------------------------------- /src/libipset/bdd/read.c: -------------------------------------------------------------------------------- 1 | /* -*- coding: utf-8 -*- 2 | * ---------------------------------------------------------------------- 3 | * Copyright © 2010, libcorkipset authors 4 | * All rights reserved. 5 | * 6 | * Please see the COPYING file in this distribution for license details. 7 | * ---------------------------------------------------------------------- 8 | */ 9 | 10 | #include 11 | #include 12 | #include 13 | 14 | #include 15 | #include 16 | #include 17 | 18 | #include "ipset/bdd/nodes.h" 19 | #include "ipset/errors.h" 20 | #include "ipset/logging.h" 21 | 22 | 23 | static const char MAGIC_NUMBER[] = "IP set"; 24 | static const size_t MAGIC_NUMBER_LENGTH = sizeof(MAGIC_NUMBER) - 1; 25 | 26 | 27 | /** 28 | * On disk, we use a different node ID scheme than we do in memory. 29 | * Terminal node IDs are non-negative, and are equal to the terminal 30 | * value. Nonterminal node IDs are negative, starting with -1. 31 | * Nonterminal -1 appears first on disk, then nonterminal -2, and so on. 32 | */ 33 | 34 | typedef int serialized_id; 35 | 36 | 37 | /** 38 | * Sets a libcork error based on the contents of errno. 39 | */ 40 | static void 41 | create_errno_error(FILE *stream) 42 | { 43 | if (ferror(stream)) { 44 | cork_error_set(IPSET_ERROR, IPSET_IO_ERROR, "%s", strerror(errno)); 45 | } else { 46 | cork_unknown_error(); 47 | } 48 | } 49 | 50 | 51 | /** 52 | * Read in a big-endian uint8 from a stream. If we can't read the 53 | * integer for some reason, return an error. 54 | */ 55 | static int 56 | read_uint8(FILE *stream, uint8_t *dest) 57 | { 58 | size_t num_read = fread(dest, sizeof(uint8_t), 1, stream); 59 | if (num_read != 1) { 60 | create_errno_error(stream); 61 | return -1; 62 | } 63 | 64 | /* for a byte, we don't need to endian-swap */ 65 | return 0; 66 | } 67 | 68 | 69 | /** 70 | * Read in a big-endian uint16 from a stream. If we can't read the 71 | * integer for some reason, return an error. 72 | */ 73 | static uint16_t 74 | read_uint16(FILE *stream, uint16_t *dest) 75 | { 76 | size_t num_read = fread(dest, sizeof(uint16_t), 1, stream); 77 | if (num_read != 1) { 78 | create_errno_error(stream); 79 | return -1; 80 | } 81 | 82 | CORK_UINT16_BIG_TO_HOST_IN_PLACE(*dest); 83 | return 0; 84 | } 85 | 86 | 87 | /** 88 | * Read in a big-endian uint32 from a stream. If we can't read the 89 | * integer for some reason, return an error. 90 | */ 91 | static uint32_t 92 | read_uint32(FILE *stream, uint32_t *dest) 93 | { 94 | size_t num_read = fread(dest, sizeof(uint32_t), 1, stream); 95 | if (num_read != 1) { 96 | create_errno_error(stream); 97 | return -1; 98 | } 99 | 100 | CORK_UINT32_BIG_TO_HOST_IN_PLACE(*dest); 101 | return 0; 102 | } 103 | 104 | 105 | /** 106 | * Read in a big-endian uint64 from a stream. If we can't read the 107 | * integer for some reason, return an error. 108 | */ 109 | static uint64_t 110 | read_uint64(FILE *stream, uint64_t *dest) 111 | { 112 | size_t num_read = fread(dest, sizeof(uint64_t), 1, stream); 113 | if (num_read != 1) { 114 | create_errno_error(stream); 115 | return -1; 116 | } 117 | 118 | CORK_UINT64_BIG_TO_HOST_IN_PLACE(*dest); 119 | return 0; 120 | } 121 | 122 | 123 | /** 124 | * A helper function that verifies that we've read exactly as many bytes 125 | * as we should, returning an error otherwise. 126 | */ 127 | static int 128 | verify_cap(size_t bytes_read, size_t cap) 129 | { 130 | if (bytes_read < cap) { 131 | /* There's extra data at the end of the stream. */ 132 | cork_error_set 133 | (IPSET_ERROR, IPSET_PARSE_ERROR, 134 | "Malformed set: extra data at end of stream."); 135 | return -1; 136 | } else if (bytes_read > cap) { 137 | /* We read more data than we were supposed to. */ 138 | cork_error_set 139 | (IPSET_ERROR, IPSET_PARSE_ERROR, 140 | "Malformed set: read too much data."); 141 | return -1; 142 | } 143 | 144 | return 0; 145 | } 146 | 147 | /** 148 | * A helper function for reading a version 1 BDD stream. 149 | */ 150 | static ipset_node_id 151 | load_v1(FILE *stream, struct ipset_node_cache *cache) 152 | { 153 | DEBUG("Stream contains v1 IP set"); 154 | ipset_node_id result; 155 | struct cork_hash_table *cache_ids = cork_pointer_hash_table_new(0, 0); 156 | 157 | /* We've already read in the magic number and version. Next should 158 | * be the length of the encoded set. */ 159 | uint64_t length; 160 | DEBUG("Reading encoded length"); 161 | ei_check(read_uint64(stream, &length)); 162 | 163 | /* The length includes the magic number, version number, and the 164 | * length field itself. Remove those to get the cap on the 165 | * remaining stream. */ 166 | 167 | size_t bytes_read = 0; 168 | size_t cap = length - 169 | MAGIC_NUMBER_LENGTH - 170 | sizeof(uint16_t) - 171 | sizeof(uint64_t); 172 | 173 | DEBUG("Length cap is %zu bytes.", cap); 174 | 175 | /* Read in the number of nonterminals. */ 176 | 177 | uint32_t nonterminal_count; 178 | DEBUG("Reading number of nonterminals"); 179 | ei_check(read_uint32(stream, &nonterminal_count)); 180 | bytes_read += sizeof(uint32_t); 181 | 182 | /* If there are no nonterminals, then there's only a single terminal 183 | * left to read. */ 184 | 185 | if (nonterminal_count == 0) { 186 | uint32_t value; 187 | DEBUG("Reading single terminal value"); 188 | ei_check(read_uint32(stream, &value)); 189 | bytes_read += sizeof(uint32_t); 190 | 191 | /* We should have reached the end of the encoded set. */ 192 | ei_check(verify_cap(bytes_read, cap)); 193 | 194 | /* Create a terminal node for this value and return it. */ 195 | cork_hash_table_free(cache_ids); 196 | return ipset_terminal_node_id(value); 197 | } 198 | 199 | /* Otherwise, read in each nonterminal. We need to keep track of a 200 | * mapping between each nonterminal's ID in the stream (which are 201 | * number consecutively from -1), and its ID in the node cache 202 | * (which could be anything). */ 203 | 204 | size_t i; 205 | for (i = 0; i < nonterminal_count; i++) { 206 | serialized_id serialized_id = -(i+1); 207 | 208 | /* Each serialized node consists of a variable index, a low 209 | * pointer, and a high pointer. */ 210 | 211 | uint8_t variable; 212 | ei_check(read_uint8(stream, &variable)); 213 | bytes_read += sizeof(uint8_t); 214 | 215 | int32_t low; 216 | ei_check(read_uint32(stream, (uint32_t *) &low)); 217 | bytes_read += sizeof(int32_t); 218 | 219 | int32_t high; 220 | ei_check(read_uint32(stream, (uint32_t *) &high)); 221 | bytes_read += sizeof(int32_t); 222 | 223 | DEBUG("Read serialized node %d = (x%d? %" PRId32 ": %" PRId32 ")", 224 | serialized_id, variable, high, low); 225 | 226 | /* Turn the low pointer into a node ID. If the pointer is >= 0, 227 | * it's a terminal value. Otherwise, its a nonterminal ID, 228 | * indexing into the serialized nonterminal array.*/ 229 | 230 | ipset_node_id low_id; 231 | 232 | if (low >= 0) { 233 | low_id = ipset_terminal_node_id(low); 234 | } else { 235 | /* The file format guarantees that any node reference points 236 | * to a node earlier in the serialized array. That means we 237 | * can assume that cache_ids has already been filled in for 238 | * this node. */ 239 | 240 | low_id = (ipset_node_id) (uintptr_t) 241 | cork_hash_table_get(cache_ids, (void *) (intptr_t) low); 242 | 243 | DEBUG(" Serialized ID %" PRId32 " is internal ID %u", 244 | low, low_id); 245 | } 246 | 247 | /* Do the same for the high pointer. */ 248 | 249 | ipset_node_id high_id; 250 | 251 | if (high >= 0) { 252 | high_id = ipset_terminal_node_id(high); 253 | } else { 254 | /* The file format guarantees that any node reference points 255 | * to a node earlier in the serialized array. That means we 256 | * can assume that cache_ids has already been filled in for 257 | * this node. */ 258 | 259 | high_id = (ipset_node_id) (uintptr_t) 260 | cork_hash_table_get(cache_ids, (void *) (intptr_t) high); 261 | 262 | DEBUG(" Serialized ID %" PRId32 " is internal ID %u", 263 | high, high_id); 264 | } 265 | 266 | /* Create a nonterminal node in the node cache. */ 267 | result = ipset_node_cache_nonterminal 268 | (cache, variable, low_id, high_id); 269 | 270 | DEBUG("Internal node %u = nonterminal(x%d? %u: %u)", 271 | result, (int) variable, high_id, low_id); 272 | 273 | /* Remember the internal node ID for this new node, in case any 274 | * later serialized nodes point to it. */ 275 | 276 | cork_hash_table_put 277 | (cache_ids, (void *) (intptr_t) serialized_id, 278 | (void *) (uintptr_t) result, NULL, NULL, NULL); 279 | } 280 | 281 | /* We should have reached the end of the encoded set. */ 282 | ei_check(verify_cap(bytes_read, cap)); 283 | 284 | /* The last node is the nonterminal for the entire set. */ 285 | cork_hash_table_free(cache_ids); 286 | return result; 287 | 288 | error: 289 | /* If there's an error, clean up the objects that we've created 290 | * before returning. */ 291 | 292 | cork_hash_table_free(cache_ids); 293 | return 0; 294 | } 295 | 296 | 297 | ipset_node_id 298 | ipset_node_cache_load(FILE *stream, struct ipset_node_cache *cache) 299 | { 300 | size_t bytes_read; 301 | 302 | /* First, read in the magic number from the stream to ensure that 303 | * this is an IP set. */ 304 | 305 | uint8_t magic[MAGIC_NUMBER_LENGTH]; 306 | 307 | DEBUG("Reading IP set magic number"); 308 | bytes_read = fread(magic, 1, MAGIC_NUMBER_LENGTH, stream); 309 | 310 | if (ferror(stream)) { 311 | create_errno_error(stream); 312 | return 0; 313 | } 314 | 315 | if (bytes_read != MAGIC_NUMBER_LENGTH) { 316 | /* We reached EOF before reading the entire magic number. */ 317 | cork_error_set 318 | (IPSET_ERROR, IPSET_PARSE_ERROR, 319 | "Unexpected end of file"); 320 | return 0; 321 | } 322 | 323 | if (memcmp(magic, MAGIC_NUMBER, MAGIC_NUMBER_LENGTH) != 0) { 324 | /* The magic number doesn't match, so this isn't a BDD. */ 325 | cork_error_set 326 | (IPSET_ERROR, IPSET_PARSE_ERROR, 327 | "Magic number doesn't match; this isn't an IP set."); 328 | return 0; 329 | } 330 | 331 | /* Read in the version number and dispatch to the right reading 332 | * function. */ 333 | 334 | uint16_t version; 335 | DEBUG("Reading IP set version"); 336 | xi_check(0, read_uint16(stream, &version)); 337 | 338 | switch (version) { 339 | case 0x0001: 340 | return load_v1(stream, cache); 341 | 342 | default: 343 | /* We don't know how to read this version number. */ 344 | cork_error_set 345 | (IPSET_ERROR, IPSET_PARSE_ERROR, 346 | "Unknown version number %" PRIu16, version); 347 | return 0; 348 | } 349 | } 350 | -------------------------------------------------------------------------------- /src/libipset/set/iterator.c: -------------------------------------------------------------------------------- 1 | /* -*- coding: utf-8 -*- 2 | * ---------------------------------------------------------------------- 3 | * Copyright © 2010, libcorkipset authors 4 | * All rights reserved. 5 | * 6 | * Please see the COPYING file in this distribution for license details. 7 | * ---------------------------------------------------------------------- 8 | */ 9 | 10 | #include 11 | 12 | #include 13 | 14 | #include "ipset/bdd/nodes.h" 15 | #include "ipset/bits.h" 16 | #include "ipset/ipset.h" 17 | #include "ipset/logging.h" 18 | 19 | 20 | #define IPV4_BIT_SIZE 32 21 | #define IPV6_BIT_SIZE 128 22 | 23 | 24 | /* Forward declarations */ 25 | 26 | static void 27 | process_assignment(struct ipset_iterator *iterator); 28 | 29 | static void 30 | expand_ipv6(struct ipset_iterator *iterator); 31 | 32 | 33 | /** 34 | * Find the highest non-EITHER bit in an assignment, starting from the 35 | * given bit index. 36 | */ 37 | static unsigned int 38 | find_last_non_either_bit(struct ipset_assignment *assignment, 39 | unsigned int starting_bit) 40 | { 41 | unsigned int i; 42 | 43 | for (i = starting_bit; i >= 1; i--) { 44 | enum ipset_tribool value = ipset_assignment_get(assignment, i); 45 | if (value != IPSET_EITHER) { 46 | return i; 47 | } 48 | } 49 | 50 | return 0; 51 | } 52 | 53 | 54 | /** 55 | * Create a generic IP address object from the current expanded 56 | * assignment. 57 | */ 58 | static void 59 | create_ip_address(struct ipset_iterator *iterator) 60 | { 61 | struct cork_ip *addr = &iterator->addr; 62 | struct ipset_expanded_assignment *exp = iterator->assignment_iterator; 63 | 64 | /* Initialize the address to all 0 bits. */ 65 | memset(addr, 0, sizeof(struct cork_ip)); 66 | 67 | /* Check variable 0 to see if this is an IPv4 or IPv6 address. */ 68 | addr->version = IPSET_BIT_GET(exp->values.buf, 0)? 4: 6; 69 | 70 | /* Copy bits from the expanded assignment. The number of bits to 71 | * copy is given as the current netmask. We'll have calculated that 72 | * already based on the non-expanded assignment. */ 73 | unsigned int i; 74 | for (i = 0; i < iterator->cidr_prefix; i++) { 75 | IPSET_BIT_SET(&addr->ip, i, IPSET_BIT_GET(exp->values.buf, i+1)); 76 | } 77 | 78 | #if IPSET_DEBUG 79 | char buf[CORK_IP_STRING_LENGTH]; 80 | cork_ip_to_raw_string(addr, buf); 81 | DEBUG("Current IP address is %s/%u", buf, iterator->cidr_prefix); 82 | #endif 83 | } 84 | 85 | 86 | /** 87 | * Advance the BDD iterator, taking into account that some assignments 88 | * need to be expanded twice. 89 | */ 90 | static void 91 | advance_assignment(struct ipset_iterator *iterator) 92 | { 93 | /* Check the current state of the iterator to determine how to 94 | * advance. */ 95 | 96 | /* In most cases, the assignment we just finished only needed to be 97 | * expanded once. So we move on to the next assignment and process 98 | * it. */ 99 | if (CORK_LIKELY(iterator->multiple_expansion_state == 100 | IPSET_ITERATOR_NORMAL)) 101 | { 102 | ipset_bdd_iterator_advance(iterator->bdd_iterator); 103 | process_assignment(iterator); 104 | return; 105 | } 106 | 107 | /* If the assignment needs to be expanded twice, we'll do the IPv4 108 | * expansion first. If that's what we've just finished, do the IPv6 109 | * expansion next. */ 110 | 111 | if (iterator->multiple_expansion_state == IPSET_ITERATOR_MULTIPLE_IPV4) { 112 | DEBUG("Expanding IPv6 second"); 113 | 114 | iterator->multiple_expansion_state = IPSET_ITERATOR_MULTIPLE_IPV6; 115 | ipset_assignment_set 116 | (iterator->bdd_iterator->assignment, 0, IPSET_FALSE); 117 | expand_ipv6(iterator); 118 | return; 119 | } 120 | 121 | /* If we've just finished the IPv6 expansion, then we've finished 122 | * with this assignment. Before moving on to the next one, we have 123 | * to reset variable 0 to EITHER (which it was before we started 124 | * this whole mess). */ 125 | 126 | if (iterator->multiple_expansion_state == IPSET_ITERATOR_MULTIPLE_IPV6) { 127 | DEBUG("Finished both expansions"); 128 | 129 | ipset_assignment_set 130 | (iterator->bdd_iterator->assignment, 0, IPSET_EITHER); 131 | ipset_bdd_iterator_advance(iterator->bdd_iterator); 132 | process_assignment(iterator); 133 | return; 134 | } 135 | } 136 | 137 | 138 | /** 139 | * Process the current expanded assignment in the current BDD 140 | * assignment. 141 | */ 142 | static void 143 | process_expanded_assignment(struct ipset_iterator *iterator) 144 | { 145 | if (iterator->assignment_iterator->finished) { 146 | /* If there isn't anything in the expanded assignment, advance 147 | * to the next BDD assignment. */ 148 | 149 | DEBUG("Expanded assignment is finished"); 150 | ipset_expanded_assignment_free(iterator->assignment_iterator); 151 | iterator->assignment_iterator = NULL; 152 | advance_assignment(iterator); 153 | } else { 154 | /* Otherwise, we've found a fully expanded assignment, so create 155 | * an IP address for it and return. */ 156 | create_ip_address(iterator); 157 | } 158 | } 159 | 160 | 161 | /** 162 | * Expand the current assignment as IPv4 addresses. 163 | */ 164 | static void 165 | expand_ipv4(struct ipset_iterator *iterator) 166 | { 167 | unsigned int last_bit; 168 | 169 | if (iterator->summarize) { 170 | last_bit = find_last_non_either_bit 171 | (iterator->bdd_iterator->assignment, IPV4_BIT_SIZE); 172 | DEBUG("Last non-either bit is %u", last_bit); 173 | } else { 174 | last_bit = IPV4_BIT_SIZE; 175 | } 176 | 177 | iterator->assignment_iterator = 178 | ipset_assignment_expand 179 | (iterator->bdd_iterator->assignment, last_bit + 1); 180 | iterator->cidr_prefix = last_bit; 181 | 182 | process_expanded_assignment(iterator); 183 | } 184 | 185 | 186 | /** 187 | * Expand the current assignment as IPv4 addresses. 188 | */ 189 | static void 190 | expand_ipv6(struct ipset_iterator *iterator) 191 | { 192 | unsigned int last_bit; 193 | 194 | if (iterator->summarize) { 195 | last_bit = find_last_non_either_bit 196 | (iterator->bdd_iterator->assignment, IPV6_BIT_SIZE); 197 | DEBUG("Last non-either bit is %u", last_bit); 198 | } else { 199 | last_bit = IPV6_BIT_SIZE; 200 | } 201 | 202 | iterator->assignment_iterator = 203 | ipset_assignment_expand 204 | (iterator->bdd_iterator->assignment, last_bit + 1); 205 | iterator->cidr_prefix = last_bit; 206 | 207 | process_expanded_assignment(iterator); 208 | } 209 | 210 | 211 | /** 212 | * Process the current assignment in the BDD iterator. 213 | */ 214 | 215 | static void 216 | process_assignment(struct ipset_iterator *iterator) 217 | { 218 | while (!iterator->bdd_iterator->finished) { 219 | if (iterator->bdd_iterator->value == iterator->desired_value) { 220 | /* If the BDD iterator hasn't finished, and the result of 221 | * the function with this assignment matches what the caller 222 | * wants, then we've found an assignment to generate IP 223 | * addresses from. 224 | * 225 | * Try to expand this assignment, and process the first 226 | * expanded assignment. We want 32 + 1 variables if the 227 | * current address is IPv4; 128 + 1 if it's IPv6. */ 228 | 229 | DEBUG("Got a matching BDD assignment"); 230 | enum ipset_tribool address_type = ipset_assignment_get 231 | (iterator->bdd_iterator->assignment, 0); 232 | 233 | if (address_type == IPSET_FALSE) { 234 | /* FALSE means IPv6*/ 235 | DEBUG("Assignment is IPv6"); 236 | iterator->multiple_expansion_state = IPSET_ITERATOR_NORMAL; 237 | expand_ipv6(iterator); 238 | return; 239 | } else if (address_type == IPSET_TRUE) { 240 | /* TRUE means IPv4*/ 241 | DEBUG("Assignment is IPv4"); 242 | iterator->multiple_expansion_state = IPSET_ITERATOR_NORMAL; 243 | expand_ipv4(iterator); 244 | return; 245 | } else { 246 | /* EITHER means that this assignment contains both IPv4 247 | * and IPv6 addresses. Expand it as IPv4 first. */ 248 | DEBUG("Assignment is both IPv4 and IPv6"); 249 | DEBUG("Expanding IPv4 first"); 250 | iterator->multiple_expansion_state = 251 | IPSET_ITERATOR_MULTIPLE_IPV4; 252 | ipset_assignment_set 253 | (iterator->bdd_iterator->assignment, 0, IPSET_TRUE); 254 | expand_ipv4(iterator); 255 | return; 256 | } 257 | } 258 | 259 | /* The BDD iterator has a value, but it doesn't match the one we 260 | * want. Advance the BDD iterator and try again. */ 261 | DEBUG("Value is %d, skipping", iterator->bdd_iterator->value); 262 | ipset_bdd_iterator_advance(iterator->bdd_iterator); 263 | } 264 | 265 | /* If we fall through, then the BDD iterator has finished. That 266 | * means there's nothing left for the set iterator. */ 267 | 268 | DEBUG("Set iterator is finished"); 269 | ipset_expanded_assignment_free(iterator->assignment_iterator); 270 | iterator->assignment_iterator = NULL; 271 | 272 | ipset_bdd_iterator_free(iterator->bdd_iterator); 273 | iterator->bdd_iterator = NULL; 274 | iterator->finished = true; 275 | } 276 | 277 | 278 | static struct ipset_iterator * 279 | create_iterator(struct ip_set *set, bool desired_value, bool summarize) 280 | { 281 | /* First allocate the iterator itself. */ 282 | struct ipset_iterator *iterator = cork_new(struct ipset_iterator); 283 | iterator->finished = false; 284 | iterator->assignment_iterator = NULL; 285 | iterator->desired_value = desired_value; 286 | iterator->summarize = summarize; 287 | 288 | /* Then create the iterator that returns each BDD assignment. */ 289 | DEBUG("Iterating set"); 290 | iterator->bdd_iterator = ipset_node_iterate(set->cache, set->set_bdd); 291 | 292 | /* Then drill down from the current BDD assignment, creating an 293 | * expanded assignment for it. */ 294 | process_assignment(iterator); 295 | return iterator; 296 | } 297 | 298 | 299 | struct ipset_iterator * 300 | ipset_iterate(struct ip_set *set, bool desired_value) 301 | { 302 | return create_iterator(set, desired_value, false); 303 | } 304 | 305 | 306 | struct ipset_iterator * 307 | ipset_iterate_networks(struct ip_set *set, bool desired_value) 308 | { 309 | return create_iterator(set, desired_value, true); 310 | } 311 | 312 | 313 | void 314 | ipset_iterator_free(struct ipset_iterator *iterator) 315 | { 316 | if (iterator->bdd_iterator != NULL) { 317 | ipset_bdd_iterator_free(iterator->bdd_iterator); 318 | } 319 | if (iterator->assignment_iterator != NULL) { 320 | ipset_expanded_assignment_free(iterator->assignment_iterator); 321 | } 322 | free(iterator); 323 | } 324 | 325 | 326 | void 327 | ipset_iterator_advance(struct ipset_iterator *iterator) 328 | { 329 | /* If we're already at the end of the iterator, don't do anything. */ 330 | 331 | if (CORK_UNLIKELY(iterator->finished)) { 332 | return; 333 | } 334 | 335 | /* Otherwise, advance the expanded assignment iterator to the next 336 | * assignment, and then drill down into it. */ 337 | 338 | DEBUG("Advancing set iterator"); 339 | ipset_expanded_assignment_advance(iterator->assignment_iterator); 340 | process_expanded_assignment(iterator); 341 | } 342 | -------------------------------------------------------------------------------- /tests/test-iterator.c: -------------------------------------------------------------------------------- 1 | /* -*- coding: utf-8 -*- 2 | * ---------------------------------------------------------------------- 3 | * Copyright © 2009, libcorkipset authors 4 | * All rights reserved. 5 | * 6 | * Please see the COPYING file in this distribution for license details. 7 | * ---------------------------------------------------------------------- 8 | */ 9 | 10 | #include 11 | 12 | #include 13 | #include 14 | 15 | #include "ipset/ipset.h" 16 | 17 | 18 | #define IPV4_BIT_SIZE 32 19 | #define IPV6_BIT_SIZE 128 20 | 21 | 22 | /*----------------------------------------------------------------------- 23 | * Iterators 24 | */ 25 | 26 | START_TEST(test_iterate_empty) 27 | { 28 | struct ip_set set; 29 | ipset_init(&set); 30 | struct ipset_iterator *it = ipset_iterate(&set, true); 31 | fail_if(it == NULL, 32 | "IP set iterator is NULL"); 33 | fail_unless(it->finished, 34 | "IP set should be empty"); 35 | ipset_iterator_free(it); 36 | ipset_done(&set); 37 | } 38 | END_TEST 39 | 40 | 41 | START_TEST(test_ipv4_iterate_01) 42 | { 43 | struct ip_set set; 44 | ipset_init(&set); 45 | 46 | struct cork_ip ip1; 47 | cork_ip_init(&ip1, "192.168.0.1"); 48 | 49 | fail_if(ipset_ip_add(&set, &ip1), 50 | "Element should not be present"); 51 | 52 | struct ipset_iterator *it = ipset_iterate(&set, true); 53 | fail_if(it == NULL, 54 | "IP set iterator is NULL"); 55 | 56 | fail_if(it->finished, 57 | "IP set shouldn't be empty"); 58 | fail_unless(cork_ip_equal(&ip1, &it->addr), 59 | "IP address 0 doesn't match"); 60 | fail_unless(it->cidr_prefix == IPV4_BIT_SIZE, 61 | "IP CIDR prefix 0 doesn't match"); 62 | 63 | ipset_iterator_advance(it); 64 | fail_unless(it->finished, 65 | "IP set should contain 1 element"); 66 | 67 | ipset_iterator_free(it); 68 | 69 | ipset_done(&set); 70 | } 71 | END_TEST 72 | 73 | 74 | START_TEST(test_ipv4_iterate_network_01) 75 | { 76 | struct ip_set set; 77 | ipset_init(&set); 78 | 79 | struct cork_ip ip1; 80 | cork_ip_init(&ip1, "192.168.0.0"); 81 | 82 | fail_if(ipset_ip_add_network(&set, &ip1, 31), 83 | "Element should not be present"); 84 | 85 | struct ipset_iterator *it = ipset_iterate_networks(&set, true); 86 | fail_if(it == NULL, 87 | "IP set iterator is NULL"); 88 | 89 | fail_if(it->finished, 90 | "IP set shouldn't be empty"); 91 | fail_unless(cork_ip_equal(&ip1, &it->addr), 92 | "IP address 0 doesn't match"); 93 | fail_unless(it->cidr_prefix == 31, 94 | "IP CIDR prefix 0 doesn't match"); 95 | 96 | ipset_iterator_advance(it); 97 | fail_unless(it->finished, 98 | "IP set should contain 1 elements"); 99 | 100 | ipset_iterator_free(it); 101 | 102 | ipset_done(&set); 103 | } 104 | END_TEST 105 | 106 | 107 | START_TEST(test_ipv4_iterate_network_02) 108 | { 109 | struct ip_set set; 110 | ipset_init(&set); 111 | 112 | struct cork_ip ip1; 113 | cork_ip_init(&ip1, "192.168.0.0"); 114 | 115 | fail_if(ipset_ip_add_network(&set, &ip1, 16), 116 | "Element should not be present"); 117 | 118 | struct ipset_iterator *it = ipset_iterate_networks(&set, true); 119 | fail_if(it == NULL, 120 | "IP set iterator is NULL"); 121 | 122 | fail_if(it->finished, 123 | "IP set shouldn't be empty"); 124 | fail_unless(cork_ip_equal(&ip1, &it->addr), 125 | "IP address 0 doesn't match"); 126 | fail_unless(it->cidr_prefix == 16, 127 | "IP CIDR prefix 0 doesn't match"); 128 | 129 | ipset_iterator_advance(it); 130 | fail_unless(it->finished, 131 | "IP set should contain 1 elements"); 132 | 133 | ipset_iterator_free(it); 134 | 135 | ipset_done(&set); 136 | } 137 | END_TEST 138 | 139 | 140 | START_TEST(test_ipv4_iterate_network_03) 141 | { 142 | struct ip_set set; 143 | ipset_init(&set); 144 | 145 | /* 146 | * If we add all of the IP addresses in a network individually, we 147 | * should still get the network as a whole from the iterator. 148 | */ 149 | 150 | struct cork_ip ip1; 151 | cork_ip_init(&ip1, "192.168.0.0"); 152 | 153 | struct cork_ip ip2; 154 | cork_ip_init(&ip2, "192.168.0.1"); 155 | 156 | fail_if(ipset_ip_add(&set, &ip1), 157 | "Element should not be present"); 158 | 159 | fail_if(ipset_ip_add(&set, &ip2), 160 | "Element should not be present"); 161 | 162 | struct ipset_iterator *it = ipset_iterate_networks(&set, true); 163 | fail_if(it == NULL, 164 | "IP set iterator is NULL"); 165 | 166 | fail_if(it->finished, 167 | "IP set shouldn't be empty"); 168 | fail_unless(cork_ip_equal(&ip1, &it->addr), 169 | "IP address 0 doesn't match"); 170 | fail_unless(it->cidr_prefix == 31, 171 | "IP CIDR prefix 0 doesn't match"); 172 | 173 | ipset_iterator_advance(it); 174 | fail_unless(it->finished, 175 | "IP set should contain 1 elements"); 176 | 177 | ipset_iterator_free(it); 178 | 179 | ipset_done(&set); 180 | } 181 | END_TEST 182 | 183 | 184 | START_TEST(test_ipv6_iterate_01) 185 | { 186 | struct ip_set set; 187 | ipset_init(&set); 188 | 189 | struct cork_ip ip1; 190 | cork_ip_init(&ip1, "fe80::1"); 191 | 192 | fail_if(ipset_ip_add(&set, &ip1), 193 | "Element should not be present"); 194 | 195 | struct ipset_iterator *it = ipset_iterate(&set, true); 196 | fail_if(it == NULL, 197 | "IP set iterator is NULL"); 198 | 199 | fail_if(it->finished, 200 | "IP set shouldn't be empty"); 201 | fail_unless(cork_ip_equal(&ip1, &it->addr), 202 | "IP address 0 doesn't match"); 203 | fail_unless(it->cidr_prefix == IPV6_BIT_SIZE, 204 | "IP CIDR prefix 0 doesn't match"); 205 | 206 | ipset_iterator_advance(it); 207 | fail_unless(it->finished, 208 | "IP set should contain 1 element"); 209 | 210 | ipset_iterator_free(it); 211 | 212 | ipset_done(&set); 213 | } 214 | END_TEST 215 | 216 | 217 | START_TEST(test_ipv6_iterate_network_01) 218 | { 219 | struct ip_set set; 220 | ipset_init(&set); 221 | 222 | struct cork_ip ip1; 223 | cork_ip_init(&ip1, "fe80::"); 224 | 225 | fail_if(ipset_ip_add_network(&set, &ip1, 127), 226 | "Element should not be present"); 227 | 228 | struct ipset_iterator *it = ipset_iterate_networks(&set, true); 229 | fail_if(it == NULL, 230 | "IP set iterator is NULL"); 231 | 232 | fail_if(it->finished, 233 | "IP set shouldn't be empty"); 234 | fail_unless(cork_ip_equal(&ip1, &it->addr), 235 | "IP address 0 doesn't match"); 236 | fail_unless(it->cidr_prefix == 127, 237 | "IP CIDR prefix 0 doesn't match (%u)", it->cidr_prefix); 238 | 239 | ipset_iterator_advance(it); 240 | fail_unless(it->finished, 241 | "IP set should contain 1 element"); 242 | 243 | ipset_iterator_free(it); 244 | 245 | ipset_done(&set); 246 | } 247 | END_TEST 248 | 249 | 250 | START_TEST(test_ipv6_iterate_network_02) 251 | { 252 | struct ip_set set; 253 | ipset_init(&set); 254 | 255 | struct cork_ip ip1; 256 | cork_ip_init(&ip1, "fe80::"); 257 | 258 | fail_if(ipset_ip_add_network(&set, &ip1, 16), 259 | "Element should not be present"); 260 | 261 | struct ipset_iterator *it = ipset_iterate_networks(&set, true); 262 | fail_if(it == NULL, 263 | "IP set iterator is NULL"); 264 | 265 | fail_if(it->finished, 266 | "IP set shouldn't be empty"); 267 | fail_unless(cork_ip_equal(&ip1, &it->addr), 268 | "IP address 0 doesn't match"); 269 | fail_unless(it->cidr_prefix == 16, 270 | "IP CIDR prefix 0 doesn't match (%u)", it->cidr_prefix); 271 | 272 | ipset_iterator_advance(it); 273 | fail_unless(it->finished, 274 | "IP set should contain 1 element"); 275 | 276 | ipset_iterator_free(it); 277 | 278 | ipset_done(&set); 279 | } 280 | END_TEST 281 | 282 | 283 | START_TEST(test_ipv6_iterate_network_03) 284 | { 285 | struct ip_set set; 286 | ipset_init(&set); 287 | 288 | /* 289 | * If we add all of the IP addresses in a network individually, we 290 | * should still get the network as a whole from the iterator. 291 | */ 292 | 293 | struct cork_ip ip1; 294 | cork_ip_init(&ip1, "fe80::"); 295 | 296 | struct cork_ip ip2; 297 | cork_ip_init(&ip2, "fe80::1"); 298 | 299 | fail_if(ipset_ip_add(&set, &ip1), 300 | "Element should not be present"); 301 | 302 | fail_if(ipset_ip_add(&set, &ip2), 303 | "Element should not be present"); 304 | 305 | struct ipset_iterator *it = ipset_iterate_networks(&set, true); 306 | fail_if(it == NULL, 307 | "IP set iterator is NULL"); 308 | 309 | fail_if(it->finished, 310 | "IP set shouldn't be empty"); 311 | fail_unless(cork_ip_equal(&ip1, &it->addr), 312 | "IP address 0 doesn't match"); 313 | fail_unless(it->cidr_prefix == 127, 314 | "IP CIDR prefix 0 doesn't match"); 315 | 316 | ipset_iterator_advance(it); 317 | fail_unless(it->finished, 318 | "IP set should contain 1 elements"); 319 | 320 | ipset_iterator_free(it); 321 | 322 | ipset_done(&set); 323 | } 324 | END_TEST 325 | 326 | 327 | START_TEST(test_generic_ip_iterate_01) 328 | { 329 | struct ip_set set; 330 | ipset_init(&set); 331 | 332 | struct cork_ip ip1; 333 | cork_ip_init(&ip1, "0.0.0.0"); 334 | 335 | struct cork_ip ip2; 336 | cork_ip_init(&ip2, "::"); 337 | 338 | struct ipset_iterator *it = ipset_iterate_networks(&set, false); 339 | fail_if(it == NULL, 340 | "IP set iterator is NULL"); 341 | 342 | fail_if(it->finished, 343 | "IP set shouldn't be empty"); 344 | fail_unless(cork_ip_equal(&ip1, &it->addr), 345 | "IP address 0 doesn't match"); 346 | fail_unless(it->cidr_prefix == 0, 347 | "IP CIDR prefix 0 doesn't match"); 348 | 349 | ipset_iterator_advance(it); 350 | fail_if(it->finished, 351 | "IP set should have more than 1 element"); 352 | fail_unless(cork_ip_equal(&ip2, &it->addr), 353 | "IP address 1 doesn't match"); 354 | fail_unless(it->cidr_prefix == 0, 355 | "IP CIDR prefix 1 doesn't match"); 356 | 357 | ipset_iterator_advance(it); 358 | fail_unless(it->finished, 359 | "IP set should contain 2 elements"); 360 | 361 | ipset_iterator_free(it); 362 | 363 | ipset_done(&set); 364 | } 365 | END_TEST 366 | 367 | 368 | START_TEST(test_generic_ip_iterate_02) 369 | { 370 | struct ip_set set; 371 | ipset_init(&set); 372 | 373 | /* 374 | * These addresses are carefully constructed so that the same BDD 375 | * variable assignments are used to store both, apart from the 376 | * IPv4/v6 discriminator variable. The goal is get a BDD that has 377 | * EITHER in the assignment for variable 0, but isn't simply the 378 | * empty or full set. 379 | */ 380 | 381 | struct cork_ip ip1; 382 | cork_ip_init(&ip1, "192.168.0.1"); /* 0xc0a80001 */ 383 | 384 | struct cork_ip ip2; 385 | cork_ip_init(&ip2, "c0a8:0001::"); 386 | 387 | fail_if(ipset_ip_add(&set, &ip1), 388 | "Element should not be present"); 389 | fail_if(ipset_ip_add_network(&set, &ip2, 32), 390 | "Element should not be present"); 391 | 392 | struct ipset_iterator *it = ipset_iterate_networks(&set, true); 393 | fail_if(it == NULL, 394 | "IP set iterator is NULL"); 395 | 396 | fail_if(it->finished, 397 | "IP set shouldn't be empty"); 398 | fail_unless(cork_ip_equal(&ip1, &it->addr), 399 | "IP address 0 doesn't match"); 400 | fail_unless(it->cidr_prefix == 32, 401 | "IP CIDR prefix 0 doesn't match"); 402 | 403 | ipset_iterator_advance(it); 404 | fail_if(it->finished, 405 | "IP set should have more than 1 element"); 406 | fail_unless(cork_ip_equal(&ip2, &it->addr), 407 | "IP address 1 doesn't match"); 408 | fail_unless(it->cidr_prefix == 32, 409 | "IP CIDR prefix 1 doesn't match"); 410 | 411 | ipset_iterator_advance(it); 412 | fail_unless(it->finished, 413 | "IP set should contain 2 elements"); 414 | 415 | ipset_iterator_free(it); 416 | 417 | ipset_done(&set); 418 | } 419 | END_TEST 420 | 421 | 422 | /*----------------------------------------------------------------------- 423 | * Testing harness 424 | */ 425 | 426 | Suite * 427 | ipset_suite() 428 | { 429 | Suite *s = suite_create("ipset"); 430 | 431 | TCase *tc_iterator = tcase_create("iterator"); 432 | tcase_add_test(tc_iterator, test_iterate_empty); 433 | tcase_add_test(tc_iterator, test_ipv4_iterate_01); 434 | tcase_add_test(tc_iterator, test_ipv4_iterate_network_01); 435 | tcase_add_test(tc_iterator, test_ipv4_iterate_network_02); 436 | tcase_add_test(tc_iterator, test_ipv4_iterate_network_03); 437 | tcase_add_test(tc_iterator, test_ipv6_iterate_01); 438 | tcase_add_test(tc_iterator, test_ipv6_iterate_network_01); 439 | tcase_add_test(tc_iterator, test_ipv6_iterate_network_02); 440 | tcase_add_test(tc_iterator, test_ipv6_iterate_network_03); 441 | tcase_add_test(tc_iterator, test_generic_ip_iterate_01); 442 | tcase_add_test(tc_iterator, test_generic_ip_iterate_02); 443 | suite_add_tcase(s, tc_iterator); 444 | 445 | return s; 446 | } 447 | 448 | 449 | int 450 | main(int argc, const char **argv) 451 | { 452 | int number_failed; 453 | Suite *suite = ipset_suite(); 454 | SRunner *runner = srunner_create(suite); 455 | 456 | ipset_init_library(); 457 | 458 | srunner_run_all(runner, CK_NORMAL); 459 | number_failed = srunner_ntests_failed(runner); 460 | srunner_free(runner); 461 | 462 | return (number_failed == 0)? EXIT_SUCCESS: EXIT_FAILURE; 463 | } 464 | --------------------------------------------------------------------------------