+++ /dev/null
-# -*- 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()