X-Git-Url: http://www.chiark.greenend.org.uk/ucgi/~ian/git?a=blobdiff_plain;f=ipaddr.py;fp=ipaddr.py;h=0000000000000000000000000000000000000000;hb=71d65e4c65572e2ef14c58e9c3a2aa32952b2b41;hp=045b95d3d6a9f17dfc8621573b0855f8412f0616;hpb=6437945aefa308c06ab14da291c5d5489c25b393;p=secnet.git diff --git a/ipaddr.py b/ipaddr.py deleted file mode 100644 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 -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 "" - else: - return "" % 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 "" - else: - return "" % 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: 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, 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 '' % 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()