Как определить диапазон IP-адресов в диапазоне в Python 2.x? - PullRequest
1 голос
/ 12 июля 2011

При заданном диапазоне IP-адресов как можно вычесть из этого диапазона код IP-адреса или диапазон IP-адресов?

Пример 1:

original_range = '10.182.110.0/24'
# Same as '10.182.110.0-10.182.110.255'
subtract_range = '10.182.110.51-10.182.254'
> diff_range = '10.182.110.0-10.182.110.50, 10.182.110.255'

Пример 2:

original_range = '10.10.20.0-10.10.20.20'
subtract_range = '10.10.20.16'
> diff_range = '10.10.20.10-10.10.20.15, 10.10.20.17-10.10.20.20'

Пример 3:

original_range = '10.170.0.0/16'
# Same as '10.170.0.0-10.170.31.255'
subtract_range = '10.170.20.16'
> diff_range = '10.170.0.0-10.170.20.15, 10.170.20.17-10.170.31.255'

Ответы [ 2 ]

2 голосов
/ 13 июля 2011

В библиотеке Google ipaddr-py есть метод с именем address_exclude (). Ниже приведены примеры, модифицированные из PEP 3144 :

In [1]: ipaddr.IPNetwork('1.1.0.0/15').address_exclude(ipaddr.IPNetwork('1.1.1.0/24'))
    Out[1]:
    [IPv4Network('1.0.0.0/16'),
     IPv4Network('1.1.0.0/24'),
     IPv4Network('1.1.2.0/23'),
     IPv4Network('1.1.4.0/22'),
     IPv4Network('1.1.8.0/21'),
     IPv4Network('1.1.16.0/20'),
     IPv4Network('1.1.32.0/19'),
     IPv4Network('1.1.64.0/18'),
     IPv4Network('1.1.128.0/17')]

    In [1]: ipaddr.IPNewtork('::1/96').address_exclude(ipaddr.IPNetwork('::1/112'))
    Out[1]:
    [IPv6Network('::1:0/112'),
     IPv6Network('::2:0/111'),
     IPv6Network('::4:0/110'),
     IPv6Network('::8:0/109'),
     IPv6Network('::10:0/108'),
     IPv6Network('::20:0/107'),
     IPv6Network('::40:0/106'),
     IPv6Network('::80:0/105'),
     IPv6Network('::100:0/104'),
     IPv6Network('::200:0/103'),
     IPv6Network('::400:0/102'),
     IPv6Network('::800:0/101'),
     IPv6Network('::1000:0/100'),
     IPv6Network('::2000:0/99'),
     IPv6Network('::4000:0/98'),
     IPv6Network('::8000:0/97')]
1 голос
/ 14 марта 2012

