| LEFT | RIGHT |
| 1 #!/usr/bin/python | 1 #!/usr/bin/python |
| 2 # | 2 # |
| 3 # Copyright 2007 Google Inc. | 3 # Copyright 2007 Google Inc. |
| 4 # Licensed to PSF under a Contributor Agreement. | 4 # Licensed to PSF under a Contributor Agreement. |
| 5 # | 5 # |
| 6 # Licensed under the Apache License, Version 2.0 (the "License"); | 6 # Licensed under the Apache License, Version 2.0 (the "License"); |
| 7 # you may not use this file except in compliance with the License. | 7 # you may not use this file except in compliance with the License. |
| 8 # You may obtain a copy of the License at | 8 # You may obtain a copy of the License at |
| 9 # | 9 # |
| 10 # http://www.apache.org/licenses/LICENSE-2.0 | 10 # http://www.apache.org/licenses/LICENSE-2.0 |
| 11 # | 11 # |
| 12 # Unless required by applicable law or agreed to in writing, software | 12 # Unless required by applicable law or agreed to in writing, software |
| 13 # distributed under the License is distributed on an "AS IS" BASIS, | 13 # distributed under the License is distributed on an "AS IS" BASIS, |
| 14 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or | 14 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or |
| 15 # implied. See the License for the specific language governing | 15 # implied. See the License for the specific language governing |
| 16 # permissions and limitations under the License. | 16 # permissions and limitations under the License. |
| 17 | 17 |
| 18 """An IPv4/IPv6 manipulation library in Python. | 18 """An IPv4/IPv6 manipulation library in Python. |
| 19 | 19 |
| 20 This library is used to create/poke/manipulate IPv4 and IPv6 addresses | 20 This library is used to create/poke/manipulate IPv4 and IPv6 addresses |
| 21 and prefixes. | 21 and networks. |
| 22 | 22 |
| 23 """ | 23 """ |
| 24 | 24 |
| 25 __version__ = '1.0.2' | 25 __version__ = 'trunk' |
| 26 | 26 |
| 27 import struct | 27 import struct |
| 28 | 28 |
| 29 class Error(Exception): | 29 class Error(Exception): |
| 30 | 30 |
| 31 """Base class for exceptions.""" | 31 """Base class for exceptions.""" |
| 32 | 32 |
| 33 | 33 |
| 34 class IPTypeError(Error): | 34 class IPTypeError(Error): |
| 35 | 35 |
| 36 """Tried to perform a v4 action on v6 object or vice versa.""" | 36 """Tried to perform a v4 action on v6 object or vice versa.""" |
| 37 | 37 |
| 38 | 38 |
| 39 class IPAddressExclusionError(Error): | 39 class IPAddressExclusionError(Error): |
| 40 | 40 |
| 41 """An Error we should never see occurred in address exclusion.""" | 41 """An Error we should never see occurred in address exclusion.""" |
| 42 | 42 |
| 43 | 43 |
| 44 class IPv4IpValidationError(Error): | 44 class IPAddressIPValidationError(Error): |
| 45 | 45 |
| 46 """Raised when an IPv4 address is invalid.""" | 46 """Raised when a single address (v4 or v6) was given a network.""" |
| 47 | 47 |
| 48 def __init__(self, ip): | 48 def __init__(self, ip): |
| 49 Error.__init__(self) | 49 Error.__init__(self) |
| 50 self.ip = ip | 50 self._ip = ip |
| 51 | 51 |
| 52 def __str__(self): | 52 def __str__(self): |
| 53 return repr(self.ip) + ' is not a valid IPv4 address' | 53 return "%s is not a valid address (hint, it's probably a network)" % ( |
| 54 | 54 repr(self._ip)) |
| 55 |
| 56 class IPv4IpValidationError(Error): |
| 57 |
| 58 """Raised when an IPv4 address is invalid.""" |
| 59 |
| 60 def __init__(self, ip): |
| 61 Error.__init__(self) |
| 62 self._ip = ip |
| 63 |
| 64 def __str__(self): |
| 65 return repr(self._ip) + ' is not a valid IPv4 address' |
| 55 | 66 |
| 56 class IPv4NetmaskValidationError(Error): | 67 class IPv4NetmaskValidationError(Error): |
| 57 | 68 |
| 58 """Raised when a netmask is invalid.""" | 69 """Raised when a netmask is invalid.""" |
| 59 | 70 |
| 60 def __init__(self, netmask): | 71 def __init__(self, netmask): |
| 61 Error.__init__(self) | 72 Error.__init__(self) |
| 62 self.netmask = netmask | 73 self.netmask = netmask |
| 63 | 74 |
| 64 def __str__(self): | 75 def __str__(self): |
| 65 return repr(self.netmask) + ' is not a valid IPv4 netmask' | 76 return repr(self.netmask) + ' is not a valid IPv4 netmask' |
| 66 | 77 |
| 67 class IPRangeError(Error): | |
| 68 | |
| 69 """Raised when an IP address range is invalid.""" | |
| 70 | 78 |
| 71 class IPv6IpValidationError(Error): | 79 class IPv6IpValidationError(Error): |
| 72 | 80 |
| 73 """Raised when an IPv6 address is invalid.""" | 81 """Raised when an IPv6 address is invalid.""" |
| 74 | 82 |
| 75 def __init__(self, ip): | 83 def __init__(self, ip): |
| 76 Error.__init__(self) | 84 Error.__init__(self) |
| 77 self.ip = ip | 85 self._ip = ip |
| 78 | 86 |
| 79 def __str__(self): | 87 def __str__(self): |
| 80 return repr(self.ip) + ' is not a valid IPv6 address' | 88 return repr(self._ip) + ' is not a valid IPv6 network' |
| 81 | 89 |
| 82 | 90 |
| 83 class IPv6NetmaskValidationError(Error): | 91 class IPv6NetmaskValidationError(Error): |
| 84 | 92 |
| 85 """Raised when an IPv6 netmask is invalid.""" | 93 """Raised when an IPv6 netmask is invalid.""" |
| 86 | 94 |
| 87 def __init__(self, netmask): | 95 def __init__(self, netmask): |
| 88 Error.__init__(self) | 96 Error.__init__(self) |
| 89 self.netmask = netmask | 97 self.netmask = netmask |
| 90 | 98 |
| 91 def __str__(self): | 99 def __str__(self): |
| 92 return repr(self.netmask) + ' is not a valid IPv6 netmask' | 100 return repr(self.netmask) + ' is not a valid IPv6 netmask' |
| 93 | 101 |
| 94 | 102 |
| 95 class PrefixlenDiffInvalidError(Error): | 103 class PrefixlenDiffInvalidError(Error): |
| 96 | 104 |
| 97 """Raised when Sub/Supernets is called with a bad prefixlen_diff.""" | 105 """Raised when Sub/Supernets is called with a bad prefixlen_diff.""" |
| 98 | 106 |
| 99 def __init__(self, error_str): | 107 def __init__(self, error_str): |
| 100 Error.__init__(self) | 108 Error.__init__(self) |
| 101 self.error_str = error_str | 109 self.error_str = error_str |
| 102 | 110 |
| 103 | 111 |
| 104 def IP(ipaddr): | 112 def IP(address, host=False, version=None): |
| 105 """Take an IP string/int and return an object of the correct type. | 113 """Take an IP string/int and return an object of the correct type. |
| 106 | 114 |
| 107 Args: | 115 Args: |
| 108 ipaddr: A string or integer, the IP address. Either IPv4 or | 116 address: A string or integer, the IP address. Either IPv4 or |
| 109 IPv6 addresses may be supplied; integers less than 2**32 will | 117 IPv6 addresses may be supplied; integers less than 2**32 will |
| 110 be considered to be IPv4. | 118 be considered to be IPv4. |
| 119 host: A bool indicating that a host rather than the default network |
| 120 object should be created. |
| 121 version: An Integer, if set, don't try to automatically |
| 122 determine what the IP address type is. important for things |
| 123 like IP(1), which could be IPv4 or IPv6. |
| 111 | 124 |
| 112 Returns: | 125 Returns: |
| 113 An IPv4 or IPv6 object. | 126 An IPv4 or IPv6 object. |
| 114 | 127 |
| 115 Raises: | 128 Raises: |
| 116 ValueError: if the string passed isn't either a v4 or a v6 | 129 ValueError: if the string passed isn't either a v4 or a v6 |
| 117 address. | 130 address. |
| 118 | 131 |
| 119 """ | 132 """ |
| 133 if version: |
| 134 if version == 4: |
| 135 return IPv4(address, host=host) |
| 136 elif version == 6: |
| 137 return IPv6(address, host=host) |
| 120 | 138 |
| 121 try: | 139 try: |
| 122 return IPv4(ipaddr) | 140 return IPv4(address, host=host) |
| 123 except (IPv4IpValidationError, IPv4NetmaskValidationError): | 141 except (IPv4IpValidationError, IPv4NetmaskValidationError): |
| 124 pass | 142 pass |
| 125 | 143 |
| 126 try: | 144 try: |
| 127 return IPv6(ipaddr) | 145 return IPv6(address, host=host) |
| 128 except (IPv6IpValidationError, IPv6NetmaskValidationError): | 146 except (IPv6IpValidationError, IPv6NetmaskValidationError): |
| 129 pass | 147 pass |
| 130 | 148 |
| 131 raise ValueError('%r does not appear to be an IPv4 or IPv6 address' % | 149 raise ValueError('%r does not appear to be an IPv4 or IPv6 address' % |
| 132 ipaddr) | 150 address) |
| 151 |
| 152 |
| 153 def IPv4(address, host=False): |
| 154 """Generic IPv4 Factory function.. |
| 155 |
| 156 Return an IPv4 address or network. |
| 157 """ |
| 158 if host: |
| 159 return IPv4Address(address) |
| 160 return IPv4Network(address) |
| 161 |
| 162 |
| 163 def IPv6(address, host=False): |
| 164 """Generic IPv6 Factory function.. |
| 165 |
| 166 Return an IPv6 address or network. |
| 167 """ |
| 168 if host: |
| 169 return IPv6Address(address) |
| 170 return IPv6Network(address) |
| 133 | 171 |
| 134 | 172 |
| 135 def _find_address_range(addresses): | 173 def _find_address_range(addresses): |
| 136 """Find a sequence of addresses.""" | 174 """Find a sequence of addresses. |
| 175 |
| 176 Args: |
| 177 addresses: a list of IPv4 or IPv6 addresses. |
| 178 |
| 179 Returns: |
| 180 A tuple containing the first and last IP addresses in the sequence. |
| 181 |
| 182 """ |
| 137 first = last = addresses[0] | 183 first = last = addresses[0] |
| 138 for ip in addresses[1:]: | 184 for ip in addresses[1:]: |
| 139 if ip.ip == last.ip + 1: | 185 if ip._ip == last._ip + 1: |
| 140 last = ip | 186 last = ip |
| 141 else: | 187 else: |
| 142 break | 188 break |
| 143 return (first, last) | 189 return (first, last) |
| 144 | 190 |
| 145 def _get_prefix_length(number1, number2, bits): | 191 def _get_prefix_length(number1, number2, bits): |
| 146 """Get the number of leading bits that are same for two numbers.""" | 192 """Get the number of leading bits that are same for two numbers. |
| 193 |
| 194 Args: |
| 195 number1: an integer. |
| 196 number2: another integer. |
| 197 bits: the maximum number of bits to compare. |
| 198 |
| 199 Returns: |
| 200 The number of leading bits that are the same for two numbers. |
| 201 |
| 202 """ |
| 147 for i in range(bits): | 203 for i in range(bits): |
| 148 if number1 >> i == number2 >> i: | 204 if number1 >> i == number2 >> i: |
| 149 return bits - i | 205 return bits - i |
| 150 return 0 | 206 return 0 |
| 151 | 207 |
| 152 def _count_righthand_zero_bits(integer, bits): | 208 def _count_righthand_zero_bits(number, bits): |
| 153 """Count the number of zero bits on the right hand side.""" | 209 """Count the number of zero bits on the right hand side. |
| 154 if integer == 0: | 210 |
| 211 Args: |
| 212 number: an integer. |
| 213 bits: maximum number of bits to count. |
| 214 |
| 215 Returns: |
| 216 The number of zero bits on the right hand side of the number. |
| 217 |
| 218 """ |
| 219 if number == 0: |
| 155 return bits | 220 return bits |
| 156 for i in range(bits): | 221 for i in range(bits): |
| 157 if (integer >> i) % 2: | 222 if (number >> i) % 2: |
| 158 return i | 223 return i |
| 159 | 224 |
| 160 def _highest_one_bit(number): | |
| 161 """Find the highest bit set to 1.""" | |
| 162 value = 0 | |
| 163 while number > 0: | |
| 164 number = number >> 1 | |
| 165 value += 1 | |
| 166 return value | |
| 167 | |
| 168 def summarize_address_range(first, last): | 225 def summarize_address_range(first, last): |
| 169 """Summarize a network range given the first and last IP addresses.""" | 226 """Summarize a network range given the first and last IP addresses. |
| 170 | 227 |
| 228 Example: |
| 229 >>> summarize_address_range(IPv4Address('1.1.1.0'), |
| 230 IPv4Address('1.1.1.130')) |
| 231 [IPv4Network('1.1.1.0/25'), IPv4Network('1.1.1.128/31'), |
| 232 IPv4Network('1.1.1.130/32')] |
| 233 |
| 234 Args: |
| 235 first: the first IPv4 or IPv6 address in the range. |
| 236 last: the last IPv4 or IPv6 address in the range. |
| 237 |
| 238 Returns: |
| 239 The address range collapsed to a list of IPv4 or IPv6 networks. |
| 240 |
| 241 Raise: |
| 242 IPTypeError: |
| 243 If the first and last objects are not IP addresses. |
| 244 If the first and last objects are not the same version. |
| 245 ValueError: |
| 246 If the last object is not greater than the first. |
| 247 If the version is not 4 or 6. |
| 248 |
| 249 """ |
| 250 |
| 251 if not (isinstance(first, BaseIP) and isinstance(last, BaseIP)): |
| 252 raise IPTypeError("first and last must be IP addresses, not networks") |
| 171 if first.version != last.version: | 253 if first.version != last.version: |
| 172 raise ValueError("IP addresses must be same version") | 254 raise IPTypeError("IP addresses must be same version") |
| 173 if first > last: | 255 if first > last: |
| 174 raise ValueError("last IP address must be greater than first") | 256 raise ValueError("last IP address must be greater than first") |
| 175 | 257 |
| 176 networks = [] | 258 networks = [] |
| 177 | 259 |
| 178 if first.version == 4: | 260 if first.version == 4: |
| 179 ip = IPv4 | 261 ip = IPv4Network |
| 180 elif first.version == 6: | 262 elif first.version == 6: |
| 181 ip = IPv6 | 263 ip = IPv6Network |
| 182 else: | 264 else: |
| 183 raise ValueError("unknown IP version") | 265 raise ValueError("unknown IP version") |
| 184 | 266 |
| 185 try: | 267 ip_bits = first._max_prefixlen |
| 186 # shortcut if the range is on a network byte boundary | 268 first = first._ip |
| 187 net = ip("%s-%s" % (first.ip_ext, last.ip_ext)) | 269 last = last._ip |
| 270 while first <= last: |
| 271 nbits = _count_righthand_zero_bits(first, ip_bits) |
| 272 current = None |
| 273 while nbits >= 0: |
| 274 addend = 2**nbits - 1 |
| 275 current = first + addend |
| 276 nbits -= 1 |
| 277 if current <= last: |
| 278 break |
| 279 prefix = _get_prefix_length(first, current, ip_bits) |
| 280 net = ip(first) |
| 281 net.prefixlen = prefix |
| 188 networks.append(net) | 282 networks.append(net) |
| 189 except IPRangeError: | 283 if current == ip._ALL_ONES: |
| 190 ip_bits = first.max_prefixlen | 284 break |
| 191 while first <= last: | 285 first = current + 1 |
| 192 nbits = _count_righthand_zero_bits(first.ip, ip_bits) | |
| 193 current = None | |
| 194 while True: | |
| 195 addend = 2**nbits - 1 | |
| 196 current = first.ip + addend | |
| 197 nbits -= 1 | |
| 198 if ip(current) <= last: | |
| 199 break | |
| 200 prefix = _get_prefix_length(first.ip, current, ip_bits) | |
| 201 networks.append(ip('%s/%s' % (first.ip_ext, prefix))) | |
| 202 first = ip(current + 1) | |
| 203 if current == ip._ALL_ONES: | |
| 204 break | |
| 205 return networks | 286 return networks |
| 206 | 287 |
| 207 def _collapse_address_list_recursive(addresses): | 288 def _collapse_address_list_recursive(addresses): |
| 208 """Loops through the addresses, collapsing concurrent netblocks. | 289 """Loops through the addresses, collapsing concurrent netblocks. |
| 209 | 290 |
| 210 Example: | 291 Example: |
| 211 | 292 |
| 212 ip1 = IPv4('1.1.0.0/24') | 293 ip1 = IPv4('1.1.0.0/24') |
| 213 ip2 = IPv4('1.1.1.0/24') | 294 ip2 = IPv4('1.1.1.0/24') |
| 214 ip3 = IPv4('1.1.2.0/24') | 295 ip3 = IPv4('1.1.2.0/24') |
| (...skipping 42 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 257 collapse_address_list([IPv4('1.1.0.0/24'), IPv4('1.1.1.0/24')]) -> | 338 collapse_address_list([IPv4('1.1.0.0/24'), IPv4('1.1.1.0/24')]) -> |
| 258 [IPv4('1.1.0.0/23')] | 339 [IPv4('1.1.0.0/23')] |
| 259 | 340 |
| 260 Args: | 341 Args: |
| 261 addresses: A list of IPv4 or IPv6 objects. | 342 addresses: A list of IPv4 or IPv6 objects. |
| 262 | 343 |
| 263 Returns: | 344 Returns: |
| 264 A list of IPv4 or IPv6 objects depending on what we were passed. | 345 A list of IPv4 or IPv6 objects depending on what we were passed. |
| 265 | 346 |
| 266 """ | 347 """ |
| 267 _addresses = [] | |
| 268 i = 0 | 348 i = 0 |
| 269 ip_bits = addresses[0].max_prefixlen | 349 addrs = [] |
| 270 # filter out IP addresses, dedup, and sort | 350 ips = [] |
| 271 addrs = sorted(set([ip for ip in addresses if ip.prefixlen == ip_bits]), | 351 nets = [] |
| 272 key=BaseIP._get_networks_key) | 352 # split IP addresses and networks |
| 273 nets = [ip for ip in addresses if ip.prefixlen < ip_bits] | 353 for ip in addresses: |
| 274 | 354 if isinstance(ip, BaseIP): |
| 275 while i < len(addrs): | 355 ips.append(ip) |
| 276 (first, last) = _find_address_range(addrs[i:]) | 356 elif ip._prefixlen == ip._max_prefixlen: |
| 277 i = addrs.index(last) + 1 | 357 ips.append(ip.ip) |
| 278 _addresses.extend( | 358 else: |
| 279 [IP(ip) for ip in summarize_address_range(first, last)]) | 359 nets.append(ip) |
| 360 # sort and dedup |
| 361 ips = sorted(set(ips)) |
| 362 nets = sorted(set(nets)) |
| 363 |
| 364 while i < len(ips): |
| 365 (first, last) = _find_address_range(ips[i:]) |
| 366 i = ips.index(last) + 1 |
| 367 addrs.extend(summarize_address_range(first, last)) |
| 280 | 368 |
| 281 return _collapse_address_list_recursive( | 369 return _collapse_address_list_recursive( |
| 282 sorted(_addresses + nets, key=BaseIP._get_networks_key)) | 370 sorted(addrs + nets, key=BaseNet._get_networks_key)) |
| 283 | 371 |
| 284 # backwards compatibility | 372 # backwards compatibility |
| 285 CollapseAddrList = collapse_address_list | 373 CollapseAddrList = collapse_address_list |
| 286 | 374 |
| 287 # Test whether this Python implementation supports byte objects that | 375 # Test whether this Python implementation supports byte objects that |
| 288 # are not identical to str ones. | 376 # are not identical to str ones. |
| 289 # We need to exclude platforms where bytes == str so that we can | 377 # We need to exclude platforms where bytes == str so that we can |
| 290 # distinguish between packed representations and strings, for example | 378 # distinguish between packed representations and strings, for example |
| 291 # b'12::' (the IPv4 address 49.50.58.58) and '12::' (an IPv6 address). | 379 # b'12::' (the IPv4 address 49.50.58.58) and '12::' (an IPv6 address). |
| 292 try: | 380 try: |
| 293 _compat_has_real_bytes = bytes != str | 381 _compat_has_real_bytes = bytes != str |
| 294 except NameError: # <Python2.6 | 382 except NameError: # <Python2.6 |
| 295 _compat_has_real_bytes = False | 383 _compat_has_real_bytes = False |
| 296 | 384 |
| 297 class BaseIP(object): | 385 |
| 386 class IPAddrBase(object): |
| 387 |
| 388 """The mother class.""" |
| 389 |
| 390 def __index__(self): |
| 391 return self._ip |
| 392 |
| 393 def __int__(self): |
| 394 return self._ip |
| 395 |
| 396 def __hex__(self): |
| 397 return hex(self._ip) |
| 398 |
| 399 |
| 400 class BaseIP(IPAddrBase): |
| 298 | 401 |
| 299 """A generic IP object. | 402 """A generic IP object. |
| 300 | 403 |
| 301 This IP class contains most of the methods which are used by | 404 This IP class contains the version independent methods which are |
| 302 the IPv4 and IPv6 classes. | 405 used by single IP addresses. |
| 303 | 406 |
| 304 """ | 407 """ |
| 305 | 408 |
| 306 def __getitem__(self, n): | 409 @property |
| 307 if n >= 0: | 410 def exploded(self): |
| 308 if self.network + n > self.broadcast: | 411 return self._explode_shorthand_ip_string() |
| 309 raise IndexError | 412 |
| 310 return self._string_from_ip_int(self.network + n) | 413 @property |
| 311 else: | 414 def compressed(self): |
| 312 n += 1 | 415 return str(self) |
| 313 if self.broadcast + n < self.network: | 416 |
| 314 raise IndexError | 417 def __init__(self, address): |
| 315 return self._string_from_ip_int(self.broadcast + n) | 418 if '/' in str(address): |
| 316 | 419 raise IPAddressIPValidationError(address) |
| 317 def __lt__(self, other): | |
| 318 try: | |
| 319 return (self.version < other.version | |
| 320 or self.ip < other.ip | |
| 321 or self.netmask < other.netmask) | |
| 322 except AttributeError: | |
| 323 return NotImplemented | |
| 324 | |
| 325 def __gt__(self, other): | |
| 326 try: | |
| 327 return (self.version > other.version | |
| 328 or self.ip > other.ip | |
| 329 or self.netmask > other.netmask) | |
| 330 except AttributeError: | |
| 331 return NotImplemented | |
| 332 | 420 |
| 333 def __eq__(self, other): | 421 def __eq__(self, other): |
| 334 try: | 422 try: |
| 335 return (self.version == other.version | 423 return not (self._ip != other._ip |
| 336 and self.ip == other.ip | 424 or self._version != other._version) |
| 337 and self.netmask == other.netmask) | |
| 338 except AttributeError: | 425 except AttributeError: |
| 339 return NotImplemented | 426 return NotImplemented |
| 340 | 427 |
| 341 def __ne__(self, other): | 428 def __ne__(self, other): |
| 342 eq = self.__eq__(other) | 429 eq = self.__eq__(other) |
| 343 if eq is NotImplemented: | 430 if eq is NotImplemented: |
| 344 return NotImplemented | 431 return NotImplemented |
| 345 return not eq | 432 return not eq |
| 346 | 433 |
| 347 def __le__(self, other): | 434 def __le__(self, other): |
| 348 gt = self.__gt__(other) | 435 gt = self.__gt__(other) |
| 349 if gt is NotImplemented: | 436 if gt is NotImplemented: |
| 350 return NotImplemented | 437 return NotImplemented |
| 351 return not gt | 438 return not gt |
| 352 | 439 |
| 353 def __ge__(self, other): | 440 def __ge__(self, other): |
| 354 lt = self.__lt__(other) | 441 lt = self.__lt__(other) |
| 355 if lt is NotImplemented: | 442 if lt is NotImplemented: |
| 356 return NotImplemented | 443 return NotImplemented |
| 357 return not lt | 444 return not lt |
| 358 | 445 |
| 446 def __lt__(self, other): |
| 447 if self._version != other._version: |
| 448 return self._version < other._version |
| 449 if self._ip != other._ip: |
| 450 return self._ip < other._ip |
| 451 return False |
| 452 |
| 453 def __gt__(self, other): |
| 454 if self._version != other._version: |
| 455 return self._version > other._version |
| 456 if self._ip != other._ip: |
| 457 return self._ip > other._ip |
| 458 return False |
| 459 |
| 359 def __repr__(self): | 460 def __repr__(self): |
| 360 return '%s(%r)' % (self.__class__.__name__, str(self)) | 461 return '%s(%r)' % (self.__class__.__name__, str(self)) |
| 361 | 462 |
| 362 def __index__(self): | 463 def __str__(self): |
| 363 return self.ip | 464 return '%s' % self._string_from_ip_int(self._ip) |
| 364 | 465 |
| 365 def __int__(self): | 466 def __hash__(self): |
| 366 return self.ip | 467 return hash(self._ip) |
| 367 | 468 |
| 368 def __hex__(self): | 469 @property |
| 369 return hex(int(self)) | 470 def version(self): |
| 471 raise NotImplementedError('BaseIP has no version') |
| 472 |
| 473 |
| 474 class BaseNet(IPAddrBase): |
| 475 |
| 476 """A generic IP object. |
| 477 |
| 478 This IP class contains the version independent methods which are |
| 479 used by networks. |
| 480 |
| 481 """ |
| 482 |
| 483 def __init__(self, address): |
| 484 self._cache = {} |
| 485 |
| 486 def __repr__(self): |
| 487 return '%s(%r)' % (self.__class__.__name__, str(self)) |
| 488 |
| 489 def iterhosts(self): |
| 490 """Generate Iterator over hosts in a network.""" |
| 491 cur = int(self.network) |
| 492 bcast = int(self.broadcast) |
| 493 while cur <= bcast: |
| 494 cur += 1 |
| 495 yield IP(cur - 1, host=True, version=self._version) |
| 496 |
| 497 def __iter__(self): |
| 498 self._cur = int(self.network) |
| 499 return self |
| 500 |
| 501 def next(self): |
| 502 if self._cur > int(self.broadcast): |
| 503 raise StopIteration |
| 504 self._cur += 1 |
| 505 return IP(self._cur - 1, host=True, version=self._version) |
| 506 |
| 507 def __getitem__(self, n): |
| 508 network = int(self.network) |
| 509 broadcast = int(self.broadcast) |
| 510 if n >= 0: |
| 511 if network + n > broadcast: |
| 512 raise IndexError |
| 513 return IP(network + n, host=True, version=self._version) |
| 514 else: |
| 515 n += 1 |
| 516 if broadcast + n < network: |
| 517 raise IndexError |
| 518 return IP(broadcast + n, host=True, |
| 519 version=self._version) |
| 520 |
| 521 def __lt__(self, other): |
| 522 try: |
| 523 if self._version != other._version: |
| 524 return self._version < other._version |
| 525 if self._ip != other._ip: |
| 526 return self._ip < other._ip |
| 527 if self.netmask != other.netmask: |
| 528 return self.netmask < other.netmask |
| 529 return False |
| 530 except AttributeError: |
| 531 return NotImplemented |
| 532 |
| 533 def __gt__(self, other): |
| 534 try: |
| 535 if self._version != other._version: |
| 536 return self._version > other._version |
| 537 if self._ip != other._ip: |
| 538 return self._ip > other._ip |
| 539 if self.netmask != other.netmask: |
| 540 return self.netmask > other.netmask |
| 541 return False |
| 542 except AttributeError: |
| 543 return NotImplemented |
| 544 |
| 545 def __eq__(self, other): |
| 546 try: |
| 547 return (self._version == other._version |
| 548 and self._ip == other._ip |
| 549 and int(self.netmask) == int(other.netmask)) |
| 550 except AttributeError: |
| 551 return NotImplemented |
| 552 |
| 553 def __ne__(self, other): |
| 554 eq = self.__eq__(other) |
| 555 if eq is NotImplemented: |
| 556 return NotImplemented |
| 557 return not eq |
| 558 |
| 559 def __str__(self): |
| 560 return '%s/%s' % (str(self.ip), |
| 561 str(self._prefixlen)) |
| 562 |
| 563 def __hash__(self): |
| 564 return hash(self._ip ^ int(self.netmask)) |
| 565 |
| 566 def __contains__(self, other): |
| 567 # Easy case, dealing with networks. |
| 568 if isinstance(other, BaseNet): |
| 569 return (int(self.network) <= int(other._ip) and |
| 570 int(self.broadcast) >= int(other.broadcast)) |
| 571 elif isinstance(other, BaseIP): |
| 572 # Check if we've got an Address |
| 573 return (int(self.network) <= int(other._ip) <= |
| 574 int(self.broadcast)) |
| 575 else: |
| 576 return IP(other) in self |
| 577 |
| 578 @property |
| 579 def network(self): |
| 580 x = self._cache.get('network') |
| 581 if x is None: |
| 582 x = IP(self._ip & int(self.netmask), host=True, |
| 583 version=self._version) |
| 584 self._cache['network'] = x |
| 585 return x |
| 586 |
| 587 @property |
| 588 def broadcast(self): |
| 589 x = self._cache.get('broadcast') |
| 590 if x is None: |
| 591 x = IP(self._ip | int(self.hostmask), host=True, |
| 592 version=self._version) |
| 593 self._cache['broadcast'] = x |
| 594 return x |
| 595 |
| 596 @property |
| 597 def hostmask(self): |
| 598 x = self._cache.get('hostmask') |
| 599 if x is None: |
| 600 x = IP(int(self.netmask) ^ self._ALL_ONES, host=True, |
| 601 version=self._version) |
| 602 self._cache['hostmask'] = x |
| 603 return x |
| 604 |
| 605 @property |
| 606 def with_prefixlen(self): |
| 607 return '%s/%d' % (str(self.ip), self._prefixlen) |
| 608 |
| 609 @property |
| 610 def with_netmask(self): |
| 611 return '%s/%s' % (str(self.ip), str(self.netmask)) |
| 612 |
| 613 @property |
| 614 def with_hostmask(self): |
| 615 return '%s/%s' % (str(self.ip), str(self.hostmask)) |
| 616 |
| 617 @property |
| 618 def numhosts(self): |
| 619 """Number of hosts in the current subnet.""" |
| 620 return int(self.broadcast) - int(self.network) + 1 |
| 621 |
| 622 @property |
| 623 def version(self): |
| 624 raise NotImplementedError('BaseNet has no version') |
| 625 |
| 626 prefixlen = property( |
| 627 fget=lambda self: self._prefixlen, |
| 628 fset=lambda self, prefixlen: self._set_prefix(prefixlen)) |
| 370 | 629 |
| 371 def address_exclude(self, other): | 630 def address_exclude(self, other): |
| 372 """Remove an address from a larger block. | 631 """Remove an address from a larger block. |
| 373 | 632 |
| 374 For example: | 633 For example: |
| 375 | 634 |
| 376 addr1 = IP('10.1.1.0/24') | 635 addr1 = IP('10.1.1.0/24') |
| 377 addr2 = IP('10.1.1.0/26') | 636 addr2 = IP('10.1.1.0/26') |
| 378 addr1.address_exclude(addr2) = | 637 addr1.address_exclude(addr2) = |
| 379 [IP('10.1.1.64/26'), IP('10.1.1.128/25')] | 638 [IP('10.1.1.64/26'), IP('10.1.1.128/25')] |
| (...skipping 18 matching lines...) Expand all Loading... |
| 398 | 657 |
| 399 Raises: | 658 Raises: |
| 400 IPTypeError: If self and other are of difffering address | 659 IPTypeError: If self and other are of difffering address |
| 401 versions. | 660 versions. |
| 402 IPAddressExclusionError: There was some unknown error in the | 661 IPAddressExclusionError: There was some unknown error in the |
| 403 address exclusion process. This likely points to a bug | 662 address exclusion process. This likely points to a bug |
| 404 elsewhere in this code. | 663 elsewhere in this code. |
| 405 ValueError: If other is not completely contained by self. | 664 ValueError: If other is not completely contained by self. |
| 406 | 665 |
| 407 """ | 666 """ |
| 408 if not self.version == other.version: | 667 if not self._version == other._version: |
| 409 raise IPTypeError("%s and %s aren't of the same version" % ( | 668 raise IPTypeError("%s and %s aren't of the same version" % ( |
| 410 str(self), str(other))) | 669 str(self), str(other))) |
| 411 | 670 |
| 412 if other not in self: | 671 if other not in self: |
| 413 raise ValueError('%s not contained in %s' % (str(other), | 672 raise ValueError('%s not contained in %s' % (str(other), |
| 414 str(self))) | 673 str(self))) |
| 415 | |
| 416 ret_addrs = [] | 674 ret_addrs = [] |
| 417 | 675 |
| 418 # Make sure we're comparing the network of other. | 676 # Make sure we're comparing the network of other. |
| 419 other = IP(other.network_ext + '/' + str(other.prefixlen)) | 677 other = IP('%s/%s' % (str(other.network), str(other.prefixlen)), |
| 678 version=other._version) |
| 420 | 679 |
| 421 s1, s2 = self.subnet() | 680 s1, s2 = self.subnet() |
| 422 while s1 != other and s2 != other: | 681 while s1 != other and s2 != other: |
| 423 if other in s1: | 682 if other in s1: |
| 424 ret_addrs.append(s2) | 683 ret_addrs.append(s2) |
| 425 s1, s2 = s1.subnet() | 684 s1, s2 = s1.subnet() |
| 426 elif other in s2: | 685 elif other in s2: |
| 427 ret_addrs.append(s1) | 686 ret_addrs.append(s1) |
| 428 s1, s2 = s2.subnet() | 687 s1, s2 = s2.subnet() |
| 429 else: | 688 else: |
| 430 # If we got here, there's a bug somewhere. | 689 # If we got here, there's a bug somewhere. |
| 431 raise IPAddressExclusionError('Error performing exclusion: ' | 690 raise IPAddressExclusionError('Error performing exclusion: ' |
| 432 's1: %s s2: %s other: %s' % | 691 's1: %s s2: %s other: %s' % |
| 433 (str(s1), str(s2), str(other))) | 692 (str(s1), str(s2), str(other))) |
| 434 if s1 == other: | 693 if s1 == other: |
| 435 ret_addrs.append(s2) | 694 ret_addrs.append(s2) |
| 436 elif s2 == other: | 695 elif s2 == other: |
| 437 ret_addrs.append(s1) | 696 ret_addrs.append(s1) |
| 438 else: | 697 else: |
| 439 # If we got here, there's a bug somewhere. | 698 # If we got here, there's a bug somewhere. |
| 440 raise IPAddressExclusionError('Error performing exclusion: ' | 699 raise IPAddressExclusionError('Error performing exclusion: ' |
| 441 's1: %s s2: %s other: %s' % | 700 's1: %s s2: %s other: %s' % |
| 442 (str(s1), str(s2), str(other))) | 701 (str(s1), str(s2), str(other))) |
| 443 | 702 |
| 444 return sorted(ret_addrs, key=BaseIP._get_networks_key) | 703 return sorted(ret_addrs, key=BaseNet._get_networks_key) |
| 445 | 704 |
| 446 def compare_networks(self, other): | 705 def compare_networks(self, other): |
| 447 """Compare two IP objects. | 706 """Compare two IP objects. |
| 448 | 707 |
| 449 This is only concerned about the comparison of the integer | 708 This is only concerned about the comparison of the integer |
| 450 representation of the network addresses. This means that the | 709 representation of the network addresses. This means that the |
| 451 host bits aren't considered at all in this method. If you want | 710 host bits aren't considered at all in this method. If you want |
| 452 to compare host bits, you can easily enough do a | 711 to compare host bits, you can easily enough do a |
| 453 'HostA.ip < HostB.ip' | 712 'HostA._ip < HostB._ip' |
| 454 | 713 |
| 455 Args: | 714 Args: |
| 456 other: An IP object. | 715 other: An IP object. |
| 457 | 716 |
| 458 Returns: | 717 Returns: |
| 459 If the IP versions of self and other are the same, returns: | 718 If the IP versions of self and other are the same, returns: |
| 460 | 719 |
| 461 -1 if self < other: | 720 -1 if self < other: |
| 462 eg: IPv4('1.1.1.0/24') < IPv4('1.1.2.0/24') | 721 eg: IPv4('1.1.1.0/24') < IPv4('1.1.2.0/24') |
| 463 IPv6('1080::200C:417A') < IPv6('1080::200B:417B') | 722 IPv6('1080::200C:417A') < IPv6('1080::200B:417B') |
| 464 0 if self == other | 723 0 if self == other |
| 465 eg: IPv4('1.1.1.1/24') == IPv4('1.1.1.2/24') | 724 eg: IPv4('1.1.1.1/24') == IPv4('1.1.1.2/24') |
| 466 IPv6('1080::200C:417A/96') == IPv6('1080::200C:417B/96') | 725 IPv6('1080::200C:417A/96') == IPv6('1080::200C:417B/96') |
| 467 1 if self > other | 726 1 if self > other |
| 468 eg: IPv4('1.1.1.0/24') > IPv4('1.1.0.0/24') | 727 eg: IPv4('1.1.1.0/24') > IPv4('1.1.0.0/24') |
| 469 IPv6('1080::1:200C:417A/112') > | 728 IPv6('1080::1:200C:417A/112') > |
| 470 IPv6('1080::0:200C:417A/112') | 729 IPv6('1080::0:200C:417A/112') |
| 471 | 730 |
| 472 If the IP versions of self and other are different, returns: | 731 If the IP versions of self and other are different, returns: |
| 473 | 732 |
| 474 -1 if self.version < other.version | 733 -1 if self._version < other._version |
| 475 eg: IPv4('10.0.0.1/24') < IPv6('::1/128') | 734 eg: IPv4('10.0.0.1/24') < IPv6('::1/128') |
| 476 1 if self.version > other.version | 735 1 if self._version > other._version |
| 477 eg: IPv6('::1/128') > IPv4('255.255.255.0/24') | 736 eg: IPv6('::1/128') > IPv4('255.255.255.0/24') |
| 478 | 737 |
| 479 """ | 738 """ |
| 480 if self.version < other.version: | 739 if self._version < other._version: |
| 481 return -1 | 740 return -1 |
| 482 if self.version > other.version: | 741 if self._version > other._version: |
| 483 return 1 | 742 return 1 |
| 484 # self.version == other.version below here: | 743 # self._version == other._version below here: |
| 485 if self.network < other.network: | 744 if self.network < other.network: |
| 486 return -1 | 745 return -1 |
| 487 if self.network > other.network: | 746 if self.network > other.network: |
| 488 return 1 | 747 return 1 |
| 489 # self.network == other.network below here: | 748 # self.network == other.network below here: |
| 490 if self.netmask < other.netmask: | 749 if self.netmask < other.netmask: |
| 491 return -1 | 750 return -1 |
| 492 if self.netmask > other.netmask: | 751 if self.netmask > other.netmask: |
| 493 return 1 | 752 return 1 |
| 494 # self.network == other.network and self.netmask == other.netmask | 753 # self.network == other.network and self.netmask == other.netmask |
| 495 return 0 | 754 return 0 |
| 496 | 755 |
| 497 def _get_networks_key(self): | 756 def _get_networks_key(self): |
| 498 """Network-only key function. | 757 """Network-only key function. |
| 499 | 758 |
| 500 Returns an object that identifies this address' network and | 759 Returns an object that identifies this address' network and |
| 501 netmask. This function is a suitable "key" argument for sorted() | 760 netmask. This function is a suitable "key" argument for sorted() |
| 502 and list.sort(). | 761 and list.sort(). |
| 503 | 762 |
| 504 """ | 763 """ |
| 505 return (self.version, self.network, self.netmask) | 764 return (self._version, self.network, self.netmask) |
| 506 | |
| 507 prefixlen = property( | |
| 508 fget=lambda self: self._prefixlen, | |
| 509 fset=lambda self, prefixlen: self._set_prefix(prefixlen)) | |
| 510 | |
| 511 def __str__(self): | |
| 512 return '%s/%s' % (self._string_from_ip_int(self.ip), | |
| 513 str(self.prefixlen)) | |
| 514 | |
| 515 def __hash__(self): | |
| 516 return hash(self.ip ^ self.netmask) | |
| 517 | |
| 518 def __contains__(self, other): | |
| 519 return self.network <= other.ip and self.broadcast >= other.broadcast | |
| 520 | |
| 521 @property | |
| 522 def ip_ext(self): | |
| 523 """Dotted decimal or colon string version of the IP address.""" | |
| 524 return self._string_from_ip_int(self.ip) | |
| 525 | |
| 526 @property | |
| 527 def ip_ext_full(self): | |
| 528 """Canonical string version of the IP address.""" | |
| 529 return self.ip_ext | |
| 530 | |
| 531 @property | |
| 532 def broadcast(self): | |
| 533 """Integer representation of the broadcast address.""" | |
| 534 return self.ip | self.hostmask | |
| 535 | |
| 536 @property | |
| 537 def broadcast_ext(self): | |
| 538 """Dotted decimal or colon string version of the broadcast.""" | |
| 539 return self._string_from_ip_int(self.broadcast) | |
| 540 | |
| 541 @property | |
| 542 def hostmask(self): | |
| 543 """Integer representation of the hostmask.""" | |
| 544 return self.netmask ^ self._ALL_ONES | |
| 545 | |
| 546 @property | |
| 547 def hostmask_ext(self): | |
| 548 """Dotted decimal or colon string version of the hostmask.""" | |
| 549 return self._string_from_ip_int(self.hostmask) | |
| 550 | |
| 551 @property | |
| 552 def network(self): | |
| 553 """Integer representation of the network.""" | |
| 554 return self.ip & self.netmask | |
| 555 | |
| 556 @property | |
| 557 def network_ext(self): | |
| 558 """Dotted decimal or colon string version of the network.""" | |
| 559 return self._string_from_ip_int(self.network) | |
| 560 | |
| 561 @property | |
| 562 def netmask_ext(self): | |
| 563 """Dotted decimal or colon string version of the netmask.""" | |
| 564 return self._string_from_ip_int(self.netmask) | |
| 565 | |
| 566 @property | |
| 567 def numhosts(self): | |
| 568 """Number of hosts in the current subnet.""" | |
| 569 return self.broadcast - self.network + 1 | |
| 570 | |
| 571 @property | |
| 572 def version(self): | |
| 573 raise NotImplementedError('BaseIP has no version') | |
| 574 | 765 |
| 575 def _ip_int_from_prefix(self, prefixlen=None): | 766 def _ip_int_from_prefix(self, prefixlen=None): |
| 576 """Turn the prefix length netmask into a int for comparison. | 767 """Turn the prefix length netmask into a int for comparison. |
| 577 | 768 |
| 578 Args: | 769 Args: |
| 579 prefixlen: An integer, the prefix length. | 770 prefixlen: An integer, the prefix length. |
| 580 | 771 |
| 581 Returns: | 772 Returns: |
| 582 An integer. | 773 An integer. |
| 583 | 774 |
| 584 """ | 775 """ |
| 585 if not prefixlen and prefixlen != 0: | 776 if not prefixlen and prefixlen != 0: |
| 586 prefixlen = self.prefixlen | 777 prefixlen = self._prefixlen |
| 587 return self._ALL_ONES ^ (self._ALL_ONES >> prefixlen) | 778 return self._ALL_ONES ^ (self._ALL_ONES >> prefixlen) |
| 588 | 779 |
| 589 def _prefix_from_ip_int(self, ip_int, mask=32): | 780 def _prefix_from_ip_int(self, ip_int, mask=32): |
| 590 """Return prefix length from the decimal netmask. | 781 """Return prefix length from the decimal netmask. |
| 591 | 782 |
| 592 Args: | 783 Args: |
| 593 ip_int: An integer, the IP address. | 784 ip_int: An integer, the IP address. |
| 594 mask: The netmask. Defaults to 32. | 785 mask: The netmask. Defaults to 32. |
| 595 | 786 |
| 596 Returns: | 787 Returns: |
| (...skipping 12 matching lines...) Expand all Loading... |
| 609 """Turn a prefix length into a dotted decimal string. | 800 """Turn a prefix length into a dotted decimal string. |
| 610 | 801 |
| 611 Args: | 802 Args: |
| 612 prefixlen: An integer, the netmask prefix length. | 803 prefixlen: An integer, the netmask prefix length. |
| 613 | 804 |
| 614 Returns: | 805 Returns: |
| 615 A string, the dotted decimal netmask string. | 806 A string, the dotted decimal netmask string. |
| 616 | 807 |
| 617 """ | 808 """ |
| 618 if not prefixlen: | 809 if not prefixlen: |
| 619 prefixlen = self.prefixlen | 810 prefixlen = self._prefixlen |
| 620 return self._string_from_ip_int(self._ip_int_from_prefix(prefixlen)) | 811 return self._string_from_ip_int(self._ip_int_from_prefix(prefixlen)) |
| 621 | 812 |
| 622 # backwards compatibility | 813 # backwards compatibility |
| 623 AddressExclude = address_exclude | 814 AddressExclude = address_exclude |
| 624 CompareNetworks = compare_networks | 815 CompareNetworks = compare_networks |
| 625 Contains = __contains__ | 816 Contains = __contains__ |
| 626 def set_prefix(self, prefixlen): self.prefixlen = prefixlen | 817 def set_prefix(self, prefixlen): self.prefixlen = prefixlen |
| 627 SetPrefix = set_prefix | 818 SetPrefix = set_prefix |
| 628 def get_prefix(self): return self.prefixlen | 819 def get_prefix(self): return self.prefixlen |
| 629 | 820 |
| 630 | 821 |
| 631 class IPv4(BaseIP): | 822 class BaseV4(object): |
| 632 | 823 |
| 633 """This class represents and manipulates 32-bit IPv4 addresses. | 824 """Base IPv4 object. |
| 634 | 825 |
| 635 Attributes: [examples for IPv4('1.2.3.4/27')] | 826 The following methods are used by IPv4 objects in both single IP |
| 636 .ip: 16909060 | 827 addresses and networks. |
| 637 .ip_ext: '1.2.3.4' | |
| 638 .ip_ext_full: '1.2.3.4' | |
| 639 .network: 16909056L | |
| 640 .network_ext: '1.2.3.0' | |
| 641 .hostmask: 31L (0x1F) | |
| 642 .hostmask_ext: '0.0.0.31' | |
| 643 .broadcast: 16909087L (0x102031F) | |
| 644 .broadcast_ext: '1.2.3.31' | |
| 645 .netmask: 4294967040L (0xFFFFFFE0) | |
| 646 .netmask_ext: '255.255.255.224' | |
| 647 .prefixlen: 27 | |
| 648 | 828 |
| 649 """ | 829 """ |
| 650 | 830 |
| 651 # Equivalent to 255.255.255.255 or 32 bits of 1's. | 831 # Equivalent to 255.255.255.255 or 32 bits of 1's. |
| 652 _ALL_ONES = (2**32) - 1 | 832 _ALL_ONES = (2**32) - 1 |
| 653 | 833 |
| 654 def __init__(self, ipaddr): | 834 def __init__(self, address): |
| 655 """Instantiate a new IPv4 object. | 835 self._version = 4 |
| 656 | 836 self._max_prefixlen = 32 |
| 657 Args: | 837 |
| 658 ipaddr: A string or integer representing the IP [& network]. | 838 def _explode_shorthand_ip_string(self, ip_str=None): |
| 839 if not ip_str: |
| 840 ip_str = str(self) |
| 841 return ip_str |
| 842 |
| 843 def _ip_int_from_string(self, ip_str): |
| 844 """Turn the given IP string into an integer for comparison. |
| 845 |
| 846 Args: |
| 847 ip_str: A string, the IP ip_str. |
| 848 |
| 849 Returns: |
| 850 The IP ip_str as an integer. |
| 851 |
| 852 """ |
| 853 packed_ip = 0 |
| 854 for oc in ip_str.split('.'): |
| 855 packed_ip = (packed_ip << 8) | int(oc) |
| 856 return packed_ip |
| 857 |
| 858 def _string_from_ip_int(self, ip_int): |
| 859 """Turns a 32-bit integer into dotted decimal notation. |
| 860 |
| 861 Args: |
| 862 ip_int: An integer, the IP address. |
| 863 |
| 864 Returns: |
| 865 The IP address as a string in dotted decimal notation. |
| 866 |
| 867 """ |
| 868 octets = [] |
| 869 for _ in xrange(4): |
| 870 octets.insert(0, str(ip_int & 0xFF)) |
| 871 ip_int >>= 8 |
| 872 return '.'.join(octets) |
| 873 |
| 874 def _is_valid_ip(self, ip_str): |
| 875 """Validate the dotted decimal notation IP/netmask string. |
| 876 |
| 877 Args: |
| 878 ip_str: A string, the IP ip_str. |
| 879 |
| 880 Returns: |
| 881 A boolean, True if the string is a valid dotted decimal IP |
| 882 string. |
| 883 |
| 884 """ |
| 885 octets = ip_str.split('.') |
| 886 if len(octets) == 1: |
| 887 # We have an integer rather than a dotted decimal IP. |
| 888 try: |
| 889 return int(ip_str) >= 0 and int(ip_str) <= self._ALL_ONES |
| 890 except ValueError: |
| 891 return False |
| 892 |
| 893 if len(octets) != 4: |
| 894 return False |
| 895 |
| 896 for octet in octets: |
| 897 try: |
| 898 if not 0 <= int(octet) <= 255: |
| 899 return False |
| 900 except ValueError: |
| 901 return False |
| 902 return True |
| 903 |
| 904 @property |
| 905 def max_prefixlen(self): |
| 906 return self._max_prefixlen |
| 907 |
| 908 @property |
| 909 def packed(self): |
| 910 """The binary representation of this address.""" |
| 911 return struct.pack('!I', self._ip) |
| 912 |
| 913 @property |
| 914 def version(self): |
| 915 return self._version |
| 916 |
| 917 |
| 918 class IPv4Address(BaseV4, BaseIP): |
| 919 |
| 920 """Represent and manipulate single IPv4 Addresses.""" |
| 921 |
| 922 def __init__(self, address): |
| 923 |
| 924 """ |
| 925 Args: |
| 926 address: A string or integer representing the IP |
| 927 '192.168.1.1' |
| 928 |
| 929 Additionally, an integer can be passed, so |
| 930 IPv4Address('192.168.1.1') == IPv4Address(3232235777). |
| 931 or, more generally |
| 932 IPv4Address(int(IPv4Address('192.168.1.1'))) == |
| 933 IPv4Address('192.168.1.1') |
| 934 |
| 935 Raises: |
| 936 IPv4IpValidationError: If ipaddr isn't a valid IPv4 address. |
| 937 IPv4NetmaskValidationError: If the netmask isn't valid for |
| 938 an IPv4 address. |
| 939 |
| 940 """ |
| 941 BaseIP.__init__(self, address) |
| 942 BaseV4.__init__(self, address) |
| 943 |
| 944 # Efficient constructor from integer. |
| 945 if isinstance(address, int) or isinstance(address, long): |
| 946 self._ip = address |
| 947 if address < 0 or address > self._ALL_ONES: |
| 948 raise IPv4IpValidationError(address) |
| 949 return |
| 950 |
| 951 # Constructing from a packed address |
| 952 if _compat_has_real_bytes: |
| 953 if isinstance(address, bytes) and len(address) == 4: |
| 954 self._ip = struct.unpack('!I', address)[0] |
| 955 return |
| 956 |
| 957 # we have a string |
| 958 if not self._is_valid_ip(address): |
| 959 raise IPv4IpValidationError(address) |
| 960 |
| 961 self._ip = self._ip_int_from_string(address) |
| 962 |
| 963 |
| 964 class IPv4Network(BaseV4, BaseNet): |
| 965 |
| 966 """This class represents and manipulates 32-bit IPv4 networks. |
| 967 |
| 968 Attributes: [examples for IPv4Network('1.2.3.4/27')] |
| 969 ._ip: 16909060 |
| 970 .ip: IPv4Address('1.2.3.4') |
| 971 .network: IPv4Address('1.2.3.0') |
| 972 .hostmask: IPv4Address('0.0.0.31') |
| 973 .broadcast: IPv4Address('1.2.3.31') |
| 974 .netmask: IPv4Address('255.255.255.224') |
| 975 .prefixlen: 27 |
| 976 |
| 977 """ |
| 978 |
| 979 def __init__(self, address): |
| 980 """Instantiate a new IPv4 network object. |
| 981 |
| 982 Args: |
| 983 address: A string or integer representing the IP [& network]. |
| 659 '192.168.1.1/32' | 984 '192.168.1.1/32' |
| 660 '192.168.1.1/255.255.255.255' | 985 '192.168.1.1/255.255.255.255' |
| 661 '192.168.1.1/0.0.0.255' | 986 '192.168.1.1/0.0.0.255' |
| 662 '192.168.1.1' | 987 '192.168.1.1' |
| 663 '192.168.1.0-192.168.1.0' | |
| 664 are all functionally the same in IPv4. That is to say, | 988 are all functionally the same in IPv4. That is to say, |
| 665 failing to provide a subnetmask will create an object with | 989 failing to provide a subnetmask will create an object with |
| 666 a mask of /32. A netmask of '255.255.255.255' is assumed | 990 a mask of /32. A netmask of '255.255.255.255' is assumed |
| 667 to be /32 and '0.0.0.0' is assumed to be /0, even though | 991 to be /32 and '0.0.0.0' is assumed to be /0, even though |
| 668 other netmasks can be expressed both as host- and | 992 other netmasks can be expressed both as host- and |
| 669 net-masks. (255.0.0.0 == 0.255.255.255) | 993 net-masks. (255.0.0.0 == 0.255.255.255) |
| 670 | 994 |
| 671 Additionally, an integer can be passed, so | 995 Additionally, an integer can be passed, so |
| 672 IPv4('192.168.1.1') == IPv4(3232235777). | 996 IPv4Network('192.168.1.1') == IPv4Network(3232235777). |
| 673 or, more generally | 997 or, more generally |
| 674 IPv4(IPv4('192.168.1.1').ip) == IPv4('192.168.1.1') | 998 IPv4Network(int(IPv4Network('192.168.1.1'))) == |
| 999 IPv4Network('192.168.1.1') |
| 675 | 1000 |
| 676 Raises: | 1001 Raises: |
| 677 IPv4IpValidationError: If ipaddr isn't a valid IPv4 address. | 1002 IPv4IpValidationError: If ipaddr isn't a valid IPv4 address. |
| 678 IPv4NetmaskValidationError: If the netmask isn't valid for | 1003 IPv4NetmaskValidationError: If the netmask isn't valid for |
| 679 an IPv4 address. | 1004 an IPv4 address. |
| 680 IPRangeError: If range of IP addresses is not valid. | 1005 |
| 681 | 1006 """ |
| 682 """ | 1007 BaseNet.__init__(self, address) |
| 683 BaseIP.__init__(self) | 1008 BaseV4.__init__(self, address) |
| 684 self._version = 4 | |
| 685 self._max_prefixlen = 32 | |
| 686 | 1009 |
| 687 # Efficient constructor from integer. | 1010 # Efficient constructor from integer. |
| 688 if isinstance(ipaddr, int) or isinstance(ipaddr, long): | 1011 if isinstance(address, int) or isinstance(address, long): |
| 689 self.ip = ipaddr | 1012 self._ip = address |
| 1013 self.ip = IPv4Address(self._ip) |
| 690 self._prefixlen = 32 | 1014 self._prefixlen = 32 |
| 691 self.netmask = self._ALL_ONES | 1015 self.netmask = IPv4Address(self._ALL_ONES) |
| 692 if ipaddr < 0 or ipaddr > self._ALL_ONES: | 1016 if address < 0 or address > self._ALL_ONES: |
| 693 raise IPv4IpValidationError(ipaddr) | 1017 raise IPv4IpValidationError(address) |
| 694 return | 1018 return |
| 695 | 1019 |
| 696 # Constructing from a packed address | 1020 # Constructing from a packed address |
| 697 if _compat_has_real_bytes: | 1021 if _compat_has_real_bytes: |
| 698 if isinstance(ipaddr, bytes) and len(ipaddr) == 4: | 1022 if isinstance(address, bytes) and len(address) == 4: |
| 699 self.ip = struct.unpack('!I', ipaddr)[0] | 1023 self._ip = struct.unpack('!I', address)[0] |
| 1024 self.ip = IPv4Address(self._ip) |
| 700 self._prefixlen = 32 | 1025 self._prefixlen = 32 |
| 701 self.netmask = self._ALL_ONES | 1026 self.netmask = IPv4Address(self._ALL_ONES) |
| 702 return | 1027 return |
| 703 | |
| 704 # Construct network from a range e.g. 1.1.1.0-1.1.1.255 | |
| 705 addr = str(ipaddr).split('-') | |
| 706 | |
| 707 if len(addr) > 2: | |
| 708 raise IPv4IpValidationError(ipaddr) | |
| 709 | |
| 710 if len(addr) == 2: | |
| 711 if not self._is_valid_ip(addr[0]): | |
| 712 raise IPv4IpValidationError(addr[0]) | |
| 713 | |
| 714 if not self._is_valid_ip(addr[1]): | |
| 715 raise IPv4IpValidationError(addr[1]) | |
| 716 | |
| 717 self.ip = self._ip_int_from_string(addr[0]) | |
| 718 | |
| 719 last = self._ip_int_from_string(addr[1]) | |
| 720 if last < self.ip: | |
| 721 raise IPRangeError("last address should be greater than " | |
| 722 "first.") | |
| 723 bits = _highest_one_bit(last - self.ip) | |
| 724 self._prefixlen = self.max_prefixlen - bits | |
| 725 self.netmask = self._ip_int_from_prefix(self._prefixlen) | |
| 726 if self.broadcast != last: | |
| 727 raise IPRangeError("range %s is not on a network byte " | |
| 728 "boundary." % ipaddr) | |
| 729 return | |
| 730 | 1028 |
| 731 # Assume input argument to be string or any object representation | 1029 # Assume input argument to be string or any object representation |
| 732 # which converts into a formatted IP prefix string. | 1030 # which converts into a formatted IP prefix string. |
| 733 addr = str(ipaddr).split('/') | 1031 addr = str(address).split('/') |
| 734 | 1032 |
| 735 if len(addr) > 2: | 1033 if len(addr) > 2: |
| 736 raise IPv4IpValidationError(ipaddr) | 1034 raise IPv4IpValidationError(address) |
| 737 | 1035 |
| 738 if not self._is_valid_ip(addr[0]): | 1036 if not self._is_valid_ip(addr[0]): |
| 739 raise IPv4IpValidationError(addr[0]) | 1037 raise IPv4IpValidationError(addr[0]) |
| 740 | 1038 |
| 741 self.ip = self._ip_int_from_string(addr[0]) | 1039 self._ip = self._ip_int_from_string(addr[0]) |
| 1040 self.ip = IPv4Address(self._ip) |
| 742 | 1041 |
| 743 if len(addr) == 2: | 1042 if len(addr) == 2: |
| 744 mask = addr[1].split('.') | 1043 mask = addr[1].split('.') |
| 745 if len(mask) == 4: | 1044 if len(mask) == 4: |
| 746 # We have dotted decimal netmask. | 1045 # We have dotted decimal netmask. |
| 747 if not self._is_valid_netmask(addr[1]): | 1046 if not self._is_valid_netmask(addr[1]): |
| 748 raise IPv4NetmaskValidationError(addr[1]) | 1047 raise IPv4NetmaskValidationError(addr[1]) |
| 749 if self._is_hostmask(addr[1]): | 1048 if self._is_hostmask(addr[1]): |
| 750 self.netmask = ( | 1049 self.netmask = IPv4Address( |
| 751 self._ip_int_from_string(addr[1]) ^ self._ALL_ONES) | 1050 self._ip_int_from_string(addr[1]) ^ self._ALL_ONES) |
| 752 else: | 1051 else: |
| 753 self.netmask = self._ip_int_from_string(addr[1]) | 1052 self.netmask = IPv4Address(self._ip_int_from_string( |
| 754 self._prefixlen = self._prefix_from_ip_int(self.netmask) | 1053 addr[1])) |
| 1054 |
| 1055 self._prefixlen = self._prefix_from_ip_int(int(self.netmask)) |
| 755 else: | 1056 else: |
| 756 # We have a netmask in prefix length form. | 1057 # We have a netmask in prefix length form. |
| 757 if not self._is_valid_netmask(addr[1]): | 1058 if not self._is_valid_netmask(addr[1]): |
| 758 raise IPv4NetmaskValidationError(addr[1]) | 1059 raise IPv4NetmaskValidationError(addr[1]) |
| 759 self._prefixlen = int(addr[1]) | 1060 self._prefixlen = int(addr[1]) |
| 760 self.netmask = self._ip_int_from_prefix(self._prefixlen) | 1061 self.netmask = IPv4Address(self._ip_int_from_prefix( |
| 1062 self._prefixlen)) |
| 761 else: | 1063 else: |
| 762 self._prefixlen = 32 | 1064 self._prefixlen = 32 |
| 763 self.netmask = self._ip_int_from_prefix(self._prefixlen) | 1065 self.netmask = IPv4Address(self._ip_int_from_prefix( |
| 1066 self._prefixlen)) |
| 1067 |
| 764 | 1068 |
| 765 def _set_prefix(self, prefixlen): | 1069 def _set_prefix(self, prefixlen): |
| 766 """Change the prefix length. | 1070 """Change the prefix length of an IPv4 network object. |
| 767 | 1071 |
| 768 Args: | 1072 Args: |
| 769 prefixlen: An integer, the new prefix length. | 1073 prefixlen: An integer, the new prefix length. |
| 770 | 1074 |
| 771 Raises: | 1075 Raises: |
| 772 IPv4NetmaskValidationError: If prefixlen is out of bounds. | 1076 IPv4NetmaskValidationError: If prefixlen is out of bounds. |
| 773 | 1077 |
| 774 """ | 1078 """ |
| 775 if not 0 <= prefixlen <= 32: | 1079 if not 0 <= prefixlen <= 32: |
| 776 raise IPv4NetmaskValidationError(prefixlen) | 1080 raise IPv4NetmaskValidationError(prefixlen) |
| 777 self._prefixlen = prefixlen | 1081 self._prefixlen = prefixlen |
| 778 self.netmask = self._ip_int_from_prefix(self._prefixlen) | 1082 self.netmask = IPv4Address(self._ip_int_from_prefix(self._prefixlen)) |
| 1083 # invalidate the element cache |
| 1084 self._cache['network'] = None |
| 1085 self._cache['hostmask'] = None |
| 1086 self._cache['broadcast'] = None |
| 779 | 1087 |
| 780 def subnet(self, prefixlen_diff=1): | 1088 def subnet(self, prefixlen_diff=1): |
| 781 """The subnets which join to make the current subnet. | 1089 """The subnets which join to make the current subnet. |
| 782 | 1090 |
| 783 In the case that self contains only one IP | 1091 In the case that self contains only one IP |
| 784 (self._prefixlen == 32), return a list with just ourself. | 1092 (self._prefixlen == 32), return a list with just ourself. |
| 785 | 1093 |
| 786 Args: | 1094 Args: |
| 787 prefixlen_diff: An integer, the amount the prefix length | 1095 prefixlen_diff: An integer, the amount the prefix length |
| 788 should be increased by. Given a /24 network and a | 1096 should be increased by. Given a /24 network and a |
| 789 prefixlen_diff of 3, for example, 8 subnets of size /27 | 1097 prefixlen_diff of 3, for example, 8 subnets of size /27 |
| 790 will be returned. The default value of 1 splits the | 1098 will be returned. The default value of 1 splits the |
| 791 current network into two halves. | 1099 current network into two halves. |
| 792 | 1100 |
| 793 Returns: | 1101 Returns: |
| 794 A list of IPv4 objects. | 1102 A list of IPv4 network objects. |
| 795 | 1103 |
| 796 Raises: | 1104 Raises: |
| 797 PrefixlenDiffInvalidError: The prefixlen_diff is too small | 1105 PrefixlenDiffInvalidError: The prefixlen_diff is too small |
| 798 or too large. | 1106 or too large. |
| 799 | 1107 |
| 800 """ | 1108 """ |
| 801 if self._prefixlen == 32: | 1109 if self._prefixlen == 32: |
| 802 return [self] | 1110 return [self] |
| 803 | 1111 |
| 804 if prefixlen_diff < 0: | 1112 if prefixlen_diff < 0: |
| 805 raise PrefixlenDiffInvalidError('prefix length diff must be > 0') | 1113 raise PrefixlenDiffInvalidError('prefix length diff must be > 0') |
| 806 new_prefixlen = self.prefixlen + prefixlen_diff | 1114 new_prefixlen = self._prefixlen + prefixlen_diff |
| 807 | 1115 |
| 808 if not self._is_valid_netmask(str(new_prefixlen)): | 1116 if not self._is_valid_netmask(str(new_prefixlen)): |
| 809 raise PrefixlenDiffInvalidError( | 1117 raise PrefixlenDiffInvalidError( |
| 810 'prefix length diff %d is invalid for netblock %s' % ( | 1118 'prefix length diff %d is invalid for netblock %s' % ( |
| 811 new_prefixlen, str(self))) | 1119 new_prefixlen, str(self))) |
| 812 | 1120 |
| 813 first = IPv4( | 1121 first = IPv4('%s/%s' % (str(self.network), |
| 814 self._string_from_ip_int(self.network) + '/' + | 1122 str(self._prefixlen + prefixlen_diff))) |
| 815 str(self._prefixlen + prefixlen_diff)) | |
| 816 subnets = [first] | 1123 subnets = [first] |
| 817 current = first | 1124 current = first |
| 818 while True: | 1125 while True: |
| 819 broadcast = current.broadcast | 1126 broadcast = current.broadcast |
| 820 if broadcast == self.broadcast: | 1127 if broadcast == self.broadcast: |
| 821 break | 1128 break |
| 822 current = IPv4(self._string_from_ip_int(broadcast + 1) + '/' + | 1129 current = IPv4('%d/%s' % (int(broadcast) + 1, str(new_prefixlen))) |
| 823 str(new_prefixlen)) | |
| 824 subnets.append(current) | 1130 subnets.append(current) |
| 825 | 1131 |
| 826 return subnets | 1132 return subnets |
| 827 | 1133 |
| 828 def supernet(self, prefixlen_diff=1): | 1134 def supernet(self, prefixlen_diff=1): |
| 829 """The supernet containing the current network. | 1135 """The supernet containing the current network. |
| 830 | 1136 |
| 831 Args: | 1137 Args: |
| 832 prefixlen_diff: An integer, the amount the prefix length of | 1138 prefixlen_diff: An integer, the amount the prefix length of |
| 833 the network should be decreased by. For example, given a | 1139 the network should be decreased by. For example, given a |
| 834 /24 network and a prefixlen_diff of 3, a supernet with a | 1140 /24 network and a prefixlen_diff of 3, a supernet with a |
| 835 /21 netmask is returned. | 1141 /21 netmask is returned. |
| 836 | 1142 |
| 837 Returns: | 1143 Returns: |
| 838 An IPv4 object. | 1144 An IPv4 network object. |
| 839 | 1145 |
| 840 Raises: | 1146 Raises: |
| 841 PrefixlenDiffInvalidError: If | 1147 PrefixlenDiffInvalidError: If |
| 842 self.prefixlen - prefixlen_diff < 0. I.e., you have a | 1148 self.prefixlen - prefixlen_diff < 0. I.e., you have a |
| 843 negative prefix length. | 1149 negative prefix length. |
| 844 | 1150 |
| 845 """ | 1151 """ |
| 846 if self.prefixlen == 0: | 1152 if self._prefixlen == 0: |
| 847 return self | 1153 return self |
| 848 if self.prefixlen - prefixlen_diff < 0: | 1154 if self.prefixlen - prefixlen_diff < 0: |
| 849 raise PrefixlenDiffInvalidError( | 1155 raise PrefixlenDiffInvalidError( |
| 850 'current prefixlen is %d, cannot have a prefixlen_diff of %d' % | 1156 'current prefixlen is %d, cannot have a prefixlen_diff of %d' % |
| 851 (self.prefixlen, prefixlen_diff)) | 1157 (self.prefixlen, prefixlen_diff)) |
| 852 return IPv4(self.ip_ext + '/' + str(self.prefixlen - prefixlen_diff)) | 1158 return IPv4('%s/%s' % (str(self.network), |
| 1159 str(self.prefixlen - prefixlen_diff))) |
| 853 | 1160 |
| 854 @property | 1161 @property |
| 855 def is_private(self): | 1162 def is_private(self): |
| 856 """Test if this address is allocated for private networks. | 1163 """Test if this address is allocated for private networks. |
| 857 | 1164 |
| 858 Returns: | 1165 Returns: |
| 859 A boolean, True if the address is reserved per RFC 1918. | 1166 A boolean, True if the address is reserved per RFC 1918. |
| 860 | 1167 |
| 861 """ | 1168 """ |
| 862 return (self in IPv4('10.0.0.0/8') or | 1169 return (self in IPv4('10.0.0.0/8') or |
| (...skipping 23 matching lines...) Expand all Loading... |
| 886 | 1193 |
| 887 @property | 1194 @property |
| 888 def is_link_local(self): | 1195 def is_link_local(self): |
| 889 """Test if the address is reserved for link-local. | 1196 """Test if the address is reserved for link-local. |
| 890 | 1197 |
| 891 Returns: | 1198 Returns: |
| 892 A boolean, True if the address is link-local per RFC 3927. | 1199 A boolean, True if the address is link-local per RFC 3927. |
| 893 | 1200 |
| 894 """ | 1201 """ |
| 895 return self in IPv4('169.254.0.0/16') | 1202 return self in IPv4('169.254.0.0/16') |
| 896 | |
| 897 @property | |
| 898 def version(self): | |
| 899 return self._version | |
| 900 | |
| 901 @property | |
| 902 def max_prefixlen(self): | |
| 903 return self._max_prefixlen | |
| 904 | |
| 905 @property | |
| 906 def packed(self): | |
| 907 """The binary representation of this address.""" | |
| 908 return struct.pack('!I', self.ip) | |
| 909 | 1203 |
| 910 def _is_hostmask(self, ip_str): | 1204 def _is_hostmask(self, ip_str): |
| 911 """Test if the IP string is a hostmask (rather than a netmask). | 1205 """Test if the IP string is a hostmask (rather than a netmask). |
| 912 | 1206 |
| 913 Args: | 1207 Args: |
| 914 ip_str: A string, the potential hostmask. | 1208 ip_str: A string, the potential hostmask. |
| 915 | 1209 |
| 916 Returns: | 1210 Returns: |
| 917 A boolean, True if the IP string is a hostmask. | 1211 A boolean, True if the IP string is a hostmask. |
| 918 | 1212 |
| 919 """ | 1213 """ |
| 920 parts = [int(x) for x in ip_str.split('.')] | 1214 parts = [int(x) for x in ip_str.split('.')] |
| 921 if parts[0] < parts[-1]: | 1215 if parts[0] < parts[-1]: |
| 922 return True | 1216 return True |
| 923 return False | 1217 return False |
| 924 | |
| 925 def _ip_int_from_string(self, ip_str): | |
| 926 """Turn the given IP string into an integer for comparison. | |
| 927 | |
| 928 Args: | |
| 929 ip_str: A string, the IP address. | |
| 930 | |
| 931 Returns: | |
| 932 The IP address as an integer. | |
| 933 | |
| 934 """ | |
| 935 packed_ip = 0 | |
| 936 for oc in ip_str.split('.'): | |
| 937 packed_ip = (packed_ip << 8) | int(oc) | |
| 938 return packed_ip | |
| 939 | |
| 940 def _string_from_ip_int(self, ip_int): | |
| 941 """Turns a 32-bit integer into dotted decimal notation. | |
| 942 | |
| 943 Args: | |
| 944 ip_int: An integer, the IP address. | |
| 945 | |
| 946 Returns: | |
| 947 The IP address as a string in dotted decimal notation. | |
| 948 | |
| 949 """ | |
| 950 octets = [] | |
| 951 for _ in xrange(4): | |
| 952 octets.insert(0, str(ip_int & 0xFF)) | |
| 953 ip_int >>= 8 | |
| 954 return '.'.join(octets) | |
| 955 | |
| 956 def _is_valid_ip(self, ip_str): | |
| 957 """Validate the dotted decimal notation IP/netmask string. | |
| 958 | |
| 959 Args: | |
| 960 ip_str: A string, the IP address. | |
| 961 | |
| 962 Returns: | |
| 963 A boolean, True if the string is a valid dotted decimal IP | |
| 964 string. | |
| 965 | |
| 966 """ | |
| 967 octets = ip_str.split('.') | |
| 968 if len(octets) == 1: | |
| 969 # We have an integer rather than a dotted decimal IP. | |
| 970 try: | |
| 971 return int(ip_str) >= 0 and int(ip_str) <= self._ALL_ONES | |
| 972 except ValueError: | |
| 973 return False | |
| 974 | |
| 975 if len(octets) != 4: | |
| 976 return False | |
| 977 | |
| 978 for octet in octets: | |
| 979 try: | |
| 980 if not 0 <= int(octet) <= 255: | |
| 981 return False | |
| 982 except ValueError: | |
| 983 return False | |
| 984 return True | |
| 985 | 1218 |
| 986 def _is_valid_netmask(self, netmask): | 1219 def _is_valid_netmask(self, netmask): |
| 987 """Verify that the netmask is valid. | 1220 """Verify that the netmask is valid. |
| 988 | 1221 |
| 989 Args: | 1222 Args: |
| 990 netmask: A string, either a prefix or dotted decimal | 1223 netmask: A string, either a prefix or dotted decimal |
| 991 netmask. | 1224 netmask. |
| 992 | 1225 |
| 993 Returns: | 1226 Returns: |
| 994 A boolean, True if the prefix represents a valid IPv4 | 1227 A boolean, True if the prefix represents a valid IPv4 |
| 995 netmask. | 1228 netmask. |
| 996 | 1229 |
| 997 """ | 1230 """ |
| 998 if len(netmask.split('.')) == 4: | 1231 if len(netmask.split('.')) == 4: |
| 999 return self._is_valid_ip(netmask) | 1232 return self._is_valid_ip(netmask) |
| 1000 try: | 1233 try: |
| 1001 netmask = int(netmask) | 1234 netmask = int(netmask) |
| 1002 except ValueError: | 1235 except ValueError: |
| 1003 return False | 1236 return False |
| 1004 return 0 <= netmask <= 32 | 1237 return 0 <= netmask <= 32 |
| 1005 | 1238 |
| 1006 # backwards compatibility | 1239 # backwards compatibility |
| 1007 Subnet = subnet | 1240 Subnet = subnet |
| 1008 Supernet = supernet | 1241 Supernet = supernet |
| 1009 IsRFC1918 = lambda self: self.is_private | 1242 IsRFC1918 = lambda self: self.is_private |
| 1010 IsMulticast = lambda self: self.is_multicast | 1243 IsMulticast = lambda self: self.is_multicast |
| 1011 IsLoopback = lambda self: self.is_loopback | 1244 IsLoopback = lambda self: self.is_loopback |
| 1012 IsLinkLocal = lambda self: self.is_link_local | 1245 IsLinkLocal = lambda self: self.is_link_local |
| 1013 | 1246 |
| 1014 class IPv6(BaseIP): | 1247 |
| 1015 | 1248 class BaseV6(object): |
| 1016 """This class respresents and manipulates 128-bit IPv6 addresses. | 1249 |
| 1017 | 1250 """Base IPv6 object. |
| 1018 Attributes: [examples for IPv6('2001:658:22A:CAFE:200::1/64')] | 1251 |
| 1019 .ip: 42540616829182469433547762482097946625L | 1252 The following methods are used by IPv6 objects in both single IP |
| 1020 .ip_ext: '2001:658:22a:cafe:200::1' | 1253 addresses and networks. |
| 1021 .ip_ext_full: '2001:0658:022a:cafe:0200:0000:0000:0001' | |
| 1022 .network: 42540616829182469433403647294022090752L | |
| 1023 .network_ext: '2001:658:22a:cafe::' | |
| 1024 .hostmask: 18446744073709551615L | |
| 1025 .hostmask_ext: '::ffff:ffff:ffff:ffff' | |
| 1026 .broadcast: 42540616829182469451850391367731642367L | |
| 1027 .broadcast_ext: '2001:658:22a:cafe:ffff:ffff:ffff:ffff' | |
| 1028 .netmask: 340282366920938463444927863358058659840L | |
| 1029 .netmask_ext: 64 | |
| 1030 .prefixlen: 64 | |
| 1031 | 1254 |
| 1032 """ | 1255 """ |
| 1033 | 1256 |
| 1034 _ALL_ONES = (2**128) - 1 | 1257 _ALL_ONES = (2**128) - 1 |
| 1035 | 1258 |
| 1036 def __init__(self, ipaddr): | 1259 def __init__(self, address): |
| 1037 """Instantiate a new IPv6 object. | |
| 1038 | |
| 1039 Args: | |
| 1040 ipaddr: A string or integer representing the IP or the IP | |
| 1041 and prefix/netmask. | |
| 1042 '2001:4860::/128' | |
| 1043 '2001:4860:0000:0000:0000:0000:0000:0000/128' | |
| 1044 '2001:4860::' | |
| 1045 are all functionally the same in IPv6. That is to say, | |
| 1046 failing to provide a subnetmask will create an object with | |
| 1047 a mask of /128. | |
| 1048 | |
| 1049 Additionally, an integer can be passed, so | |
| 1050 IPv6('2001:4860::') == | |
| 1051 IPv6(42541956101370907050197289607612071936L). | |
| 1052 or, more generally | |
| 1053 IPv6(IPv6('2001:4860::').ip) == IPv6('2001:4860::') | |
| 1054 | |
| 1055 Raises: | |
| 1056 IPv6IpValidationError: If ipaddr isn't a valid IPv6 address. | |
| 1057 IPv6NetmaskValidationError: If the netmask isn't valid for | |
| 1058 an IPv6 address. | |
| 1059 IPRangeError: If range of IP addresses is not valid. | |
| 1060 | |
| 1061 """ | |
| 1062 BaseIP.__init__(self) | |
| 1063 self._version = 6 | 1260 self._version = 6 |
| 1064 self._max_prefixlen = 128 | 1261 self._max_prefixlen = 128 |
| 1065 | 1262 |
| 1066 # Efficient constructor from integer. | 1263 def _ip_int_from_string(self, ip_str=None): |
| 1067 if isinstance(ipaddr, long) or isinstance(ipaddr, int): | 1264 """Turn an IPv6 ip_str into an integer. |
| 1068 self.ip = ipaddr | 1265 |
| 1069 self._prefixlen = 128 | 1266 Args: |
| 1070 self.netmask = self._ALL_ONES | 1267 ip_str: A string, the IPv6 ip_str. |
| 1071 if ipaddr < 0 or ipaddr > self._ALL_ONES: | 1268 |
| 1072 raise IPv6IpValidationError(ipaddr) | 1269 Returns: |
| 1073 return | 1270 A long, the IPv6 ip_str. |
| 1074 | |
| 1075 # Constructing from a packed address | |
| 1076 if _compat_has_real_bytes: | |
| 1077 if isinstance(ipaddr, bytes) and len(ipaddr) == 16: | |
| 1078 tmp = struct.unpack('!QQ', ipaddr) | |
| 1079 self.ip = (tmp[0] << 64) | tmp[1] | |
| 1080 self._prefixlen = 128 | |
| 1081 self.netmask = self._ALL_ONES | |
| 1082 return | |
| 1083 | |
| 1084 addr_str = str(ipaddr) | |
| 1085 if not addr_str: | |
| 1086 raise IPv6IpValidationError('') | |
| 1087 | |
| 1088 # Construct network from a range e.g. ::-::15 | |
| 1089 addr = addr_str.split('-') | |
| 1090 | |
| 1091 if len(addr) > 2: | |
| 1092 raise IPv6IpValidationError(ipaddr) | |
| 1093 | |
| 1094 if len(addr) == 2: | |
| 1095 if not self._is_valid_ip(addr[0]): | |
| 1096 raise IPv6IpValidationError(addr[0]) | |
| 1097 | |
| 1098 if not self._is_valid_ip(addr[1]): | |
| 1099 raise IPv6IpValidationError(addr[1]) | |
| 1100 | |
| 1101 self.ip = self._ip_int_from_string(addr[0]) | |
| 1102 | |
| 1103 last = self._ip_int_from_string(addr[1]) | |
| 1104 if last < self.ip: | |
| 1105 raise IPRangeError("last address should be greater than " | |
| 1106 "first.") | |
| 1107 bits = _highest_one_bit(last - self.ip) | |
| 1108 self._prefixlen = self.max_prefixlen - bits | |
| 1109 self.netmask = self._ip_int_from_prefix(self._prefixlen) | |
| 1110 if self._ip_int_from_string(IPv6(self)[-1]) != last: | |
| 1111 raise IPRangeError("range %s is not on a network byte " | |
| 1112 "boundary." % ipaddr) | |
| 1113 return | |
| 1114 | |
| 1115 # Assume input argument to be string or any object representation | |
| 1116 # which converts into a formatted IP prefix string. | |
| 1117 addr = addr_str.split('/') | |
| 1118 if len(addr) > 1: | |
| 1119 if self._is_valid_netmask(addr[1]): | |
| 1120 self._prefixlen = int(addr[1]) | |
| 1121 else: | |
| 1122 raise IPv6NetmaskValidationError(addr[1]) | |
| 1123 else: | |
| 1124 self._prefixlen = 128 | |
| 1125 | |
| 1126 self.netmask = self._ip_int_from_prefix(self._prefixlen) | |
| 1127 | |
| 1128 if not self._is_valid_ip(addr[0]): | |
| 1129 raise IPv6IpValidationError(addr[0]) | |
| 1130 | |
| 1131 self.ip = self._ip_int_from_string(addr[0]) | |
| 1132 | |
| 1133 @property | |
| 1134 def ip_ext_full(self): | |
| 1135 """Returns the expanded version of the IPv6 string.""" | |
| 1136 return self._explode_shorthand_ip_string(self.ip_ext) | |
| 1137 | |
| 1138 def _set_prefix(self, prefixlen): | |
| 1139 """Change the prefix length. | |
| 1140 | |
| 1141 Args: | |
| 1142 prefixlen: An integer, the new prefix length. | |
| 1143 | |
| 1144 Raises: | |
| 1145 IPv6NetmaskValidationError: If prefixlen is out of bounds. | |
| 1146 | |
| 1147 """ | |
| 1148 if not 0 <= prefixlen <= 128: | |
| 1149 raise IPv6NetmaskValidationError(prefixlen) | |
| 1150 self._prefixlen = prefixlen | |
| 1151 self.netmask = self._ip_int_from_prefix(self.prefixlen) | |
| 1152 | |
| 1153 def subnet(self, prefixlen_diff=1): | |
| 1154 """The subnets which join to make the current subnet. | |
| 1155 | |
| 1156 In the case that self contains only one IP | |
| 1157 (self._prefixlen == 128), return a list with just ourself. | |
| 1158 | |
| 1159 Args: | |
| 1160 prefixlen_diff: An integer, the amount the prefix length | |
| 1161 should be increased by. | |
| 1162 | |
| 1163 Returns: | |
| 1164 A list of IPv6 objects. | |
| 1165 | |
| 1166 Raises: | |
| 1167 PrefixlenDiffInvalidError: The prefixlen_diff is too small | |
| 1168 or too large. | |
| 1169 | |
| 1170 """ | |
| 1171 # Preserve original functionality (return [self] if | |
| 1172 # self.prefixlen == 128). | |
| 1173 if self.prefixlen == 128: | |
| 1174 return [self] | |
| 1175 | |
| 1176 if prefixlen_diff < 0: | |
| 1177 raise PrefixlenDiffInvalidError('Prefix length diff must be > 0') | |
| 1178 new_prefixlen = self.prefixlen + prefixlen_diff | |
| 1179 if not self._is_valid_netmask(str(new_prefixlen)): | |
| 1180 raise PrefixlenDiffInvalidError( | |
| 1181 'Prefix length diff %d is invalid for netblock %s' % ( | |
| 1182 new_prefixlen, str(self))) | |
| 1183 first = IPv6( | |
| 1184 self._string_from_ip_int(self.network) + '/' + | |
| 1185 str(self._prefixlen + prefixlen_diff)) | |
| 1186 subnets = [first] | |
| 1187 current = first | |
| 1188 while True: | |
| 1189 broadcast = current.broadcast | |
| 1190 if current.broadcast == self.broadcast: | |
| 1191 break | |
| 1192 current = IPv6(self._string_from_ip_int(broadcast + 1) + '/' + | |
| 1193 str(new_prefixlen)) | |
| 1194 subnets.append(current) | |
| 1195 | |
| 1196 return subnets | |
| 1197 | |
| 1198 def supernet(self, prefixlen_diff=1): | |
| 1199 """The supernet containing the current network. | |
| 1200 | |
| 1201 Args: | |
| 1202 prefixlen_diff: An integer, the amount the prefix length of the | |
| 1203 network should be decreased by. For example, given a /96 | |
| 1204 network and a prefixlen_diff of 3, a supernet with a /93 | |
| 1205 netmask is returned. | |
| 1206 | |
| 1207 Returns: | |
| 1208 An IPv6 object. | |
| 1209 | |
| 1210 Raises: | |
| 1211 PrefixlenDiffInvalidError: If | |
| 1212 self._prefixlen - prefixlen_diff < 0. I.e., you have a | |
| 1213 negative prefix length. | |
| 1214 | |
| 1215 """ | |
| 1216 if self.prefixlen == 0: | |
| 1217 return self | |
| 1218 if self.prefixlen - prefixlen_diff < 0: | |
| 1219 raise PrefixlenDiffInvalidError( | |
| 1220 'current prefixlen is %d, cannot have a prefixlen_diff of %d' % | |
| 1221 (self.prefixlen, prefixlen_diff)) | |
| 1222 return IPv6(self.ip_ext + '/' + str(self.prefixlen - prefixlen_diff)) | |
| 1223 | |
| 1224 @property | |
| 1225 def is_multicast(self): | |
| 1226 """Test if the address is reserved for multicast use. | |
| 1227 | |
| 1228 Returns: | |
| 1229 A boolean, True if the address is a multicast address. | |
| 1230 See RFC 2373 2.7 for details. | |
| 1231 | |
| 1232 """ | |
| 1233 return self in IPv6('ff00::/8') | |
| 1234 | |
| 1235 @property | |
| 1236 def is_unspecified(self): | |
| 1237 """Test if the address is unspecified. | |
| 1238 | |
| 1239 Returns: | |
| 1240 A boolean, True if this is the unspecified address as defined in | |
| 1241 RFC 2373 2.5.2. | |
| 1242 | |
| 1243 """ | |
| 1244 return self == IPv6('::') | |
| 1245 | |
| 1246 @property | |
| 1247 def is_loopback(self): | |
| 1248 """Test if the address is a loopback adddress. | |
| 1249 | |
| 1250 Returns: | |
| 1251 A boolean, True if the address is a loopback address as defined in | |
| 1252 RFC 2373 2.5.3. | |
| 1253 | |
| 1254 """ | |
| 1255 return self == IPv6('::1') | |
| 1256 | |
| 1257 @property | |
| 1258 def is_link_local(self): | |
| 1259 """Test if the address is reserved for link-local. | |
| 1260 | |
| 1261 Returns: | |
| 1262 A boolean, True if the address is reserved per RFC 4291. | |
| 1263 | |
| 1264 """ | |
| 1265 return self in IPv6('fe80::/10') | |
| 1266 | |
| 1267 @property | |
| 1268 def is_site_local(self): | |
| 1269 """Test if the address is reserved for site-local. | |
| 1270 | |
| 1271 Note that the site-local address space has been deprecated by RFC 3879. | |
| 1272 Use is_private to test if this address is in the space of unique local | |
| 1273 addresses as defined by RFC 4193. | |
| 1274 | |
| 1275 Returns: | |
| 1276 A boolean, True if the address is reserved per RFC 3513 2.5.6. | |
| 1277 | |
| 1278 """ | |
| 1279 return self in IPv6('fec0::/10') | |
| 1280 | |
| 1281 @property | |
| 1282 def is_private(self): | |
| 1283 """Test if this address is allocated for private networks. | |
| 1284 | |
| 1285 Returns: | |
| 1286 A boolean, True if the address is reserved per RFC 4193. | |
| 1287 | |
| 1288 """ | |
| 1289 return self in IPv6('fc00::/7') | |
| 1290 | |
| 1291 @property | |
| 1292 def version(self): | |
| 1293 return self._version | |
| 1294 | |
| 1295 @property | |
| 1296 def max_prefixlen(self): | |
| 1297 return self._max_prefixlen | |
| 1298 | |
| 1299 @property | |
| 1300 def packed(self): | |
| 1301 """The binary representation of this address.""" | |
| 1302 return struct.pack('!QQ', self.ip >> 64, self.ip & (2**64 - 1)) | |
| 1303 | |
| 1304 def _is_shorthand_ip(self, ip_str=None): | |
| 1305 """Determine if the address is shortened. | |
| 1306 | |
| 1307 Args: | |
| 1308 ip_str: A string, the IPv6 address. | |
| 1309 | |
| 1310 Returns: | |
| 1311 A boolean, True if the address is shortened. | |
| 1312 | |
| 1313 """ | |
| 1314 if ip_str.count('::') == 1: | |
| 1315 return True | |
| 1316 return False | |
| 1317 | |
| 1318 def _explode_shorthand_ip_string(self, ip_str): | |
| 1319 """Expand a shortened IPv6 address. | |
| 1320 | |
| 1321 Args: | |
| 1322 ip_str: A string, the IPv6 address. | |
| 1323 | |
| 1324 Returns: | |
| 1325 A string, the expanded IPv6 address. | |
| 1326 | |
| 1327 """ | |
| 1328 if self._is_shorthand_ip(ip_str): | |
| 1329 new_ip = [] | |
| 1330 hextet = ip_str.split('::') | |
| 1331 sep = len(hextet[0].split(':')) + len(hextet[1].split(':')) | |
| 1332 new_ip = hextet[0].split(':') | |
| 1333 | |
| 1334 for _ in xrange(8 - sep): | |
| 1335 new_ip.append('0000') | |
| 1336 new_ip += hextet[1].split(':') | |
| 1337 | |
| 1338 # Now need to make sure every hextet is 4 lower case characters. | |
| 1339 # If a hextet is < 4 characters, we've got missing leading 0's. | |
| 1340 ret_ip = [] | |
| 1341 for hextet in new_ip: | |
| 1342 ret_ip.append(('0' * (4 - len(hextet)) + hextet).lower()) | |
| 1343 return ':'.join(ret_ip) | |
| 1344 # We've already got a longhand ip_str. | |
| 1345 return ip_str | |
| 1346 | |
| 1347 def _is_valid_ip(self, ip_str=None): | |
| 1348 """Ensure we have a valid IPv6 address. | |
| 1349 | |
| 1350 Probably not as exhaustive as it should be. | |
| 1351 | |
| 1352 Args: | |
| 1353 ip_str: A string, the IPv6 address. | |
| 1354 | |
| 1355 Returns: | |
| 1356 A boolean, True if this is a valid IPv6 address. | |
| 1357 | 1271 |
| 1358 """ | 1272 """ |
| 1359 if not ip_str: | 1273 if not ip_str: |
| 1360 ip_str = self.ip_ext | 1274 ip_str = str(self.ip) |
| 1361 | |
| 1362 # We need to have at least one ':'. | |
| 1363 if ':' not in ip_str: | |
| 1364 return False | |
| 1365 | |
| 1366 # We can only have one '::' shortener. | |
| 1367 if ip_str.count('::') > 1: | |
| 1368 return False | |
| 1369 | |
| 1370 # '::' should be encompassed by start, digits or end. | |
| 1371 if ':::' in ip_str: | |
| 1372 return False | |
| 1373 | |
| 1374 # A single colon can neither start nor end an address. | |
| 1375 if ((ip_str.startswith(':') and not ip_str.startswith('::')) or | |
| 1376 (ip_str.endswith(':') and not ip_str.endswith('::'))): | |
| 1377 return False | |
| 1378 | |
| 1379 # If we have no concatenation, we need to have 8 fields with 7 ':'. | |
| 1380 if '::' not in ip_str and ip_str.count(':') != 7: | |
| 1381 # We might have an IPv4 mapped address. | |
| 1382 if ip_str.count('.') != 3: | |
| 1383 return False | |
| 1384 | |
| 1385 ip_str = self._explode_shorthand_ip_string(ip_str) | |
| 1386 | |
| 1387 # Now that we have that all squared away, let's check that each of the | |
| 1388 # hextets are between 0x0 and 0xFFFF. | |
| 1389 for hextet in ip_str.split(':'): | |
| 1390 if hextet.count('.') == 3: | |
| 1391 # If we have an IPv4 mapped address, the IPv4 portion has to be | |
| 1392 # at the end of the IPv6 portion. | |
| 1393 if not ip_str.split(':')[-1] == hextet: | |
| 1394 return False | |
| 1395 try: | |
| 1396 IPv4(hextet) | |
| 1397 except IPv4IpValidationError: | |
| 1398 return False | |
| 1399 elif int(hextet, 16) < 0x0 or int(hextet, 16) > 0xFFFF: | |
| 1400 return False | |
| 1401 return True | |
| 1402 | |
| 1403 def _is_valid_netmask(self, prefixlen): | |
| 1404 """Verify that the netmask/prefixlen is valid. | |
| 1405 | |
| 1406 Args: | |
| 1407 prefixlen: A string, the netmask in prefix length format. | |
| 1408 | |
| 1409 Returns: | |
| 1410 A boolean, True if the prefix represents a valid IPv6 | |
| 1411 netmask. | |
| 1412 | |
| 1413 """ | |
| 1414 try: | |
| 1415 prefixlen = int(prefixlen) | |
| 1416 except ValueError: | |
| 1417 return False | |
| 1418 return 0 <= prefixlen <= 128 | |
| 1419 | |
| 1420 def _ip_int_from_string(self, ip_str=None): | |
| 1421 """Turn an IPv6 address into an integer. | |
| 1422 | |
| 1423 Args: | |
| 1424 ip_str: A string, the IPv6 address. | |
| 1425 | |
| 1426 Returns: | |
| 1427 A long, the IPv6 address. | |
| 1428 | |
| 1429 """ | |
| 1430 if not ip_str: | |
| 1431 ip_str = self.ip_ext | |
| 1432 | 1275 |
| 1433 ip_int = 0 | 1276 ip_int = 0 |
| 1434 | 1277 |
| 1435 fields = self._explode_shorthand_ip_string(ip_str).split(':') | 1278 fields = self._explode_shorthand_ip_string(ip_str).split(':') |
| 1436 | 1279 |
| 1437 # Do we have an IPv4 mapped (::ffff:a.b.c.d) or compact (::a.b.c.d) | 1280 # Do we have an IPv4 mapped (::ffff:a.b.c.d) or compact (::a.b.c.d) |
| 1438 # address? | 1281 # ip_str? |
| 1439 if fields[-1].count('.') == 3: | 1282 if fields[-1].count('.') == 3: |
| 1440 ipv4_string = fields.pop() | 1283 ipv4_string = fields.pop() |
| 1441 ipv4_int = IPv4(ipv4_string).ip | 1284 ipv4_int = IPv4(ipv4_string)._ip |
| 1442 octets = [] | 1285 octets = [] |
| 1443 for _ in xrange(2): | 1286 for _ in xrange(2): |
| 1444 octets.append(hex(ipv4_int & 0xFFFF).lstrip('0x').rstrip('L')) | 1287 octets.append(hex(ipv4_int & 0xFFFF).lstrip('0x').rstrip('L')) |
| 1445 ipv4_int >>= 16 | 1288 ipv4_int >>= 16 |
| 1446 fields.extend(reversed(octets)) | 1289 fields.extend(reversed(octets)) |
| 1447 | 1290 |
| 1448 for field in fields: | 1291 for field in fields: |
| 1449 ip_int = (ip_int << 16) + int(field, 16) | 1292 ip_int = (ip_int << 16) + int(field, 16) |
| 1450 | 1293 |
| 1451 return ip_int | 1294 return ip_int |
| (...skipping 52 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1504 ip_int: An integer, the IP address. | 1347 ip_int: An integer, the IP address. |
| 1505 | 1348 |
| 1506 Returns: | 1349 Returns: |
| 1507 A string, the hexadecimal representation of the address. | 1350 A string, the hexadecimal representation of the address. |
| 1508 | 1351 |
| 1509 Raises: | 1352 Raises: |
| 1510 ValueError: The address is bigger than 128 bits of all ones. | 1353 ValueError: The address is bigger than 128 bits of all ones. |
| 1511 | 1354 |
| 1512 """ | 1355 """ |
| 1513 if not ip_int and ip_int != 0: | 1356 if not ip_int and ip_int != 0: |
| 1514 ip_int = self.ip | 1357 ip_int = int(self._ip) |
| 1515 | 1358 |
| 1516 if ip_int > self._ALL_ONES: | 1359 if ip_int > self._ALL_ONES: |
| 1517 raise ValueError('IPv6 address is too large') | 1360 raise ValueError('IPv6 address is too large') |
| 1518 | 1361 |
| 1519 hex_str = '%032x' % ip_int | 1362 hex_str = '%032x' % ip_int |
| 1520 hextets = [] | 1363 hextets = [] |
| 1521 for x in range(0, 32, 4): | 1364 for x in range(0, 32, 4): |
| 1522 hextets.append('%x' % int(hex_str[x:x+4], 16)) | 1365 hextets.append('%x' % int(hex_str[x:x+4], 16)) |
| 1523 | 1366 |
| 1524 hextets = self._compress_hextets(hextets) | 1367 hextets = self._compress_hextets(hextets) |
| 1525 return ':'.join(hextets) | 1368 return ':'.join(hextets) |
| 1526 | 1369 |
| 1527 @property | 1370 def _explode_shorthand_ip_string(self, ip_str=None): |
| 1528 def netmask_ext(self): | 1371 """Expand a shortened IPv6 address. |
| 1529 """IPv6 extended netmask. | 1372 |
| 1530 | 1373 Args: |
| 1531 We don't deal with netmasks in IPv6 like we do in IPv4. This is | 1374 ip_str: A string, the IPv6 address. |
| 1532 here strictly for IPv4 compatibility. We simply return the | 1375 |
| 1533 prefix length. | 1376 Returns: |
| 1534 | 1377 A string, the expanded IPv6 address. |
| 1535 Returns: | 1378 |
| 1536 An integer. | 1379 """ |
| 1537 | 1380 if not ip_str: |