| # Lint as: python2, python3 |
| # Copyright (c) 2014 The Chromium OS Authors. All rights reserved. |
| # Use of this source code is governed by a BSD-style license that can be |
| # found in the LICENSE file. |
| |
| from __future__ import absolute_import |
| from __future__ import division |
| from __future__ import print_function |
| |
| import logging |
| from six.moves import map |
| from six.moves import zip |
| |
| from autotest_lib.client.common_lib import error |
| |
| def from_addr(addr, prefix_len=None): |
| """Build a Netblock object. |
| |
| @param addr: string IP address with optional prefix length |
| (e.g. '192.168.1.1' or '192.168.1.1/24'). If |addr| has no |
| prefix length, then use the |prefix_len| parameter. |
| @param prefix_len: int number of bits forming the IP subnet prefix for |
| |addr|. This value will be preferred to the parsed value if |
| |addr| has a prefix length as well. If |addr| |
| has no prefix length and |prefix_len| is None, then an error |
| will be thrown. |
| |
| """ |
| if addr is None: |
| raise error.TestError('netblock.from_addr() expects non-None addr ' |
| 'parameter.') |
| |
| prefix_sep_count = addr.count('/') |
| if prefix_sep_count > 1: |
| raise error.TestError('Invalid IP address found: "%s".' % addr) |
| |
| if prefix_sep_count == 1: |
| addr_str, prefix_len_str = addr.split('/') |
| else: |
| # No prefix separator. Assume addr looks like '192.168.1.1' |
| addr_str = addr |
| # Rely on passed in |prefix_len| |
| prefix_len_str = None |
| |
| if prefix_len is not None and prefix_len_str is not None: |
| logging.warning('Ignoring parsed prefix length of %s in favor of ' |
| 'passed in value %d', prefix_len_str, prefix_len) |
| elif prefix_len is not None and prefix_len_str is None: |
| pass |
| elif prefix_len is None and prefix_len_str is not None: |
| prefix_len = int(prefix_len_str) |
| else: |
| raise error.TestError('Cannot construct netblock without knowing ' |
| 'prefix length for addr: "%s".' % addr) |
| |
| return Netblock(addr_str, prefix_len) |
| |
| |
| class Netblock(object): |
| """Utility class for transforming netblock address to related strings.""" |
| |
| @staticmethod |
| def _octets_to_addr(octets): |
| """Transform a list of bytes into a string IP address. |
| |
| @param octets list of ints (e.g. [192.168.0.1]). |
| @return string IP address (e.g. '192.168.0.1.'). |
| |
| """ |
| return '.'.join(map(str, octets)) |
| |
| |
| @staticmethod |
| def _int_to_octets(num): |
| """Tranform a 32 bit number into a list of 4 octets. |
| |
| @param num: number to convert to octets. |
| @return list of int values <= 8 bits long. |
| |
| """ |
| return [(num >> s) & 0xff for s in (24, 16, 8, 0)] |
| |
| |
| @property |
| def netblock(self): |
| """@return the IPv4 address/prefix, e.g., '192.168.0.1/24'.""" |
| return '/'.join([self._octets_to_addr(self._octets), |
| str(self.prefix_len)]) |
| |
| |
| @property |
| def netmask(self): |
| """@return the IPv4 netmask, e.g., '255.255.255.0'.""" |
| return self._octets_to_addr(self._mask_octets) |
| |
| |
| @property |
| def prefix_len(self): |
| """@return the IPv4 prefix len, e.g., 24.""" |
| return self._prefix_len |
| |
| |
| @property |
| def subnet(self): |
| """@return the IPv4 subnet, e.g., '192.168.0.0'.""" |
| octets = [a & m for a, m in zip(self._octets, self._mask_octets)] |
| return self._octets_to_addr(octets) |
| |
| |
| @property |
| def broadcast(self): |
| """@return the IPv4 broadcast address, e.g., '192.168.0.255'.""" |
| octets = [a | (m ^ 0xff) |
| for a, m in zip(self._octets, self._mask_octets)] |
| return self._octets_to_addr(octets) |
| |
| |
| @property |
| def addr(self): |
| """@return the IPv4 address, e.g., '192.168.0.1'.""" |
| return self._octets_to_addr(self._octets) |
| |
| |
| def __init__(self, addr_str, prefix_len): |
| """Construct a Netblock. |
| |
| @param addr_str: string IP address (e.g. '192.168.1.1'). |
| @param prefix_len: int length of subnet prefix (e.g. 24). |
| |
| """ |
| self._octets = list(map(int, addr_str.split('.'))) |
| mask_bits = (-1 << (32 - prefix_len)) & 0xffffffff |
| self._mask_octets = self._int_to_octets(mask_bits) |
| self._prefix_len = prefix_len |
| |
| |
| def get_addr_in_block(self, offset): |
| """Get an address in a subnet. |
| |
| For instance if this netblock represents 192.168.0.1/24, |
| then get_addr_in_block(5) would return 192.168.0.5. |
| |
| @param offset int offset in block, (e.g. 5). |
| @return string address (e.g. '192.168.0.5'). |
| |
| """ |
| offset = self._int_to_octets(offset) |
| octets = [(a & m) + o |
| for a, m, o in zip(self._octets, self._mask_octets, offset)] |
| return self._octets_to_addr(octets) |