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

408 statements  

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 )