summaryrefslogtreecommitdiff
path: root/trunk/ipaddr.py
diff options
context:
space:
mode:
Diffstat (limited to 'trunk/ipaddr.py')
-rw-r--r--trunk/ipaddr.py194
1 files changed, 80 insertions, 114 deletions
diff --git a/trunk/ipaddr.py b/trunk/ipaddr.py
index 30768d9..c9aee63 100644
--- a/trunk/ipaddr.py
+++ b/trunk/ipaddr.py
@@ -839,8 +839,8 @@ class _BaseNet(_IPAddrBase):
"""
return (self._version, self.network, self.netmask)
- def _ip_int_from_prefix(self, prefixlen=None):
- """Turn the prefix length netmask into a int for comparison.
+ def _ip_int_from_prefix(self, prefixlen):
+ """Turn the prefix length into a bitwise netmask.
Args:
prefixlen: An integer, the prefix length.
@@ -849,42 +849,90 @@ class _BaseNet(_IPAddrBase):
An integer.
"""
- if not prefixlen and prefixlen != 0:
- prefixlen = self._prefixlen
return self._ALL_ONES ^ (self._ALL_ONES >> prefixlen)
- def _prefix_from_ip_int(self, ip_int, mask=32):
- """Return prefix length from the decimal netmask.
+ def _prefix_from_ip_int(self, ip_int):
+ """Return prefix length from a bitwise netmask.
Args:
- ip_int: An integer, the IP address.
- mask: The netmask. Defaults to 32.
+ ip_int: An integer, the netmask in expanded bitwise format.
Returns:
An integer, the prefix length.
+ Raises:
+ NetmaskValueError: If the input is not a valid netmask.
+
"""
- while mask:
- if ip_int & 1 == 1:
+ prefixlen = self._max_prefixlen
+ while prefixlen:
+ if ip_int & 1:
break
ip_int >>= 1
- mask -= 1
+ prefixlen -= 1
+
+ if ip_int == (1 << prefixlen) - 1:
+ return prefixlen
+ else:
+ raise NetmaskValueError('Bit pattern does not match /1*0*/')
+
+ def _prefix_from_prefix_string(self, prefixlen_str):
+ """Turn a prefix length string into an integer.
+
+ Args:
+ prefixlen_str: A decimal string containing the prefix length.
+
+ Returns:
+ The prefix length as an integer.
- return mask
+ Raises:
+ NetmaskValueError: If the input is malformed or out of range.
+
+ """
+ try:
+ if not _BaseV4._DECIMAL_DIGITS.issuperset(prefixlen_str):
+ raise ValueError
+ prefixlen = int(prefixlen_str)
+ if not (0 <= prefixlen <= self._max_prefixlen):
+ raise ValueError
+ except ValueError:
+ raise NetmaskValueError('%s is not a valid prefix length' %
+ prefixlen_str)
+ return prefixlen
- def _ip_string_from_prefix(self, prefixlen=None):
- """Turn a prefix length into a dotted decimal string.
+ def _prefix_from_ip_string(self, ip_str):
+ """Turn a netmask/hostmask string into a prefix length.
Args:
- prefixlen: An integer, the netmask prefix length.
+ ip_str: A netmask or hostmask, formatted as an IP address.
Returns:
- A string, the dotted decimal netmask string.
+ The prefix length as an integer.
+
+ Raises:
+ NetmaskValueError: If the input is not a netmask or hostmask.
"""
- if not prefixlen:
- prefixlen = self._prefixlen
- return self._string_from_ip_int(self._ip_int_from_prefix(prefixlen))
+ # Parse the netmask/hostmask like an IP address.
+ try:
+ ip_int = self._ip_int_from_string(ip_str)
+ except AddressValueError:
+ raise NetmaskValueError('%s is not a valid netmask' % ip_str)
+
+ # Try matching a netmask (this would be /1*0*/ as a bitwise regexp).
+ # Note that the two ambiguous cases (all-ones and all-zeroes) are
+ # treated as netmasks.
+ try:
+ return self._prefix_from_ip_int(ip_int)
+ except NetmaskValueError:
+ pass
+
+ # Invert the bits, and try matching a /0+1+/ hostmask instead.
+ ip_int ^= self._ALL_ONES
+ try:
+ return self._prefix_from_ip_int(ip_int)
+ except NetmaskValueError:
+ raise NetmaskValueError('%s is not a valid netmask' % ip_str)
def iter_subnets(self, prefixlen_diff=1, new_prefix=None):
"""The subnets which join to make the current subnet.
@@ -927,7 +975,7 @@ class _BaseNet(_IPAddrBase):
raise ValueError('prefix length diff must be > 0')
new_prefixlen = self._prefixlen + prefixlen_diff
- if not self._is_valid_netmask(str(new_prefixlen)):
+ if new_prefixlen > self._max_prefixlen:
raise ValueError(
'prefix length diff %d is invalid for netblock %s' % (
new_prefixlen, str(self)))
@@ -1227,9 +1275,6 @@ class IPv4Network(_BaseV4, _BaseNet):
"""
- # the valid octets for host and netmasks. only useful for IPv4.
- _valid_mask_octets = set((255, 254, 252, 248, 240, 224, 192, 128, 0))
-
def __init__(self, address, strict=False):
"""Instantiate a new IPv4 network object.
@@ -1292,31 +1337,18 @@ class IPv4Network(_BaseV4, _BaseNet):
self.ip = IPv4Address(self._ip)
if len(addr) == 2:
- mask = addr[1].split('.')
- if len(mask) == 4:
- # We have dotted decimal netmask.
- if self._is_valid_netmask(addr[1]):
- self.netmask = IPv4Address(self._ip_int_from_string(
- addr[1]))
- elif self._is_hostmask(addr[1]):
- self.netmask = IPv4Address(
- self._ip_int_from_string(addr[1]) ^ self._ALL_ONES)
- else:
- raise NetmaskValueError('%s is not a valid netmask'
- % addr[1])
-
- self._prefixlen = self._prefix_from_ip_int(int(self.netmask))
- else:
- # We have a netmask in prefix length form.
- if not self._is_valid_netmask(addr[1]):
- raise NetmaskValueError(addr[1])
- self._prefixlen = int(addr[1])
- self.netmask = IPv4Address(self._ip_int_from_prefix(
- self._prefixlen))
+ try:
+ # Check for a netmask in prefix length form.
+ self._prefixlen = self._prefix_from_prefix_string(addr[1])
+ except NetmaskValueError:
+ # Check for a netmask or hostmask in dotted-quad form.
+ # This may raise NetmaskValueError.
+ self._prefixlen = self._prefix_from_ip_string(addr[1])
else:
self._prefixlen = self._max_prefixlen
- self.netmask = IPv4Address(self._ip_int_from_prefix(
- self._prefixlen))
+
+ self.netmask = IPv4Address(self._ip_int_from_prefix(self._prefixlen))
+
if strict:
if self.ip != self.network:
raise ValueError('%s has host bits set' %
@@ -1324,53 +1356,6 @@ class IPv4Network(_BaseV4, _BaseNet):
if self._prefixlen == (self._max_prefixlen - 1):
self.iterhosts = self.__iter__
- def _is_hostmask(self, ip_str):
- """Test if the IP string is a hostmask (rather than a netmask).
-
- Args:
- ip_str: A string, the potential hostmask.
-
- Returns:
- A boolean, True if the IP string is a hostmask.
-
- """
- bits = ip_str.split('.')
- try:
- parts = [int(x) for x in bits if int(x) in self._valid_mask_octets]
- except ValueError:
- return False
- if len(parts) != len(bits):
- return False
- if parts[0] < parts[-1]:
- return True
- return False
-
- def _is_valid_netmask(self, netmask):
- """Verify that the netmask is valid.
-
- Args:
- netmask: A string, either a prefix or dotted decimal
- netmask.
-
- Returns:
- A boolean, True if the prefix represents a valid IPv4
- netmask.
-
- """
- mask = netmask.split('.')
- if len(mask) == 4:
- if [x for x in mask if int(x) not in self._valid_mask_octets]:
- return False
- if [y for idx, y in enumerate(mask) if idx > 0 and
- y > mask[idx - 1]]:
- return False
- return True
- try:
- netmask = int(netmask)
- except ValueError:
- return False
- return 0 <= netmask <= self._max_prefixlen
-
# backwards compatibility
IsRFC1918 = lambda self: self.is_private
IsMulticast = lambda self: self.is_multicast
@@ -1861,10 +1846,8 @@ class IPv6Network(_BaseV6, _BaseNet):
self.ip = IPv6Address(self._ip)
if len(addr) == 2:
- if self._is_valid_netmask(addr[1]):
- self._prefixlen = int(addr[1])
- else:
- raise NetmaskValueError(addr[1])
+ # This may raise NetmaskValueError
+ self._prefixlen = self._prefix_from_prefix_string(addr[1])
else:
self._prefixlen = self._max_prefixlen
@@ -1877,23 +1860,6 @@ class IPv6Network(_BaseV6, _BaseNet):
if self._prefixlen == (self._max_prefixlen - 1):
self.iterhosts = self.__iter__
- def _is_valid_netmask(self, prefixlen):
- """Verify that the netmask/prefixlen is valid.
-
- Args:
- prefixlen: A string, the netmask in prefix length format.
-
- Returns:
- A boolean, True if the prefix represents a valid IPv6
- netmask.
-
- """
- try:
- prefixlen = int(prefixlen)
- except ValueError:
- return False
- return 0 <= prefixlen <= self._max_prefixlen
-
@property
def with_netmask(self):
return self.with_prefixlen