| ## This file is part of Scapy |
| ## See http://www.secdev.org/projects/scapy for more informations |
| ## Copyright (C) Philippe Biondi <phil@secdev.org> |
| ## This program is published under a GPLv2 license |
| |
| ## Copyright (C) 2005 Guillaume Valadon <guedou@hongo.wide.ad.jp> |
| ## Arnaud Ebalard <arnaud.ebalard@eads.net> |
| |
| """ |
| Utility functions for IPv6. |
| """ |
| from __future__ import absolute_import |
| import random |
| import socket |
| import struct |
| |
| from scapy.config import conf |
| import scapy.consts |
| from scapy.data import * |
| from scapy.utils import * |
| from scapy.compat import * |
| from scapy.pton_ntop import * |
| from scapy.volatile import RandMAC |
| from scapy.error import warning |
| from functools import reduce |
| from scapy.modules.six.moves import range |
| |
| |
| def construct_source_candidate_set(addr, plen, laddr): |
| """ |
| Given all addresses assigned to a specific interface ('laddr' parameter), |
| this function returns the "candidate set" associated with 'addr/plen'. |
| |
| Basically, the function filters all interface addresses to keep only those |
| that have the same scope as provided prefix. |
| |
| This is on this list of addresses that the source selection mechanism |
| will then be performed to select the best source address associated |
| with some specific destination that uses this prefix. |
| """ |
| def cset_sort(x,y): |
| x_global = 0 |
| if in6_isgladdr(x): |
| x_global = 1 |
| y_global = 0 |
| if in6_isgladdr(y): |
| y_global = 1 |
| res = y_global - x_global |
| if res != 0 or y_global != 1: |
| return res |
| # two global addresses: if one is native, it wins. |
| if not in6_isaddr6to4(x): |
| return -1; |
| return -res |
| |
| cset = [] |
| if in6_isgladdr(addr) or in6_isuladdr(addr): |
| cset = (x for x in laddr if x[1] == IPV6_ADDR_GLOBAL) |
| elif in6_islladdr(addr): |
| cset = (x for x in laddr if x[1] == IPV6_ADDR_LINKLOCAL) |
| elif in6_issladdr(addr): |
| cset = (x for x in laddr if x[1] == IPV6_ADDR_SITELOCAL) |
| elif in6_ismaddr(addr): |
| if in6_ismnladdr(addr): |
| cset = [('::1', 16, scapy.consts.LOOPBACK_INTERFACE)] |
| elif in6_ismgladdr(addr): |
| cset = (x for x in laddr if x[1] == IPV6_ADDR_GLOBAL) |
| elif in6_ismlladdr(addr): |
| cset = (x for x in laddr if x[1] == IPV6_ADDR_LINKLOCAL) |
| elif in6_ismsladdr(addr): |
| cset = (x for x in laddr if x[1] == IPV6_ADDR_SITELOCAL) |
| elif addr == '::' and plen == 0: |
| cset = (x for x in laddr if x[1] == IPV6_ADDR_GLOBAL) |
| cset = [x[0] for x in cset] |
| # TODO convert the cmd use into a key |
| cset.sort(key=cmp_to_key(cset_sort)) # Sort with global addresses first |
| return cset |
| |
| def get_source_addr_from_candidate_set(dst, candidate_set): |
| """ |
| This function implement a limited version of source address selection |
| algorithm defined in section 5 of RFC 3484. The format is very different |
| from that described in the document because it operates on a set |
| of candidate source address for some specific route. |
| """ |
| |
| def scope_cmp(a, b): |
| """ |
| Given two addresses, returns -1, 0 or 1 based on comparison of |
| their scope |
| """ |
| scope_mapper = {IPV6_ADDR_GLOBAL: 4, |
| IPV6_ADDR_SITELOCAL: 3, |
| IPV6_ADDR_LINKLOCAL: 2, |
| IPV6_ADDR_LOOPBACK: 1} |
| sa = in6_getscope(a) |
| if sa == -1: |
| sa = IPV6_ADDR_LOOPBACK |
| sb = in6_getscope(b) |
| if sb == -1: |
| sb = IPV6_ADDR_LOOPBACK |
| |
| sa = scope_mapper[sa] |
| sb = scope_mapper[sb] |
| |
| if sa == sb: |
| return 0 |
| if sa > sb: |
| return 1 |
| return -1 |
| |
| def rfc3484_cmp(source_a, source_b): |
| """ |
| The function implements a limited version of the rules from Source |
| Address selection algorithm defined section of RFC 3484. |
| """ |
| |
| # Rule 1: Prefer same address |
| if source_a == dst: |
| return 1 |
| if source_b == dst: |
| return 1 |
| |
| # Rule 2: Prefer appropriate scope |
| tmp = scope_cmp(source_a, source_b) |
| if tmp == -1: |
| if scope_cmp(source_a, dst) == -1: |
| return 1 |
| else: |
| return -1 |
| elif tmp == 1: |
| if scope_cmp(source_b, dst) == -1: |
| return 1 |
| else: |
| return -1 |
| |
| # Rule 3: cannot be easily implemented |
| # Rule 4: cannot be easily implemented |
| # Rule 5: does not make sense here |
| # Rule 6: cannot be implemented |
| # Rule 7: cannot be implemented |
| |
| # Rule 8: Longest prefix match |
| tmp1 = in6_get_common_plen(source_a, dst) |
| tmp2 = in6_get_common_plen(source_b, dst) |
| if tmp1 > tmp2: |
| return 1 |
| elif tmp2 > tmp1: |
| return -1 |
| return 0 |
| |
| if not candidate_set: |
| # Should not happen |
| return None |
| |
| candidate_set.sort(key=cmp_to_key(rfc3484_cmp), reverse=True) |
| |
| return candidate_set[0] |
| |
| |
| # Think before modify it : for instance, FE::1 does exist and is unicast |
| # there are many others like that. |
| # TODO : integrate Unique Local Addresses |
| def in6_getAddrType(addr): |
| naddr = inet_pton(socket.AF_INET6, addr) |
| paddr = inet_ntop(socket.AF_INET6, naddr) # normalize |
| addrType = 0 |
| # _Assignable_ Global Unicast Address space |
| # is defined in RFC 3513 as those in 2000::/3 |
| if ((orb(naddr[0]) & 0xE0) == 0x20): |
| addrType = (IPV6_ADDR_UNICAST | IPV6_ADDR_GLOBAL) |
| if naddr[:2] == b' \x02': # Mark 6to4 @ |
| addrType |= IPV6_ADDR_6TO4 |
| elif orb(naddr[0]) == 0xff: # multicast |
| addrScope = paddr[3] |
| if addrScope == '2': |
| addrType = (IPV6_ADDR_LINKLOCAL | IPV6_ADDR_MULTICAST) |
| elif addrScope == 'e': |
| addrType = (IPV6_ADDR_GLOBAL | IPV6_ADDR_MULTICAST) |
| else: |
| addrType = (IPV6_ADDR_GLOBAL | IPV6_ADDR_MULTICAST) |
| elif ((orb(naddr[0]) == 0xfe) and ((int(paddr[2], 16) & 0xC) == 0x8)): |
| addrType = (IPV6_ADDR_UNICAST | IPV6_ADDR_LINKLOCAL) |
| elif paddr == "::1": |
| addrType = IPV6_ADDR_LOOPBACK |
| elif paddr == "::": |
| addrType = IPV6_ADDR_UNSPECIFIED |
| else: |
| # Everything else is global unicast (RFC 3513) |
| # Even old deprecated (RFC3879) Site-Local addresses |
| addrType = (IPV6_ADDR_GLOBAL | IPV6_ADDR_UNICAST) |
| |
| return addrType |
| |
| def in6_mactoifaceid(mac, ulbit=None): |
| """ |
| Compute the interface ID in modified EUI-64 format associated |
| to the Ethernet address provided as input. |
| value taken by U/L bit in the interface identifier is basically |
| the reversed value of that in given MAC address it can be forced |
| to a specific value by using optional 'ulbit' parameter. |
| """ |
| if len(mac) != 17: return None |
| m = "".join(mac.split(':')) |
| if len(m) != 12: return None |
| first = int(m[0:2], 16) |
| if ulbit is None or not (ulbit == 0 or ulbit == 1): |
| ulbit = [1,'-',0][first & 0x02] |
| ulbit *= 2 |
| first = "%.02x" % ((first & 0xFD) | ulbit) |
| eui64 = first + m[2:4] + ":" + m[4:6] + "FF:FE" + m[6:8] + ":" + m[8:12] |
| return eui64.upper() |
| |
| def in6_ifaceidtomac(ifaceid): # TODO: finish commenting function behavior |
| """ |
| Extract the mac address from provided iface ID. Iface ID is provided |
| in printable format ("XXXX:XXFF:FEXX:XXXX", eventually compressed). None |
| is returned on error. |
| """ |
| try: |
| ifaceid = inet_pton(socket.AF_INET6, "::"+ifaceid)[8:16] |
| except: |
| return None |
| if ifaceid[3:5] != b'\xff\xfe': |
| return None |
| first = struct.unpack("B", ifaceid[:1])[0] |
| ulbit = 2*[1,'-',0][first & 0x02] |
| first = struct.pack("B", ((first & 0xFD) | ulbit)) |
| oui = first + ifaceid[1:3] |
| end = ifaceid[5:] |
| l = ["%.02x" % orb(x) for x in list(oui + end)] |
| return ":".join(l) |
| |
| def in6_addrtomac(addr): |
| """ |
| Extract the mac address from provided address. None is returned |
| on error. |
| """ |
| mask = inet_pton(socket.AF_INET6, "::ffff:ffff:ffff:ffff") |
| x = in6_and(mask, inet_pton(socket.AF_INET6, addr)) |
| ifaceid = inet_ntop(socket.AF_INET6, x)[2:] |
| return in6_ifaceidtomac(ifaceid) |
| |
| def in6_addrtovendor(addr): |
| """ |
| Extract the MAC address from a modified EUI-64 constructed IPv6 |
| address provided and use the IANA oui.txt file to get the vendor. |
| The database used for the conversion is the one loaded by Scapy, |
| based on Wireshark (/usr/share/wireshark/wireshark/manuf) None |
| is returned on error, "UNKNOWN" if the vendor is unknown. |
| """ |
| mac = in6_addrtomac(addr) |
| if mac is None: |
| return None |
| |
| res = conf.manufdb._get_manuf(mac) |
| if len(res) == 17 and res.count(':') != 5: # Mac address, i.e. unknown |
| res = "UNKNOWN" |
| |
| return res |
| |
| def in6_getLinkScopedMcastAddr(addr, grpid=None, scope=2): |
| """ |
| Generate a Link-Scoped Multicast Address as described in RFC 4489. |
| Returned value is in printable notation. |
| |
| 'addr' parameter specifies the link-local address to use for generating |
| Link-scoped multicast address IID. |
| |
| By default, the function returns a ::/96 prefix (aka last 32 bits of |
| returned address are null). If a group id is provided through 'grpid' |
| parameter, last 32 bits of the address are set to that value (accepted |
| formats : b'\x12\x34\x56\x78' or '12345678' or 0x12345678 or 305419896). |
| |
| By default, generated address scope is Link-Local (2). That value can |
| be modified by passing a specific 'scope' value as an argument of the |
| function. RFC 4489 only authorizes scope values <= 2. Enforcement |
| is performed by the function (None will be returned). |
| |
| If no link-local address can be used to generate the Link-Scoped IPv6 |
| Multicast address, or if another error occurs, None is returned. |
| """ |
| if not scope in [0, 1, 2]: |
| return None |
| try: |
| if not in6_islladdr(addr): |
| return None |
| addr = inet_pton(socket.AF_INET6, addr) |
| except: |
| warning("in6_getLinkScopedMcastPrefix(): Invalid address provided") |
| return None |
| |
| iid = addr[8:] |
| |
| if grpid is None: |
| grpid = b'\x00\x00\x00\x00' |
| else: |
| if isinstance(grpid, (bytes, str)): |
| if len(grpid) == 8: |
| try: |
| grpid = int(grpid, 16) & 0xffffffff |
| except: |
| warning("in6_getLinkScopedMcastPrefix(): Invalid group id provided") |
| return None |
| elif len(grpid) == 4: |
| try: |
| grpid = struct.unpack("!I", grpid)[0] |
| except: |
| warning("in6_getLinkScopedMcastPrefix(): Invalid group id provided") |
| return None |
| grpid = struct.pack("!I", grpid) |
| |
| flgscope = struct.pack("B", 0xff & ((0x3 << 4) | scope)) |
| plen = b'\xff' |
| res = b'\x00' |
| a = b'\xff' + flgscope + res + plen + iid + grpid |
| |
| return inet_ntop(socket.AF_INET6, a) |
| |
| def in6_get6to4Prefix(addr): |
| """ |
| Returns the /48 6to4 prefix associated with provided IPv4 address |
| On error, None is returned. No check is performed on public/private |
| status of the address |
| """ |
| try: |
| addr = inet_pton(socket.AF_INET, addr) |
| addr = inet_ntop(socket.AF_INET6, b'\x20\x02'+addr+b'\x00'*10) |
| except: |
| return None |
| return addr |
| |
| def in6_6to4ExtractAddr(addr): |
| """ |
| Extract IPv4 address embedded in 6to4 address. Passed address must be |
| a 6to4 address. None is returned on error. |
| """ |
| try: |
| addr = inet_pton(socket.AF_INET6, addr) |
| except: |
| return None |
| if addr[:2] != b" \x02": |
| return None |
| return inet_ntop(socket.AF_INET, addr[2:6]) |
| |
| |
| def in6_getLocalUniquePrefix(): |
| """ |
| Returns a pseudo-randomly generated Local Unique prefix. Function |
| follows recommendation of Section 3.2.2 of RFC 4193 for prefix |
| generation. |
| """ |
| # Extracted from RFC 1305 (NTP) : |
| # NTP timestamps are represented as a 64-bit unsigned fixed-point number, |
| # in seconds relative to 0h on 1 January 1900. The integer part is in the |
| # first 32 bits and the fraction part in the last 32 bits. |
| |
| # epoch = (1900, 1, 1, 0, 0, 0, 5, 1, 0) |
| # x = time.time() |
| # from time import gmtime, strftime, gmtime, mktime |
| # delta = mktime(gmtime(0)) - mktime(self.epoch) |
| # x = x-delta |
| |
| tod = time.time() # time of day. Will bother with epoch later |
| i = int(tod) |
| j = int((tod - i)*(2**32)) |
| tod = struct.pack("!II", i,j) |
| mac = RandMAC() |
| # construct modified EUI-64 ID |
| eui64 = inet_pton(socket.AF_INET6, '::' + in6_mactoifaceid(mac))[8:] |
| import hashlib |
| globalid = hashlib.sha1(tod+eui64).digest()[:5] |
| return inet_ntop(socket.AF_INET6, b'\xfd' + globalid + b'\x00'*10) |
| |
| def in6_getRandomizedIfaceId(ifaceid, previous=None): |
| """ |
| Implements the interface ID generation algorithm described in RFC 3041. |
| The function takes the Modified EUI-64 interface identifier generated |
| as described in RFC 4291 and an optional previous history value (the |
| first element of the output of this function). If no previous interface |
| identifier is provided, a random one is generated. The function returns |
| a tuple containing the randomized interface identifier and the history |
| value (for possible future use). Input and output values are provided in |
| a "printable" format as depicted below. |
| |
| ex: |
| >>> in6_getRandomizedIfaceId('20b:93ff:feeb:2d3') |
| ('4c61:76ff:f46a:a5f3', 'd006:d540:db11:b092') |
| >>> in6_getRandomizedIfaceId('20b:93ff:feeb:2d3', |
| previous='d006:d540:db11:b092') |
| ('fe97:46fe:9871:bd38', 'eeed:d79c:2e3f:62e') |
| """ |
| |
| s = b"" |
| if previous is None: |
| d = b"".join(chb(x) for x in range(256)) |
| for _ in range(8): |
| s += chb(random.choice(d)) |
| previous = s |
| s = inet_pton(socket.AF_INET6, "::"+ifaceid)[8:] + previous |
| import hashlib |
| s = hashlib.md5(s).digest() |
| s1,s2 = s[:8],s[8:] |
| s1 = chb(orb(s1[0]) | 0x04) + s1[1:] |
| s1 = inet_ntop(socket.AF_INET6, b"\xff"*8 + s1)[20:] |
| s2 = inet_ntop(socket.AF_INET6, b"\xff"*8 + s2)[20:] |
| return (s1, s2) |
| |
| |
| _rfc1924map = [ '0','1','2','3','4','5','6','7','8','9','A','B','C','D','E', |
| 'F','G','H','I','J','K','L','M','N','O','P','Q','R','S','T', |
| 'U','V','W','X','Y','Z','a','b','c','d','e','f','g','h','i', |
| 'j','k','l','m','n','o','p','q','r','s','t','u','v','w','x', |
| 'y','z','!','#','$','%','&','(',')','*','+','-',';','<','=', |
| '>','?','@','^','_','`','{','|','}','~' ] |
| |
| def in6_ctop(addr): |
| """ |
| Convert an IPv6 address in Compact Representation Notation |
| (RFC 1924) to printable representation ;-) |
| Returns None on error. |
| """ |
| if len(addr) != 20 or not reduce(lambda x,y: x and y, |
| [x in _rfc1924map for x in addr]): |
| return None |
| i = 0 |
| for c in addr: |
| j = _rfc1924map.index(c) |
| i = 85*i + j |
| res = [] |
| for j in range(4): |
| res.append(struct.pack("!I", i%2**32)) |
| i = i//(2**32) |
| res.reverse() |
| return inet_ntop(socket.AF_INET6, b"".join(res)) |
| |
| def in6_ptoc(addr): |
| """ |
| Converts an IPv6 address in printable representation to RFC |
| 1924 Compact Representation ;-) |
| Returns None on error. |
| """ |
| try: |
| d=struct.unpack("!IIII", inet_pton(socket.AF_INET6, addr)) |
| except: |
| return None |
| res = 0 |
| m = [2**96, 2**64, 2**32, 1] |
| for i in range(4): |
| res += d[i]*m[i] |
| rem = res |
| res = [] |
| while rem: |
| res.append(_rfc1924map[rem%85]) |
| rem = rem//85 |
| res.reverse() |
| return "".join(res) |
| |
| |
| def in6_isaddr6to4(x): |
| """ |
| Return True if provided address (in printable format) is a 6to4 |
| address (being in 2002::/16). |
| """ |
| x = inet_pton(socket.AF_INET6, x) |
| return x[:2] == b' \x02' |
| |
| conf.teredoPrefix = "2001::" # old one was 3ffe:831f (it is a /32) |
| conf.teredoServerPort = 3544 |
| |
| def in6_isaddrTeredo(x): |
| """ |
| Return True if provided address is a Teredo, meaning it is under |
| the /32 conf.teredoPrefix prefix value (by default, 2001::). |
| Otherwise, False is returned. Address must be passed in printable |
| format. |
| """ |
| our = inet_pton(socket.AF_INET6, x)[0:4] |
| teredoPrefix = inet_pton(socket.AF_INET6, conf.teredoPrefix)[0:4] |
| return teredoPrefix == our |
| |
| def teredoAddrExtractInfo(x): |
| """ |
| Extract information from a Teredo address. Return value is |
| a 4-tuple made of IPv4 address of Teredo server, flag value (int), |
| mapped address (non obfuscated) and mapped port (non obfuscated). |
| No specific checks are performed on passed address. |
| """ |
| addr = inet_pton(socket.AF_INET6, x) |
| server = inet_ntop(socket.AF_INET, addr[4:8]) |
| flag = struct.unpack("!H",addr[8:10])[0] |
| mappedport = struct.unpack("!H",strxor(addr[10:12],b'\xff'*2))[0] |
| mappedaddr = inet_ntop(socket.AF_INET, strxor(addr[12:16],b'\xff'*4)) |
| return server, flag, mappedaddr, mappedport |
| |
| def in6_iseui64(x): |
| """ |
| Return True if provided address has an interface identifier part |
| created in modified EUI-64 format (meaning it matches *::*:*ff:fe*:*). |
| Otherwise, False is returned. Address must be passed in printable |
| format. |
| """ |
| eui64 = inet_pton(socket.AF_INET6, '::ff:fe00:0') |
| x = in6_and(inet_pton(socket.AF_INET6, x), eui64) |
| return x == eui64 |
| |
| def in6_isanycast(x): # RFC 2526 |
| if in6_iseui64(x): |
| s = '::fdff:ffff:ffff:ff80' |
| packed_x = inet_pton(socket.AF_INET6, x) |
| packed_s = inet_pton(socket.AF_INET6, s) |
| x_and_s = in6_and(packed_x, packed_s) |
| return x_and_s == packed_s |
| else: |
| # not EUI-64 |
| #| n bits | 121-n bits | 7 bits | |
| #+---------------------------------+------------------+------------+ |
| #| subnet prefix | 1111111...111111 | anycast ID | |
| #+---------------------------------+------------------+------------+ |
| # | interface identifier field | |
| warning('in6_isanycast(): TODO not EUI-64') |
| return 0 |
| |
| def _in6_bitops(a1, a2, operator=0): |
| a1 = struct.unpack('4I', a1) |
| a2 = struct.unpack('4I', a2) |
| fop = [ lambda x,y: x | y, |
| lambda x,y: x & y, |
| lambda x,y: x ^ y |
| ] |
| ret = map(fop[operator%len(fop)], a1, a2) |
| return b"".join(struct.pack('I', x) for x in ret) |
| |
| def in6_or(a1, a2): |
| """ |
| Provides a bit to bit OR of provided addresses. They must be |
| passed in network format. Return value is also an IPv6 address |
| in network format. |
| """ |
| return _in6_bitops(a1, a2, 0) |
| |
| def in6_and(a1, a2): |
| """ |
| Provides a bit to bit AND of provided addresses. They must be |
| passed in network format. Return value is also an IPv6 address |
| in network format. |
| """ |
| return _in6_bitops(a1, a2, 1) |
| |
| def in6_xor(a1, a2): |
| """ |
| Provides a bit to bit XOR of provided addresses. They must be |
| passed in network format. Return value is also an IPv6 address |
| in network format. |
| """ |
| return _in6_bitops(a1, a2, 2) |
| |
| def in6_cidr2mask(m): |
| """ |
| Return the mask (bitstring) associated with provided length |
| value. For instance if function is called on 48, return value is |
| b'\xff\xff\xff\xff\xff\xff\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'. |
| |
| """ |
| if m > 128 or m < 0: |
| raise Scapy_Exception("value provided to in6_cidr2mask outside [0, 128] domain (%d)" % m) |
| |
| t = [] |
| for i in range(0, 4): |
| t.append(max(0, 2**32 - 2**(32-min(32, m)))) |
| m -= 32 |
| |
| return b"".join(struct.pack('!I', x) for x in t) |
| |
| def in6_getnsma(a): |
| """ |
| Return link-local solicited-node multicast address for given |
| address. Passed address must be provided in network format. |
| Returned value is also in network format. |
| """ |
| |
| r = in6_and(a, inet_pton(socket.AF_INET6, '::ff:ffff')) |
| r = in6_or(inet_pton(socket.AF_INET6, 'ff02::1:ff00:0'), r) |
| return r |
| |
| def in6_getnsmac(a): # return multicast Ethernet address associated with multicast v6 destination |
| """ |
| Return the multicast mac address associated with provided |
| IPv6 address. Passed address must be in network format. |
| """ |
| |
| a = struct.unpack('16B', a)[-4:] |
| mac = '33:33:' |
| mac += ':'.join("%.2x" %x for x in a) |
| return mac |
| |
| def in6_getha(prefix): |
| """ |
| Return the anycast address associated with all home agents on a given |
| subnet. |
| """ |
| r = in6_and(inet_pton(socket.AF_INET6, prefix), in6_cidr2mask(64)) |
| r = in6_or(r, inet_pton(socket.AF_INET6, '::fdff:ffff:ffff:fffe')) |
| return inet_ntop(socket.AF_INET6, r) |
| |
| def in6_ptop(str): |
| """ |
| Normalizes IPv6 addresses provided in printable format, returning the |
| same address in printable format. (2001:0db8:0:0::1 -> 2001:db8::1) |
| """ |
| return inet_ntop(socket.AF_INET6, inet_pton(socket.AF_INET6, str)) |
| |
| def in6_isincluded(addr, prefix, plen): |
| """ |
| Returns True when 'addr' belongs to prefix/plen. False otherwise. |
| """ |
| temp = inet_pton(socket.AF_INET6, addr) |
| pref = in6_cidr2mask(plen) |
| zero = inet_pton(socket.AF_INET6, prefix) |
| return zero == in6_and(temp, pref) |
| |
| def in6_isllsnmaddr(str): |
| """ |
| Return True if provided address is a link-local solicited node |
| multicast address, i.e. belongs to ff02::1:ff00:0/104. False is |
| returned otherwise. |
| """ |
| temp = in6_and(b"\xff"*13+b"\x00"*3, inet_pton(socket.AF_INET6, str)) |
| temp2 = b'\xff\x02\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\xff\x00\x00\x00' |
| return temp == temp2 |
| |
| def in6_isdocaddr(str): |
| """ |
| Returns True if provided address in printable format belongs to |
| 2001:db8::/32 address space reserved for documentation (as defined |
| in RFC 3849). |
| """ |
| return in6_isincluded(str, '2001:db8::', 32) |
| |
| def in6_islladdr(str): |
| """ |
| Returns True if provided address in printable format belongs to |
| _allocated_ link-local unicast address space (fe80::/10) |
| """ |
| return in6_isincluded(str, 'fe80::', 10) |
| |
| def in6_issladdr(str): |
| """ |
| Returns True if provided address in printable format belongs to |
| _allocated_ site-local address space (fec0::/10). This prefix has |
| been deprecated, address being now reserved by IANA. Function |
| will remain for historic reasons. |
| """ |
| return in6_isincluded(str, 'fec0::', 10) |
| |
| def in6_isuladdr(str): |
| """ |
| Returns True if provided address in printable format belongs to |
| Unique local address space (fc00::/7). |
| """ |
| return in6_isincluded(str, 'fc00::', 7) |
| |
| # TODO : we should see the status of Unique Local addresses against |
| # global address space. |
| # Up-to-date information is available through RFC 3587. |
| # We should review function behavior based on its content. |
| def in6_isgladdr(str): |
| """ |
| Returns True if provided address in printable format belongs to |
| _allocated_ global address space (2000::/3). Please note that, |
| Unique Local addresses (FC00::/7) are not part of global address |
| space, and won't match. |
| """ |
| return in6_isincluded(str, '2000::', 3) |
| |
| def in6_ismaddr(str): |
| """ |
| Returns True if provided address in printable format belongs to |
| allocated Multicast address space (ff00::/8). |
| """ |
| return in6_isincluded(str, 'ff00::', 8) |
| |
| def in6_ismnladdr(str): |
| """ |
| Returns True if address belongs to node-local multicast address |
| space (ff01::/16) as defined in RFC |
| """ |
| return in6_isincluded(str, 'ff01::', 16) |
| |
| def in6_ismgladdr(str): |
| """ |
| Returns True if address belongs to global multicast address |
| space (ff0e::/16). |
| """ |
| return in6_isincluded(str, 'ff0e::', 16) |
| |
| def in6_ismlladdr(str): |
| """ |
| Returns True if address belongs to link-local multicast address |
| space (ff02::/16) |
| """ |
| return in6_isincluded(str, 'ff02::', 16) |
| |
| def in6_ismsladdr(str): |
| """ |
| Returns True if address belongs to site-local multicast address |
| space (ff05::/16). Site local address space has been deprecated. |
| Function remains for historic reasons. |
| """ |
| return in6_isincluded(str, 'ff05::', 16) |
| |
| def in6_isaddrllallnodes(str): |
| """ |
| Returns True if address is the link-local all-nodes multicast |
| address (ff02::1). |
| """ |
| return (inet_pton(socket.AF_INET6, "ff02::1") == |
| inet_pton(socket.AF_INET6, str)) |
| |
| def in6_isaddrllallservers(str): |
| """ |
| Returns True if address is the link-local all-servers multicast |
| address (ff02::2). |
| """ |
| return (inet_pton(socket.AF_INET6, "ff02::2") == |
| inet_pton(socket.AF_INET6, str)) |
| |
| def in6_getscope(addr): |
| """ |
| Returns the scope of the address. |
| """ |
| if in6_isgladdr(addr) or in6_isuladdr(addr): |
| scope = IPV6_ADDR_GLOBAL |
| elif in6_islladdr(addr): |
| scope = IPV6_ADDR_LINKLOCAL |
| elif in6_issladdr(addr): |
| scope = IPV6_ADDR_SITELOCAL |
| elif in6_ismaddr(addr): |
| if in6_ismgladdr(addr): |
| scope = IPV6_ADDR_GLOBAL |
| elif in6_ismlladdr(addr): |
| scope = IPV6_ADDR_LINKLOCAL |
| elif in6_ismsladdr(addr): |
| scope = IPV6_ADDR_SITELOCAL |
| elif in6_ismnladdr(addr): |
| scope = IPV6_ADDR_LOOPBACK |
| else: |
| scope = -1 |
| elif addr == '::1': |
| scope = IPV6_ADDR_LOOPBACK |
| else: |
| scope = -1 |
| return scope |
| |
| def in6_get_common_plen(a, b): |
| """ |
| Return common prefix length of IPv6 addresses a and b. |
| """ |
| def matching_bits(byte1, byte2): |
| for i in range(8): |
| cur_mask = 0x80 >> i |
| if (byte1 & cur_mask) != (byte2 & cur_mask): |
| return i |
| return 8 |
| |
| tmpA = inet_pton(socket.AF_INET6, a) |
| tmpB = inet_pton(socket.AF_INET6, b) |
| for i in range(16): |
| mbits = matching_bits(orb(tmpA[i]), orb(tmpB[i])) |
| if mbits != 8: |
| return 8*i + mbits |
| return 128 |
| |
| def in6_isvalid(address): |
| """Return True if 'address' is a valid IPv6 address string, False |
| otherwise.""" |
| |
| try: |
| socket.inet_pton(socket.AF_INET6, address) |
| return True |
| except: |
| return False |