My favorites | Sign in
Project Home Downloads
Search
for
NetAddrFAQ  
Frequently Asked Questions
Phase-Support
Updated Oct 5, 2010 by drk...@gmail.com

Frequently Asked Questions

Updated for release 0.6

Does the IP() class represent an IP address or a subnet and why is there a separate CIDR() class?

Short answer

The IP() class represents an individual IP address and the CIDR() class represents a subnet, network or VLAN.

Subnet representation is the main remit of the CIDR() class.

The support for CIDR subnet prefixes (x.x.x.x/y) via the IP() constructor is mainly for user convenience and follows the Rule of Least Surprise for those coming to netaddr from other less Pythonic networking libraries.

Longer (more verbose) answer

The netaddr module currently contains two major object groups :-

  1. scalar objects - such as IP() and EUI(). They reduce down into anything simpler; zero dimensional.
  2. vector (sequence) objects - such as IPRange() (version 0.6 onwards), CIDR() and Wildcard(). Ordered, fixed size containers of individual elements with a definite start and end value. One dimensional.

First, consider an analogy from basic Python analogy comparing operations on a string and a tuple.

In [1]: s = 'foobar'

In [2]: t = ('foobar',)

Iterating over a string yields individual characters.

In [3]: [i for i in s]
Out[3]: ['f', 'o', 'o', 'b', 'a', 'r']

Iterating over a tuple yields individual strings.

In [4]: [i for i in t]
Out[4]: ['foobar']

A tuple cannot be compared directly to a string.

In [5]: s == t
Out[5]: False

To successfully compare them, the string and tuple need to be converted into the same terms.

#   Index the tuple.
In [6]: s == t[0]
Out[6]: True

#   Alternatively, convert the string into a tuple, result is the same.
In [7]: (s,) == t
Out[7]: True

The string is anologous to how the scalar objects in netaddr behave and the tuple is analogous to how a sequence objects behave.

Now, consider the behaviour of an IP() object and a CIDR() object performing the same operations shown above.

Iterating over and IP() object yields octets (not other IP addresses as you might expect).

In [1]: from netaddr import IP, CIDR

In [2]: [i for i in IP('192.168.0.0/30')]
Out[2]: [192, 168, 0, 0]

Iterating over a CIDR() object yields IP() objects (as you might expect).

In [3]: [i for i in CIDR('192.168.0.0/30')]
Out[3]:
[IP('192.168.0.0'),
 IP('192.168.0.1'),
 IP('192.168.0.2'),
 IP('192.168.0.3')]

In netaddr an IP() object is not equivalent to a CIDR() object (even a /32 CIDR).

In [4]: IP('192.168.0.1/32') == CIDR('192.168.0.1/32')
Out[4]: False

Compare objects in IP terms by accessing an IP() object from within our CIDR() object.

In [5]: IP('192.168.0.1/32') == CIDR('192.168.0.1/32')[0]
Out[5]: True

In [5]: IP('192.168.0.1/32') == CIDR('192.168.0.1/32').network
Out[5]: True

Compare objects in CIDR terms by using the .cidr() method on the IP() object to return the equivalent CIDR() object for comparison.

In [7]: IP('192.168.0.1/32').cidr() == CIDR('192.168.0.1/32')
Out[7]: True

Here is the iteration on an IP() object using the CIDR call through method.

In [8]: [i for i in IP('192.168.0.0/30').cidr()]
Out[8]:
[IP('192.168.0.0'),
 IP('192.168.0.1'),
 IP('192.168.0.2'),
 IP('192.168.0.3')]

How do I access the base (network) and broadcast addresses in a CIDR object?

From release 0.6 onwards, you can now just use the .network and .broadcast properties.

This is how you access them as individual IP objects.

In [1]: from netaddr import CIDR

In [2]: cidr = CIDR('192.168.0.0/24')

In [3]: cidr.network, cidr.broadcast
Out[3]: (IP('192.168.0.0'), IP('192.168.0.255'))

and as address strings :-

In [4]: cidr.fmt = str

In [3]: cidr.network, cidr.broadcast
Out[3]: ('192.168.0.0', '192.168.0.255')

In release 0.5 and earlier, this was the way to do it :-

Use the list index (getitem) operation to access the address you require.

It is not exposed as .network() and .broadcast() to keep the CIDR() object interface lean and free of cruft.

In [1]: from netaddr import IP, CIDR

In [2]: cidr = CIDR('192.168.0.0/24')

