Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.11/site-packages/scapy/layers/dhcp6.py: 62%

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

656 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) Philippe Biondi <phil@secdev.org> 

6# Copyright (C) 2005 Guillaume Valadon <guedou@hongo.wide.ad.jp> 

7# Arnaud Ebalard <arnaud.ebalard@eads.net> 

8 

9""" 

10DHCPv6: Dynamic Host Configuration Protocol for IPv6. [RFC 3315,8415] 

11""" 

12 

13import socket 

14import struct 

15import time 

16 

17from scapy.ansmachine import AnsweringMachine 

18from scapy.arch import get_if_hwaddr, in6_getifaddr 

19from scapy.config import conf 

20from scapy.data import EPOCH, ETHER_ANY 

21from scapy.compat import raw, orb 

22from scapy.error import warning 

23from scapy.fields import BitField, ByteEnumField, ByteField, FieldLenField, \ 

24 FlagsField, IntEnumField, IntField, MACField, \ 

25 PacketListField, ShortEnumField, ShortField, StrField, StrFixedLenField, \ 

26 StrLenField, UTCTimeField, X3BytesField, XIntField, XShortEnumField, \ 

27 PacketLenField, UUIDField, FieldListField 

28from scapy.data import IANA_ENTERPRISE_NUMBERS 

29from scapy.layers.dns import DNSStrField 

30from scapy.layers.inet import UDP 

31from scapy.layers.inet6 import DomainNameListField, IP6Field, IP6ListField, \ 

32 IPv6 

33from scapy.packet import Packet, bind_bottom_up 

34from scapy.pton_ntop import inet_pton 

35from scapy.sendrecv import send 

36from scapy.themes import Color 

37from scapy.utils6 import in6_addrtovendor, in6_islladdr 

38 

39############################################################################# 

40# Helpers ## 

41############################################################################# 

42 

43 

44def get_cls(name, fallback_cls): 

45 return globals().get(name, fallback_cls) 

46 

47 

48dhcp6_cls_by_type = {1: "DHCP6_Solicit", 

49 2: "DHCP6_Advertise", 

50 3: "DHCP6_Request", 

51 4: "DHCP6_Confirm", 

52 5: "DHCP6_Renew", 

53 6: "DHCP6_Rebind", 

54 7: "DHCP6_Reply", 

55 8: "DHCP6_Release", 

56 9: "DHCP6_Decline", 

57 10: "DHCP6_Reconf", 

58 11: "DHCP6_InfoRequest", 

59 12: "DHCP6_RelayForward", 

60 13: "DHCP6_RelayReply", 

61 36: "DHCP6_AddrRegInform", 

62 37: "DHCP6_AddrRegReply", 

63 } 

64 

65 

66def _dhcp6_dispatcher(x, *args, **kargs): 

67 cls = conf.raw_layer 

68 if len(x) >= 2: 

69 cls = get_cls(dhcp6_cls_by_type.get(orb(x[0]), "Raw"), conf.raw_layer) 

70 return cls(x, *args, **kargs) 

71 

72############################################################################# 

73############################################################################# 

74# DHCPv6 # 

75############################################################################# 

76############################################################################# 

77 

78 

79All_DHCP_Relay_Agents_and_Servers = "ff02::1:2" 

80All_DHCP_Servers = "ff05::1:3" # Site-Local scope : deprecated by 3879 

81 

82dhcp6opts = {1: "CLIENTID", 

83 2: "SERVERID", 

84 3: "IA_NA", 

85 4: "IA_TA", 

86 5: "IAADDR", 

87 6: "ORO", 

88 7: "PREFERENCE", 

89 8: "ELAPSED_TIME", 

90 9: "RELAY_MSG", 

91 11: "AUTH", 

92 12: "UNICAST", 

93 13: "STATUS_CODE", 

94 14: "RAPID_COMMIT", 

95 15: "USER_CLASS", 

96 16: "VENDOR_CLASS", 

97 17: "VENDOR_OPTS", 

98 18: "INTERFACE_ID", 

99 19: "RECONF_MSG", 

100 20: "RECONF_ACCEPT", 

101 21: "SIP Servers Domain Name List", # RFC3319 

102 22: "SIP Servers IPv6 Address List", # RFC3319 

103 23: "DNS Recursive Name Server Option", # RFC3646 

104 24: "Domain Search List option", # RFC3646 

105 25: "OPTION_IA_PD", # RFC3633 

106 26: "OPTION_IAPREFIX", # RFC3633 

107 27: "OPTION_NIS_SERVERS", # RFC3898 

108 28: "OPTION_NISP_SERVERS", # RFC3898 

109 29: "OPTION_NIS_DOMAIN_NAME", # RFC3898 

110 30: "OPTION_NISP_DOMAIN_NAME", # RFC3898 

111 31: "OPTION_SNTP_SERVERS", # RFC4075 

112 32: "OPTION_INFORMATION_REFRESH_TIME", # RFC4242 

113 33: "OPTION_BCMCS_SERVER_D", # RFC4280 

114 34: "OPTION_BCMCS_SERVER_A", # RFC4280 

115 36: "OPTION_GEOCONF_CIVIC", # RFC-ietf-geopriv-dhcp-civil-09.txt 

116 37: "OPTION_REMOTE_ID", # RFC4649 

117 38: "OPTION_SUBSCRIBER_ID", # RFC4580 

118 39: "OPTION_CLIENT_FQDN", # RFC4704 

119 40: "OPTION_PANA_AGENT", # RFC5192 

120 41: "OPTION_NEW_POSIX_TIMEZONE", # RFC4833 

121 42: "OPTION_NEW_TZDB_TIMEZONE", # RFC4833 

122 48: "OPTION_LQ_CLIENT_LINK", # RFC5007 

123 56: "OPTION_NTP_SERVER", # RFC5908 

124 59: "OPT_BOOTFILE_URL", # RFC5970 

125 60: "OPT_BOOTFILE_PARAM", # RFC5970 

126 61: "OPTION_CLIENT_ARCH_TYPE", # RFC5970 

127 62: "OPTION_NII", # RFC5970 

128 65: "OPTION_ERP_LOCAL_DOMAIN_NAME", # RFC6440 

129 66: "OPTION_RELAY_SUPPLIED_OPTIONS", # RFC6422 

130 68: "OPTION_VSS", # RFC6607 

131 79: "OPTION_CLIENT_LINKLAYER_ADDR", # RFC6939 

132 103: "OPTION_CAPTIVE_PORTAL", # RFC8910 

133 112: "OPTION_MUD_URL", # RFC8520 

134 148: "OPTION_ADDR_REG_ENABLE", # RFC9686 

135 } 

136 

137dhcp6opts_by_code = {1: "DHCP6OptClientId", 

138 2: "DHCP6OptServerId", 

139 3: "DHCP6OptIA_NA", 

140 4: "DHCP6OptIA_TA", 

141 5: "DHCP6OptIAAddress", 

142 6: "DHCP6OptOptReq", 

143 7: "DHCP6OptPref", 

144 8: "DHCP6OptElapsedTime", 

145 9: "DHCP6OptRelayMsg", 

146 11: "DHCP6OptAuth", 

147 12: "DHCP6OptServerUnicast", 

148 13: "DHCP6OptStatusCode", 

149 14: "DHCP6OptRapidCommit", 

150 15: "DHCP6OptUserClass", 

151 16: "DHCP6OptVendorClass", 

152 17: "DHCP6OptVendorSpecificInfo", 

153 18: "DHCP6OptIfaceId", 

154 19: "DHCP6OptReconfMsg", 

155 20: "DHCP6OptReconfAccept", 

156 21: "DHCP6OptSIPDomains", # RFC3319 

157 22: "DHCP6OptSIPServers", # RFC3319 

158 23: "DHCP6OptDNSServers", # RFC3646 

159 24: "DHCP6OptDNSDomains", # RFC3646 

160 25: "DHCP6OptIA_PD", # RFC3633 

161 26: "DHCP6OptIAPrefix", # RFC3633 

162 27: "DHCP6OptNISServers", # RFC3898 

163 28: "DHCP6OptNISPServers", # RFC3898 

164 29: "DHCP6OptNISDomain", # RFC3898 

165 30: "DHCP6OptNISPDomain", # RFC3898 

166 31: "DHCP6OptSNTPServers", # RFC4075 

167 32: "DHCP6OptInfoRefreshTime", # RFC4242 

168 33: "DHCP6OptBCMCSDomains", # RFC4280 

169 34: "DHCP6OptBCMCSServers", # RFC4280 

170 # 36: "DHCP6OptGeoConf", #RFC-ietf-geopriv-dhcp-civil-09.txt # noqa: E501 

171 37: "DHCP6OptRemoteID", # RFC4649 

172 38: "DHCP6OptSubscriberID", # RFC4580 

173 39: "DHCP6OptClientFQDN", # RFC4704 

174 40: "DHCP6OptPanaAuthAgent", # RFC-ietf-dhc-paa-option-05.txt # noqa: E501 

175 41: "DHCP6OptNewPOSIXTimeZone", # RFC4833 

176 42: "DHCP6OptNewTZDBTimeZone", # RFC4833 

177 43: "DHCP6OptRelayAgentERO", # RFC4994 

178 # 44: "DHCP6OptLQQuery", #RFC5007 

179 # 45: "DHCP6OptLQClientData", #RFC5007 

180 # 46: "DHCP6OptLQClientTime", #RFC5007 

181 # 47: "DHCP6OptLQRelayData", #RFC5007 

182 48: "DHCP6OptLQClientLink", # RFC5007 

183 56: "DHCP6OptNTPServer", # RFC5908 

184 59: "DHCP6OptBootFileUrl", # RFC5790 

185 60: "DHCP6OptBootFileParam", # RFC5970 

186 61: "DHCP6OptClientArchType", # RFC5970 

187 62: "DHCP6OptClientNetworkInterId", # RFC5970 

188 65: "DHCP6OptERPDomain", # RFC6440 

189 66: "DHCP6OptRelaySuppliedOpt", # RFC6422 

190 68: "DHCP6OptVSS", # RFC6607 

191 79: "DHCP6OptClientLinkLayerAddr", # RFC6939 

192 103: "DHCP6OptCaptivePortal", # RFC8910 

193 112: "DHCP6OptMudUrl", # RFC8520 

194 148: "DHCP6OptAddrRegEnable", # RFC9686 

195 } 

196 

197 

198# sect 7.3 RFC 8415 : DHCP6 Messages types 

199# also RFC 9686 

200dhcp6types = {1: "SOLICIT", 

201 2: "ADVERTISE", 

202 3: "REQUEST", 

203 4: "CONFIRM", 

204 5: "RENEW", 

205 6: "REBIND", 

206 7: "REPLY", 

207 8: "RELEASE", 

208 9: "DECLINE", 

209 10: "RECONFIGURE", 

210 11: "INFORMATION-REQUEST", 

211 12: "RELAY-FORW", 

212 13: "RELAY-REPL", 

213 36: "ADDR-REG-INFORM", 

214 37: "ADDR-REG-REPLY", 

215 } 

216 

217 

