chiark / gitweb /
Python IP addresses: Use modern ipaddr.py - supports IPv6
authorIan Jackson <ijackson@chiark.greenend.org.uk>
Sun, 14 Sep 2014 23:28:56 +0000 (00:28 +0100)
committerIan Jackson <ijackson@chiark.greenend.org.uk>
Thu, 2 Oct 2014 15:30:22 +0000 (16:30 +0100)
Switch to using the modern ipaddr.py from Scott Kitterman, and our own
ipaddrset.py.

The upshot is that make-secnet-sites now supports IPv6.

Aside from adjusting the code in make-secnet-sites to conform to the
new API, we also delete the old Cendio ipaddr.py, and delete the code
to install it, and document the new dependency both in INSTALL and in
the Debian package metadata.

Signed-off-by: Ian Jackson <ijackson@chiark.greenend.org.uk>
INSTALL
Makefile.in
debian/control
ipaddr.py [deleted file]
make-secnet-sites

diff --git a/INSTALL b/INSTALL
index c0236bcc99bb126a3b006c527490204adb8abf35..6ab3b92137d514df916fe299d7a4462b1f26d7e7 100644 (file)
--- a/INSTALL
+++ b/INSTALL
@@ -27,6 +27,11 @@ linux/Documentation/networking/tuntap.txt
 If you're using TUN/TAP on a platform other than Linux-2.4, see
 http://vtun.sourceforge.net/tun/
 
+You will probably be using the supplied `make-secnet-sites' program to
+generate your VPN's list of sites as a secnet configuration from a
+more-human-writeable form.  If so you need to install the standard
+`ipaddr' Python module (python-ipaddr on Debian-derived systems).
+
 ** System and network configuration
 
 If you intend to start secnet as root, I suggest you create a userid
index dfbfa1d090a0729a8375fd6ad177c5831ab24e7f..8bb3bd9efea3fc64f71d184a5aa0c06d8e91b99c 100644 (file)
@@ -163,7 +163,6 @@ installdirs:
 install: installdirs
        $(INSTALL_PROGRAM) secnet $(sbindir)/`echo secnet|sed '$(transform)'`
        $(INSTALL_PROGRAM) ${srcdir}/make-secnet-sites $(sbindir)/`echo make-secnet-sites|sed '$(transform)'`
-       $(INSTALL) ${srcdir}/ipaddr.py $(prefix)/share/secnet/ipaddr.py
        $(INSTALL) ${srcdir}/ipaddrset.py $(prefix)/share/secnet/ipaddrset.py
        $(INSTALL) secnet.8 $(mandir)/man8/secnet.8
 
index eba2515692560296d9ee24a3b102d2f78c82138e..af9c2f9ef1099c095366cff5cd386508acdea77e 100644 (file)
@@ -9,7 +9,7 @@ Standards-Version: 3.0.1
 
 Package: secnet
 Architecture: any
-Depends: ${shlibs:Depends}, ${misc:Depends}
+Depends: ${shlibs:Depends}, ${misc:Depends}, python-ipaddr
 Recommends: python
 Description: VPN software for distributed networks
  secnet allows multiple private networks, each 'hidden' behind a single
