[![Build Status](https://travis-ci.org/bbayles/network-finder.svg?branch=master)](https://travis-ci.org/bbayles/network-finder)
[![Coverage Status](https://coveralls.io/repos/bbayles/network-finder/badge.svg?branch=master&service=github)](https://coveralls.io/github/bbayles/network-finder?branch=master)

## network-finder

`network_finder` is a Python module for determining whether an IP address or
IP subnet is contained within another IP subnet. For example, `192.0.2.1` is
contained by `192.0.2.0/24`, and `192.0.2.0/24` is contained by `192.0.2.0/16`.
IPv6 addresses are supported.

CPython 3.4 and above, CPython 2.7, and PyPy are supported.

`network-finder` is similar to
[py-radix](https://github.com/mjschultz/py-radix), but uses binary search
instead of prefix matching. It is slower than `py-radix`, but can use less
memory. 

## Installation

After cloning the repository, run `python setup.py install` to install the
module.

By default a C extension (generated by [Cython](http://cython.org/)) will be
built. Set the environment variable `NO_CYTHON=true` to skip building the
module. The C extension will not be built when using PyPy.

## Usage

`network-finder`'s API is intentionally similar to `py-radix`'s. These examples
show how to build a list of subnets and then use them for matching.

```python
from network_finder import NetworkFinder
network_finder = NetworkFinder()

cidr_data = {
    '192.0.2.0/24': 'Local network',
    '192.0.2.0/25': 'Trusted zone',
    '192.0.2.128/25': 'Untrusted zone',
    '192.0.2.254': 'Mainframe',
}

# You can set the `data` attribute on subnets when adding them
for key, value in cidr_data.items():
    subnet = network_finder.add(key)
    subnet.data = value

# search_exact requires the specific search term to be present
local_network = network_finder.search_exact('192.0.2.0/24')
assert network_finder.search_exact('192.0.2.0/32') is None

# search_best returns the smallest subnet that contains the search term
# (longest prefix match)
trusted_zone = network_finder.search_best('192.0.2.10')
assert network_finder.search_best('198.51.100.1') is None

# search_worst returns the largest subnet that contains the search term
# (shortest prefix match)
local_network = network_finder.search_worst('192.0.2.254')
assert network_finder.search_worst('198.51.100.1') is None

# search_covered returns a list of subnets that are contained within the search
# term.
local_ranges = network_finder.search_covered('192.0.2.0/24')
all_ranges = network_finder.search_covered('0.0.0.0/0')
assert local_ranges == all_ranges

# search_covering returns a list of subnets that contain the search term,.
names = [x.data for x in network_finder.search_covering('192.0.2.254')]
assert sorted(names) == ['Local network', 'Mainframe', 'Untrusted zone']
```