218##################################################################### 

219# DHCPv6 DUID related stuff # 

220##################################################################### 

221 

222duidtypes = {1: "Link-layer address plus time", 

223 2: "Vendor-assigned unique ID based on Enterprise Number", 

224 3: "Link-layer Address", 

225 4: "UUID"} 

226 

227# DUID hardware types - RFC 826 - Extracted from 

228# http://www.iana.org/assignments/arp-parameters on 31/10/06 

229# We should add the length of every kind of address. 

230duidhwtypes = {0: "NET/ROM pseudo", # Not referenced by IANA 

231 1: "Ethernet (10Mb)", 

232 2: "Experimental Ethernet (3Mb)", 

233 3: "Amateur Radio AX.25", 

234 4: "Proteon ProNET Token Ring", 

235 5: "Chaos", 

236 6: "IEEE 802 Networks", 

237 7: "ARCNET", 

238 8: "Hyperchannel", 

239 9: "Lanstar", 

240 10: "Autonet Short Address", 

241 11: "LocalTalk", 

242 12: "LocalNet (IBM PCNet or SYTEK LocalNET)", 

243 13: "Ultra link", 

244 14: "SMDS", 

245 15: "Frame Relay", 

246 16: "Asynchronous Transmission Mode (ATM)", 

247 17: "HDLC", 

248 18: "Fibre Channel", 

249 19: "Asynchronous Transmission Mode (ATM)", 

250 20: "Serial Line", 

251 21: "Asynchronous Transmission Mode (ATM)", 

252 22: "MIL-STD-188-220", 

253 23: "Metricom", 

254 24: "IEEE 1394.1995", 

255 25: "MAPOS", 

256 26: "Twinaxial", 

257 27: "EUI-64", 

258 28: "HIPARP", 

259 29: "IP and ARP over ISO 7816-3", 

260 30: "ARPSec", 

261 31: "IPsec tunnel", 

262 32: "InfiniBand (TM)", 

263 33: "TIA-102 Project 25 Common Air Interface (CAI)"} 

264 

265 

266class _UTCTimeField(UTCTimeField): 

267 def __init__(self, *args, **kargs): 

268 epoch_2000 = (2000, 1, 1, 0, 0, 0, 5, 1, 0) # required Epoch 

269 UTCTimeField.__init__(self, epoch=epoch_2000, *args, **kargs) 

270 

271 

272class _LLAddrField(MACField): 

273 pass 

274 

275# XXX We only support Ethernet addresses at the moment. _LLAddrField 

276# will be modified when needed. Ask us. --arno 

277 

278 

279class DUID_LLT(Packet): # sect 9.2 RFC 3315 

280 name = "DUID - Link-layer address plus time" 

281 fields_desc = [ShortEnumField("type", 1, duidtypes), 

282 XShortEnumField("hwtype", 1, duidhwtypes), 

283 _UTCTimeField("timeval", 0), # i.e. 01 Jan 2000 

284 _LLAddrField("lladdr", ETHER_ANY)] 

285 

286 

287class DUID_EN(Packet): # sect 9.3 RFC 3315 

288 name = "DUID - Assigned by Vendor Based on Enterprise Number" 

289 fields_desc = [ShortEnumField("type", 2, duidtypes), 

290 IntEnumField("enterprisenum", 311, IANA_ENTERPRISE_NUMBERS), 

291 StrField("id", "")] 

292 

293 

294class DUID_LL(Packet): # sect 9.4 RFC 3315 

295 name = "DUID - Based on Link-layer Address" 

296 fields_desc = [ShortEnumField("type", 3, duidtypes), 

297 XShortEnumField("hwtype", 1, duidhwtypes), 

298 _LLAddrField("lladdr", ETHER_ANY)] 

299 

300 

301class DUID_UUID(Packet): # RFC 6355 

302 name = "DUID - Based on UUID" 

303 fields_desc = [ShortEnumField("type", 4, duidtypes), 

304 UUIDField("uuid", None, uuid_fmt=UUIDField.FORMAT_BE)] 

305 

306 

307duid_cls = {1: "DUID_LLT", 

308 2: "DUID_EN", 

309 3: "DUID_LL", 

310 4: "DUID_UUID"} 

311 

312##################################################################### 

313# DHCPv6 Options classes # 

314##################################################################### 

315 

316 

317class _DHCP6OptGuessPayload(Packet): 

318 @staticmethod 

319 def _just_guess_payload_class(cls, payload): 

320 # try to guess what option is in the payload 

321 if len(payload) <= 2: 

322 return conf.raw_layer 

323 opt = struct.unpack("!H", payload[:2])[0] 

324 clsname = dhcp6opts_by_code.get(opt, None) 

325 if clsname is None: 

326 return cls 

327 return get_cls(clsname, cls) 

328 

329 def guess_payload_class(self, payload): 

330 # this method is used in case of all derived classes 

331 # from _DHCP6OptGuessPayload in this file 

332 return _DHCP6OptGuessPayload._just_guess_payload_class( 

333 DHCP6OptUnknown, 

334 payload 

335 ) 

336 

337 

338class _DHCP6OptGuessPayloadElt(_DHCP6OptGuessPayload): 

339 """ 

340 Same than _DHCP6OptGuessPayload but made for lists 

341 in case of list of different suboptions 

342 e.g. in ianaopts in DHCP6OptIA_NA 

343 """ 

344 @classmethod 

345 def dispatch_hook(cls, payload=None, *args, **kargs): 

346 return cls._just_guess_payload_class(conf.raw_layer, payload) 

347 

348 def extract_padding(self, s): 

349 return b"", s 

350 

351 

352class DHCP6OptUnknown(_DHCP6OptGuessPayload): # A generic DHCPv6 Option 

353 name = "Unknown DHCPv6 Option" 

354 fields_desc = [ShortEnumField("optcode", 0, dhcp6opts), 

355 FieldLenField("optlen", None, length_of="data", fmt="!H"), 

356 StrLenField("data", "", 

357 length_from=lambda pkt: pkt.optlen)] 

358 

359 

360def _duid_dispatcher(x): 

361 cls = conf.raw_layer 

362 if len(x) > 4: 

363 o = struct.unpack("!H", x[:2])[0] 

364 cls = get_cls(duid_cls.get(o, conf.raw_layer), conf.raw_layer) 

365 return cls(x) 

366 

367 

368class DHCP6OptClientId(_DHCP6OptGuessPayload): # RFC 8415 sect 21.2 

369 name = "DHCP6 Client Identifier Option" 

370 fields_desc = [ShortEnumField("optcode", 1, dhcp6opts), 

371 FieldLenField("optlen", None, length_of="duid", fmt="!H"), 

372 PacketLenField("duid", "", _duid_dispatcher, 

373 length_from=lambda pkt: pkt.optlen)] 

374 

375 

376class DHCP6OptServerId(DHCP6OptClientId): # RFC 8415 sect 21.3 

377 name = "DHCP6 Server Identifier Option" 

378 optcode = 2 

379 

380# Should be encapsulated in the option field of IA_NA or IA_TA options 

381# Can only appear at that location. 

382 

383 

384class DHCP6OptIAAddress(_DHCP6OptGuessPayload): # RFC 8415 sect 21.6 

385 name = "DHCP6 IA Address Option (IA_TA or IA_NA suboption)" 

386 fields_desc = [ShortEnumField("optcode", 5, dhcp6opts), 

387 FieldLenField("optlen", None, length_of="iaaddropts", 

388 fmt="!H", adjust=lambda pkt, x: x + 24), 

389 IP6Field("addr", "::"), 

390 IntEnumField("preflft", 0, {0xffffffff: "infinity"}), 

391 IntEnumField("validlft", 0, {0xffffffff: "infinity"}), 

392 # last field IAaddr-options is not defined in the 

393 # reference document. We copy what wireshark does: read 

394 # more dhcp6 options and excpect failures 

395 PacketListField("iaaddropts", [], 

396 _DHCP6OptGuessPayloadElt, 

397 length_from=lambda pkt: pkt.optlen - 24)] 

398 

399 def guess_payload_class(self, payload): 

400 return conf.padding_layer 

401 

402 

403class DHCP6OptIA_NA(_DHCP6OptGuessPayload): # RFC 8415 sect 21.4 

404 name = "DHCP6 Identity Association for Non-temporary Addresses Option" 

405 fields_desc = [ShortEnumField("optcode", 3, dhcp6opts), 

406 FieldLenField("optlen", None, length_of="ianaopts", 

407 fmt="!H", adjust=lambda pkt, x: x + 12), 

408 XIntField("iaid", None), 

409 IntField("T1", None), 

410 IntField("T2", None), 

411 PacketListField("ianaopts", [], _DHCP6OptGuessPayloadElt, 

412 length_from=lambda pkt: pkt.optlen - 12)] 

413 

414 

415class DHCP6OptIA_TA(_DHCP6OptGuessPayload): # RFC 8415 sect 21.5 

416 name = "DHCP6 Identity Association for Temporary Addresses Option" 

417 fields_desc = [ShortEnumField("optcode", 4, dhcp6opts), 

418 FieldLenField("optlen", None, length_of="iataopts", 

419 fmt="!H", adjust=lambda pkt, x: x + 4), 

420 XIntField("iaid", None), 

421 PacketListField("iataopts", [], _DHCP6OptGuessPayloadElt, 

422 length_from=lambda pkt: pkt.optlen - 4)] 

423 

424 

425# DHCPv6 Option Request Option # 

426 

427class _OptReqListField(StrLenField): 

428 islist = 1 

429 

430 def i2h(self, pkt, x): 

431 if not x: 

432 return [] 

433 return x 

434 

435 def i2len(self, pkt, x): 

436 return 2 * len(x) 

437 

438 def any2i(self, pkt, x): 

439 return x 

440 

441 def i2repr(self, pkt, x): 

442 s = [] 

443 for y in self.i2h(pkt, x): 

444 if y in dhcp6opts: 

445 s.append(dhcp6opts[y]) 

446 else: 

447 s.append("%d" % y) 

448 return "[%s]" % ", ".join(s) 

449 

450 def m2i(self, pkt, x): 

451 r = [] 

452 while len(x) != 0: 

453 if len(x) < 2: 

454 warning("Odd length for requested option field. Rejecting last byte") # noqa: E501 

455 return r 

456 r.append(struct.unpack("!H", x[:2])[0]) 

457 x = x[2:] 

458 return r 

459 

460 def i2m(self, pkt, x): 

461 return b"".join(struct.pack('!H', y) for y in x) 

462 

463# A client may include an ORO in a solicit, Request, Renew, Rebind, 

464# Confirm or Information-request 

465 

466 

467class DHCP6OptOptReq(_DHCP6OptGuessPayload): # RFC 8415 sect 21.7 

468 name = "DHCP6 Option Request Option" 

469 fields_desc = [ShortEnumField("optcode", 6, dhcp6opts), 

470 FieldLenField("optlen", None, length_of="reqopts", fmt="!H"), # noqa: E501 

471 _OptReqListField("reqopts", [23, 24], 

472 length_from=lambda pkt: pkt.optlen)] 

