Home | History | Annotate | Download | only in scapy
      1 ## This file is part of Scapy
      2 ## See http://www.secdev.org/projects/scapy for more informations
      3 ## Copyright (C) Philippe Biondi <phil (at] secdev.org>
      4 ## This program is published under a GPLv2 license
      5 
      6 ## Copyright (C) 2005  Guillaume Valadon <guedou (at] hongo.wide.ad.jp>
      7 ##                     Arnaud Ebalard <arnaud.ebalard (at] eads.net>
      8 
      9 """
     10 Utility functions for IPv6.
     11 """
     12 from __future__ import absolute_import
     13 import random
     14 import socket
     15 import struct
     16 
     17 from scapy.config import conf
     18 import scapy.consts
     19 from scapy.data import *
     20 from scapy.utils import *
     21 from scapy.compat import *
     22 from scapy.pton_ntop import *
     23 from scapy.volatile import RandMAC
     24 from scapy.error import warning
     25 from functools import reduce
     26 from scapy.modules.six.moves import range
     27 
     28 
     29 def construct_source_candidate_set(addr, plen, laddr):
     30     """
     31     Given all addresses assigned to a specific interface ('laddr' parameter),
     32     this function returns the "candidate set" associated with 'addr/plen'.
     33     
     34     Basically, the function filters all interface addresses to keep only those
     35     that have the same scope as provided prefix.
     36     
     37     This is on this list of addresses that the source selection mechanism 
     38     will then be performed to select the best source address associated
     39     with some specific destination that uses this prefix.
     40     """
     41     def cset_sort(x,y):
     42         x_global = 0
     43         if in6_isgladdr(x):
     44             x_global = 1
     45         y_global = 0
     46         if in6_isgladdr(y):
     47             y_global = 1
     48         res = y_global - x_global
     49         if res != 0 or y_global != 1:
     50             return res
     51         # two global addresses: if one is native, it wins.
     52         if not in6_isaddr6to4(x):
     53             return -1;
     54         return -res
     55 
     56     cset = []
     57     if in6_isgladdr(addr) or in6_isuladdr(addr):
     58         cset = (x for x in laddr if x[1] == IPV6_ADDR_GLOBAL)
     59     elif in6_islladdr(addr):
     60         cset = (x for x in laddr if x[1] == IPV6_ADDR_LINKLOCAL)
     61     elif in6_issladdr(addr):
     62         cset = (x for x in laddr if x[1] == IPV6_ADDR_SITELOCAL)
     63     elif in6_ismaddr(addr):
     64         if in6_ismnladdr(addr):
     65             cset = [('::1', 16, scapy.consts.LOOPBACK_INTERFACE)]
     66         elif in6_ismgladdr(addr):
     67             cset = (x for x in laddr if x[1] == IPV6_ADDR_GLOBAL)
     68         elif in6_ismlladdr(addr):
     69             cset = (x for x in laddr if x[1] == IPV6_ADDR_LINKLOCAL)
     70         elif in6_ismsladdr(addr):
     71             cset = (x for x in laddr if x[1] == IPV6_ADDR_SITELOCAL)
     72     elif addr == '::' and plen == 0:
     73         cset = (x for x in laddr if x[1] == IPV6_ADDR_GLOBAL)
     74     cset = [x[0] for x in cset]
     75     # TODO convert the cmd use into a key
     76     cset.sort(key=cmp_to_key(cset_sort)) # Sort with global addresses first
     77     return cset            
     78 
     79 def get_source_addr_from_candidate_set(dst, candidate_set):
     80     """
     81     This function implement a limited version of source address selection
     82     algorithm defined in section 5 of RFC 3484. The format is very different
     83     from that described in the document because it operates on a set 
     84     of candidate source address for some specific route.
     85     """
     86 
     87     def scope_cmp(a, b):
     88         """
     89         Given two addresses, returns -1, 0 or 1 based on comparison of
     90         their scope
     91         """
     92         scope_mapper = {IPV6_ADDR_GLOBAL: 4,
     93                         IPV6_ADDR_SITELOCAL: 3,
     94                         IPV6_ADDR_LINKLOCAL: 2,
     95                         IPV6_ADDR_LOOPBACK: 1}
     96         sa = in6_getscope(a)
     97         if sa == -1:
     98             sa = IPV6_ADDR_LOOPBACK
     99         sb = in6_getscope(b)
    100         if sb == -1:
    101             sb = IPV6_ADDR_LOOPBACK
    102 
    103         sa = scope_mapper[sa]
    104         sb = scope_mapper[sb]
    105 
    106         if sa == sb:
    107             return 0
    108         if sa > sb:
    109             return 1
    110         return -1
    111 
    112     def rfc3484_cmp(source_a, source_b):
    113         """
    114         The function implements a limited version of the rules from Source
    115         Address selection algorithm defined section of RFC 3484.
    116         """
    117 
    118         # Rule 1: Prefer same address
    119         if source_a == dst:
    120             return 1
    121         if source_b == dst:
    122             return 1
    123 
    124         # Rule 2: Prefer appropriate scope
    125         tmp = scope_cmp(source_a, source_b)
    126         if tmp == -1:
    127             if scope_cmp(source_a, dst) == -1:
    128                 return 1
    129             else:
    130                 return -1
    131         elif tmp == 1:
    132             if scope_cmp(source_b, dst) == -1:
    133                 return 1
    134             else:
    135                 return -1
    136 
    137         # Rule 3: cannot be easily implemented
    138         # Rule 4: cannot be easily implemented
    139         # Rule 5: does not make sense here
    140         # Rule 6: cannot be implemented
    141         # Rule 7: cannot be implemented
    142         
    143         # Rule 8: Longest prefix match
    144         tmp1 = in6_get_common_plen(source_a, dst)
    145         tmp2 = in6_get_common_plen(source_b, dst)
    146         if tmp1 > tmp2:
    147             return 1
    148         elif tmp2 > tmp1:
    149             return -1
    150         return 0
    151     
    152     if not candidate_set:
    153         # Should not happen
    154         return None
    155 
    156     candidate_set.sort(key=cmp_to_key(rfc3484_cmp), reverse=True)
    157     
    158     return candidate_set[0]
    159 
    160 
    161 # Think before modify it : for instance, FE::1 does exist and is unicast
    162 # there are many others like that.
    163 # TODO : integrate Unique Local Addresses
    164 def in6_getAddrType(addr):
    165     naddr = inet_pton(socket.AF_INET6, addr)
    166     paddr = inet_ntop(socket.AF_INET6, naddr) # normalize
    167     addrType = 0
    168     # _Assignable_ Global Unicast Address space
    169     # is defined in RFC 3513 as those in 2000::/3
    170     if ((orb(naddr[0]) & 0xE0) == 0x20):
    171         addrType = (IPV6_ADDR_UNICAST | IPV6_ADDR_GLOBAL)
    172         if naddr[:2] == b' \x02': # Mark 6to4 @
    173             addrType |= IPV6_ADDR_6TO4
    174     elif orb(naddr[0]) == 0xff: # multicast
    175         addrScope = paddr[3]
    176         if addrScope == '2':
    177             addrType = (IPV6_ADDR_LINKLOCAL | IPV6_ADDR_MULTICAST)
    178         elif addrScope == 'e':
    179             addrType = (IPV6_ADDR_GLOBAL | IPV6_ADDR_MULTICAST)
    180         else:
    181             addrType = (IPV6_ADDR_GLOBAL | IPV6_ADDR_MULTICAST)
    182     elif ((orb(naddr[0]) == 0xfe) and ((int(paddr[2], 16) & 0xC) == 0x8)):
    183         addrType = (IPV6_ADDR_UNICAST | IPV6_ADDR_LINKLOCAL)
    184     elif paddr == "::1":
    185         addrType = IPV6_ADDR_LOOPBACK
    186     elif paddr == "::":
    187         addrType = IPV6_ADDR_UNSPECIFIED
    188     else:
    189         # Everything else is global unicast (RFC 3513)
    190         # Even old deprecated (RFC3879) Site-Local addresses
    191         addrType = (IPV6_ADDR_GLOBAL | IPV6_ADDR_UNICAST)
    192 
    193     return addrType
    194 
    195 def in6_mactoifaceid(mac, ulbit=None):
    196     """
    197     Compute the interface ID in modified EUI-64 format associated 
    198     to the Ethernet address provided as input.
    199     value taken by U/L bit in the interface identifier is basically 
    200     the reversed value of that in given MAC address it can be forced
    201     to a specific value by using optional 'ulbit' parameter.
    202     """
    203     if len(mac) != 17: return None
    204     m = "".join(mac.split(':'))
    205     if len(m) != 12: return None
    206     first = int(m[0:2], 16)
    207     if ulbit is None or not (ulbit == 0 or ulbit == 1):
    208         ulbit = [1,'-',0][first & 0x02]
    209     ulbit *= 2
    210     first = "%.02x" % ((first & 0xFD) | ulbit)
    211     eui64 = first + m[2:4] + ":" + m[4:6] + "FF:FE" + m[6:8] + ":" + m[8:12]
    212     return eui64.upper()
    213 
    214 def in6_ifaceidtomac(ifaceid): # TODO: finish commenting function behavior
    215     """
    216     Extract the mac address from provided iface ID. Iface ID is provided 
    217     in printable format ("XXXX:XXFF:FEXX:XXXX", eventually compressed). None 
    218     is returned on error.
    219     """
    220     try:
    221         ifaceid = inet_pton(socket.AF_INET6, "::"+ifaceid)[8:16]
    222     except:
    223         return None
    224     if ifaceid[3:5] != b'\xff\xfe':
    225         return None
    226     first = struct.unpack("B", ifaceid[:1])[0]
    227     ulbit = 2*[1,'-',0][first & 0x02]
    228     first = struct.pack("B", ((first & 0xFD) | ulbit))
    229     oui = first + ifaceid[1:3]
    230     end = ifaceid[5:]
    231     l = ["%.02x" % orb(x) for x in list(oui + end)]
    232     return ":".join(l)
    233 
    234 def in6_addrtomac(addr):
    235     """
    236     Extract the mac address from provided address. None is returned
    237     on error.
    238     """
    239     mask = inet_pton(socket.AF_INET6, "::ffff:ffff:ffff:ffff")
    240     x = in6_and(mask, inet_pton(socket.AF_INET6, addr))
    241     ifaceid = inet_ntop(socket.AF_INET6, x)[2:]
    242     return in6_ifaceidtomac(ifaceid)
    243 
    244 def in6_addrtovendor(addr):
    245     """
    246     Extract the MAC address from a modified EUI-64 constructed IPv6
    247     address provided and use the IANA oui.txt file to get the vendor.
    248     The database used for the conversion is the one loaded by Scapy,
    249     based on Wireshark (/usr/share/wireshark/wireshark/manuf)  None
    250     is returned on error, "UNKNOWN" if the vendor is unknown.
    251     """
    252     mac = in6_addrtomac(addr)
    253     if mac is None:
    254         return None
    255 
    256     res = conf.manufdb._get_manuf(mac)
    257     if len(res) == 17 and res.count(':') != 5: # Mac address, i.e. unknown
    258         res = "UNKNOWN"
    259 
    260     return res
    261 
    262 def in6_getLinkScopedMcastAddr(addr, grpid=None, scope=2):
    263     """
    264     Generate a Link-Scoped Multicast Address as described in RFC 4489.
    265     Returned value is in printable notation.
    266 
    267     'addr' parameter specifies the link-local address to use for generating
    268     Link-scoped multicast address IID.
    269     
    270     By default, the function returns a ::/96 prefix (aka last 32 bits of 
    271     returned address are null). If a group id is provided through 'grpid' 
    272     parameter, last 32 bits of the address are set to that value (accepted 
    273     formats : b'\x12\x34\x56\x78' or '12345678' or 0x12345678 or 305419896).
    274 
    275     By default, generated address scope is Link-Local (2). That value can 
    276     be modified by passing a specific 'scope' value as an argument of the
    277     function. RFC 4489 only authorizes scope values <= 2. Enforcement
    278     is performed by the function (None will be returned).
    279     
    280     If no link-local address can be used to generate the Link-Scoped IPv6
    281     Multicast address, or if another error occurs, None is returned.
    282     """
    283     if not scope in [0, 1, 2]:
    284         return None    
    285     try:
    286         if not in6_islladdr(addr):
    287             return None
    288         addr = inet_pton(socket.AF_INET6, addr)
    289     except:
    290         warning("in6_getLinkScopedMcastPrefix(): Invalid address provided")
    291         return None
    292 
    293     iid = addr[8:]
    294 
    295     if grpid is None:
    296         grpid = b'\x00\x00\x00\x00'
    297     else:
    298         if isinstance(grpid, (bytes, str)):
    299             if len(grpid) == 8:
    300                 try:
    301                     grpid = int(grpid, 16) & 0xffffffff
    302                 except:
    303                     warning("in6_getLinkScopedMcastPrefix(): Invalid group id provided")
    304                     return None
    305             elif len(grpid) == 4:
    306                 try:
    307                     grpid = struct.unpack("!I", grpid)[0]
    308                 except:
    309                     warning("in6_getLinkScopedMcastPrefix(): Invalid group id provided")
    310                     return None
    311         grpid = struct.pack("!I", grpid)
    312 
    313     flgscope = struct.pack("B", 0xff & ((0x3 << 4) | scope))
    314     plen = b'\xff'
    315     res = b'\x00'
    316     a = b'\xff' + flgscope + res + plen + iid + grpid
    317 
    318     return inet_ntop(socket.AF_INET6, a)
    319 
    320 def in6_get6to4Prefix(addr):
    321     """
    322     Returns the /48 6to4 prefix associated with provided IPv4 address
    323     On error, None is returned. No check is performed on public/private
    324     status of the address
    325     """
    326     try:
    327         addr = inet_pton(socket.AF_INET, addr)
    328         addr = inet_ntop(socket.AF_INET6, b'\x20\x02'+addr+b'\x00'*10)
    329     except:
    330         return None
    331     return addr
    332 
    333 def in6_6to4ExtractAddr(addr):
    334     """
    335     Extract IPv4 address embedded in 6to4 address. Passed address must be
    336     a 6to4 address. None is returned on error.
    337     """
    338     try:
    339         addr = inet_pton(socket.AF_INET6, addr)
    340     except:
    341         return None
    342     if addr[:2] != b" \x02":
    343         return None
    344     return inet_ntop(socket.AF_INET, addr[2:6])
    345     
    346 
    347 def in6_getLocalUniquePrefix():
    348     """
    349     Returns a pseudo-randomly generated Local Unique prefix. Function
    350     follows recommendation of Section 3.2.2 of RFC 4193 for prefix
    351     generation.
    352     """
    353     # Extracted from RFC 1305 (NTP) :
    354     # NTP timestamps are represented as a 64-bit unsigned fixed-point number, 
    355     # in seconds relative to 0h on 1 January 1900. The integer part is in the 
    356     # first 32 bits and the fraction part in the last 32 bits.
    357 
    358     # epoch = (1900, 1, 1, 0, 0, 0, 5, 1, 0) 
    359     # x = time.time()
    360     # from time import gmtime, strftime, gmtime, mktime
    361     # delta = mktime(gmtime(0)) - mktime(self.epoch)
    362     # x = x-delta
    363 
    364     tod = time.time() # time of day. Will bother with epoch later
    365     i = int(tod)
    366     j = int((tod - i)*(2**32))
    367     tod = struct.pack("!II", i,j)
    368     mac = RandMAC()
    369     # construct modified EUI-64 ID
    370     eui64 = inet_pton(socket.AF_INET6, '::' + in6_mactoifaceid(mac))[8:] 
    371     import hashlib
    372     globalid = hashlib.sha1(tod+eui64).digest()[:5]
    373     return inet_ntop(socket.AF_INET6, b'\xfd' + globalid + b'\x00'*10)
    374 
    375 def in6_getRandomizedIfaceId(ifaceid, previous=None):
    376     """
    377     Implements the interface ID generation algorithm described in RFC 3041.
    378     The function takes the Modified EUI-64 interface identifier generated
    379     as described in RFC 4291 and an optional previous history value (the
    380     first element of the output of this function). If no previous interface
    381     identifier is provided, a random one is generated. The function returns
    382     a tuple containing the randomized interface identifier and the history
    383     value (for possible future use). Input and output values are provided in
    384     a "printable" format as depicted below.
    385     
    386     ex: 
    387     >>> in6_getRandomizedIfaceId('20b:93ff:feeb:2d3')
    388     ('4c61:76ff:f46a:a5f3', 'd006:d540:db11:b092')
    389     >>> in6_getRandomizedIfaceId('20b:93ff:feeb:2d3',
    390                                  previous='d006:d540:db11:b092')
    391     ('fe97:46fe:9871:bd38', 'eeed:d79c:2e3f:62e')
    392     """
    393 
    394     s = b""
    395     if previous is None:
    396         d = b"".join(chb(x) for x in range(256))
    397         for _ in range(8):
    398             s += chb(random.choice(d))
    399         previous = s
    400     s = inet_pton(socket.AF_INET6, "::"+ifaceid)[8:] + previous
    401     import hashlib
    402     s = hashlib.md5(s).digest()
    403     s1,s2 = s[:8],s[8:]
    404     s1 = chb(orb(s1[0]) | 0x04) + s1[1:]
    405     s1 = inet_ntop(socket.AF_INET6, b"\xff"*8 + s1)[20:]
    406     s2 = inet_ntop(socket.AF_INET6, b"\xff"*8 + s2)[20:]    
    407     return (s1, s2)
    408 
    409 
    410 _rfc1924map = [ '0','1','2','3','4','5','6','7','8','9','A','B','C','D','E',
    411                 'F','G','H','I','J','K','L','M','N','O','P','Q','R','S','T',
    412                 'U','V','W','X','Y','Z','a','b','c','d','e','f','g','h','i',
    413                 'j','k','l','m','n','o','p','q','r','s','t','u','v','w','x',
    414                 'y','z','!','#','$','%','&','(',')','*','+','-',';','<','=',
    415                 '>','?','@','^','_','`','{','|','}','~' ]
    416 
    417 def in6_ctop(addr):
    418     """
    419     Convert an IPv6 address in Compact Representation Notation 
    420     (RFC 1924) to printable representation ;-)
    421     Returns None on error.
    422     """
    423     if len(addr) != 20 or not reduce(lambda x,y: x and y, 
    424                                      [x in _rfc1924map for x in addr]):
    425         return None
    426     i = 0
    427     for c in addr:
    428         j = _rfc1924map.index(c)
    429         i = 85*i + j
    430     res = []
    431     for j in range(4):
    432         res.append(struct.pack("!I", i%2**32))
    433         i = i//(2**32)
    434     res.reverse()
    435     return inet_ntop(socket.AF_INET6, b"".join(res))
    436 
    437 def in6_ptoc(addr):
    438     """
    439     Converts an IPv6 address in printable representation to RFC 
    440     1924 Compact Representation ;-) 
    441     Returns None on error.
    442     """    
    443     try:
    444         d=struct.unpack("!IIII", inet_pton(socket.AF_INET6, addr))
    445     except:
    446         return None
    447     res = 0
    448     m = [2**96, 2**64, 2**32, 1]
    449     for i in range(4):
    450         res += d[i]*m[i]
    451     rem = res
    452     res = []
    453     while rem:
    454         res.append(_rfc1924map[rem%85])
    455         rem = rem//85
    456     res.reverse()
    457     return "".join(res)
    458 
    459     
    460 def in6_isaddr6to4(x):
    461     """
    462     Return True if provided address (in printable format) is a 6to4
    463     address (being in 2002::/16).
    464     """
    465     x = inet_pton(socket.AF_INET6, x)
    466     return x[:2] == b' \x02'
    467 
    468 conf.teredoPrefix = "2001::" # old one was 3ffe:831f (it is a /32)
    469 conf.teredoServerPort = 3544
    470 
    471 def in6_isaddrTeredo(x):
    472     """
    473     Return True if provided address is a Teredo, meaning it is under 
    474     the /32 conf.teredoPrefix prefix value (by default, 2001::).
    475     Otherwise, False is returned. Address must be passed in printable
    476     format.
    477     """
    478     our = inet_pton(socket.AF_INET6, x)[0:4]
    479     teredoPrefix = inet_pton(socket.AF_INET6, conf.teredoPrefix)[0:4]
    480     return teredoPrefix == our
    481 
    482 def teredoAddrExtractInfo(x):
    483     """
    484     Extract information from a Teredo address. Return value is 
    485     a 4-tuple made of IPv4 address of Teredo server, flag value (int),
    486     mapped address (non obfuscated) and mapped port (non obfuscated).
    487     No specific checks are performed on passed address.
    488     """
    489     addr = inet_pton(socket.AF_INET6, x)
    490     server = inet_ntop(socket.AF_INET, addr[4:8])
    491     flag = struct.unpack("!H",addr[8:10])[0]
    492     mappedport = struct.unpack("!H",strxor(addr[10:12],b'\xff'*2))[0] 
    493     mappedaddr = inet_ntop(socket.AF_INET, strxor(addr[12:16],b'\xff'*4))
    494     return server, flag, mappedaddr, mappedport
    495 
    496 def in6_iseui64(x):
    497     """
    498     Return True if provided address has an interface identifier part
    499     created in modified EUI-64 format (meaning it matches *::*:*ff:fe*:*). 
    500     Otherwise, False is returned. Address must be passed in printable
    501     format.
    502     """
    503     eui64 = inet_pton(socket.AF_INET6, '::ff:fe00:0')
    504     x = in6_and(inet_pton(socket.AF_INET6, x), eui64)
    505     return x == eui64
    506 
    507 def in6_isanycast(x): # RFC 2526
    508     if in6_iseui64(x):
    509         s = '::fdff:ffff:ffff:ff80'
    510         packed_x = inet_pton(socket.AF_INET6, x)
    511         packed_s = inet_pton(socket.AF_INET6, s)
    512         x_and_s = in6_and(packed_x, packed_s) 
    513         return x_and_s == packed_s
    514     else:
    515         # not EUI-64 
    516         #|              n bits             |    121-n bits    |   7 bits   |
    517         #+---------------------------------+------------------+------------+
    518         #|           subnet prefix         | 1111111...111111 | anycast ID |
    519         #+---------------------------------+------------------+------------+
    520         #                                  |   interface identifier field  |
    521         warning('in6_isanycast(): TODO not EUI-64')
    522         return 0
    523 
    524 def _in6_bitops(a1, a2, operator=0):
    525     a1 = struct.unpack('4I', a1)
    526     a2 = struct.unpack('4I', a2)
    527     fop = [ lambda x,y: x | y,
    528             lambda x,y: x & y,
    529             lambda x,y: x ^ y
    530           ]
    531     ret = map(fop[operator%len(fop)], a1, a2)
    532     return b"".join(struct.pack('I', x) for x in ret)
    533 
    534 def in6_or(a1, a2):
    535     """
    536     Provides a bit to bit OR of provided addresses. They must be 
    537     passed in network format. Return value is also an IPv6 address
    538     in network format.
    539     """
    540     return _in6_bitops(a1, a2, 0)
    541 
    542 def in6_and(a1, a2):
    543     """
    544     Provides a bit to bit AND of provided addresses. They must be 
    545     passed in network format. Return value is also an IPv6 address
    546     in network format.
    547     """
    548     return _in6_bitops(a1, a2, 1)
    549 
    550 def in6_xor(a1, a2):
    551     """
    552     Provides a bit to bit XOR of provided addresses. They must be 
    553     passed in network format. Return value is also an IPv6 address
    554     in network format.
    555     """
    556     return _in6_bitops(a1, a2, 2)
    557 
    558 def in6_cidr2mask(m):
    559     """
    560     Return the mask (bitstring) associated with provided length 
    561     value. For instance if function is called on 48, return value is
    562     b'\xff\xff\xff\xff\xff\xff\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'.
    563     
    564     """
    565     if m > 128 or m < 0:
    566         raise Scapy_Exception("value provided to in6_cidr2mask outside [0, 128] domain (%d)" % m)
    567 
    568     t = []
    569     for i in range(0, 4):
    570         t.append(max(0, 2**32  - 2**(32-min(32, m))))
    571         m -= 32
    572 
    573     return b"".join(struct.pack('!I', x) for x in t)
    574 
    575 def in6_getnsma(a): 
    576     """
    577     Return link-local solicited-node multicast address for given
    578     address. Passed address must be provided in network format.
    579     Returned value is also in network format.
    580     """
    581 
    582     r = in6_and(a, inet_pton(socket.AF_INET6, '::ff:ffff'))
    583     r = in6_or(inet_pton(socket.AF_INET6, 'ff02::1:ff00:0'), r)
    584     return r
    585 
    586 def in6_getnsmac(a): # return multicast Ethernet address associated with multicast v6 destination
    587     """
    588     Return the multicast mac address associated with provided
    589     IPv6 address. Passed address must be in network format. 
    590     """
    591 
    592     a = struct.unpack('16B', a)[-4:]
    593     mac = '33:33:'
    594     mac += ':'.join("%.2x" %x for x in a)
    595     return mac
    596 
    597 def in6_getha(prefix): 
    598     """
    599     Return the anycast address associated with all home agents on a given
    600     subnet.
    601     """
    602     r = in6_and(inet_pton(socket.AF_INET6, prefix), in6_cidr2mask(64))
    603     r = in6_or(r, inet_pton(socket.AF_INET6, '::fdff:ffff:ffff:fffe'))
    604     return inet_ntop(socket.AF_INET6, r)
    605 
    606 def in6_ptop(str): 
    607     """
    608     Normalizes IPv6 addresses provided in printable format, returning the 
    609     same address in printable format. (2001:0db8:0:0::1 -> 2001:db8::1)
    610     """
    611     return inet_ntop(socket.AF_INET6, inet_pton(socket.AF_INET6, str))
    612 
    613 def in6_isincluded(addr, prefix, plen):
    614     """
    615     Returns True when 'addr' belongs to prefix/plen. False otherwise.
    616     """
    617     temp = inet_pton(socket.AF_INET6, addr)
    618     pref = in6_cidr2mask(plen)
    619     zero = inet_pton(socket.AF_INET6, prefix)
    620     return zero == in6_and(temp, pref)
    621 
    622 def in6_isllsnmaddr(str):
    623     """
    624     Return True if provided address is a link-local solicited node
    625     multicast address, i.e. belongs to ff02::1:ff00:0/104. False is
    626     returned otherwise.
    627     """
    628     temp = in6_and(b"\xff"*13+b"\x00"*3, inet_pton(socket.AF_INET6, str))
    629     temp2 = b'\xff\x02\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\xff\x00\x00\x00'
    630     return temp == temp2
    631 
    632 def in6_isdocaddr(str):
    633     """
    634     Returns True if provided address in printable format belongs to
    635     2001:db8::/32 address space reserved for documentation (as defined 
    636     in RFC 3849).
    637     """
    638     return in6_isincluded(str, '2001:db8::', 32)
    639 
    640 def in6_islladdr(str):
    641     """
    642     Returns True if provided address in printable format belongs to
    643     _allocated_ link-local unicast address space (fe80::/10)
    644     """
    645     return in6_isincluded(str, 'fe80::', 10)
    646 
    647 def in6_issladdr(str):
    648     """
    649     Returns True if provided address in printable format belongs to
    650     _allocated_ site-local address space (fec0::/10). This prefix has 
    651     been deprecated, address being now reserved by IANA. Function 
    652     will remain for historic reasons.
    653     """
    654     return in6_isincluded(str, 'fec0::', 10)
    655 
    656 def in6_isuladdr(str):
    657     """
    658     Returns True if provided address in printable format belongs to
    659     Unique local address space (fc00::/7).
    660     """
    661     return in6_isincluded(str, 'fc00::', 7)
    662 
    663 # TODO : we should see the status of Unique Local addresses against
    664 #        global address space.
    665 #        Up-to-date information is available through RFC 3587. 
    666 #        We should review function behavior based on its content.
    667 def in6_isgladdr(str):
    668     """
    669     Returns True if provided address in printable format belongs to
    670     _allocated_ global address space (2000::/3). Please note that,
    671     Unique Local addresses (FC00::/7) are not part of global address
    672     space, and won't match.
    673     """
    674     return in6_isincluded(str, '2000::', 3)
    675 
    676 def in6_ismaddr(str):
    677     """
    678     Returns True if provided address in printable format belongs to 
    679     allocated Multicast address space (ff00::/8).
    680     """
    681     return in6_isincluded(str, 'ff00::', 8)
    682 
    683 def in6_ismnladdr(str):
    684     """
    685     Returns True if address belongs to node-local multicast address
    686     space (ff01::/16) as defined in RFC 
    687     """
    688     return in6_isincluded(str, 'ff01::', 16)
    689 
    690 def in6_ismgladdr(str):
    691     """
    692     Returns True if address belongs to global multicast address
    693     space (ff0e::/16).
    694     """
    695     return in6_isincluded(str, 'ff0e::', 16)
    696 
    697 def in6_ismlladdr(str):
    698     """
    699     Returns True if address belongs to link-local multicast address
    700     space (ff02::/16)
    701     """
    702     return in6_isincluded(str, 'ff02::', 16)
    703 
    704 def in6_ismsladdr(str):
    705     """
    706     Returns True if address belongs to site-local multicast address
    707     space (ff05::/16). Site local address space has been deprecated.
    708     Function remains for historic reasons.
    709     """
    710     return in6_isincluded(str, 'ff05::', 16)
    711 
    712 def in6_isaddrllallnodes(str):
    713     """
    714     Returns True if address is the link-local all-nodes multicast 
    715     address (ff02::1). 
    716     """
    717     return (inet_pton(socket.AF_INET6, "ff02::1") ==
    718             inet_pton(socket.AF_INET6, str))
    719 
    720 def in6_isaddrllallservers(str):
    721     """
    722     Returns True if address is the link-local all-servers multicast 
    723     address (ff02::2). 
    724     """
    725     return (inet_pton(socket.AF_INET6, "ff02::2") ==
    726             inet_pton(socket.AF_INET6, str))
    727 
    728 def in6_getscope(addr):
    729     """
    730     Returns the scope of the address.
    731     """
    732     if in6_isgladdr(addr) or in6_isuladdr(addr):
    733         scope = IPV6_ADDR_GLOBAL
    734     elif in6_islladdr(addr):
    735         scope = IPV6_ADDR_LINKLOCAL
    736     elif in6_issladdr(addr):
    737         scope = IPV6_ADDR_SITELOCAL
    738     elif in6_ismaddr(addr):
    739         if in6_ismgladdr(addr):
    740             scope = IPV6_ADDR_GLOBAL
    741         elif in6_ismlladdr(addr):
    742             scope = IPV6_ADDR_LINKLOCAL
    743         elif in6_ismsladdr(addr):
    744             scope = IPV6_ADDR_SITELOCAL
    745         elif in6_ismnladdr(addr):
    746             scope = IPV6_ADDR_LOOPBACK
    747         else:
    748             scope = -1
    749     elif addr == '::1':
    750         scope = IPV6_ADDR_LOOPBACK
    751     else:
    752         scope = -1
    753     return scope
    754 
    755 def in6_get_common_plen(a, b):
    756     """
    757     Return common prefix length of IPv6 addresses a and b.
    758     """
    759     def matching_bits(byte1, byte2):
    760         for i in range(8):
    761             cur_mask = 0x80 >> i
    762             if (byte1 & cur_mask) != (byte2 & cur_mask):
    763                 return i
    764         return 8
    765         
    766     tmpA = inet_pton(socket.AF_INET6, a)
    767     tmpB = inet_pton(socket.AF_INET6, b)
    768     for i in range(16):
    769         mbits = matching_bits(orb(tmpA[i]), orb(tmpB[i]))
    770         if mbits != 8:
    771             return 8*i + mbits
    772     return 128
    773 
    774 def in6_isvalid(address):
    775     """Return True if 'address' is a valid IPv6 address string, False
    776        otherwise."""
    777 
    778     try:
    779         socket.inet_pton(socket.AF_INET6, address)
    780         return True
    781     except:
    782         return False
    783