• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1## This file is part of Scapy
2## See http://www.secdev.org/projects/scapy for more informations
3## Copyright (C) Philippe Biondi <phil@secdev.org>
4## This program is published under a GPLv2 license
5
6## Copyright (C) 2005  Guillaume Valadon <guedou@hongo.wide.ad.jp>
7##                     Arnaud Ebalard <arnaud.ebalard@eads.net>
8
9"""
10Utility functions for IPv6.
11"""
12from __future__ import absolute_import
13import random
14import socket
15import struct
16
17from scapy.config import conf
18import scapy.consts
19from scapy.data import *
20from scapy.utils import *
21from scapy.compat import *
22from scapy.pton_ntop import *
23from scapy.volatile import RandMAC
24from scapy.error import warning
25from functools import reduce
26from scapy.modules.six.moves import range
27
28
29def 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
79def 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
164def 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
195def 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
214def 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
234def 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
244def 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
262def 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
320def 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
333def 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
347def 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
375def 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
417def 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
437def 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
460def 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
468conf.teredoPrefix = "2001::" # old one was 3ffe:831f (it is a /32)
469conf.teredoServerPort = 3544
470
471def 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
482def 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
496def 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
507def 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
524def _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
534def 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
542def 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
550def 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
558def 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
575def 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
586def 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
597def 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
606def 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
613def 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
622def 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
632def 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
640def 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
647def 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
656def 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.
667def 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
676def 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
683def 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
690def 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
697def 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
704def 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
712def 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
720def 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
728def 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
755def 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
774def 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