473 

474 

475# DHCPv6 Preference Option # 

476 

477# emise par un serveur pour affecter le choix fait par le client. Dans 

478# les messages Advertise, a priori 

479class DHCP6OptPref(_DHCP6OptGuessPayload): # RFC 8415 sect 21.8 

480 name = "DHCP6 Preference Option" 

481 fields_desc = [ShortEnumField("optcode", 7, dhcp6opts), 

482 ShortField("optlen", 1), 

483 ByteField("prefval", 255)] 

484 

485 

486# DHCPv6 Elapsed Time Option # 

487 

488class _ElapsedTimeField(ShortField): 

489 def i2repr(self, pkt, x): 

490 if x == 0xffff: 

491 return "infinity (0xffff)" 

492 return "%.2f sec" % (self.i2h(pkt, x) / 100.) 

493 

494 

495class DHCP6OptElapsedTime(_DHCP6OptGuessPayload): # RFC 8415 sect 21.9 

496 name = "DHCP6 Elapsed Time Option" 

497 fields_desc = [ShortEnumField("optcode", 8, dhcp6opts), 

498 ShortField("optlen", 2), 

499 _ElapsedTimeField("elapsedtime", 0)] 

500 

501 

502# DHCPv6 Authentication Option # 

503 

504# The following fields are set in an Authentication option for the 

505# Reconfigure Key Authentication Protocol: 

506# 

507# protocol 3 

508# 

509# algorithm 1 

510# 

511# RDM 0 

512# 

513# The format of the Authentication information for the Reconfigure Key 

514# Authentication Protocol is: 

515# 

516# 0 1 2 3 

517# 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 

518# +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 

519# | Type | Value (128 bits) | 

520# +-+-+-+-+-+-+-+-+ | 

521# . . 

522# . . 

523# . +-+-+-+-+-+-+-+-+ 

524# | | 

525# +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 

526# 

527# Type Type of data in Value field carried in this option: 

528# 

529# 1 Reconfigure Key value (used in Reply message). 

530# 

531# 2 HMAC-MD5 digest of the message (used in Reconfigure 

532# message). 

533# 

534# Value Data as defined by field. 

535 

536# https://www.iana.org/assignments/auth-namespaces 

537_dhcp6_auth_proto = { 

538 0: "configuration token", 

539 1: "delayed authentication", 

540 2: "delayed authentication (obsolete)", 

541 3: "reconfigure key", 

542} 

543_dhcp6_auth_alg = { 

544 0: "configuration token", 

545 1: "HMAC-MD5", 

546} 

547_dhcp6_auth_rdm = { 

548 0: "use of a monotonically increasing value" 

549} 

550 

551 

552class DHCP6OptAuth(_DHCP6OptGuessPayload): # RFC 8415 sect 21.11 

553 name = "DHCP6 Option - Authentication" 

554 fields_desc = [ShortEnumField("optcode", 11, dhcp6opts), 

555 FieldLenField("optlen", None, length_of="authinfo", 

556 fmt="!H", adjust=lambda pkt, x: x + 11), 

557 ByteEnumField("proto", 3, _dhcp6_auth_proto), 

558 ByteEnumField("alg", 1, _dhcp6_auth_alg), 

559 ByteEnumField("rdm", 0, _dhcp6_auth_rdm), 

560 StrFixedLenField("replay", b"\x00" * 8, 8), 

561 StrLenField("authinfo", "", 

562 length_from=lambda pkt: pkt.optlen - 11)] 

563 

564# DHCPv6 Server Unicast Option # 

565 

566 

567class _SrvAddrField(IP6Field): 

568 def i2h(self, pkt, x): 

569 if x is None: 

570 return "::" 

571 return x 

572 

573 def i2m(self, pkt, x): 

574 return inet_pton(socket.AF_INET6, self.i2h(pkt, x)) 

575 

576 

577class DHCP6OptServerUnicast(_DHCP6OptGuessPayload): # RFC 8415 sect 21.12 

578 name = "DHCP6 Server Unicast Option" 

579 fields_desc = [ShortEnumField("optcode", 12, dhcp6opts), 

580 ShortField("optlen", 16), 

581 _SrvAddrField("srvaddr", None)] 

582 

583 

584# DHCPv6 Status Code Option # 

585 

586dhcp6statuscodes = {0: "Success", # RFC 8415 sect 21.13 

587 1: "UnspecFail", 

588 2: "NoAddrsAvail", 

589 3: "NoBinding", 

590 4: "NotOnLink", 

591 5: "UseMulticast", 

592 6: "NoPrefixAvail"} # From RFC3633 

593 

594 

595class DHCP6OptStatusCode(_DHCP6OptGuessPayload): # RFC 8415 sect 21.13 

596 name = "DHCP6 Status Code Option" 

597 fields_desc = [ShortEnumField("optcode", 13, dhcp6opts), 

598 FieldLenField("optlen", None, length_of="statusmsg", 

599 fmt="!H", adjust=lambda pkt, x:x + 2), 

600 ShortEnumField("statuscode", None, dhcp6statuscodes), 

601 StrLenField("statusmsg", "", 

602 length_from=lambda pkt: pkt.optlen - 2)] 

603 

604 

605# DHCPv6 Rapid Commit Option # 

606 

607class DHCP6OptRapidCommit(_DHCP6OptGuessPayload): # RFC 8415 sect 21.14 

608 name = "DHCP6 Rapid Commit Option" 

609 fields_desc = [ShortEnumField("optcode", 14, dhcp6opts), 

610 ShortField("optlen", 0)] 

611 

612 

613# DHCPv6 User Class Option # 

614 

615class _UserClassDataField(PacketListField): 

616 def i2len(self, pkt, z): 

617 if z is None or z == []: 

618 return 0 

619 return sum(len(raw(x)) for x in z) 

620 

621 def getfield(self, pkt, s): 

622 tmp_len = self.length_from(pkt) 

623 lst = [] 

624 remain, payl = s[:tmp_len], s[tmp_len:] 

625 while len(remain) > 0: 

626 p = self.m2i(pkt, remain) 

627 if conf.padding_layer in p: 

628 pad = p[conf.padding_layer] 

629 remain = pad.load 

630 del pad.underlayer.payload 

631 else: 

632 remain = "" 

633 lst.append(p) 

634 return payl, lst 

635 

636 

637class USER_CLASS_DATA(Packet): 

638 name = "user class data" 

639 fields_desc = [FieldLenField("len", None, length_of="data"), 

640 StrLenField("data", "", 

641 length_from=lambda pkt: pkt.len)] 

642 

643 def guess_payload_class(self, payload): 

644 return conf.padding_layer 

645 

646 

647class DHCP6OptUserClass(_DHCP6OptGuessPayload): # RFC 8415 sect 21.15 

648 name = "DHCP6 User Class Option" 

649 fields_desc = [ShortEnumField("optcode", 15, dhcp6opts), 

650 FieldLenField("optlen", None, fmt="!H", 

651 length_of="userclassdata"), 

652 _UserClassDataField("userclassdata", [], USER_CLASS_DATA, 

653 length_from=lambda pkt: pkt.optlen)] 

654 

655 

656# DHCPv6 Vendor Class Option # 

657 

658class _VendorClassDataField(_UserClassDataField): 

659 pass 

660 

661 

662class VENDOR_CLASS_DATA(USER_CLASS_DATA): 

663 name = "vendor class data" 

664 

665 

666class DHCP6OptVendorClass(_DHCP6OptGuessPayload): # RFC 8415 sect 21.16 

667 name = "DHCP6 Vendor Class Option" 

668 fields_desc = [ShortEnumField("optcode", 16, dhcp6opts), 

669 FieldLenField("optlen", None, length_of="vcdata", fmt="!H", 

670 adjust=lambda pkt, x: x + 4), 

671 IntEnumField("enterprisenum", None, 

672 IANA_ENTERPRISE_NUMBERS), 

673 _VendorClassDataField("vcdata", [], VENDOR_CLASS_DATA, 

674 length_from=lambda pkt: pkt.optlen - 4)] # noqa: E501 

675 

676# DHCPv6 Vendor-Specific Information Option # 

677 

678 

679class VENDOR_SPECIFIC_OPTION(_DHCP6OptGuessPayload): 

680 name = "vendor specific option data" 

681 fields_desc = [ShortField("optcode", None), 

682 FieldLenField("optlen", None, length_of="optdata"), 

683 StrLenField("optdata", "", 

684 length_from=lambda pkt: pkt.optlen)] 

685 

686 def guess_payload_class(self, payload): 

687 return conf.padding_layer 

688 

689# The third one that will be used for nothing interesting 

690 

691 

692class DHCP6OptVendorSpecificInfo(_DHCP6OptGuessPayload): # RFC 8415 sect 21.17 

693 name = "DHCP6 Vendor-specific Information Option" 

694 fields_desc = [ShortEnumField("optcode", 17, dhcp6opts), 

695 FieldLenField("optlen", None, length_of="vso", fmt="!H", 

696 adjust=lambda pkt, x: x + 4), 

697 IntEnumField("enterprisenum", None, 

698 IANA_ENTERPRISE_NUMBERS), 

699 _VendorClassDataField("vso", [], VENDOR_SPECIFIC_OPTION, 

700 length_from=lambda pkt: pkt.optlen - 4)] # noqa: E501 

701 

702# DHCPv6 Interface-ID Option # 

703 

704# Repasser sur cette option a la fin. Elle a pas l'air d'etre des 

705# masses critique. 

706 

707 

708class DHCP6OptIfaceId(_DHCP6OptGuessPayload): # RFC 8415 sect 21.18 

709 name = "DHCP6 Interface-Id Option" 

710 fields_desc = [ShortEnumField("optcode", 18, dhcp6opts), 

711 FieldLenField("optlen", None, fmt="!H", 

712 length_of="ifaceid"), 

713 StrLenField("ifaceid", "", 

714 length_from=lambda pkt: pkt.optlen)] 

715 

716 

717# DHCPv6 Reconfigure Message Option # 

718 

719# A server includes a Reconfigure Message option in a Reconfigure 

720# message to indicate to the client whether the client responds with a 

721# renew message or an Information-request message. 

722class DHCP6OptReconfMsg(_DHCP6OptGuessPayload): # RFC 8415 sect 21.19 

723 name = "DHCP6 Reconfigure Message Option" 

724 fields_desc = [ShortEnumField("optcode", 19, dhcp6opts), 

725 ShortField("optlen", 1), 

726 ByteEnumField("msgtype", 11, {5: "Renew Message", 

727 11: "Information Request"})] 

728 

729 

730# DHCPv6 Reconfigure Accept Option # 

731 

732# A client uses the Reconfigure Accept option to announce to the 

733# server whether the client is willing to accept Recoonfigure 

734# messages, and a server uses this option to tell the client whether 

735# or not to accept Reconfigure messages. The default behavior in the 

736# absence of this option, means unwillingness to accept reconfigure 

737# messages, or instruction not to accept Reconfigure messages, for the 

738# client and server messages, respectively. 

739class DHCP6OptReconfAccept(_DHCP6OptGuessPayload): # RFC 8415 sect 21.20 

