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