Вот функции, которые я использую для этого (перемещение назад и вперед между форматом, который использует Qualys, и экземплярами объектов, предоставляемых ipaddr:

def is_valid_ip_address(address, version=None):
    """ Check validity of address
        Return True if 'address' is a valid ipv4 or ipv6 address.
    """
    # Validate version:
    if version:
        if not isinstance(version, int):
            raise TypeError, 'Version is not of type "int"'
            return False
        if not (version == 4 or version == 6):
            raise ValueError, 'IP version is set to an invalid number: %s' % version
            return False
    try:
        ipaddr.IPAddress(address.strip(),version)
    except ValueError:
        return False
    return True

def is_valid_ipv4_address(address):
    """ Check validity of address
        Return True if 'address' is a valid ipv4 address.
    """
    return is_valid_ip_address(address,4)

def is_valid_ipv6_address(address):
    """ Check validity of address
        Return True if 'address' is a valid ipv6 address.
    """
    return is_valid_ip_address(address,6)

def is_valid_ip_range(iprange, version=None):
    """ Check validity of iprange
        Return True if 'iprange' is a range of ip addresses in a format that Qulys's API will accept (i.e. "startip-endip" where startip < endip).
    """
    # Validate version:
    if version:
        if not isinstance(version, int):
            raise TypeError, 'Version is not of type "int"'
            return False
        if not (version == 4 or version == 6):
            raise ValueError, 'IP version is set to an invalid number: %s' % version
            return False

    try:
        (start_ip,end_ip) = iprange.split('-')
        start_ip = start_ip.strip()
        end_ip = end_ip.strip()
        if ipaddr.IPAddress(start_ip) == ipaddr.IPAddress(end_ip):
            logging.debug('%s/%s-%s, Error: %s' % (package,module,version,'Start and End IP Address in an IP Range can not be the same IP Address.'))
            return False
        # A valid range requires:
        # 1) The start_ip must be a valid ip address.
        # 2) The end_ip must be a valid ip address.
        # 3) The start_ip must be less than the end_ip.
        # Although socket operations (as are used in qualysconnect.util) are nice, it's not feasible to determine that the start_ip is less than the 
        # end_ip without considerable effort.  We'll use the ipaddr.summarize_address_range function to test all three at one time.
        ipaddr.summarize_address_range(ipaddr.IPAddress(start_ip,version),ipaddr.IPAddress(end_ip,version))
    except ipaddr.AddressValueError, e:
        logging.debug('%s/%s-%s, Error: %s' % (package,module,version,e))
        return False
    except ValueError, e:
        logging.debug('%s/%s-%s, Error: %s' % (package,module,version,e))
        return False
    return True

def is_valid_ipv4_range(iprange):
    """ Check validity of iprange
        Return True if 'iprange' is a range of ipv4 addresses in a format that Qulys's API will accept (i.e. "startip-endip" where startip < endip).
    """
    return is_valid_ip_range(iprange,4)

def is_valid_ipv6_range(iprange):
    """ Check validity of iprange
        Return True if 'iprange' is a range of ipv4 addresses in a format that Qulys's API will accept (i.e. "startip-endip" where startip < endip).
    """
    return is_valid_ip_range(iprange,6)

def cidr_to_ip(cidr,version=None):
    """ Convert an ip address or ip range provided in cidr notation (either bitmask or netmask notation) to the ip address or ip range format that is
        accepted by Qualys's API. (e.g. cidr_to_ip('10.0.0.0/24') returns the string '10.0.0.0-10.0.0.255'.
        Returns a String containing an ip address or ip range that can be provided to the Qualys API. 
    """
    # Validate version:
    if version:
        if not isinstance(version, int):
            raise TypeError, 'Version is not of type "int"'
            return False
        if not (version == 4 or version == 6):
            raise ValueError, 'IP version is set to an invalid number: %s' % version
            return False
    try:
        cidr_net = ipaddr.IPNetwork(cidr,version)
    except ValueError, e:
        logging.debug('%s/%s-%s, Error: %s' % (package,module,version,e))
        raise ValueError, e
    if cidr_net[0] == cidr_net[-1]:
        return str(cidr_net[0])
    iprange = '%s-%s' % (cidr_net[0],cidr_net[-1])
    return iprange

def cidr_to_ipv4(cidr):
    """ Convert an ipv4 address or ipv4 range provided in cidr notation (either bitmask or netmask notation) to the ip address or ip range format that is
        accepted by Qualys's API. (e.g. cidr_to_ip('192.0.2.0/24') returns the string '192.0.2.0-192.0.2.255'.
        Returns a String containing an ip address or ip range that can be provided to the Qualys API. 
    """
    return cidr_to_ip(cidr,4)

def cidr_to_ipv6(cidr):
    """ Convert an ipv6 address or ipv6 range provided in cidr notation (either bitmask or netmask notation) to the ip address or ip range format that is
        accepted by Qualys's API. (e.g. cidr_to_ip('2001:db8::fff/120') returns the string '2001:db8::f00-2001:db8::fff'.
        Returns a String containing an ipv6 address or ipv6 range that can be provided to the Qualys API. 
    """
    return cidr_to_ip(cidr,4)

def decode_ip_string(ipstring):
    """ Validates ipstring is in a format that can be provided to the Qualys API, if it is not in a format that can be accepted by the Qualys API, it attempts
        to put it in a format that is acceptable (e.g. converting cidr notation to the ip range notation that Qualys expects)
        Returns a string that is valid to hand to the 'ips' key in the Qualys API.
    """
    cml=[]
    ip_list = ipstring.split(',')
    # Should probably check for any repeated or overlapping IP addresses here, but skipping for now.
    for i in ip_list:
        # This is a kludge, but I couldn't come up with a good way to make the error report the string that generated the error, rather than the
        # potentially modified version of the string that caused the error.
        new_i=i 
        if '/' in i:
            new_i = cidr_to_ip(i)
        if (is_valid_ip_address(new_i) or is_valid_ip_range(new_i)):
            cml.append(new_i)
        else:
            raise ValueError, "IP argument cannot be parsed, \'%s\' is not a valid IP Range or IP Address" % i
    return ",".join(cml)

def ip_string_to_cidr(ipstring):
    """ Accepts ipstring - a string list of IPs in the format the Qualys expects or has provided (via API calls) and returns a list of ipaddr.IPNetwork objects."""
    ret_list = []
    ip_list = ipstring.split(',')
    for i in ip_list:
        if is_valid_ip_address(i):
            ret_list.append(ipaddr.IPNetwork(i.strip()))
        elif is_valid_ip_range(i.strip()):
            (start_ip,end_ip) = i.split('-')
            range_list = ipaddr.summarize_address_range(ipaddr.IPAddress(start_ip.strip()), ipaddr.IPAddress(end_ip.strip()))
            for j in range_list:
                ret_list.append(j)
    return ipaddr.collapse_address_list(ret_list)

def ip_list_to_ip_string(iplist):       
    return decode_ip_string(",".join([decode_ip_string(str(i)) for i in iplist]))

Для ваших конкретных примеров я бы использовал функциивыше, и метод 'exclude_address' в экземплярах объектов ipaddr.IPNetwork для написания новой функции, которая принимает входные данные original_range и subtract_range, возвращая список объектов ipaddr (или ip_string в ожидаемом формате Qualys с использованием функции ip_list_to_ip_string, приведенной выше).часть будет состоять в том, что когда вы запустите 'ip_string_to_cidr (exclude_list)', вы получите список объектов IPNetwork, которые необходимо вычесть из 'original_range'.

Если вам нужна дополнительная помощь, я, вероятно, могу собрать некоторыевроде функции exclude_ips, так как она мне понадобится в какой-то момент, просто дайте мне знать.

...