740 name = "DHCP6 Reconfigure Accept Option" 

741 fields_desc = [ShortEnumField("optcode", 20, dhcp6opts), 

742 ShortField("optlen", 0)] 

743 

744 

745class DHCP6OptSIPDomains(_DHCP6OptGuessPayload): # RFC3319 

746 name = "DHCP6 Option - SIP Servers Domain Name List" 

747 fields_desc = [ShortEnumField("optcode", 21, dhcp6opts), 

748 FieldLenField("optlen", None, length_of="sipdomains"), 

749 DomainNameListField("sipdomains", [], 

750 length_from=lambda pkt: pkt.optlen)] 

751 

752 

753class DHCP6OptSIPServers(_DHCP6OptGuessPayload): # RFC3319 

754 name = "DHCP6 Option - SIP Servers IPv6 Address List" 

755 fields_desc = [ShortEnumField("optcode", 22, dhcp6opts), 

756 FieldLenField("optlen", None, length_of="sipservers"), 

757 IP6ListField("sipservers", [], 

758 length_from=lambda pkt: pkt.optlen)] 

759 

760 

761class DHCP6OptDNSServers(_DHCP6OptGuessPayload): # RFC3646 

762 name = "DHCP6 Option - DNS Recursive Name Server" 

763 fields_desc = [ShortEnumField("optcode", 23, dhcp6opts), 

764 FieldLenField("optlen", None, length_of="dnsservers"), 

765 IP6ListField("dnsservers", [], 

766 length_from=lambda pkt: pkt.optlen)] 

767 

768 

769class DHCP6OptDNSDomains(_DHCP6OptGuessPayload): # RFC3646 

770 name = "DHCP6 Option - Domain Search List option" 

771 fields_desc = [ShortEnumField("optcode", 24, dhcp6opts), 

772 FieldLenField("optlen", None, length_of="dnsdomains"), 

773 DomainNameListField("dnsdomains", [], 

774 length_from=lambda pkt: pkt.optlen)] 

775 

776 

777class DHCP6OptIAPrefix(_DHCP6OptGuessPayload): # RFC 8415 sect 21.22 

778 name = "DHCP6 Option - IA Prefix option" 

779 fields_desc = [ShortEnumField("optcode", 26, dhcp6opts), 

780 FieldLenField("optlen", None, length_of="iaprefopts", 

781 adjust=lambda pkt, x: x + 25), 

782 IntEnumField("preflft", 0, {0xffffffff: "infinity"}), 

783 IntEnumField("validlft", 0, {0xffffffff: "infinity"}), 

784 ByteField("plen", 48), # TODO: Challenge that default value 

785 # See RFC 8168 

786 IP6Field("prefix", "2001:db8::"), # At least, global and won't hurt # noqa: E501 

787 # We copy what wireshark does: read more dhcp6 options and 

788 # expect failures 

789 PacketListField("iaprefopts", [], 

790 _DHCP6OptGuessPayloadElt, 

791 length_from=lambda pkt: pkt.optlen - 25)] 

792 

793 def guess_payload_class(self, payload): 

794 return conf.padding_layer 

795 

796 

797class DHCP6OptIA_PD(_DHCP6OptGuessPayload): # RFC 8415 sect 21.21 

798 name = "DHCP6 Option - Identity Association for Prefix Delegation" 

799 fields_desc = [ShortEnumField("optcode", 25, dhcp6opts), 

800 FieldLenField("optlen", None, length_of="iapdopt", 

801 fmt="!H", adjust=lambda pkt, x: x + 12), 

802 XIntField("iaid", None), 

803 IntField("T1", None), 

804 IntField("T2", None), 

805 PacketListField("iapdopt", [], _DHCP6OptGuessPayloadElt, 

806 length_from=lambda pkt: pkt.optlen - 12)] 

807 

808 

809class DHCP6OptNISServers(_DHCP6OptGuessPayload): # RFC3898 

810 name = "DHCP6 Option - NIS Servers" 

811 fields_desc = [ShortEnumField("optcode", 27, dhcp6opts), 

812 FieldLenField("optlen", None, length_of="nisservers"), 

813 IP6ListField("nisservers", [], 

814 length_from=lambda pkt: pkt.optlen)] 

815 

816 

817class DHCP6OptNISPServers(_DHCP6OptGuessPayload): # RFC3898 

818 name = "DHCP6 Option - NIS+ Servers" 

819 fields_desc = [ShortEnumField("optcode", 28, dhcp6opts), 

820 FieldLenField("optlen", None, length_of="nispservers"), 

821 IP6ListField("nispservers", [], 

822 length_from=lambda pkt: pkt.optlen)] 

823 

824 

825class DHCP6OptNISDomain(_DHCP6OptGuessPayload): # RFC3898 

826 name = "DHCP6 Option - NIS Domain Name" 

827 fields_desc = [ShortEnumField("optcode", 29, dhcp6opts), 

828 FieldLenField("optlen", None, length_of="nisdomain"), 

829 DNSStrField("nisdomain", "", 

830 length_from=lambda pkt: pkt.optlen)] 

831 

832 

833class DHCP6OptNISPDomain(_DHCP6OptGuessPayload): # RFC3898 

834 name = "DHCP6 Option - NIS+ Domain Name" 

835 fields_desc = [ShortEnumField("optcode", 30, dhcp6opts), 

836 FieldLenField("optlen", None, length_of="nispdomain"), 

837 DNSStrField("nispdomain", "", 

838 length_from=lambda pkt: pkt.optlen)] 

839 

840 

841class DHCP6OptSNTPServers(_DHCP6OptGuessPayload): # RFC4075 

842 name = "DHCP6 option - SNTP Servers" 

843 fields_desc = [ShortEnumField("optcode", 31, dhcp6opts), 

844 FieldLenField("optlen", None, length_of="sntpservers"), 

845 IP6ListField("sntpservers", [], 

846 length_from=lambda pkt: pkt.optlen)] 

847 

848 

849IRT_DEFAULT = 86400 

850IRT_MINIMUM = 600 

851 

852 

853class DHCP6OptInfoRefreshTime(_DHCP6OptGuessPayload): # RFC4242 

854 name = "DHCP6 Option - Information Refresh Time" 

855 fields_desc = [ShortEnumField("optcode", 32, dhcp6opts), 

856 ShortField("optlen", 4), 

857 IntField("reftime", IRT_DEFAULT)] # One day 

858 

859 

860class DHCP6OptBCMCSDomains(_DHCP6OptGuessPayload): # RFC4280 

861 name = "DHCP6 Option - BCMCS Domain Name List" 

862 fields_desc = [ShortEnumField("optcode", 33, dhcp6opts), 

863 FieldLenField("optlen", None, length_of="bcmcsdomains"), 

864 DomainNameListField("bcmcsdomains", [], 

865 length_from=lambda pkt: pkt.optlen)] 

866 

867 

868class DHCP6OptBCMCSServers(_DHCP6OptGuessPayload): # RFC4280 

869 name = "DHCP6 Option - BCMCS Addresses List" 

870 fields_desc = [ShortEnumField("optcode", 34, dhcp6opts), 

871 FieldLenField("optlen", None, length_of="bcmcsservers"), 

872 IP6ListField("bcmcsservers", [], 

873 length_from=lambda pkt: pkt.optlen)] 

874 

875 

876_dhcp6_geoconf_what = { 

877 0: "DHCP server", 

878 1: "closest network element", 

879 2: "client" 

880} 

881 

882 

883class DHCP6OptGeoConfElement(Packet): 

884 fields_desc = [ByteField("CAtype", 0), 

885 FieldLenField("CAlength", None, length_of="CAvalue"), 

886 StrLenField("CAvalue", "", 

887 length_from=lambda pkt: pkt.CAlength)] 

888 

889 

890class DHCP6OptGeoConf(_DHCP6OptGuessPayload): # RFC 4776 

891 name = "DHCP6 Option - Civic Location" 

892 fields_desc = [ShortEnumField("optcode", 36, dhcp6opts), 

893 FieldLenField("optlen", None, length_of="ca_elts", 

894 adjust=lambda x: x + 3), 

895 ByteEnumField("what", 2, _dhcp6_geoconf_what), 

896 StrFixedLenField("country_code", "FR", 2), 

897 PacketListField("ca_elts", [], DHCP6OptGeoConfElement, 

898 length_from=lambda pkt: pkt.optlen - 3)] 

899 

900# TODO: see if we encounter opaque values from vendor devices 

901 

902 

903class DHCP6OptRemoteID(_DHCP6OptGuessPayload): # RFC4649 

904 name = "DHCP6 Option - Relay Agent Remote-ID" 

905 fields_desc = [ShortEnumField("optcode", 37, dhcp6opts), 

906 FieldLenField("optlen", None, length_of="remoteid", 

907 adjust=lambda pkt, x: x + 4), 

908 IntEnumField("enterprisenum", None, 

909 IANA_ENTERPRISE_NUMBERS), 

910 StrLenField("remoteid", "", 

911 length_from=lambda pkt: pkt.optlen - 4)] 

912 

913 

914class DHCP6OptSubscriberID(_DHCP6OptGuessPayload): # RFC4580 

915 name = "DHCP6 Option - Subscriber ID" 

916 fields_desc = [ShortEnumField("optcode", 38, dhcp6opts), 

917 FieldLenField("optlen", None, length_of="subscriberid"), 

918 # subscriberid default value should be at least 1 byte long 

919 # but we don't really care 

920 StrLenField("subscriberid", "", 

921 length_from=lambda pkt: pkt.optlen)] 

922 

923 

924class DHCP6OptClientFQDN(_DHCP6OptGuessPayload): # RFC4704 

925 name = "DHCP6 Option - Client FQDN" 

926 fields_desc = [ShortEnumField("optcode", 39, dhcp6opts), 

927 FieldLenField("optlen", None, length_of="fqdn", 

928 adjust=lambda pkt, x: x + 1), 

929 BitField("res", 0, 5), 

930 FlagsField("flags", 0, 3, "SON"), 

931 DNSStrField("fqdn", "", 

932 length_from=lambda pkt: pkt.optlen - 1)] 

933 

934 

935class DHCP6OptPanaAuthAgent(_DHCP6OptGuessPayload): # RFC5192 

936 name = "DHCP6 PANA Authentication Agent Option" 

937 fields_desc = [ShortEnumField("optcode", 40, dhcp6opts), 

938 FieldLenField("optlen", None, length_of="paaaddr"), 

939 IP6ListField("paaaddr", [], 

940 length_from=lambda pkt: pkt.optlen)] 

941 

942 

943class DHCP6OptNewPOSIXTimeZone(_DHCP6OptGuessPayload): # RFC4833 

944 name = "DHCP6 POSIX Timezone Option" 

945 fields_desc = [ShortEnumField("optcode", 41, dhcp6opts), 

946 FieldLenField("optlen", None, length_of="optdata"), 

947 StrLenField("optdata", "", 

948 length_from=lambda pkt: pkt.optlen)] 

949 

950 