diff --git a/ipaddr.py b/ipaddr.py
deleted file mode 100644 (file)
index 045b95d..0000000
--- a/ipaddr.py
+++ /dev/null
@@ -1,1244 +0,0 @@
-# -*- coding: iso-8859-1 -*-
-# ipaddr.py -- handle IP addresses and set of IP addresses.
-# Copyright (C) 1996-2000 Cendio Systems AB
-#
-# This program is free software; you can redistribute it and/or modify
-# it under the terms of the GNU General Public License as published by
-# the Free Software Foundation; either version 2 of the License, or
-# (at your option) any later version.
-# 
-# This program is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-# GNU General Public License for more details.
-# 
-# You should have received a copy of the GNU General Public License
-# along with this program; if not, write to the Free Software
-# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
-
-"""IP address manipulation.
-
-This module is useful if you need to manipulate IP addresses or sets
-of IP addresses.
-
-The main classes are:
-
-    ipaddr   -- a single IP address.
-    netmask  -- a netmask.
-    network  -- an IP address/netmask combination.  It is often, but
-               not always, better to use the ip_set class instead.
-    ip_set   -- a set of IP addresses, that may or may not be adjacent.
-
-So, what can you do with this module?  As a simple example of the kind
-of things this module can do, this code computes the set of all IP
-addresses except 127.0.0.0/8 and prints it, expressed as a union of
-network/netmask pairs.
-
-    import ipaddr
-
-    s = ipaddr.ip_set()
-    s.add_network(ipaddr.network('127.0.0.0', '255.0.0.0',
-                                 ipaddr.DEMAND_FILTER))
-    for nw in s.complement().as_list_of_networks():
-       print nw.ip_str() + '/' + nw.mask.netmask_bits_str
-
-Errors are reported by raising an exception from the following
-exception hierarcy:
-
-Exception       # The standard Python base exception class.
- |
- +-- BadType    # Only raised if the programmer makes an error.
- +-- IpError    # Base class for errors that depend on the data.
-      |
-      +-- SetNotRepresentable
-      +-- BrokenIpAddress
-      |    |
-      |    +-- PartNegative
-      |    +-- PartOverflow
-      |
-      +-- BrokenNetmask
-      |    |
-      |    +-- NeedOneBit
-      |    +-- NeedMoreBits
-      |    +-- NeedLessBits
-      |
-      +-- BrokenNetwork
-           |
-           +-- EmptyIpAddress
-           +-- EmptyNetmask
-           +-- BrokenNetworkAddress
-           +-- NetworkAddressClash
-           +-- BroadcastAddressClash
-  
-BadType may be raised at any time if the programmer makes an error
-(such as passing a dictionary to a function that expects a string).
-SetNotRepresentable may be raised by ip_set.as_str_range().  All other
-exceptions are raised from the constructors and helper functions only.
-
-The following constants are present in this module:
-
-    DEMAND_NONE                See class network.
-    DEMAND_FILTER      See class network.
-    DEMAND_NETWORK     See class network.
-    DEMAND_INTERFACE   See class network.
-
-    hostmask           A netmask object with all 32 bits set.
-    complete_network   A network object representing all IP addresses.
-    complete_set       An ip_set object representing all IP addresses.
-    broadcast_network  A network object representing 255.255.255.255.
-    broadcast_set      An ip_set object representing 255.255.255.255.
-    
-The as_ipaddr function can be used when you have an object that you
-know are an ipaddr or network, and you want to get the ipaddr part.
-
-All the other functions in this module are internal helper functions,
-and they should not be used.
-
-The internal representation used for IP addresses is currently a long
-number.  That may change in the future, so where the internal
-representation is visible, you should do nothing with it except
-compare it to None.
-
-This module was developed by Cendio Systems AB for use in the Fuego
-Firewall.  Bug reports can be sent to Per Cederqvist <ceder@cendio.se>
-who is currently acting as maintainer for this module.
-
-Brief history:
-    1997-03-11      Module created, and used internally.
-    2000-03-09 1.0: First non-public beta release outside of Cendio Systems.
-    2000-03-17 1.1: First public release under the GNU GPL license.
-
-"""
-
-
-import copy
-import string
-import types
-
-# The error messages are marked with a call to this function, so that
-# they can easily be found and translated.
-def _(s):
-    return s
-
-# The exception hierarchy.
-class IpError(Exception):
-    """Base class for errors that are cause by errors in input data.
-    """
-    def __str__(self):
-       return self.format % self.args
-
-class SetNotRepresentable(IpError):
-    format = _("The set of IP addresses cannot be represented "
-              "as a single network range")
-
-class BrokenIpAddress(IpError):
-    format = _("Felaktigt IP-nummer")
-
-class PartNegative(BrokenIpAddress):
-    format = _("En komponent i IP-numret är negativ")
-
-class PartOverflow(BrokenIpAddress):
-    format = _("En komponent i IP-numret är större än 255")
-
-class BrokenNetmask(IpError):
-    format = _("Felaktig nätmask")
-
-class NeedOneBit(BrokenNetmask):
-    format = _("Minst en bit måste vara ettställd")
-
-class NeedMoreBits(BrokenNetmask):
-    format = _("Minst %d bitar måste vara ettställda")
-
-class NeedLessBits(BrokenNetmask):
-    format = _("Högst %d bitar får vara ettställda")
-
-class BrokenNetwork(IpError):
-    """Base class for errors regarding network objects.
-    """
-
-class EmptyIpAddress(BrokenNetwork):
-    format = _("IP-nummer ej ifyllt")
-
-class EmptyNetmask(BrokenNetwork):
-    format = _("Nätmask ej ifylld")
-
-class BrokenNetworkAddress(BrokenNetwork):
-    format = _("Med denna nätmask är %s ett otillåtet nätverksnummer; "
-              "menar du %s?")
-
-class NetworkAddressClash(BrokenNetwork):
-    format = _("Med denna nätmask krockar Fuegons adress med nätnumret")
-
-class BroadcastAddressClash(BrokenNetwork):
-    format = _("Med denna nätmask krockar Fuegons adress "
-              "med broadcastadressen")
-
-class BadType(Exception):
-    """An object of an unexpected type was passed to a function.
-    """
-    pass
-
-# These constants are used with netmasks and networks to specify what
-# the code expects.
-#
-#  DEMAND_NONE: netmask 0-32 (inclusive)
-#  DEMAND_FILTER: netmask 0-32, the host part must be all zeroes
-#  DEMAND_NETWORK: netmask 1-32, the host part must be all zeroes
-#  DEMAND_INTERFACE: netmask 1-30, the host part must *not* be all zeroes
-
-DEMAND_NONE = 1
-DEMAND_FILTER = 2
-DEMAND_NETWORK = 3
-DEMAND_INTERFACE = 4
-
-def bits_to_intrep(bits):
-    """Convert BITS to the internal representation.
-
-    BITS should be a number in the range 0-32 (inclusive).
-
-    """
-    return pow(2L, 32) - pow(2L, 32-bits)
-
-
-def intrep_with_bit_set(bit):
-    """Return an internal representation with bit BIT set.
-
-    BIT should be a number in the range 1-32, where bit 1 is the
-    leftmost.  Examples:
-
-      intrep_with_bit_set(1) --> the internal representation of 128.0.0.0
-      intrep_with_bit_set(32) --> the internal representation of 0.0.0.1
-    """
-    assert 0 < bit and bit <= 32
-
-    return pow(2L, 32-bit)
-
-
-__ONES = {0:0, 128:1, 192:2, 224:3,
-         240:4, 248:5, 252:6, 254:7}
-
-def tuple_to_bits(mask):
-    """Convert MASK to bits.
-
-    MASK should be a tuple of four integers in the range 0-255 (inclusive).
-
-    Raises BrokenNetmask if MASK is not a valid netmask.
-    """
-
-    if mask == None:
-       return None
-    else:
-       (a, b, c, d) = mask
-
-       if a == 255 and b == 255 and c == 255 and d == 255:
-           return 32
-
-       try:
-           if a == 255 and b == 255 and c == 255:
-               return 24 + __ONES[d]
-           elif a == 255 and b == 255  and d == 0:
-               return 16 + __ONES[c]
-           elif a == 255 and c == 0  and d == 0:
-               return 8 + __ONES[b]
-           elif b == 0 and c == 0  and d == 0:
-               return __ONES[a]
-       except KeyError:
-           pass
-
-       raise BrokenNetmask()
-
-
-def intrep_to_dotted_decimal(t):
-    """Convert T to dotted-decimal notation.
-
-    T should be the internal representation used py ipaddr.py.
-    """
-
-    return (str(int(t>>24)) + '.' + str(int((t>>16) & 255))
-           + '.' + str(int((t>>8) & 255)) + '.' + str(int(t & 255)))
-
-
-def as_ipaddr(nwip):
-    """Return the IP address object of NWIP.
-
-    NWIP may be an ipaddr object, which is returned unchanged,
-    or a network object, in which case the ipaddr part of it is
-    returned.
-    """
-
-    if isinstance(nwip, ipaddr):
-       return nwip
-    elif isinstance(nwip, network):
-       return nwip.ip
-    else:
-       raise BadType('Expected a network or ipaddr object', nwip)
-
-
-class ipaddr:
-    """Handle IP addresses.
-
-    Sample use:
-
-        ip1 = ipaddr('12.3.5.1')
-       ip2 = ipaddr([12, 3, 5, 1])
-       print ip1.ip_str()
-       >>> '12.3.5.1'
-       print ip1.intrep
-       >>> 201524481L
-       print ip2.ip_str()
-       >>> '12.3.5.1'
-       print ip2.intrep
-       >>> 201524481L
-
-    An ipaddr object can have two states: empty or good.
-    The status can be examined like this:
-
-       if ip.intrep == None:
-           handle_empty(m.user_input())
-       else:
-           handle_good(ip)
-
-    All other members should only be used in the good state.  The
-    value stored in the intrep member should only be compared against
-    None.  The type and value of it is an internal detail that may
-    change in the future.
-
-    """
-
-    def __init__(self, ip):
-       """Create an ipaddr from IP (a string, tuple or list).
-
-       The empty string or None may be given; it is handled as the
-       empty IP number.
-       """
-
-       if type(ip) == types.StringType:
-           self.__user_input = ip
-           ip = string.strip(ip)
-       else:
-           self.__user_input = None
-
-       # The empty IP number?
-
-       if ip == '' or ip == None:
-           self.__ip_str = ''
-           self.intrep = None
-           if ip == None:
-               self.__user_input = ''
-           return
-
-       if type(ip) == types.StringType:
-
-           # Convert a string.
-
-           try:
-               [a, b, c, d] = map(string.atoi, string.splitfields(ip, '.'))
-           except:
-               raise BrokenIpAddress()
-
-           if a < 0 or b < 0 or c < 0 or d < 0:
-               raise PartNegative()
-
-           if a > 255 or b > 255 or c > 255 or d > 255:
-               raise PartOverflow()
-
-           self.intrep = (long(a) << 24) + (b << 16) + (c << 8) + d
-
-       else:
-           assert type(ip) == types.LongType
-           self.intrep = ip
-
-       self.__ip_str = None
-
-    def ip_str(self):
-       if self.__ip_str == None:
-           self.__ip_str = intrep_to_dotted_decimal(self.intrep)
-       return self.__ip_str
-
-    def user_input(self):
-       if self.__user_input == None:
-           # This object was constructed from a tuple.  Generate a string.
-           self.__user_input = self.ip_str()
-       return self.__user_input
-
-    def compare(self, other):
-       """Compare this IP address with OTHER.
-
-       Returns -1, 0 or 1 if this IP address is less than, equal to,
-       or greater than OTHER (which should be an ipaddr object).
-       """
-       # FIXME: should we rename this __cmp__?  It needs to handle
-       # other types of the OTHER argument first.
-
-       if self.intrep == other.intrep:
-           return 0
-       if self.intrep < other.intrep:
-           return -1
-       else:
-           return 1
-
-    def __str__(self):
-        if self.intrep is None:
-            return "<ipaddr empty>"
-        else:
-            return "<ipaddr %s>" % self.ip_str()
-
-    def __repr__(self):
-       if self.intrep is None:
-           return "ipaddr.ipaddr('')"
-       else:
-           return "ipaddr.ipaddr('%s')" % self.ip_str()
-
-
-class netmask:
-    """Handle netmasks.
-
-    Sample use:
-
-       # Four ways to initialize a netmask.
-        nm1 = netmask('255.255.128.0', DEMAND_NONE)
-       nm2 = netmask([255, 255, 128, 0], DEMAND_NONE)
-       nm3 = netmask('17', DEMAND_NONE)
-       nm4 = netmask(17, DEMAND_NONE)
-       print nm1.netmask_str()
-       >>> '255.255.128.0'
-       print nm1.intrep
-       >>> (255, 255, 128, 0)
-       print nm1.netmask_bits
-       >>> 17
-       print nm1.netmask_bits_str
-       >>> '17'
-
-    A netmask can have two states: empty or good.  The state
-    can be examined like this:
-
-       if m.intrep == None:
-           handle_empty(m.user_input())
-       else:
-           handle_good(m)
-
-    All other members should be used only in the good state.
-
-    """
-
-    def __check_range(self, bits, minbits, maxbits):
-       if bits < minbits:
-           if minbits == 1:
-               raise NeedOneBit()
-           else:
-               raise NeedMoreBits(minbits)
-       elif bits > maxbits:
-           raise NeedLessBits(maxbits)
-
-
-    def __set_from_bits(self, bits, minbits, maxbits):
-       self.__check_range(bits, minbits, maxbits)
-       self.intrep = bits_to_intrep(bits)
-       self.netmask_bits = bits
-
-
-    def __set_from_tuple(self, tpl, minbits, maxbits):
-       bits = tuple_to_bits(tpl)
-       self.__check_range(bits, minbits, maxbits)
-       self.intrep = bits_to_intrep(bits)
-       self.netmask_bits = bits
-
-    DEMANDS = {DEMAND_NONE:(0,32),
-              DEMAND_FILTER:(0,32),
-              DEMAND_NETWORK:(1,32),
-              DEMAND_INTERFACE:(1,30)}
-
-    def __init__(self, mask, demand):
-       """Create a netmask from MASK (a string, tuple or number) and DEMAND.
-
-       The empty string or None may be given; it is handled as the
-       empty netmask.
-
-       See class network for a description of the DEMAND parameter.
-       """
-
-       (minbits, maxbits) = self.DEMANDS[demand]
-       self.demand = demand
-
-       if type(mask) == types.StringType:
-           self.__user_input = mask
-           mask = string.strip(mask)
-       else:
-           self.__user_input = None
-
-       if mask == '' or mask == None:
-
-           # Handle empty netmasks.
-
-           self.__netmask_str = ''
-           self.intrep = None
-           self.netmask_bits_str = ''
-           self.netmask_bits = None
-           if self.__user_input == None:
-               self.input = ''
-           return
-
-       # Decode the MASK argument and set self.netmask_bits
-       # and self.intrep.
-
-       if type(mask) == types.StringType:
-
-           # Is this a string containing a single number?
-           try:
-               bits = string.atoi(mask)
-           except (OverflowError, ValueError):
-               bits = None
-
-           if bits != None:
-
-               # This is a string containing a single number.
-
-               self.__set_from_bits(bits, minbits, maxbits)
-
-           else:
-
-               # Interpret the netmask as a dotted four-tuple.
-               try:
-                   [a, b, c, d] = map(string.atoi,
-                                      string.splitfields(mask, '.'))
-               except:
-                   raise BrokenNetmask()
-
-               self.__set_from_tuple((a, b, c, d), minbits, maxbits)
-
-       elif type(mask) == types.IntType:
-
-           # This is a number, representing the number of bits in the mask.
-
-           self.__set_from_bits(mask, minbits, maxbits)
-
-       else:
-
-           # This is a tuple or list.
-
-           if len(mask) != 4:
-               raise BadType('Wrong len of tuple/list')
-
-           (a, b, c, d) = (mask[0], mask[1], mask[2], mask[3])
-
-           self.__set_from_tuple((a, b, c, d), minbits, maxbits)
-
-       self.__netmask_str = None
-       self.netmask_bits_str = repr(self.netmask_bits)
-
-    def netmask_str(self):
-       if self.__netmask_str == None:
-           self.__netmask_str = intrep_to_dotted_decimal(self.intrep)
-       return self.__netmask_str
-
-    def user_input(self):
-       if self.__user_input == None:
-           # This object was constructed from a tuple or an integer.
-           self.__user_input = self.ip_str()
-       return self.__user_input
-
-    def __str__(self):
-        if self.intrep is None:
-            return "<netmask empty>"
-        else:
-            return "<netmask /%d>" % self.netmask_bits
-
-    def __repr__(self):
-        if self.intrep is None:
-            return "ipaddr.netmask('')"
-        else:
-            return "ipaddr.netmask(%d, %d)" % (self.netmask_bits, self.demand)
-
-
-hostmask = netmask(32, DEMAND_NONE)
-       
-
-class network:
-    """Designate a network or host.
-
-    The constructor takes three arguments: the IP number part, the
-    netmask part, and a demand parameter.  See class ipaddr and class
-    netmask for a description of the first two arguments.  The demand
-    argument can be one of the following constants:
-
-    DEMAND_NONE
-        No special demands.
-    DEMAND_FILTER
-        The host part must be all zeroes.
-    DEMAND_NETWORK
-        The netmask must be 1-32
-       The host part must be all zeroes.
-    DEMAND_INTERFACE
-        The netmask must be 1-30
-       The host part must *not* be all zeroes (the network address)
-        or all ones (the broadcast address).
-
-    The following members exist and are set by the constructor:
-
-      ip.user_input()          # a caching function
-      ip_str()                 # a caching function
-      ip.intrep
-      mask.user_input()                # a caching function
-      mask.netmask_str()       # a caching function
-      mask.intrep
-      mask.netmask_bits
-      mask.netmask_bits_str
-      network_str()            # a caching function
-      network_intrep
-      broadcast_str()          # a caching function
-      broadcast_intrep
-      host_part_str()          # a caching function
-      host_part_intrep
-
-    """
-
-    def __init__(self, ip, mask, demand):
-       self.ip = ipaddr(ip)
-       self.mask = netmask(mask, demand)
-
-       if self.ip.intrep == None:
-           raise EmptyIpAddress()
-
-       if self.mask.intrep == None:
-           raise EmptyNetmask()
-
-       self._precompute()
-
-    def _precompute(self):
-       self.__lower_str = None
-       self.__upper_str = None
-
-       self.network_intrep = self.ip.intrep & self.mask.intrep
-       self.broadcast_intrep = (self.network_intrep |
-                               (pow(2L, 32)-1-self.mask.intrep))
-       self.host_part_intrep = self.ip.intrep - self.network_intrep
-
-       self.__network_str = None
-       self.__broadcast_str = None
-       self.__host_part_str = None
-
-       demand = self.mask.demand
-
-       if demand == DEMAND_NONE:
-           pass
-       elif demand == DEMAND_FILTER or demand == DEMAND_NETWORK:
-           if self.host_part_intrep != 0L:
-               raise BrokenNetworkAddress(self.ip_str(), self.network_str())
-       elif demand == DEMAND_INTERFACE:
-           if self.host_part_intrep == 0L:
-               raise NetworkAddressClash()
-           elif self.broadcast_intrep == self.ip.intrep:
-               raise BroadcastAddressClash()
-       else:
-           raise BadType('Bad value for the demand parameter', demand)
-
-    def network_str(self):
-       if self.__network_str == None:
-           self.__network_str = intrep_to_dotted_decimal(self.network_intrep)
-       return self.__network_str
-
-    def broadcast_str(self):
-       if self.__broadcast_str == None:
-           self.__broadcast_str = intrep_to_dotted_decimal(
-               self.broadcast_intrep)
-       return self.__broadcast_str
-
-    def host_part_str(self):
-       if self.__host_part_str == None:
-           self.__host_part_str = intrep_to_dotted_decimal(
-               self.host_part_intrep)
-       return self.__host_part_str
-
-    def overlaps(self, other):
-       """Returns true if the network overlaps with OTHER.
-
-       OTHER must be a network object or an ipaddr object.  If it
-       is empty this method will always return false.
-
-       """
-
-       if self.network_intrep == None:
-           return 0
-
-       if isinstance(other, ipaddr):
-
-           if other.intrep == None:
-               return 0
-
-           return (self.mask.intrep & other.intrep) == self.network_intrep
-       else:
-           if other.network_intrep == None:
-               return 0
-
-           mask = self.mask.intrep & other.mask.intrep
-           return (mask & self.ip.intrep) == (mask & other.ip.intrep)
-
-    def intersection(self, other):
-       """Return the intersection of the network and OTHER.
-
-       The return value is a network object with DEMAND_FILTER.  If
-       the intersection is empty this method will return None.
-
-       OTHER must be a network object or an ipaddr object.  The
-       intersection will be empty if it is empty.
-       """
-
-       if self.network_intrep == None:
-           return None
-
-       if isinstance(other, ipaddr):
-
-           if other.intrep == None:
-               return None
-
-           prefix_mask = self.mask.intrep
-           short_net = self.network_intrep
-           long_ip = other.intrep
-           result = network(other.intrep, 32, DEMAND_FILTER)
-       else:
-           if other.network_intrep == None:
-               return None
-           
-           if self.mask.netmask_bits < other.mask.netmask_bits:
-               prefix_mask = self.mask.intrep
-               short_net = self.network_intrep
-               long_ip = other.network_intrep
-               result = network(other.network_intrep, other.mask.netmask_bits,
-                                DEMAND_FILTER)
-           else:
-               prefix_mask = other.mask.intrep
-               short_net = other.network_intrep
-               long_ip = self.network_intrep
-               result = network(self.network_intrep, self.mask.netmask_bits,
-                                DEMAND_FILTER)
-
-       if (long_ip & prefix_mask) != (short_net & prefix_mask):
-           return None
-
-       return result
-
-    def is_subset(self, nwip):
-       """Return true if NWIP is a subset of this network.
-
-       NWIP must be a network object or an ipaddr object.
-       """
-
-       if not self.overlaps(nwip):
-           return 0
-
-       if isinstance(nwip, ipaddr):
-           return 1
-
-       return nwip.mask.netmask_bits <= self.mask.netmask_bits
-
-    def is_same_set(self, nwip):
-       """Return true if NWIP contains the same set as this network.
-
-       NWIP must be a network object or an ipaddr object.
-       """
-
-       if isinstance(nwip, ipaddr):
-           return (self.mask.netmask_bits == 32
-                   and self.ip.intrep == nwip.intrep)
-       else:
-           return (self.mask.netmask_bits == nwip.mask.netmask_bits
-                   and self.network_intrep == nwip.network_intrep)
-
-    def subtract(self, nwip):
-       """Create a list of new network objects by subtracting NWIP from self.
-
-       The result consists of networks that together span all
-       IP addresses that are present in self, except those that are
-       present in NWIP.  (The result may be empty or contain several
-       disjoint network objects.)
-
-       Don't use this!  This method is slow.  The ip_set class can do
-       this kind of things in a more efficient way.
-       """
-
-       if not self.overlaps(nwip):
-           # No overlap at all, so NWIP cannot affect the result.
-           return [self]
-
-       if isinstance(nwip, ipaddr):
-           bits = 32
-           intrep = nwip.intrep
-       else:
-           assert isinstance(nwip, network)
-           bits = nwip.mask.netmask_bits
-           intrep = nwip.ip.intrep
-       nets = []
-       while bits > self.mask.netmask_bits:
-           nets.append(network(compute_neighbor(intrep, bits),
-                               bits, DEMAND_FILTER))
-           bits = bits - 1
-       return nets
-
-    def subtract_nwips(self, nwips):
-       """Create a list of new network objects by subtracting NWIPS.
-
-       The result consists of networks that together span all
-       IP addresses that are present in self, except those that are
-       present in NWIPS.  (The result may be empty or contain
-       several disjoint network objects.)  NWIPS should be a list
-       of network or ipaddr objects.
-
-       Don't use this!  This method is slow.  The ip_set class can do
-       this kind of things in a more efficient way.
-       """
-
-       subtracted = [self]
-       for s in nwips:
-           # precondition<A>: SUBTRACTED is a list of networks
-           tmp = []
-           for nw in subtracted:
-               tmp = tmp + nw.subtract(s)
-           subtracted = tmp
-           # postcondition: SUBTRACTED is a list of networks that
-           # spans all IP addresses that were present in
-           # precondition<A>, except those that are present in S.
-
-       return subtracted
-
-    def __compute_lower_upper(self):
-       if self.__lower_str != None:
-           return
-       assert self.network_intrep != None and self.broadcast_intrep != None
-
-       self.__lower_str = intrep_to_dotted_decimal(self.network_intrep + 1)
-       self.__upper_str = intrep_to_dotted_decimal(self.broadcast_intrep - 1)
-
-    def lower_host(self):
-       self.__compute_lower_upper()
-       return self.__lower_str
-
-    def upper_host(self):
-       self.__compute_lower_upper()
-       return self.__upper_str
-
-    def __repr__(self):
-       return _("{network %s/%d}") % (self.ip_str(), self.mask.netmask_bits)
-
-    def ip_str(self):
-       return self.ip.ip_str()
-
-
-class ip_set:
-    def __init__(self, nwip=None):
-       """Create an ip_set.
-
-       If the optional argument NWIP is supplied, the set is
-       initialized to it, otherwise the created set will be empty.
-       NWIP must be a network or ipaddr object.
-       """
-
-       # [[0L, 3L], [5L, 7L]] means 0.0.0.0/29 \ 0.0.0.4/32
-       self.__set = []
-
-       if nwip != None:
-           self.append(nwip)
-
-    def subtract_set(self, other):
-       """Remove all IP-numbers in OTHER from this.
-
-       OTHER should be an ip_set object.
-       """
-
-       self.subtract_list(other.__set)
-
-    def subtract_ips(self, ips):
-       """Remove all IP-numbers in IPS from this.
-
-       IPS should be a list of ipaddr objects.
-       """
-
-       for ip in ips:
-           self.subtract_list([[ip.intrep, ip.intrep]])
-
-    def subtract_list(self, other):
-       # Don't use this method directly, unless you are the test suite.
-       ix = 0
-       iy = 0
-       while ix < len(self.__set) and iy < len(other):
-           if self.__set[ix][1] < other[iy][0]:
-               # The entire range survived.
-               ix = ix + 1
-           elif self.__set[ix][0] > other[iy][1]:
-               # The entire other range is unused, so discard it.
-               iy = iy + 1
-           elif self.__set[ix][0] >= other[iy][0]:
-               if self.__set[ix][1] <= other[iy][1]:
-                   # The entire range is subtracted.
-                   del self.__set[ix]
-               else:
-                   # The start of the range is subtracted, but
-                   # the rest of the range may survive.  (As a matter
-                   # of fact, at least one number *will* survive,
-                   # since there should be a gap between other[iy][1]
-                   # and other[iy+1][0], but we don't use that fact.)
-                   self.__set[ix][0] = other[iy][1] + 1
-                   iy = iy + 1
-           else:
-               # The first part of the range survives.
-               end = self.__set[ix][1]
-               assert self.__set[ix][1] >= other[iy][0]
-               self.__set[ix][1] = other[iy][0] - 1
-               ix = ix + 1
-               if end > other[iy][1]:
-                   # The part that extends past the subtractor may survive.
-                   self.__set[ix:ix] = [[other[iy][1] + 1, end]]
-               # Retain the subtractor -- it may still kill some
-               # other range.
-
-    def add_set(self, other):
-       """Add all IP-numbers in OTHER to this.
-
-       OTHER should be an ip_set object.
-       """
-
-       self.add_list(other.__set)
-
-    def add_list(self, other):
-       # Don't use this method directly, unless you are the test suite.
-       ix = 0
-       iy = 0
-       res = []
-       while ix < len(self.__set) or iy < len(other):
-           # Remove the first range
-           if ix < len(self.__set):
-               if iy < len(other):
-                   if self.__set[ix][0] < other[iy][0]:
-                       rng = self.__set[ix]
-                       ix = ix + 1
-                   else:
-                       rng = other[iy]
-                       iy = iy + 1
-               else:
-                   rng = self.__set[ix]
-                   ix = ix + 1
-           else:
-               rng = other[iy]
-               iy = iy + 1
-
-           # Join this range to the list we already have collected.
-           if len(res) == 0:
-               # This is the first element.
-               res.append(rng)
-           elif rng[0] <= res[-1][1] + 1:
-               # This extends (or is consumed by) the last range.
-               res[-1][1] = max(res[-1][1], rng[1])
-           else:
-               # There is a gap between the previous range and this one.
-               res.append(rng)
-
-       self.__set = res
-
-    def append(self, nwip):
-       """Add NWIP to this.
-
-       NWIP should be a network object or ipaddr object.
-       """
-
-       if isinstance(nwip, network):
-           self.add_network(nwip)
-       else:
-           self.add_ipaddr(nwip)
-
-    def add_network(self, nw):
-       """Add NW to this.
-
-       NW should be a network object.
-       """
-       self.add_list([[nw.network_intrep, nw.broadcast_intrep]])
-
-    def add_range(self, lo_ip, hi_ip):
-       """Add the range of IP numbers specified by LO_IP and HI_IP to this.
-
-       LO_IP and HI_IP should be ipaddr objects.  They specify a
-       range of IP numbers.  Both LO_IP and HI_IP are included in the
-       range.
-       """
-
-       assert lo_ip.intrep != None
-       assert hi_ip.intrep != None
-       assert lo_ip.intrep <= hi_ip.intrep
-       self.add_list([[lo_ip.intrep, hi_ip.intrep]])
-
-    def add_ipaddr(self, ip):
-       """Add IP to this.
-
-       IP should be an ipaddr object.
-       """
-
-       assert ip.intrep != None
-       self.add_list([[ip.intrep, ip.intrep]])
-
-    def complement(self):
-       """Return everything not contained in this ip_set.
-
-       The return value is a new ip_set.  This is not modified.
-       """
-
-       pre = -1L
-       lst = []
-       for [lo, hi] in self.__set:
-           if lo != 0:
-               lst.append([pre+1, lo-1])
-           pre = hi
-       if pre < pow(2L, 32) - 1:
-           lst.append([pre+1, pow(2L, 32) - 1])
-       res = ip_set()
-       res.add_list(lst)
-       return res
-
-    def intersection(self, other):
-       """Return the intersection of this and OTHER.
-
-       The return value is a new ip_set.  This is not modified.
-       OTHER should be an ip_set, network or ipaddr object.
-       """
-
-       res = []
-       ix = 0
-       iy = 0
-       x = copy.deepcopy(self.__set)
-
-       if isinstance(other, ip_set):
-           y = copy.deepcopy(other.__set)
-       elif isinstance(other, network):
-           y = [[other.network_intrep, other.broadcast_intrep]]
-       elif isinstance(other, ipaddr):
-           y = [[other.intrep, other.intrep]]
-       else:
-           raise BadType('expected an ip_set, network or ipaddr argument')
-
-       while ix < len(x) and iy < len(y):
-           if x[ix][1] < y[iy][0]:
-               # The first entry on x doesn't overlap with anything on y.
-               ix = ix + 1
-           elif x[ix][0] > y[iy][1]:
-               # The first entry on y doesn't overlap with anything on x.
-               iy = iy + 1
-           else:
-               # Some overlap exists.
-
-               # Trim away any leading edges.
-               if x[ix][0] < y[iy][0]:
-                   # x starts before y
-                   x[ix][0] = y[iy][0]
-               elif x[ix][0] > y[iy][0]:
-                   # y starts before x
-                   y[iy][0] = x[ix][0]
-
-               # The ranges start at the same point (at least after
-               # the trimming).
-               if x[ix][1] == y[iy][1]:
-                   # The ranges are equal.
-                   res.append(x[ix])
-                   ix = ix + 1
-                   iy = iy + 1
-               elif x[ix][1] < y[iy][1]:
-                   # x is the smaller range
-                   res.append(x[ix])
-                   ix = ix + 1
-               else:
-                   # y is the smaller range
-                   res.append(y[iy])
-                   iy = iy + 1
-
-       result = ip_set()
-       result.add_list(res)
-       return result
-
-
-    def as_list_of_networks(self):
-       """Return this set as a list of networks.
-
-       The returned value is a list of network objects, that are
-       created with DEMAND_FILTER.  This method may be expensive, so
-       it should only be used when necessary.
-       """
-
-       bm = []
-       for [a, b] in self.__set:
-
-           lomask = 1L
-           lobit = 1L
-           himask = pow(2L, 32)-2
-           bits = 32
-           while a <= b:
-               if a & lomask != 0L:
-                   bm.append((bits, a))
-                   a = a + lobit
-               elif b & lomask != lomask:
-                   bm.append((bits, b & himask))
-                   b = b - lobit
-               else:
-                   lomask = (lomask << 1) | 1
-                   lobit = lobit << 1
-                   himask = himask ^ lobit
-                   bits = bits - 1
-                   assert(bits >= 0)
-       bm.sort()
-       res = []
-       for (mask, ip) in bm:
-           res.append(network(ip, mask, DEMAND_FILTER))
-       return res
-
-    def as_list_of_ranges(self):
-       """Return the set of IP addresses as a list of ranges.
-
-       Each range is a list of two long numbers.  Sample return
-       value: [[1L, 3L], [0x7f000001L, 0x7f000001L]], meaning
-       the set 0.0.0.1, 0.0.0.2, 0.0.0.3, 127.0.0.1.
-       """
-
-       # This method is currently very cheap, since this is the
-       # current internal representation.
-
-       return self.__set
-
-    def as_str_range(self):
-       """Return the set as a string, such as "1.2.3.4-1.2.3.8".
-
-       The returned value always has the form a.b.c.d-e.f.g.h.
-       Raises SetNotRepresentable if the set cannot be represented as a
-       single interval, or if it is the empty set.
-       """
-       if len(self.__set) != 1:
-           raise SetNotRepresentable()
-       return "%s-%s" % (intrep_to_dotted_decimal(self.__set[0][0]),
-                         intrep_to_dotted_decimal(self.__set[0][1]))
-
-    def contains(self, ip):
-       """Return true if IP is contained in the set.
-
-       IP should be an ipaddr object.  The empty ipaddr is never contained.
-       """
-
-       if ip.intrep == None:
-           return 0
-
-       for [lo, hi] in self.__set:
-           if lo <= ip.intrep <= hi:
-               return 1
-       return 0
-
-    def overlaps(self, nwip):
-       """Return true if NWIP overlaps the set of IP addresses.
-
-       NWIP may be an ipaddr, network or ip_set object.
-       """
-
-       if isinstance(nwip, ipaddr):
-           return self.contains(nwip)
-       elif isinstance(nwip, ip_set):
-           # This could be optimized -- we don't really need
-           # to compute the intersection.
-           return not self.intersection(nwip).is_empty()
-       elif isinstance(nwip, network):
-           wanted_low = nwip.network_intrep
-           wanted_high = nwip.broadcast_intrep
-           if wanted_low == None or wanted_high == None:
-               return 0
-           for [lo, hi] in self.__set:
-               if lo > wanted_high:
-                   # We are past the interresting interval.
-                   return 0
-               if lo >= wanted_low or hi >= wanted_low:
-                   return 1
-           return 0
-       else:
-           raise BadType('Expected an ipaddr, ip_set or network instance')
-
-    def is_empty(self):
-       """Return true if this ip_set is empty.
-       """
-
-       return len(self.__set) == 0
-
-    def any_ip(self):
-       """Return one of the IP addresses contained in ip_set.
-
-       This method may only be called if the set is non-empty.  You
-       can use the is_empty method to test for emptiness.
-
-       This picks an IP address from the set and returns it as an
-       ipaddr object.  Given the same set of IP addresses, this
-       method will always return the same IP address, but which IP
-       address it chooses is explicitly undocumented and may change
-       if the underlying implementation of ip_set ever changes.
-       """
-
-       assert not self.is_empty()
-       return ipaddr(self.__set[0][0])
-
-    def __str__(self):
-       res = []
-       for rng in self.__set:
-           if rng[0] == rng[1]:
-               res.append(intrep_to_dotted_decimal(rng[0]))
-           else:
-               res.append('%s-%s' % (intrep_to_dotted_decimal(rng[0]),
-                                     intrep_to_dotted_decimal(rng[1])))
-       return '<ipaddr.ip_set(%s)>' % string.join(res, ', ')
-
-complete_network = network(0L, 0, DEMAND_FILTER)
-complete_set = ip_set(complete_network)
-broadcast_network = network('255.255.255.255', 32, DEMAND_FILTER)
-broadcast_set = ip_set(broadcast_network)
-
-def compute_neighbor(intrep, bits):
-    xor_mask = intrep_with_bit_set(bits)
-    and_mask = bits_to_intrep(bits)
-    return (intrep ^ xor_mask) & and_mask
-
-
-if __name__ == '__main__':
-    # Test/demo code.  With no arguments, this will print a page
-    # of data that can be useful when trying to interpret an
-    # ipnumber/netmask pair.  With two arguments, it will print some
-    # information about the IP number and netmask that was entered.
-
-    import sys
-    if len(sys.argv) == 1:
-       print "Netmasks\n========"
-       for i in range(0, 17):
-           if i != 16:
-               print '%2d' % i,
-               print '%-13s' % netmask(i, DEMAND_NONE).netmask_str(),
-           else:
-               print ' ' * 16,
-           print i + 16, '%-16s' % netmask(i + 16, DEMAND_NONE).netmask_str()
-       print _("\n\nIP intervals\n============")
-       for i in range(9):
-           for j in range(0, 4):
-               print '%2d' % (8*j + i),
-           print '%3d' % (netmask(i, DEMAND_NONE).intrep >> 24),
-           x = 0
-           need_break = 0
-           if i < 8:
-               for j in range(0, 256, pow(2, 8-i)):
-                   if need_break:
-                       print
-                       print ' ' * 15,
-                       need_break = 0
-                   print '%3d-%-3d' % (j, j + pow(2, 8-i)-1),
-                   x = x + 1
-                   if x % 8 == 0:
-                       need_break = 1
-           else:
-               print '0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13...',
-           print
-       sys.exit(0)
-
-    if len(sys.argv) != 3:
-       sys.stderr.write(_("Usage: python ipaddr.py IP_ADDRESS NETMASK\n"))
-       sys.exit(1)
-    nw = network(sys.argv[1], sys.argv[2], DEMAND_NONE)
-    print nw
-    print "IP address:       ", nw.ip.ip_str()
-    print "Netmask:          ", nw.mask.netmask_str(),
-    print " (/" + nw.mask.netmask_bits_str + ")"
-    print "Network address:  ", nw.network_str()
-    print "Broadcast address:", nw.broadcast_str()
index b8a2077d8e76274b326817b65dfc523e0f20ec62..ebf7468cdfa90e2960ff2999a91ae55ccde03b6d 100755 (executable)
@@ -56,10 +56,11 @@ import os
 import getopt
 import re
 
