├── 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 | [](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 |
--------------------------------------------------------------------------------