951class DHCP6OptNewTZDBTimeZone(_DHCP6OptGuessPayload): # RFC4833 

952 name = "DHCP6 TZDB Timezone Option" 

953 fields_desc = [ShortEnumField("optcode", 42, dhcp6opts), 

954 FieldLenField("optlen", None, length_of="optdata"), 

955 StrLenField("optdata", "", 

956 length_from=lambda pkt: pkt.optlen)] 

957 

958 

959class DHCP6OptRelayAgentERO(_DHCP6OptGuessPayload): # RFC4994 

960 name = "DHCP6 Option - RelayRequest Option" 

961 fields_desc = [ShortEnumField("optcode", 43, dhcp6opts), 

962 FieldLenField("optlen", None, length_of="reqopts", 

963 fmt="!H"), 

964 _OptReqListField("reqopts", [23, 24], 

965 length_from=lambda pkt: pkt.optlen)] 

966 

967 

968class DHCP6OptLQClientLink(_DHCP6OptGuessPayload): # RFC5007 

969 name = "DHCP6 Client Link Option" 

970 fields_desc = [ShortEnumField("optcode", 48, dhcp6opts), 

971 FieldLenField("optlen", None, length_of="linkaddress"), 

972 IP6ListField("linkaddress", [], 

973 length_from=lambda pkt: pkt.optlen)] 

974 

975 

976class DHCP6NTPSubOptSrvAddr(Packet): # RFC5908 sect 4.1 

977 name = "DHCP6 NTP Server Address Suboption" 

978 fields_desc = [ShortField("optcode", 1), 

979 ShortField("optlen", 16), 

980 IP6Field("addr", "::")] 

981 

982 def extract_padding(self, s): 

983 return b"", s 

984 

985 

986class DHCP6NTPSubOptMCAddr(Packet): # RFC5908 sect 4.2 

987 name = "DHCP6 NTP Multicast Address Suboption" 

988 fields_desc = [ShortField("optcode", 2), 

989 ShortField("optlen", 16), 

990 IP6Field("addr", "::")] 

991 

992 def extract_padding(self, s): 

993 return b"", s 

994 

995 

996class DHCP6NTPSubOptSrvFQDN(Packet): # RFC5908 sect 4.3 

997 name = "DHCP6 NTP Server FQDN Suboption" 

998 fields_desc = [ShortField("optcode", 3), 

999 FieldLenField("optlen", None, length_of="fqdn"), 

1000 DNSStrField("fqdn", "", 

1001 length_from=lambda pkt: pkt.optlen)] 

1002 

1003 def extract_padding(self, s): 

1004 return b"", s 

1005 

1006 

1007_ntp_subopts = {1: DHCP6NTPSubOptSrvAddr, 

1008 2: DHCP6NTPSubOptMCAddr, 

1009 3: DHCP6NTPSubOptSrvFQDN} 

1010 

1011 

1012def _ntp_subopt_dispatcher(p, **kwargs): 

1013 cls = conf.raw_layer 

1014 if len(p) >= 2: 

1015 o = struct.unpack("!H", p[:2])[0] 

1016 cls = _ntp_subopts.get(o, conf.raw_layer) 

1017 return cls(p, **kwargs) 

1018 

1019 

1020class DHCP6OptNTPServer(_DHCP6OptGuessPayload): # RFC5908 

1021 name = "DHCP6 NTP Server Option" 

1022 fields_desc = [ShortEnumField("optcode", 56, dhcp6opts), 

1023 FieldLenField("optlen", None, length_of="ntpserver", 

1024 fmt="!H"), 

1025 PacketListField("ntpserver", [], 

1026 _ntp_subopt_dispatcher, 

1027 length_from=lambda pkt: pkt.optlen)] 

1028 

1029 

1030class DHCP6OptBootFileUrl(_DHCP6OptGuessPayload): # RFC5970 

1031 name = "DHCP6 Boot File URL Option" 

1032 fields_desc = [ShortEnumField("optcode", 59, dhcp6opts), 

1033 FieldLenField("optlen", None, length_of="optdata"), 

1034 StrLenField("optdata", "", 

1035 length_from=lambda pkt: pkt.optlen)] 

1036 

1037 

1038class DHCP6OptClientArchType(_DHCP6OptGuessPayload): # RFC5970 

1039 name = "DHCP6 Client System Architecture Type Option" 

1040 fields_desc = [ShortEnumField("optcode", 61, dhcp6opts), 

1041 FieldLenField("optlen", None, length_of="archtypes", 

1042 fmt="!H"), 

1043 FieldListField("archtypes", [], 

1044 ShortField("archtype", 0), 

1045 length_from=lambda pkt: pkt.optlen)] 

1046 

1047 

1048class DHCP6OptClientNetworkInterId(_DHCP6OptGuessPayload): # RFC5970 

1049 name = "DHCP6 Client Network Interface Identifier Option" 

1050 fields_desc = [ShortEnumField("optcode", 62, dhcp6opts), 

1051 ShortField("optlen", 3), 

1052 ByteField("iitype", 0), 

1053 ByteField("iimajor", 0), 

1054 ByteField("iiminor", 0)] 

1055 

1056 

1057class DHCP6OptERPDomain(_DHCP6OptGuessPayload): # RFC6440 

1058 name = "DHCP6 Option - ERP Domain Name List" 

1059 fields_desc = [ShortEnumField("optcode", 65, dhcp6opts), 

1060 FieldLenField("optlen", None, length_of="erpdomain"), 

1061 DomainNameListField("erpdomain", [], 

1062 length_from=lambda pkt: pkt.optlen)] 

1063 

1064 

1065class DHCP6OptRelaySuppliedOpt(_DHCP6OptGuessPayload): # RFC6422 

1066 name = "DHCP6 Relay-Supplied Options Option" 

1067 fields_desc = [ShortEnumField("optcode", 66, dhcp6opts), 

1068 FieldLenField("optlen", None, length_of="relaysupplied", 

1069 fmt="!H"), 

1070 PacketListField("relaysupplied", [], 

1071 _DHCP6OptGuessPayloadElt, 

1072 length_from=lambda pkt: pkt.optlen)] 

1073 

1074 

1075# Virtual Subnet selection 

1076class DHCP6OptVSS(_DHCP6OptGuessPayload): # RFC6607 

1077 name = "DHCP6 Option - Virtual Subnet Selection" 

1078 fields_desc = [ShortEnumField("optcode", 68, dhcp6opts), 

1079 FieldLenField("optlen", None, length_of="data", 

1080 adjust=lambda pkt, x: x + 1), 

1081 ByteField("type", 255), # Default Global/default table 

1082 StrLenField("data", "", 

1083 length_from=lambda pkt: pkt.optlen)] 

1084 

1085 

1086# "Client link-layer address type. The link-layer type MUST be a valid hardware # noqa: E501 

1087# type assigned by the IANA, as described in [RFC0826] 

1088class DHCP6OptClientLinkLayerAddr(_DHCP6OptGuessPayload): # RFC6939 

1089 name = "DHCP6 Option - Client Link Layer address" 

1090 fields_desc = [ShortEnumField("optcode", 79, dhcp6opts), 

1091 FieldLenField("optlen", None, length_of="clladdr", 

1092 adjust=lambda pkt, x: x + 2), 

1093 ShortField("lltype", 1), # ethernet 

1094 _LLAddrField("clladdr", ETHER_ANY)] 

1095 

1096 

1097class DHCP6OptCaptivePortal(_DHCP6OptGuessPayload): # RFC8910 

1098 name = "DHCP6 Option - Captive-Portal" 

1099 fields_desc = [ShortEnumField("optcode", 103, dhcp6opts), 

1100 FieldLenField("optlen", None, length_of="URI"), 

1101 StrLenField("URI", "", 

1102 length_from=lambda pkt: pkt.optlen)] 

1103 

1104 

1105class DHCP6OptMudUrl(_DHCP6OptGuessPayload): # RFC8520 

1106 name = "DHCP6 Option - MUD URL" 

1107 fields_desc = [ShortEnumField("optcode", 112, dhcp6opts), 

1108 FieldLenField("optlen", None, length_of="mudstring"), 

1109 StrLenField("mudstring", "", 

1110 length_from=lambda pkt: pkt.optlen, 

1111 max_length=253, 

1112 )] 

1113 

1114 

1115class DHCP6OptAddrRegEnable(_DHCP6OptGuessPayload): # RFC 9686 sect 4.1 

1116 name = "DHCP6 Address Registration Option" 

1117 fields_desc = [ShortEnumField("optcode", 148, dhcp6opts), 

1118 ShortField("optlen", 0)] 

1119 

1120 

1121##################################################################### 

1122# DHCPv6 messages # 

1123##################################################################### 

1124 

1125# Some state parameters of the protocols that should probably be 

1126# useful to have in the configuration (and keep up-to-date) 

1127DHCP6RelayAgentUnicastAddr = "" 

1128DHCP6RelayHopCount = "" 

1129DHCP6ServerUnicastAddr = "" 

1130DHCP6ClientUnicastAddr = "" 

1131DHCP6ClientIA_TA = "" 

1132DHCP6ClientIA_NA = "" 

1133DHCP6ClientIAID = "" 

1134T1 = "" # Voir 2462 

1135T2 = "" # Voir 2462 

1136DHCP6ServerDUID = "" 

1137DHCP6CurrentTransactionID = "" # devrait etre utilise pour matcher une 

1138# reponse et mis a jour en mode client par une valeur aleatoire pour 

1139# laquelle on attend un retour de la part d'un serveur. 

1140DHCP6PrefVal = "" # la valeur de preference a utiliser dans 

1141# les options preference 

1142 

1143# Emitted by : 

1144# - server : ADVERTISE, REPLY, RECONFIGURE, RELAY-REPL (vers relay) 

1145# - client : SOLICIT, REQUEST, CONFIRM, RENEW, REBIND, RELEASE, DECLINE, 

1146# INFORMATION REQUEST 

1147# - relay : RELAY-FORW (toward server) 

1148 

1149##################################################################### 

1150# DHCPv6 messages sent between Clients and Servers (types 1 to 11) 

1151# Comme specifie en section 15.1 de la RFC 3315, les valeurs de 

1152# transaction id sont selectionnees de maniere aleatoire par le client 

1153# a chaque emission et doivent matcher dans les reponses faites par 

1154# les clients 

1155 

1156 

1157class DHCP6(_DHCP6OptGuessPayload): 

1158 name = "DHCPv6 Generic Message" 

1159 fields_desc = [ByteEnumField("msgtype", None, dhcp6types), 

1160 X3BytesField("trid", 0x000000)] 

1161 overload_fields = {UDP: {"sport": 546, "dport": 547}} 

1162 

1163 def hashret(self): 

1164 return struct.pack("!I", self.trid)[1:4] 

1165 

1166# DHCPv6 Relay Message Option # 

1167 

1168# Relayed message is seen as a payload. 

1169 

1170 

1171class DHCP6OptRelayMsg(_DHCP6OptGuessPayload): # RFC 8415 sect 21.10 

1172 name = "DHCP6 Relay Message Option" 

