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