1 import socket, types, copy, bisect, re
4 """A class representing a CIDRized IPv4 network value.
6 Specifically, it is representing contiguous IPv4 network blocks
7 that can be expressed as a ip-address/network length pair."""
9 # FIXME: we should probably actually make this class immutable and
10 # add methods that generate copies of this class with different
11 # netlens or whatever.
13 ip4addr_re = re.compile("^\d{1,3}(\.\d{1,3}){0,3}(/\d{1,2})?$")
15 def __init__(self, address, netlen = -1):
16 """This takes either a formatted string in CIDR notation:
17 (e.g., "127.0.0.1/32"), A tuple consisting of an formatting
18 string IPv4 address and a numeric network length, or the same
21 if not Cidr.ip4addr_re.search(address):
22 raise ValueError, repr(address) + \
23 " is not a valid CIDR representation"
26 if type(address) == types.StringType:
28 self.addr, self.netlen = address.split("/", 1)
30 self.addr, self.netlen = address, 32
31 elif type(address) == types.TupleType:
32 self.addr, self.netlen = address
34 raise TypeError, "address must be a string or a tuple"
39 # convert string network lengths to integer
40 if type(self.netlen) == types.StringType:
41 self.netlen = int(self.netlen)
45 def __cmp__(self, other):
46 """One CIDR network block is less than another if the start
47 address is numerically less or if the block is larger. That
48 is, supernets will sort before subnets. This ordering allows
49 for an effienct search for subnets of a given network."""
51 # FIXME: have to convert to longs to overcome signedness problems.
52 # There is probably a better way to do this.
53 res = (self.numaddr & 0xFFFFFFFFL) - (other.numaddr & 0xFFFFFFFFL)
54 if (res < 0 ): return -1
55 if (res > 0): return 1
56 res = self.netlen - other.netlen
60 return self.addr + "/" + str(self.netlen)
63 return "<" + str(self) + ">"
66 """This method should be called after any change to the main
67 internal state: netlen or numaddr."""
69 # make sure the network length is valid
70 if self.netlen > 32 or self.netlen < 0:
71 raise TypeError, "network length must be between 0 and 32"
73 # convert the string ipv4 address to a 32bit number
74 self.numaddr = self._convert_ip4str(self.addr)
75 # calculate our netmask
76 self.mask = self._mask(self.netlen)
77 # force the cidr address into correct masked notation
78 self.numaddr &= self.mask
80 # convert the number back to a string to normalize the string
81 self.addr = self._convert_ip4addr(self.numaddr)
83 def is_supernet(self, other):
84 """returns True if the other Cidr object is a supernet (an
85 enclosing network block) of this one. A Cidr object is a
86 supernet of itself."""
87 return other.numaddr & self.mask == self.numaddr
89 def is_subnet(self, other):
90 """returns True if the other Cidr object is a subnet (an
91 enclosednetwork block) of this one. A Cidr object is a
93 return self.numaddr & other.mask == other.numaddr
96 """return the netmask of this Cidr network"""
97 return self._convert_ip4addr(self.mask)
100 """return the length (in number of addresses) of this network block"""
101 return 1 << (32 - self.netlen);
104 """return the last IP address in this network block"""
105 return self._convert_ip4addr(self.numaddr + self.length() - 1)
107 def _convert_ip4str(self, addr):
109 for octet in addr.split(".", 3):
112 raise SyntaxWarning, "octet " + str(o) + " isn't in range"
117 def _convert_ip4addr(self, numaddr):
118 res = str((numaddr & 0xFF000000) >> 24 & 0xFF) + "." + \
119 str((numaddr & 0x00FF0000) >> 16) + "." + \
120 str((numaddr & 0x0000FF00) >> 8) + "." + \
121 str(numaddr & 0x000000FF)
124 def _mask(self, len):
125 return 0xFFFFFFFF << (32 - len)
128 # we can get away with a shallow copy (so far)
129 return copy.copy(self)
132 def valid_cidr(address):
133 """Returns the converted Cidr object if 'address' is valid
134 CIDR notation, False if not. For the purposes of this module,
135 valid CIDR notation consists of 1 to 4 octets separated by '.'
136 characters, with an optional trailing "/netlen"."""
138 if isinstance(address, Cidr): return address
147 if __name__ == "__main__":
148 a = Cidr("127.00.000.1/24")
149 b = Cidr("127.0.0.1", 32)
150 c = Cidr("24.232.119.192", 26)
151 d = Cidr("24.232.119.0", 24)
152 e = Cidr(("24.224.0.0", 11))
153 f = Cidr("216.168.111.0/27");
154 g = Cidr("127.0.0.2/31");
155 h = Cidr("127.0.0.16/32")
159 bad = Cidr("24.261.119.0", 32)
163 print "cidr:", a, "num addresses:", a.length(), "ending address", \
164 a.end(), "netmask", a.netmask()
166 clist = [a, b, c, d, e, f, g, h]
167 print "unsorted list of cidr objects:\n ", clist
170 print "sorted list of cidr object:\n ", clist