1173 fields_desc = [ShortEnumField("optcode", 9, dhcp6opts), 

1174 FieldLenField("optlen", None, fmt="!H", 

1175 length_of="message"), 

1176 PacketLenField("message", DHCP6(), _dhcp6_dispatcher, 

1177 length_from=lambda p: p.optlen)] 

1178 

1179##################################################################### 

1180# Solicit Message : sect 17.1.1 RFC3315 

1181# - sent by client 

1182# - must include a client identifier option 

1183# - the client may include IA options for any IAs to which it wants the 

1184# server to assign address 

1185# - The client use IA_NA options to request the assignment of 

1186# non-temporary addresses and uses IA_TA options to request the 

1187# assignment of temporary addresses 

1188# - The client should include an Option Request option to indicate the 

1189# options the client is interested in receiving (eventually 

1190# including hints) 

1191# - The client includes a Reconfigure Accept option if is willing to 

1192# accept Reconfigure messages from the server. 

1193# Le cas du send and reply est assez particulier car suivant la 

1194# presence d'une option rapid commit dans le solicit, l'attente 

1195# s'arrete au premier message de reponse recu ou alors apres un 

1196# timeout. De la meme maniere, si un message Advertise arrive avec une 

1197# valeur de preference de 255, il arrete l'attente et envoie une 

1198# Request. 

1199# - The client announces its intention to use DHCP authentication by 

1200# including an Authentication option in its solicit message. The 

1201# server selects a key for the client based on the client's DUID. The 

1202# client and server use that key to authenticate all DHCP messages 

1203# exchanged during the session 

1204 

1205 

1206class DHCP6_Solicit(DHCP6): 

1207 name = "DHCPv6 Solicit Message" 

1208 msgtype = 1 

1209 overload_fields = {UDP: {"sport": 546, "dport": 547}} 

1210 

1211##################################################################### 

1212# Advertise Message 

1213# - sent by server 

1214# - Includes a server identifier option 

1215# - Includes a client identifier option 

1216# - the client identifier option must match the client's DUID 

1217# - transaction ID must match 

1218 

1219 

1220class DHCP6_Advertise(DHCP6): 

1221 name = "DHCPv6 Advertise Message" 

1222 msgtype = 2 

1223 overload_fields = {UDP: {"sport": 547, "dport": 546}} 

1224 

1225 def answers(self, other): 

1226 return (isinstance(other, DHCP6_Solicit) and 

1227 other.msgtype == 1 and 

1228 self.trid == other.trid) 

1229 

1230##################################################################### 

1231# Request Message 

1232# - sent by clients 

1233# - includes a server identifier option 

1234# - the content of Server Identifier option must match server's DUID 

1235# - includes a client identifier option 

1236# - must include an ORO Option (even with hints) p40 

1237# - can includes a reconfigure Accept option indicating whether or 

1238# not the client is willing to accept Reconfigure messages from 

1239# the server (p40) 

1240# - When the server receives a Request message via unicast from a 

1241# client to which the server has not sent a unicast option, the server 

1242# discards the Request message and responds with a Reply message 

1243# containing Status Code option with the value UseMulticast, a Server 

1244# Identifier Option containing the server's DUID, the client 

1245# Identifier option from the client message and no other option. 

1246 

1247 

1248class DHCP6_Request(DHCP6): 

1249 name = "DHCPv6 Request Message" 

1250 msgtype = 3 

1251 

1252##################################################################### 

1253# Confirm Message 

1254# - sent by clients 

1255# - must include a client identifier option 

1256# - When the server receives a Confirm Message, the server determines 

1257# whether the addresses in the Confirm message are appropriate for the 

1258# link to which the client is attached. cf p50 

1259 

1260 

1261class DHCP6_Confirm(DHCP6): 

1262 name = "DHCPv6 Confirm Message" 

1263 msgtype = 4 

1264 

1265##################################################################### 

1266# Renew Message 

1267# - sent by clients 

1268# - must include a server identifier option 

1269# - content of server identifier option must match the server's identifier 

1270# - must include a client identifier option 

1271# - the clients includes any IA assigned to the interface that may 

1272# have moved to a new link, along with the addresses associated with 

1273# those IAs in its confirm messages 

1274# - When the server receives a Renew message that contains an IA 

1275# option from a client, it locates the client's binding and verifies 

1276# that the information in the IA from the client matches the 

1277# information for that client. If the server cannot find a client 

1278# entry for the IA the server returns the IA containing no addresses 

1279# with a status code option est to NoBinding in the Reply message. cf 

1280# p51 pour le reste. 

1281 

1282 

1283class DHCP6_Renew(DHCP6): 

1284 name = "DHCPv6 Renew Message" 

1285 msgtype = 5 

1286 

1287##################################################################### 

1288# Rebind Message 

1289# - sent by clients 

1290# - must include a client identifier option 

1291# cf p52 

1292 

1293 

1294class DHCP6_Rebind(DHCP6): 

1295 name = "DHCPv6 Rebind Message" 

1296 msgtype = 6 

1297 

1298##################################################################### 

1299# Reply Message 

1300# - sent by servers 

1301# - the message must include a server identifier option 

1302# - transaction-id field must match the value of original message 

1303# The server includes a Rapid Commit option in the Reply message to 

1304# indicate that the reply is in response to a solicit message 

1305# - if the client receives a reply message with a Status code option 

1306# with the value UseMulticast, the client records the receipt of the 

1307# message and sends subsequent messages to the server through the 

1308# interface on which the message was received using multicast. The 

1309# client resends the original message using multicast 

1310# - When the client receives a NotOnLink status from the server in 

1311# response to a Confirm message, the client performs DHCP server 

1312# solicitation as described in section 17 and client-initiated 

1313# configuration as descrribed in section 18 (RFC 3315) 

1314# - when the client receives a NotOnLink status from the server in 

1315# response to a Request, the client can either re-issue the Request 

1316# without specifying any addresses or restart the DHCP server 

1317# discovery process. 

1318# - the server must include a server identifier option containing the 

1319# server's DUID in the Reply message 

1320 

1321 

1322class DHCP6_Reply(DHCP6): 

1323 name = "DHCPv6 Reply Message" 

1324 msgtype = 7 

1325 

1326 overload_fields = {UDP: {"sport": 547, "dport": 546}} 

1327 

1328 def answers(self, other): 

1329 

1330 types = (DHCP6_Solicit, DHCP6_InfoRequest, DHCP6_Confirm, DHCP6_Rebind, 

1331 DHCP6_Decline, DHCP6_Request, DHCP6_Release, DHCP6_Renew) 

1332 

1333 return (isinstance(other, types) and self.trid == other.trid) 

1334 

1335##################################################################### 

1336# Release Message 

1337# - sent by clients 

1338# - must include a server identifier option 

1339# cf p53 

1340 

1341 

1342class DHCP6_Release(DHCP6): 

1343 name = "DHCPv6 Release Message" 

1344 msgtype = 8 

1345 

1346##################################################################### 

1347# Decline Message 

1348# - sent by clients 

1349# - must include a client identifier option 

1350# - Server identifier option must match server identifier 

1351# - The addresses to be declined must be included in the IAs. Any 

1352# addresses for the IAs the client wishes to continue to use should 

1353# not be in added to the IAs. 

1354# - cf p54 

1355 

1356 

1357class DHCP6_Decline(DHCP6): 

1358 name = "DHCPv6 Decline Message" 

1359 msgtype = 9 

1360 

1361##################################################################### 

1362# Reconfigure Message 

1363# - sent by servers 

1364# - must be unicast to the client 

1365# - must include a server identifier option 

1366# - must include a client identifier option that contains the client DUID 

1367# - must contain a Reconfigure Message Option and the message type 

1368# must be a valid value 

1369# - the server sets the transaction-id to 0 

1370# - The server must use DHCP Authentication in the Reconfigure 

1371# message. Autant dire que ca va pas etre le type de message qu'on va 

1372# voir le plus souvent. 

1373 

1374 

1375class DHCP6_Reconf(DHCP6): 

1376 name = "DHCPv6 Reconfigure Message" 

1377 msgtype = 10 

1378 overload_fields = {UDP: {"sport": 547, "dport": 546}} 

1379 

1380 

1381##################################################################### 

1382# Information-Request Message 

1383# - sent by clients when needs configuration information but no 

1384# addresses. 

1385# - client should include a client identifier option to identify 

1386# itself. If it doesn't the server is not able to return client 

1387# specific options or the server can choose to not respond to the 

1388# message at all. The client must include a client identifier option 

1389# if the message will be authenticated. 

1390# - client must include an ORO of option she's interested in receiving 

1391# (can include hints) 

1392 

1393class DHCP6_InfoRequest(DHCP6): 

1394 name = "DHCPv6 Information Request Message" 

1395 msgtype = 11 

1396 

1397##################################################################### 

1398# sent between Relay Agents and Servers 

1399# 

1400# Normalement, doit inclure une option "Relay Message Option" 

1401# peut en inclure d'autres. 

1402# voir section 7.1 de la 3315 

1403 

1404# Relay-Forward Message 

1405# - sent by relay agents to servers 

1406# If the relay agent relays messages to the All_DHCP_Servers multicast 

1407# address or other multicast addresses, it sets the Hop Limit field to 

1408# 32. 

1409 

1410 

1411class DHCP6_RelayForward(_DHCP6OptGuessPayload, Packet): 

1412 name = "DHCPv6 Relay Forward Message (Relay Agent/Server Message)" 

1413 fields_desc = [ByteEnumField("msgtype", 12, dhcp6types), 

1414 ByteField("hopcount", None), 

1415 IP6Field("linkaddr", "::"), 

1416 IP6Field("peeraddr", "::")] 

1417 overload_fields = {UDP: {"sport": 547, "dport": 547}} 

1418 

1419 def hashret(self): # we filter on peer address field 

1420 return inet_pton(socket.AF_INET6, self.peeraddr) 

1421 

1422##################################################################### 

1423# sent between Relay Agents and Servers 

1424# Normalement, doit inclure une option "Relay Message Option" 

1425# peut en inclure d'autres. 

1426# Les valeurs des champs hop-count, link-addr et peer-addr 

1427# sont copiees du message Forward associe. POur le suivi de session. 

1428# Pour le moment, comme decrit dans le commentaire, le hashret 

1429# se limite au contenu du champ peer address. 

1430# Voir section 7.2 de la 3315. 

1431 

1432# Relay-Reply Message 

1433# - sent by servers to relay agents 

1434# - if the solicit message was received in a Relay-Forward message, 

1435# the server constructs a relay-reply message with the Advertise 

1436# message in the payload of a relay-message. cf page 37/101. Envoie de 

1437# ce message en unicast au relay-agent. utilisation de l'adresse ip 

1438# presente en ip source du paquet recu 

1439 

1440 

1441class DHCP6_RelayReply(DHCP6_RelayForward): 

1442 name = "DHCPv6 Relay Reply Message (Relay Agent/Server Message)" 

1443 msgtype = 13 

1444 

1445 def hashret(self): # We filter on peer address field. 

1446 return inet_pton(socket.AF_INET6, self.peeraddr) 

