Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.11/site-packages/scapy/utils6.py: 22%
Shortcuts on this page
r m x toggle line displays
j k next/prev highlighted chunk
0 (zero) top of page
1 (one) first highlighted chunk
Shortcuts on this page
r m x toggle line displays
j k next/prev highlighted chunk
0 (zero) top of page
1 (one) first highlighted chunk
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>
8"""
9Utility functions for IPv6.
10"""
11import socket
12import struct
13import time
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
31from typing import (
32 Iterator,
33 List,
34 Optional,
35 Tuple,
36 Union,
37 cast,
38)
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'.
51 Basically, the function filters all interface addresses to keep only those
52 that have the same scope as provided prefix.
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
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
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 """
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
126 sa = scope_mapper[sa]
127 sb = scope_mapper[sb]
129 if sa == sb:
130 return 0
131 if sa > sb:
132 return 1
133 return -1
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 """
142 # Rule 1: Prefer same address
143 if source_a == dst:
144 return 1
145 if source_b == dst:
146 return 1
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
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
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
176 if not candidate_set:
177 # Should not happen
178 return ""
180 candidate_set.sort(key=cmp_to_key(rfc3484_cmp), reverse=True)
182 return candidate_set[0]
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)
218 return addrType
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()
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
257 if ifaceid[3:5] != b'\xff\xfe': # Check for burned-in MAC address
258 return None
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)
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)
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
298 res = conf.manufdb._get_manuf(mac)
299 if len(res) == 17 and res.count(':') != 5: # Mac address, i.e. unknown
300 res = "UNKNOWN"
302 return res
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.
311 'addr' parameter specifies the link-local address to use for generating
312 Link-scoped multicast address IID.
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).
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).
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
337 iid = baddr[8:]
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)
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
373 return inet_ntop(socket.AF_INET6, a)
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
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])
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.
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
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)
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.
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 """
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)
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 '>', '?', '@', '^', '_', '`', '{', '|', '}', '~']
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))
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)
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'
533conf.teredoPrefix = "2001::" # old one was 3ffe:831f (it is a /32)
534conf.teredoServerPort = 3544
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
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
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
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
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)
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)
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)
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'.
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
639 t = []
640 for i in range(0, 4):
641 t.append(max(0, 2**32 - 2**(32 - min(32, m))))
642 m -= 32
644 return b"".join(struct.pack('!I', x) for x in t)
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")
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
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 """
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
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 """
683 ba = struct.unpack('16B', a)[-4:]
684 mac = '33:33:'
685 mac += ':'.join("%.2x" % x for x in ba)
686 return mac
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)
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))
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)
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
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)
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)
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)
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)
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.
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)
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)
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)
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)
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)
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)
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))
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))
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
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
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
904def in6_isvalid(address):
905 # type: (str) -> bool
906 """Return True if 'address' is a valid IPv6 address string, False
907 otherwise."""
909 try:
910 inet_pton(socket.AF_INET6, address)
911 return True
912 except Exception:
913 return False
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
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)
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 )