-# The ipaddr library is installed as part of secnet
+import ipaddr
+
 sys.path.insert(0,"/usr/local/share/secnet")
 sys.path.insert(0,"/usr/share/secnet")
-import ipaddr
+import ipaddrset
 
 VERSION="0.1.18"
 
@@ -68,22 +69,19 @@ VERSION="0.1.18"
 class single_ipaddr:
        "An IP address"
        def __init__(self,w):
-               self.addr=ipaddr.ipaddr(w[1])
+               self.addr=ipaddr.IPAddress(w[1])
        def __str__(self):
-               return '"%s"'%self.addr.ip_str()
+               return '"%s"'%self.addr
 
 class networks:
        "A set of IP addresses specified as a list of networks"
        def __init__(self,w):
-               self.set=ipaddr.ip_set()
+               self.set=ipaddrset.IPAddressSet()
                for i in w[1:]:
-                       x=string.split(i,"/")
-                       self.set.append(ipaddr.network(x[0],x[1],
-                               ipaddr.DEMAND_NETWORK))
+                       x=ipaddr.IPNetwork(i,strict=True)
+                       self.set.append([x])
        def __str__(self):
-               return string.join(map(lambda x:'"%s/%s"'%(x.ip_str(),
-                       x.mask.netmask_bits_str),
-                       self.set.as_list_of_networks()),",")
+               return ",".join(map((lambda n: '"%s"'%n), self.set.networks()))
 
 class dhgroup:
        "A Diffie-Hellman group"
@@ -522,13 +520,7 @@ def checkconstraints(n,p,ra):
        else:
                new_ra=ra
        if n.properties.has_key("networks"):
-               # I'd like to do this:
-               # n.properties["networks"].set.is_subset(new_ra)
-               # but there isn't an is_subset() method
-               # Instead we see if we intersect with the complement of new_ra
-               rac=new_ra.complement()
-               i=rac.intersection(n.properties["networks"].set)
-               if not i.is_empty():
+               if not n.properties["networks"].set <= new_ra:
                        moan("%s %s networks out of bounds"%(n.type,n.name))
                if n.properties.has_key("peer"):
                        if not n.properties["networks"].set.contains(
@@ -537,7 +529,7 @@ def checkconstraints(n,p,ra):
        for i in n.children.keys():
                checkconstraints(n.children[i],new_p,new_ra)
 
-checkconstraints(root,{},ipaddr.complete_set)
+checkconstraints(root,{},ipaddrset.complete_set())
 
 if complaints>0:
        if complaints==1: print "There was 1 problem."