1447 

1448 def answers(self, other): 

1449 return (isinstance(other, DHCP6_RelayForward) and 

1450 self.hopcount == other.hopcount and 

1451 self.linkaddr == other.linkaddr and 

1452 self.peeraddr == other.peeraddr) 

1453 

1454 

1455##################################################################### 

1456# Address Registration-Inform Message (RFC 9686) 

1457# - sent by clients who generated their own address and need it registered 

1458 

1459class DHCP6_AddrRegInform(DHCP6): 

1460 name = "DHCPv6 Information Request Message" 

1461 msgtype = 36 

1462 

1463##################################################################### 

1464# Address Registration-Reply Message (RFC 9686) 

1465# - sent by servers who respond to the address registration-inform message 

1466 

1467 

1468class DHCP6_AddrRegReply(DHCP6): 

1469 name = "DHCPv6 Information Reply Message" 

1470 msgtype = 37 

1471 

1472 

1473bind_bottom_up(UDP, _dhcp6_dispatcher, {"dport": 547}) 

1474bind_bottom_up(UDP, _dhcp6_dispatcher, {"dport": 546}) 

1475 

1476 

1477class DHCPv6_am(AnsweringMachine): 

1478 function_name = "dhcp6d" 

1479 filter = "udp and port 546 and port 547" 

1480 send_function = staticmethod(send) 

1481 

1482 def usage(self): 

1483 msg = """ 

1484DHCPv6_am.parse_options( dns="2001:500::1035", domain="localdomain, local", 

1485 duid=None, iface=conf.iface, advpref=255, sntpservers=None, 

1486 sipdomains=None, sipservers=None, 

1487 nisdomain=None, nisservers=None, 

1488 nispdomain=None, nispservers=None, 

1489 bcmcsdomains=None, bcmcsservers=None) 

1490 

1491 debug : When set, additional debugging information is printed. 

1492 

1493 duid : some DUID class (DUID_LLT, DUID_LL or DUID_EN). If none 

1494 is provided a DUID_LLT is constructed based on the MAC 

1495 address of the sending interface and launch time of dhcp6d 

1496 answering machine. 

1497 

1498 iface : the interface to listen/reply on if you do not want to use 

1499 conf.iface. 

1500 

1501 advpref : Value in [0,255] given to Advertise preference field. 

1502 By default, 255 is used. Be aware that this specific 

1503 value makes clients stops waiting for further Advertise 

1504 messages from other servers. 

1505 

1506 dns : list of recursive DNS servers addresses (as a string or list). 

1507 By default, it is set empty and the associated DHCP6OptDNSServers 

1508 option is inactive. See RFC 3646 for details. 

1509 domain : a list of DNS search domain (as a string or list). By default, 

1510 it is empty and the associated DHCP6OptDomains option is inactive. 

1511 See RFC 3646 for details. 

1512 

1513 sntpservers : a list of SNTP servers IPv6 addresses. By default, 

1514 it is empty and the associated DHCP6OptSNTPServers option 

1515 is inactive. 

1516 

1517 sipdomains : a list of SIP domains. By default, it is empty and the 

1518 associated DHCP6OptSIPDomains option is inactive. See RFC 3319 

1519 for details. 

1520 sipservers : a list of SIP servers IPv6 addresses. By default, it is 

1521 empty and the associated DHCP6OptSIPDomains option is inactive. 

1522 See RFC 3319 for details. 

1523 

1524 nisdomain : a list of NIS domains. By default, it is empty and the 

1525 associated DHCP6OptNISDomains option is inactive. See RFC 3898 

1526 for details. See RFC 3646 for details. 

1527 nisservers : a list of NIS servers IPv6 addresses. By default, it is 

1528 empty and the associated DHCP6OptNISServers option is inactive. 

1529 See RFC 3646 for details. 

1530 

1531 nispdomain : a list of NIS+ domains. By default, it is empty and the 

1532 associated DHCP6OptNISPDomains option is inactive. See RFC 3898 

1533 for details. 

1534 nispservers : a list of NIS+ servers IPv6 addresses. By default, it is 

1535 empty and the associated DHCP6OptNISServers option is inactive. 

1536 See RFC 3898 for details. 

1537 

1538 bcmcsdomain : a list of BCMCS domains. By default, it is empty and the 

1539 associated DHCP6OptBCMCSDomains option is inactive. See RFC 4280 

1540 for details. 

1541 bcmcsservers : a list of BCMCS servers IPv6 addresses. By default, it is 

1542 empty and the associated DHCP6OptBCMCSServers option is inactive. 

1543 See RFC 4280 for details. 

1544 

1545 If you have a need for others, just ask ... or provide a patch.""" 

1546 print(msg) 

1547 

1548 def parse_options(self, dns="2001:500::1035", domain="localdomain, local", 

1549 startip="2001:db8::1", endip="2001:db8::20", duid=None, 

1550 sntpservers=None, sipdomains=None, sipservers=None, 

1551 nisdomain=None, nisservers=None, nispdomain=None, 

1552 nispservers=None, bcmcsservers=None, bcmcsdomains=None, 

1553 iface=None, debug=0, advpref=255): 

1554 def norm_list(val, param_name): 

1555 if val is None: 

1556 return None 

1557 if isinstance(val, list): 

1558 return val 

1559 elif isinstance(val, str): 

1560 tmp_len = val.split(',') 

1561 return [x.strip() for x in tmp_len] 

1562 else: 

1563 print("Bad '%s' parameter provided." % param_name) 

1564 self.usage() 

1565 return -1 

1566 

1567 if iface is None: 

1568 iface = conf.iface 

1569 

1570 self.debug = debug 

1571 

1572 # Dictionary of provided DHCPv6 options, keyed by option type 

1573 self.dhcpv6_options = {} 

1574 

1575 for o in [(dns, "dns", 23, lambda x: DHCP6OptDNSServers(dnsservers=x)), 

1576 (domain, "domain", 24, lambda x: DHCP6OptDNSDomains(dnsdomains=x)), # noqa: E501 

1577 (sntpservers, "sntpservers", 31, lambda x: DHCP6OptSNTPServers(sntpservers=x)), # noqa: E501 

1578 (sipservers, "sipservers", 22, lambda x: DHCP6OptSIPServers(sipservers=x)), # noqa: E501 

1579 (sipdomains, "sipdomains", 21, lambda x: DHCP6OptSIPDomains(sipdomains=x)), # noqa: E501 

1580 (nisservers, "nisservers", 27, lambda x: DHCP6OptNISServers(nisservers=x)), # noqa: E501 

1581 (nisdomain, "nisdomain", 29, lambda x: DHCP6OptNISDomain(nisdomain=(x + [""])[0])), # noqa: E501 

1582 (nispservers, "nispservers", 28, lambda x: DHCP6OptNISPServers(nispservers=x)), # noqa: E501 

1583 (nispdomain, "nispdomain", 30, lambda x: DHCP6OptNISPDomain(nispdomain=(x + [""])[0])), # noqa: E501 

1584 (bcmcsservers, "bcmcsservers", 33, lambda x: DHCP6OptBCMCSServers(bcmcsservers=x)), # noqa: E501 

1585 (bcmcsdomains, "bcmcsdomains", 34, lambda x: DHCP6OptBCMCSDomains(bcmcsdomains=x))]: # noqa: E501 

1586 

1587 opt = norm_list(o[0], o[1]) 

1588 if opt == -1: # Usage() was triggered 

1589 return False 

1590 elif opt is None: # We won't return that option 

1591 pass 

1592 else: 

1593 self.dhcpv6_options[o[2]] = o[3](opt) 

1594 

1595 if self.debug: 

1596 print("\n[+] List of active DHCPv6 options:") 

1597 opts = sorted(self.dhcpv6_options) 

1598 for i in opts: 

1599 print(" %d: %s" % (i, repr(self.dhcpv6_options[i]))) 

1600 

1601 # Preference value used in Advertise. 

1602 self.advpref = advpref 

1603 

1604 # IP Pool 

1605 self.startip = startip 

1606 self.endip = endip 

1607 # XXX TODO Check IPs are in same subnet 

1608 

1609 #### 

1610 # The interface we are listening/replying on 

1611 self.iface = iface 

1612 

1613 #### 

1614 # Generate a server DUID 

1615 if duid is not None: 

1616 self.duid = duid 

1617 else: 

1618 # Timeval 

1619 epoch = (2000, 1, 1, 0, 0, 0, 5, 1, 0) 

1620 delta = time.mktime(epoch) - EPOCH 

1621 timeval = time.time() - delta 

1622 

1623 # Mac Address 

1624 mac = get_if_hwaddr(iface) 

1625 

1626 self.duid = DUID_LLT(timeval=timeval, lladdr=mac) 

1627 

1628 if self.debug: 

1629 print("\n[+] Our server DUID:") 

1630 self.duid.show(label_lvl=" " * 4) 

1631 

1632 #### 

1633 # Find the source address we will use 

1634 self.src_addr = None 

1635 try: 

1636 addr = next(x for x in in6_getifaddr() if x[2] == iface and in6_islladdr(x[0])) # noqa: E501 

1637 except (StopIteration, RuntimeError): 

1638 warning("Unable to get a Link-Local address") 

1639 return 

1640 else: 

1641 self.src_addr = addr[0] 

1642 

1643 #### 

1644 # Our leases 

1645 self.leases = {} 

1646 

1647 if self.debug: 

1648 print("\n[+] Starting DHCPv6 service on %s:" % self.iface) 

1649 

1650 def is_request(self, p): 

1651 if IPv6 not in p: 

1652 return False 

1653 

1654 src = p[IPv6].src 

1655 

1656 p = p[IPv6].payload 

1657 if not isinstance(p, UDP) or p.sport != 546 or p.dport != 547: 

1658 return False 

1659 

1660 p = p.payload 

1661 if not isinstance(p, DHCP6): 

1662 return False 

1663 

1664 # Message we considered client messages : 

1665 # Solicit (1), Request (3), Confirm (4), Renew (5), Rebind (6) 

1666 # Decline (9), Release (8), Information-request (11), 

1667 if not (p.msgtype in [1, 3, 4, 5, 6, 8, 9, 11]): 

1668 return False 

1669 

1670 # Message validation following section 15 of RFC 3315 

1671 

1672 if ((p.msgtype == 1) or # Solicit 

1673 (p.msgtype == 6) or # Rebind 

1674 (p.msgtype == 4)): # Confirm 

1675 if ((DHCP6OptClientId not in p) or 

1676 DHCP6OptServerId in p): 

1677 return False 

1678 

1679 if (p.msgtype == 6 or # Rebind 

1680 p.msgtype == 4): # Confirm 

1681 # XXX We do not reply to Confirm or Rebind as we 

1682 # XXX do not support address assignment 

1683 return False 

1684 

1685 elif (p.msgtype == 3 or # Request 

1686 p.msgtype == 5 or # Renew 

1687 p.msgtype == 8): # Release 

1688 

1689 # Both options must be present 

1690 if ((DHCP6OptServerId not in p) or 

1691 (DHCP6OptClientId not in p)): 