In [3]: cidr[0]
Out[3]: netaddr.address.IP('192.168.0.0')

You can access the broadcast address like this :-

In [4]: cidr[-1]
Out[4]: netaddr.address.IP('192.168.0.255')

Why do partial CIDRs 192, 192.168, 192.168.0 all have a /24 netmask?

Good question. Calling the CIDR() constructor with a partial IPv4 address (n/a for IPv6) invokes the older classful IP (version 4) addressing rules to help figure out what the implicit subnet (CIDR prefix) is.

Details can be found in various RFCs but here is the basic rule set :-

Address Class Prefix (bits) Address Range Prefix (CIDR) Subnet Mask
Class A 0 0.0.0.0-127.255.255.255 /8 255.0.0.0
Class B 01 128.0.0.0-191.255.255.255 /16 255.255.0.0
Class C 011 192.0.0.0-223.255.255.255 /24 255.255.255.0
Class D (multicast) 0111 224.0.0.0-239.255.255.255 /4 240.0.0.0
Class E (reserved) 1111 240.0.0.0-255.255.255.255 /32 255.255.255.255


Why is feature x in netaddr slower than version x in a similar library?

netaddr is under active development and this question is becoming more irrelevant with each new release.

As a general rule netaddr follows the conventional wisdom that states :-

  • make it work
  • make it work right
  • make it work fast

From release 0.6 onwards, a lot more emphasis is being placed on performance improvements as much of the core infrastructure code is now in place and working well.

Here is an example of the vast speed improvements made so far :-

netaddr version 0.5.x

In [1]: import netaddr

In [2]: netaddr.__version__
Out[2]: '0.5.2'

In [3]: timeit netaddr.IP('1.1.1.1')
10000 loops, best of 3: 42 µs per loop

In [4]: timeit netaddr.IP('1.1.1.1/255.255.255.0')
10000 loops, best of 3: 146 µs per loop

In [5]: timeit netaddr.IP('fe80::/64')
10000 loops, best of 3: 68.4 µs per loop

In [6]: timeit netaddr.IP('1.1.1.1', netaddr.AT_INET)
10000 loops, best of 3: 34 µs per loop

In [7]: timeit netaddr.IP('fe80::/64', netaddr.AT_INET6)
10000 loops, best of 3: 59.2 µs per loop

netaddr version 0.6.x

In [1]: import netaddr

In [2]: netaddr.__version__
Out[2]: '0.6'

In [3]: timeit netaddr.IP('1.1.1.1')
100000 loops, best of 3: 11.5 µs per loop

In [4]: timeit netaddr.IP('1.1.1.1/255.255.255.0')
10000 loops, best of 3: 53.7 µs per loop

In [5]: timeit netaddr.IP('fe80::/64')
10000 loops, best of 3: 38.8 µs per loop

In [6]: timeit netaddr.IP('1.1.1.1', netaddr.AT_INET)
100000 loops, best of 3: 8.9 µs per loop

In [7]: timeit netaddr.IP('fe80::/64', netaddr.AT_INET6)
10000 loops, best of 3: 27.2 µs per loop

As you can see in some cases there is now almost a 4-fold improvement!

netaddr is not standing still and there are more improvements and features on the way. Stay tuned!

Comment by vaa...@gmail.com, Dec 10, 2008

- Does IP hold an ip or a subnet ? (IP('192.168.0.0/24') works fine, but IP('192.168.0.0/24').ipv4() forgets the netmask.)

- CIDR is not able to use ".ipv6()" nor ".ipv4()" why ?

- CIDR has a .netmask() or .prefixlen() to get the netmask in different format, but how to access the base IP (and not with a dirty getitem(0,...) )

Comment by vaa...@gmail.com, Dec 10, 2008

CIDR('192') gets a netmask of 24, as of CIDR('192.168') and CIDR('192.168.0') ... the latter seems OK but not the other... is it normal ?

Comment by project member drk...@gmail.com, Dec 10, 2008

Covered comments above with FAQ updates and the following tickets :-

http://code.google.com/p/netaddr/issues/detail?id=17 http://code.google.com/p/netaddr/issues/detail?id=18

Thanks for help improving this project.

Comment by cwaidoh, May 31, 2009

Because the broadcast address is actually a configurable property of a network, and will not always be the last address in the network, the CIDR.broadcast property should either be settable (via the constructor) or should not exist.


Sign in to add a comment
Powered by Google Project Hosting