diff --git a/Doc/howto/index.rst b/Doc/howto/index.rst --- a/Doc/howto/index.rst +++ b/Doc/howto/index.rst @@ -28,4 +28,5 @@ urllib2.rst webservers.rst argparse.rst + ipaddress.rst diff --git a/Doc/howto/ipaddress.rst b/Doc/howto/ipaddress.rst new file mode 100644 --- /dev/null +++ b/Doc/howto/ipaddress.rst @@ -0,0 +1,311 @@ +.. _ipaddress-howto: + +*************** +Ipaddress Howto +*************** + +:author: Peter Moody + +.. topic:: Abstract + + This document is a gentle introduction to :mod:`ipaddress` module. + + +Creating Address/Network/Interface objects +========================================== + +Since :mod:`ipaddress` is a module for inspecting and manipulating IP address, +the first thing you'll want to do ixs create some objects. You can use +:mod:`ipaddress` to create objects from strings, integers or other +:mod:`ipaddress` objects. + + +Creating Addresses +------------------ + +Addresses are the most basic unit. They are indivisible. + +To create an IPv4 address:: + + # string constructor + >>> ipaddress.IPv4Address('192.0.2.1') + IPv4Address('192.0.2.1') + + # integer constructor + >>> ipaddress.IPv4Address(3221225985) + IPv4Address('192.0.2.1') + + # copy constructor + >>> addr = ipaddress.IPv4Address('192.0.2.1') + >>> ipaddress.IPv4Address(addr) + IPv4Address('192.0.2.1') + + +and to create an IPv6 address:: + + # string constructor + >>> ipaddress.IPv6Address('2001:DB8::1') + IPv6Address('2001:db8::1') + + # integer constructor - this one's a mouthful + >>> ipaddress.IPv6Address(42540766411282592856903984951653826561L) + IPv6Address('2001:db8::1') + + # copy constructor + >>> ipaddress.IPv6Address(addr) + IPv6Address('2001:db8::1') + + +Creating Networks +----------------- + +Addresses are usually grouped together in Networks, so :mod:`ipaddress` provides +a way to create, inspect and manipulate those as well. The constructors look +identical to their corresponding address constructors. + + +IPv4 Network:: + + >>> ipaddress.IPv4Network('192.0.2.0/24') + IPv4Network('192.0.2.0/24') + >>> ipaddress.IPv4Network(3221225984) + IPv4Network('192.0.2.0/32') + >>> addr = ipaddress.IPv4Network('192.0.2.0/24') + >>> ipaddress.IPv4Network(addr) + IPv4Network('192.0.2.0/24') + + +IPv6 Network:: + + >>> ipaddress.IPv6Network('2001:db8::0/96') + IPv6Network('2001:db8::/96') + >>> ipaddress.IPv6Network(42540766411282592856903984951653826560L) + IPv6Network('2001:db8::/128') + >>> addr = ipaddress.IPv6Network('2001:db8::0/96') + >>> ipaddress.IPv6Network(addr) + IPv6Network('2001:db8::/96') + + +Network objects cannot have any host bits set. The practical effect of this is +that ``192.0.2.1/24`` does not describe a network. It's referred to as an +interface object since the ip-on-a-network notation is commonly used to describe +network interfaces of a computer on a given network. + + +.. note:: + + When creating a network object from an integer, the prefix length (netmask) + is assumed to be all ones. So IPv4 networks will have a /32 netmask and IPv6 + networks will have a /128 netmask. + + +Creating hybrid objects +----------------------- + +As mentioned just above, if you need to describe an address on a particular +network, neither the address nor the network classes is appropriate. Since the +notation ``192.0.2.1/24`` is so common among network engineers and the people +who write tools for firewalls and routers, :mod:`ipaddress` provides a set of +hybrid classes. By now, the constructor syntax should look familiar. + + +IPv4Interface:: + + >>> ipaddress.IPv4Interface('192.0.2.1/24') + IPv4Interface('192.0.2.1/24') + >>> ipaddress.IPv4Interface(3221225985) + IPv4Interface('192.0.2.1/32') + >>> addr = ipaddress.IPv4Interface('192.0.2.1/24') + >>> ipaddress.IPv4Interface(addr) + IPv4Interface('192.0.2.1/24') + + +IPv6Interface:: + + >>> ipaddress.IPv6Interface('2001:db8::1/96') + IPv6Interface('2001:db8::1/96') + >>> ipaddress.IPv6Interface(42540766411282592856903984951653826561L) + IPv6Interface('2001:db8::1/128') + >>> addr = ipaddress.IPv6Interface('2001:db8::1/96') + >>> ipaddress.IPv6Interface(addr) + IPv6Interface('2001:db8::1/96') + + +.. note:: + + Just like with the network objects, when you create an interface object with + an integer, the netmask is assumed to be all ones. + + +Finally, if you don't know at the time coding what type of addresses you might +be handling, or you don't really care and you'd like the same code to handle +both, :mod:`ipaddress` provides generic factory functions which look at the +address and try to return an object of the correct for you: + + +Addresses Factory Function +.......................... + + >>> ipaddress.ip_address('192.0.2.1') + IPv4Address('192.0.2.1') + >>> ipaddress.ip_address('2001:db8::1') + IPv6Address('2001:db8::1') + >>> ipaddress.ip_address(1) + IPv4Address('0.0.0.1') + >>> addr = ipaddress.ip_address('2001:db8::1') + >>> ipaddress.ip_address(addr) + IPv6Address('2001:db8::1') + + +Networks Factory Function +......................... + + >>> ipaddress.ip_network('192.0.2.0/24') + IPv4Network('192.0.2.0/24') + >>> addr = ipaddress.ip_network('192.0.2.0/24') + >>> ipaddress.ip_network(addr) + IPv4Network('192.0.2.0/24') + >>> ipaddress.ip_network('2001:db8::0/96') + IPv6Network('2001:db8::/96') + + +Interfaces Factory Function +........................... + + >>> ipaddress.ip_interface('192.0.2.1/24') + IPv4Interface('192.0.2.1/24') + >>> ipaddress.ip_interface('2001:db8::1/96') + IPv6Interface('2001:db8::1/96') + +.. note:: + + Since IPv4 addresses are 2^32 bits and IPv6 addresses are 2^128 bits, all + integers <= 2^32 - 1 are assumed to be IPv4. If you know that an address is + an IPv6 address, you should pass version=6 to ip_address(): + + >>> ipaddress.ip_address(3221225985) + IPv4Address('192.0.2.1') + >>> ipaddress.ip_address(3221225985, version=6) + IPv6Address('::c000:201') + + +Inspecting Address/Network/Interface Objects +============================================ + +You've gone to the trouble of creating an IPv(4|6)(Address|Network|Interface) +object, so you probably want to get information about it. :mod:`ipaddress` +tries to make doing this easy and intuitive. + + +Extracting the IP version:: + + >>> addr4 = ipaddress.ip_address('192.0.2.1') + >>> addr6 = ipaddress.ip_address('2001:db8::1') + >>> addr6.version + 6 + >>> addr4.version + 4 + + +Obtaining the network/interface:: + + >>> net4 = ipaddress.ip_network('192.0.2.0/24') + >>> net6 = ipaddress.ip_network('2001:db8::0/96') + + +Finding out how many individual addresses are in a network:: + + >>> net4.numhosts + 256 + >>> net6.numhosts + 4294967296L + + +Iterating through the 'usable' addresses on a network:: + + >>> for x in net4.iterhosts(): + print x + 192.0.2.1 + 192.0.2.2 + 192.0.2.3 + 192.0.2.4 + [snip] + 192.0.2.252 + 192.0.2.253 + 192.0.2.254 + + +Obtaining the hostmask or the netmask: + + >>> net6.netmask + IPv6Address('ffff:ffff:ffff:ffff:ffff:ffff::') + >>> net6.hostmask + IPv6Address('::ffff:ffff') + + +Exploding or compressing the address:: + + >>> net6.exploded + '2001:0000:0000:0000:0000:0000:0000:0000/96' + >>> addr6.exploded + '2001:0000:0000:0000:0000:0000:0000:0001' + + +Networks/Interfaces as lists +============================ + +It's sometimes useful to treat networks (and interfaces) as lists. This allows +us to index them like this:: + + >>> net6[1] + IPv6Address('2001::1') + >>> net6[-1] + IPv6Address('2001::ffff:ffff') + >>> ipaddress.ip_interface('192.0.2.1/24')[-1] + IPv4Address('192.0.2.255') + + +This also means that network and interface objects lend themselves to using the +list membership test syntax in like this:: + + if address in network: + # do something + + +Address, Network and Interface objects can be 'in' a network or an interface +object:: + + >>> net4 = ipaddress.ip_network('192.0.2.0/25') + >>> net4 in ipaddress.ip_network('192.0.2.0/24') + True + >>> net4 in ipaddress.ip_interface('192.0.2.0/25') + True + net4 in ipaddress.ip_interface('192.0.2.0/26') + + +Comparisons +=========== + +:mod:`ipaddress` provides some simply, hopefully intuitive ways to compare +objects, where it makes sense:: + + >>> ipaddress.ip_address('192.0.2.1') < ipaddress.ip_address('192.0.2.2') + True + +A :exc:`TypeError` exception is raised if you try to compare objects of +different versions or different types. + + +Exceptions raised by :mod:`ipaddress` +===================================== + +If you try to create an address/network/interface object with an invalid value +for either the address or netmask, :mod:`ipaddress` will raise an +:exc:`AddressValueError` or :exc:`NetmaskValueError` respectively. Both of +these exceptions have :exc:`ValueError` as their parent class, so if you're not +concerned with the particular type of error, you can do the following:: + + try: + ipaddress.ip_address(address) + except ValueError: + print 'address/netmask is invalid: %s' % address