1692 return False 

1693 # provided server DUID must match ours 

1694 duid = p[DHCP6OptServerId].duid 

1695 if not isinstance(duid, type(self.duid)): 

1696 return False 

1697 if raw(duid) != raw(self.duid): 

1698 return False 

1699 

1700 if (p.msgtype == 5 or # Renew 

1701 p.msgtype == 8): # Release 

1702 # XXX We do not reply to Renew or Release as we 

1703 # XXX do not support address assignment 

1704 return False 

1705 

1706 elif p.msgtype == 9: # Decline 

1707 # XXX We should check if we are tracking that client 

1708 if not self.debug: 

1709 return False 

1710 

1711 bo = Color.bold 

1712 g = Color.green + bo 

1713 b = Color.blue + bo 

1714 n = Color.normal 

1715 r = Color.red 

1716 

1717 vendor = in6_addrtovendor(src) 

1718 if (vendor and vendor != "UNKNOWN"): 

1719 vendor = " [" + b + vendor + n + "]" 

1720 else: 

1721 vendor = "" 

1722 src = bo + src + n 

1723 

1724 it = p 

1725 addrs = [] 

1726 while it: 

1727 lst = [] 

1728 if isinstance(it, DHCP6OptIA_NA): 

1729 lst = it.ianaopts 

1730 elif isinstance(it, DHCP6OptIA_TA): 

1731 lst = it.iataopts 

1732 

1733 addrs += [x.addr for x in lst if isinstance(x, DHCP6OptIAAddress)] # noqa: E501 

1734 it = it.payload 

1735 

1736 addrs = [bo + x + n for x in addrs] 

1737 if self.debug: 

1738 msg = r + "[DEBUG]" + n + " Received " + g + "Decline" + n 

1739 msg += " from " + bo + src + vendor + " for " 

1740 msg += ", ".join(addrs) + n 

1741 print(msg) 

1742 

1743 # See RFC 3315 sect 18.1.7 

1744 

1745 # Sent by a client to warn us she has determined 

1746 # one or more addresses assigned to her is already 

1747 # used on the link. 

1748 # We should simply log that fact. No messaged should 

1749 # be sent in return. 

1750 

1751 # - Message must include a Server identifier option 

1752 # - the content of the Server identifier option must 

1753 # match the server's identifier 

1754 # - the message must include a Client Identifier option 

1755 return False 

1756 

1757 elif p.msgtype == 11: # Information-Request 

1758 if DHCP6OptServerId in p: 

1759 duid = p[DHCP6OptServerId].duid 

1760 if not isinstance(duid, type(self.duid)): 

1761 return False 

1762 if raw(duid) != raw(self.duid): 

1763 return False 

1764 if ((DHCP6OptIA_NA in p) or 

1765 (DHCP6OptIA_TA in p) or 

1766 (DHCP6OptIA_PD in p)): 

1767 return False 

1768 else: 

1769 return False 

1770 

1771 return True 

1772 

1773 def print_reply(self, req, reply): 

1774 def norm(s): 

1775 if s.startswith("DHCPv6 "): 

1776 s = s[7:] 

1777 if s.endswith(" Message"): 

1778 s = s[:-8] 

1779 return s 

1780 

1781 if reply is None: 

1782 return 

1783 

1784 bo = Color.bold 

1785 g = Color.green + bo 

1786 b = Color.blue + bo 

1787 n = Color.normal 

1788 reqtype = g + norm(req.getlayer(UDP).payload.name) + n 

1789 reqsrc = req.getlayer(IPv6).src 

1790 vendor = in6_addrtovendor(reqsrc) 

1791 if (vendor and vendor != "UNKNOWN"): 

1792 vendor = " [" + b + vendor + n + "]" 

1793 else: 

1794 vendor = "" 

1795 reqsrc = bo + reqsrc + n 

1796 reptype = g + norm(reply.getlayer(UDP).payload.name) + n 

1797 

1798 print("Sent %s answering to %s from %s%s" % (reptype, reqtype, reqsrc, vendor)) # noqa: E501 

1799 

1800 def make_reply(self, req): 

1801 p = req[IPv6] 

1802 req_src = p.src 

1803 

1804 p = p.payload.payload 

1805 

1806 msgtype = p.msgtype 

1807 trid = p.trid 

1808 

1809 def _include_options(query, answer): 

1810 """ 

1811 Include options from the DHCPv6 query 

1812 """ 

1813 

1814 # See which options should be included 

1815 reqopts = [] 

1816 if query.haslayer(DHCP6OptOptReq): # add only asked ones 

1817 reqopts = query[DHCP6OptOptReq].reqopts 

1818 for o, opt in self.dhcpv6_options.items(): 

1819 if o in reqopts: 

1820 answer /= opt 

1821 else: 

1822 # advertise everything we have available 

1823 # Should not happen has clients MUST include 

1824 # and ORO in requests (sec 18.1.1) -- arno 

1825 for o, opt in self.dhcpv6_options.items(): 

1826 answer /= opt 

1827 

1828 if msgtype == 1: # SOLICIT (See Sect 17.1 and 17.2 of RFC 3315) 

1829 

1830 # XXX We don't support address or prefix assignment 

1831 # XXX We also do not support relay function --arno 

1832 

1833 client_duid = p[DHCP6OptClientId].duid 

1834 resp = IPv6(src=self.src_addr, dst=req_src) 

1835 resp /= UDP(sport=547, dport=546) 

1836 

1837 if p.haslayer(DHCP6OptRapidCommit): 

1838 # construct a Reply packet 

1839 resp /= DHCP6_Reply(trid=trid) 

1840 resp /= DHCP6OptRapidCommit() # See 17.1.2 

1841 resp /= DHCP6OptServerId(duid=self.duid) 

1842 resp /= DHCP6OptClientId(duid=client_duid) 

1843 

1844 else: # No Rapid Commit in the packet. Reply with an Advertise 

1845 

1846 if (p.haslayer(DHCP6OptIA_NA) or 

1847 p.haslayer(DHCP6OptIA_TA)): 

1848 # XXX We don't assign addresses at the moment 

1849 msg = "Scapy6 dhcp6d does not support address assignment" 

1850 resp /= DHCP6_Advertise(trid=trid) 

1851 resp /= DHCP6OptStatusCode(statuscode=2, statusmsg=msg) 

1852 resp /= DHCP6OptServerId(duid=self.duid) 

1853 resp /= DHCP6OptClientId(duid=client_duid) 

1854 

1855 elif p.haslayer(DHCP6OptIA_PD): 

1856 # XXX We don't assign prefixes at the moment 

1857 msg = "Scapy6 dhcp6d does not support prefix assignment" 

1858 resp /= DHCP6_Advertise(trid=trid) 

1859 resp /= DHCP6OptStatusCode(statuscode=6, statusmsg=msg) 

1860 resp /= DHCP6OptServerId(duid=self.duid) 

1861 resp /= DHCP6OptClientId(duid=client_duid) 

1862 

1863 else: # Usual case, no request for prefixes or addresse 

1864 resp /= DHCP6_Advertise(trid=trid) 

1865 resp /= DHCP6OptPref(prefval=self.advpref) 

1866 resp /= DHCP6OptServerId(duid=self.duid) 

1867 resp /= DHCP6OptClientId(duid=client_duid) 

1868 resp /= DHCP6OptReconfAccept() 

1869 

1870 _include_options(p, resp) 

1871 

1872 return resp 

1873 

1874 elif msgtype == 3: # REQUEST (INFO-REQUEST is further below) 

1875 client_duid = p[DHCP6OptClientId].duid 

1876 resp = IPv6(src=self.src_addr, dst=req_src) 

1877 resp /= UDP(sport=547, dport=546) 

1878 resp /= DHCP6_Solicit(trid=trid) 

1879 resp /= DHCP6OptServerId(duid=self.duid) 

1880 resp /= DHCP6OptClientId(duid=client_duid) 

1881 

1882 _include_options(p, resp) 

1883 

1884 return resp 

1885 

1886 elif msgtype == 4: # CONFIRM 

1887 # see Sect 18.1.2 

1888 

1889 # Client want to check if addresses it was assigned 

1890 # are still appropriate 

1891 

1892 # Server must discard any Confirm messages that 

1893 # do not include a Client Identifier option OR 

1894 # THAT DO INCLUDE a Server Identifier Option 

1895 

1896 # XXX we must discard the SOLICIT if it is received with 

1897 # a unicast destination address 

1898 

1899 pass 

1900 

1901 elif msgtype == 5: # RENEW 

1902 # see Sect 18.1.3 

1903 

1904 # Clients want to extend lifetime of assigned addresses 

1905 # and update configuration parameters. This message is sent 

1906 # specifically to the server that provided her the info 

1907 

1908 # - Received message must include a Server Identifier 

1909 # option. 

1910 # - the content of server identifier option must match 

1911 # the server's identifier. 

1912 # - the message must include a Client identifier option 

1913 

1914 pass 

1915 

1916 elif msgtype == 6: # REBIND 

1917 # see Sect 18.1.4 

1918 

1919 # Same purpose as the Renew message but sent to any 

1920 # available server after he received no response 

1921 # to its previous Renew message. 

1922 

1923 # - Message must include a Client Identifier Option 

1924 # - Message can't include a Server identifier option 

1925 

1926 # XXX we must discard the SOLICIT if it is received with 

1927 # a unicast destination address 

1928 

1929 pass 

1930 

1931 elif msgtype == 8: # RELEASE 

1932 # See RFC 3315 section 18.1.6 

1933 

1934 # Message is sent to the server to indicate that 

1935 # she will no longer use the addresses that was assigned 

1936 # We should parse the message and verify our dictionary 

1937 # to log that fact. 

1938 

1939 # - The message must include a server identifier option 

1940 # - The content of the Server Identifier option must 

1941 # match the server's identifier 

1942 # - the message must include a Client Identifier option 

1943 

1944 pass 

1945 

1946 elif msgtype == 9: # DECLINE 

1947 # See RFC 3315 section 18.1.7 

1948 pass 

1949 

1950 elif msgtype == 11: # INFO-REQUEST 

1951 client_duid = None 

1952 if not p.haslayer(DHCP6OptClientId): 

1953 if self.debug: 

1954 warning("Received Info Request message without Client Id option") # noqa: E501 

1955 else: 

1956 client_duid = p[DHCP6OptClientId].duid 

1957 

1958 resp = IPv6(src=self.src_addr, dst=req_src) 

1959 resp /= UDP(sport=547, dport=546) 

1960 resp /= DHCP6_Reply(trid=trid) 

1961 resp /= DHCP6OptServerId(duid=self.duid) 

1962 

1963 if client_duid: 

1964 resp /= DHCP6OptClientId(duid=client_duid) 

1965 

1966 # Stack requested options if available 

1967 for o, opt in self.dhcpv6_options.items(): 

1968 resp /= opt 

1969 

1970 return resp 

1971 

1972 else: 

1973 # what else ? 

1974 pass 

1975 

1976 # - We won't support reemission 

1977 # - We won't support relay role, nor relay forwarded messages 

1978 # at the beginning