Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.8/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

647 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_raw_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 

62 

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

64 cls = conf.raw_layer 

65 if len(x) >= 2: 

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

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

68 

69############################################################################# 

70############################################################################# 

71# DHCPv6 # 

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

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

74 

75 

76All_DHCP_Relay_Agents_and_Servers = "ff02::1:2" 

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

78 

79dhcp6opts = {1: "CLIENTID", 

80 2: "SERVERID", 

81 3: "IA_NA", 

82 4: "IA_TA", 

83 5: "IAADDR", 

84 6: "ORO", 

85 7: "PREFERENCE", 

86 8: "ELAPSED_TIME", 

87 9: "RELAY_MSG", 

88 11: "AUTH", 

89 12: "UNICAST", 

90 13: "STATUS_CODE", 

91 14: "RAPID_COMMIT", 

92 15: "USER_CLASS", 

93 16: "VENDOR_CLASS", 

94 17: "VENDOR_OPTS", 

95 18: "INTERFACE_ID", 

96 19: "RECONF_MSG", 

97 20: "RECONF_ACCEPT", 

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

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

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

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

102 25: "OPTION_IA_PD", # RFC3633 

103 26: "OPTION_IAPREFIX", # RFC3633 

104 27: "OPTION_NIS_SERVERS", # RFC3898 

105 28: "OPTION_NISP_SERVERS", # RFC3898 

106 29: "OPTION_NIS_DOMAIN_NAME", # RFC3898 

107 30: "OPTION_NISP_DOMAIN_NAME", # RFC3898 

108 31: "OPTION_SNTP_SERVERS", # RFC4075 

109 32: "OPTION_INFORMATION_REFRESH_TIME", # RFC4242 

110 33: "OPTION_BCMCS_SERVER_D", # RFC4280 

111 34: "OPTION_BCMCS_SERVER_A", # RFC4280 

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

113 37: "OPTION_REMOTE_ID", # RFC4649 

114 38: "OPTION_SUBSCRIBER_ID", # RFC4580 

115 39: "OPTION_CLIENT_FQDN", # RFC4704 

116 40: "OPTION_PANA_AGENT", # RFC5192 

117 41: "OPTION_NEW_POSIX_TIMEZONE", # RFC4833 

118 42: "OPTION_NEW_TZDB_TIMEZONE", # RFC4833 

119 48: "OPTION_LQ_CLIENT_LINK", # RFC5007 

120 56: "OPTION_NTP_SERVER", # RFC5908 

121 59: "OPT_BOOTFILE_URL", # RFC5970 

122 60: "OPT_BOOTFILE_PARAM", # RFC5970 

123 61: "OPTION_CLIENT_ARCH_TYPE", # RFC5970 

124 62: "OPTION_NII", # RFC5970 

125 65: "OPTION_ERP_LOCAL_DOMAIN_NAME", # RFC6440 

126 66: "OPTION_RELAY_SUPPLIED_OPTIONS", # RFC6422 

127 68: "OPTION_VSS", # RFC6607 

128 79: "OPTION_CLIENT_LINKLAYER_ADDR", # RFC6939 

129 103: "OPTION_CAPTIVE_PORTAL", # RFC8910 

130 112: "OPTION_MUD_URL", # RFC8520 

131 } 

132 

133dhcp6opts_by_code = {1: "DHCP6OptClientId", 

134 2: "DHCP6OptServerId", 

135 3: "DHCP6OptIA_NA", 

136 4: "DHCP6OptIA_TA", 

137 5: "DHCP6OptIAAddress", 

138 6: "DHCP6OptOptReq", 

139 7: "DHCP6OptPref", 

140 8: "DHCP6OptElapsedTime", 

141 9: "DHCP6OptRelayMsg", 

142 11: "DHCP6OptAuth", 

143 12: "DHCP6OptServerUnicast", 

144 13: "DHCP6OptStatusCode", 

145 14: "DHCP6OptRapidCommit", 

146 15: "DHCP6OptUserClass", 

147 16: "DHCP6OptVendorClass", 

148 17: "DHCP6OptVendorSpecificInfo", 

149 18: "DHCP6OptIfaceId", 

150 19: "DHCP6OptReconfMsg", 

151 20: "DHCP6OptReconfAccept", 

152 21: "DHCP6OptSIPDomains", # RFC3319 

153 22: "DHCP6OptSIPServers", # RFC3319 

154 23: "DHCP6OptDNSServers", # RFC3646 

155 24: "DHCP6OptDNSDomains", # RFC3646 

156 25: "DHCP6OptIA_PD", # RFC3633 

157 26: "DHCP6OptIAPrefix", # RFC3633 

158 27: "DHCP6OptNISServers", # RFC3898 

159 28: "DHCP6OptNISPServers", # RFC3898 

160 29: "DHCP6OptNISDomain", # RFC3898 

161 30: "DHCP6OptNISPDomain", # RFC3898 

162 31: "DHCP6OptSNTPServers", # RFC4075 

163 32: "DHCP6OptInfoRefreshTime", # RFC4242 

164 33: "DHCP6OptBCMCSDomains", # RFC4280 

165 34: "DHCP6OptBCMCSServers", # RFC4280 

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

167 37: "DHCP6OptRemoteID", # RFC4649 

168 38: "DHCP6OptSubscriberID", # RFC4580 

169 39: "DHCP6OptClientFQDN", # RFC4704 

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

171 41: "DHCP6OptNewPOSIXTimeZone", # RFC4833 

172 42: "DHCP6OptNewTZDBTimeZone", # RFC4833 

173 43: "DHCP6OptRelayAgentERO", # RFC4994 

174 # 44: "DHCP6OptLQQuery", #RFC5007 

175 # 45: "DHCP6OptLQClientData", #RFC5007 

176 # 46: "DHCP6OptLQClientTime", #RFC5007 

177 # 47: "DHCP6OptLQRelayData", #RFC5007 

178 48: "DHCP6OptLQClientLink", # RFC5007 

179 56: "DHCP6OptNTPServer", # RFC5908 

180 59: "DHCP6OptBootFileUrl", # RFC5790 

181 60: "DHCP6OptBootFileParam", # RFC5970 

182 61: "DHCP6OptClientArchType", # RFC5970 

183 62: "DHCP6OptClientNetworkInterId", # RFC5970 

184 65: "DHCP6OptERPDomain", # RFC6440 

185 66: "DHCP6OptRelaySuppliedOpt", # RFC6422 

186 68: "DHCP6OptVSS", # RFC6607 

187 79: "DHCP6OptClientLinkLayerAddr", # RFC6939 

188 103: "DHCP6OptCaptivePortal", # RFC8910 

189 112: "DHCP6OptMudUrl", # RFC8520 

190 } 

191 

192 

193# sect 7.3 RFC 8415 : DHCP6 Messages types 

194dhcp6types = {1: "SOLICIT", 

195 2: "ADVERTISE", 

196 3: "REQUEST", 

197 4: "CONFIRM", 

198 5: "RENEW", 

199 6: "REBIND", 

200 7: "REPLY", 

201 8: "RELEASE", 

202 9: "DECLINE", 

203 10: "RECONFIGURE", 

204 11: "INFORMATION-REQUEST", 

205 12: "RELAY-FORW", 

206 13: "RELAY-REPL"} 

207 

208 

209##################################################################### 

210# DHCPv6 DUID related stuff # 

211##################################################################### 

212 

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

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

215 3: "Link-layer Address", 

216 4: "UUID"} 

217 

218# DUID hardware types - RFC 826 - Extracted from 

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

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

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

222 1: "Ethernet (10Mb)", 

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

224 3: "Amateur Radio AX.25", 

225 4: "Proteon ProNET Token Ring", 

226 5: "Chaos", 

227 6: "IEEE 802 Networks", 

228 7: "ARCNET", 

229 8: "Hyperchannel", 

230 9: "Lanstar", 

231 10: "Autonet Short Address", 

232 11: "LocalTalk", 

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

234 13: "Ultra link", 

235 14: "SMDS", 

236 15: "Frame Relay", 

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

238 17: "HDLC", 

239 18: "Fibre Channel", 

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

241 20: "Serial Line", 

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

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

244 23: "Metricom", 

245 24: "IEEE 1394.1995", 

246 25: "MAPOS", 

247 26: "Twinaxial", 

248 27: "EUI-64", 

249 28: "HIPARP", 

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

251 30: "ARPSec", 

252 31: "IPsec tunnel", 

253 32: "InfiniBand (TM)", 

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

255 

256 

257class _UTCTimeField(UTCTimeField): 

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

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

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

261 

262 

263class _LLAddrField(MACField): 

264 pass 

265 

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

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

268 

269 

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

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

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

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

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

275 _LLAddrField("lladdr", ETHER_ANY)] 

276 

277 

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

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

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

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

282 StrField("id", "")] 

283 

284 

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

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

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

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

289 _LLAddrField("lladdr", ETHER_ANY)] 

290 

291 

292class DUID_UUID(Packet): # RFC 6355 

293 name = "DUID - Based on UUID" 

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

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

296 

297 

298duid_cls = {1: "DUID_LLT", 

299 2: "DUID_EN", 

300 3: "DUID_LL", 

301 4: "DUID_UUID"} 

302 

303##################################################################### 

304# DHCPv6 Options classes # 

305##################################################################### 

306 

307 

308class _DHCP6OptGuessPayload(Packet): 

309 @staticmethod 

310 def _just_guess_payload_class(cls, payload): 

311 # try to guess what option is in the payload 

312 if len(payload) <= 2: 

313 return conf.raw_layer 

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

315 clsname = dhcp6opts_by_code.get(opt, None) 

316 if clsname is None: 

317 return cls 

318 return get_cls(clsname, cls) 

319 

320 def guess_payload_class(self, payload): 

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

322 # from _DHCP6OptGuessPayload in this file 

323 return _DHCP6OptGuessPayload._just_guess_payload_class( 

324 DHCP6OptUnknown, 

325 payload 

326 ) 

327 

328 

329class _DHCP6OptGuessPayloadElt(_DHCP6OptGuessPayload): 

330 """ 

331 Same than _DHCP6OptGuessPayload but made for lists 

332 in case of list of different suboptions 

333 e.g. in ianaopts in DHCP6OptIA_NA 

334 """ 

335 @classmethod 

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

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

338 

339 def extract_padding(self, s): 

340 return b"", s 

341 

342 

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

344 name = "Unknown DHCPv6 Option" 

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

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

347 StrLenField("data", "", 

348 length_from=lambda pkt: pkt.optlen)] 

349 

350 

351def _duid_dispatcher(x): 

352 cls = conf.raw_layer 

353 if len(x) > 4: 

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

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

356 return cls(x) 

357 

358 

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

360 name = "DHCP6 Client Identifier Option" 

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

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

363 PacketLenField("duid", "", _duid_dispatcher, 

364 length_from=lambda pkt: pkt.optlen)] 

365 

366 

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

368 name = "DHCP6 Server Identifier Option" 

369 optcode = 2 

370 

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

372# Can only appear at that location. 

373 

374 

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

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

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

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

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

380 IP6Field("addr", "::"), 

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

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

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

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

385 # more dhcp6 options and excpect failures 

386 PacketListField("iaaddropts", [], 

387 _DHCP6OptGuessPayloadElt, 

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

389 

390 def guess_payload_class(self, payload): 

391 return conf.padding_layer 

392 

393 

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

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

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

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

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

399 XIntField("iaid", None), 

400 IntField("T1", None), 

401 IntField("T2", None), 

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

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

404 

405 

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

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

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

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

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

411 XIntField("iaid", None), 

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

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

414 

415 

416# DHCPv6 Option Request Option # 

417 

418class _OptReqListField(StrLenField): 

419 islist = 1 

420 

421 def i2h(self, pkt, x): 

422 if not x: 

423 return [] 

424 return x 

425 

426 def i2len(self, pkt, x): 

427 return 2 * len(x) 

428 

429 def any2i(self, pkt, x): 

430 return x 

431 

432 def i2repr(self, pkt, x): 

433 s = [] 

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

435 if y in dhcp6opts: 

436 s.append(dhcp6opts[y]) 

437 else: 

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

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

440 

441 def m2i(self, pkt, x): 

442 r = [] 

443 while len(x) != 0: 

444 if len(x) < 2: 

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

446 return r 

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

448 x = x[2:] 

449 return r 

450 

451 def i2m(self, pkt, x): 

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

453 

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

455# Confirm or Information-request 

456 

457 

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

459 name = "DHCP6 Option Request Option" 

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

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

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

463 length_from=lambda pkt: pkt.optlen)] 

464 

465 

466# DHCPv6 Preference Option # 

467 

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

469# les messages Advertise, a priori 

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

471 name = "DHCP6 Preference Option" 

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

473 ShortField("optlen", 1), 

474 ByteField("prefval", 255)] 

475 

476 

477# DHCPv6 Elapsed Time Option # 

478 

479class _ElapsedTimeField(ShortField): 

480 def i2repr(self, pkt, x): 

481 if x == 0xffff: 

482 return "infinity (0xffff)" 

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

484 

485 

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

487 name = "DHCP6 Elapsed Time Option" 

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

489 ShortField("optlen", 2), 

490 _ElapsedTimeField("elapsedtime", 0)] 

491 

492 

493# DHCPv6 Authentication Option # 

494 

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

496# Reconfigure Key Authentication Protocol: 

497# 

498# protocol 3 

499# 

500# algorithm 1 

501# 

502# RDM 0 

503# 

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

505# Authentication Protocol is: 

506# 

507# 0 1 2 3 

508# 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 

509# +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 

510# | Type | Value (128 bits) | 

511# +-+-+-+-+-+-+-+-+ | 

512# . . 

513# . . 

514# . +-+-+-+-+-+-+-+-+ 

515# | | 

516# +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 

517# 

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

519# 

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

521# 

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

523# message). 

524# 

525# Value Data as defined by field. 

526 

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

528_dhcp6_auth_proto = { 

529 0: "configuration token", 

530 1: "delayed authentication", 

531 2: "delayed authentication (obsolete)", 

532 3: "reconfigure key", 

533} 

534_dhcp6_auth_alg = { 

535 0: "configuration token", 

536 1: "HMAC-MD5", 

537} 

538_dhcp6_auth_rdm = { 

539 0: "use of a monotonically increasing value" 

540} 

541 

542 

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

544 name = "DHCP6 Option - Authentication" 

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

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

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

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

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

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

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

552 StrLenField("authinfo", "", 

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

554 

555# DHCPv6 Server Unicast Option # 

556 

557 

558class _SrvAddrField(IP6Field): 

559 def i2h(self, pkt, x): 

560 if x is None: 

561 return "::" 

562 return x 

563 

564 def i2m(self, pkt, x): 

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

566 

567 

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

569 name = "DHCP6 Server Unicast Option" 

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

571 ShortField("optlen", 16), 

572 _SrvAddrField("srvaddr", None)] 

573 

574 

575# DHCPv6 Status Code Option # 

576 

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

578 1: "UnspecFail", 

579 2: "NoAddrsAvail", 

580 3: "NoBinding", 

581 4: "NotOnLink", 

582 5: "UseMulticast", 

583 6: "NoPrefixAvail"} # From RFC3633 

584 

585 

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

587 name = "DHCP6 Status Code Option" 

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

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

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

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

592 StrLenField("statusmsg", "", 

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

594 

595 

596# DHCPv6 Rapid Commit Option # 

597 

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

599 name = "DHCP6 Rapid Commit Option" 

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

601 ShortField("optlen", 0)] 

602 

603 

604# DHCPv6 User Class Option # 

605 

606class _UserClassDataField(PacketListField): 

607 def i2len(self, pkt, z): 

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

609 return 0 

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

611 

612 def getfield(self, pkt, s): 

613 tmp_len = self.length_from(pkt) 

614 lst = [] 

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

616 while len(remain) > 0: 

617 p = self.m2i(pkt, remain) 

618 if conf.padding_layer in p: 

619 pad = p[conf.padding_layer] 

620 remain = pad.load 

621 del pad.underlayer.payload 

622 else: 

623 remain = "" 

624 lst.append(p) 

625 return payl, lst 

626 

627 

628class USER_CLASS_DATA(Packet): 

629 name = "user class data" 

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

631 StrLenField("data", "", 

632 length_from=lambda pkt: pkt.len)] 

633 

634 def guess_payload_class(self, payload): 

635 return conf.padding_layer 

636 

637 

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

639 name = "DHCP6 User Class Option" 

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

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

642 length_of="userclassdata"), 

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

644 length_from=lambda pkt: pkt.optlen)] 

645 

646 

647# DHCPv6 Vendor Class Option # 

648 

649class _VendorClassDataField(_UserClassDataField): 

650 pass 

651 

652 

653class VENDOR_CLASS_DATA(USER_CLASS_DATA): 

654 name = "vendor class data" 

655 

656 

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

658 name = "DHCP6 Vendor Class Option" 

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

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

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

662 IntEnumField("enterprisenum", None, 

663 IANA_ENTERPRISE_NUMBERS), 

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

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

666 

667# DHCPv6 Vendor-Specific Information Option # 

668 

669 

670class VENDOR_SPECIFIC_OPTION(_DHCP6OptGuessPayload): 

671 name = "vendor specific option data" 

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

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

674 StrLenField("optdata", "", 

675 length_from=lambda pkt: pkt.optlen)] 

676 

677 def guess_payload_class(self, payload): 

678 return conf.padding_layer 

679 

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

681 

682 

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

684 name = "DHCP6 Vendor-specific Information Option" 

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

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

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

688 IntEnumField("enterprisenum", None, 

689 IANA_ENTERPRISE_NUMBERS), 

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

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

692 

693# DHCPv6 Interface-ID Option # 

694 

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

696# masses critique. 

697 

698 

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

700 name = "DHCP6 Interface-Id Option" 

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

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

703 length_of="ifaceid"), 

704 StrLenField("ifaceid", "", 

705 length_from=lambda pkt: pkt.optlen)] 

706 

707 

708# DHCPv6 Reconfigure Message Option # 

709 

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

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

712# renew message or an Information-request message. 

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

714 name = "DHCP6 Reconfigure Message Option" 

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

716 ShortField("optlen", 1), 

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

718 11: "Information Request"})] 

719 

720 

721# DHCPv6 Reconfigure Accept Option # 

722 

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

724# server whether the client is willing to accept Recoonfigure 

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

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

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

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

729# client and server messages, respectively. 

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

731 name = "DHCP6 Reconfigure Accept Option" 

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

733 ShortField("optlen", 0)] 

734 

735 

736class DHCP6OptSIPDomains(_DHCP6OptGuessPayload): # RFC3319 

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

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

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

740 DomainNameListField("sipdomains", [], 

741 length_from=lambda pkt: pkt.optlen)] 

742 

743 

744class DHCP6OptSIPServers(_DHCP6OptGuessPayload): # RFC3319 

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

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

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

748 IP6ListField("sipservers", [], 

749 length_from=lambda pkt: pkt.optlen)] 

750 

751 

752class DHCP6OptDNSServers(_DHCP6OptGuessPayload): # RFC3646 

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

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

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

756 IP6ListField("dnsservers", [], 

757 length_from=lambda pkt: pkt.optlen)] 

758 

759 

760class DHCP6OptDNSDomains(_DHCP6OptGuessPayload): # RFC3646 

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

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

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

764 DomainNameListField("dnsdomains", [], 

765 length_from=lambda pkt: pkt.optlen)] 

766 

767 

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

769 name = "DHCP6 Option - IA Prefix option" 

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

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

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

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

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

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

776 # See RFC 8168 

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

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

779 # expect failures 

780 PacketListField("iaprefopts", [], 

781 _DHCP6OptGuessPayloadElt, 

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

783 

784 def guess_payload_class(self, payload): 

785 return conf.padding_layer 

786 

787 

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

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

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

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

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

793 XIntField("iaid", None), 

794 IntField("T1", None), 

795 IntField("T2", None), 

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

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

798 

799 

800class DHCP6OptNISServers(_DHCP6OptGuessPayload): # RFC3898 

801 name = "DHCP6 Option - NIS Servers" 

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

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

804 IP6ListField("nisservers", [], 

805 length_from=lambda pkt: pkt.optlen)] 

806 

807 

808class DHCP6OptNISPServers(_DHCP6OptGuessPayload): # RFC3898 

809 name = "DHCP6 Option - NIS+ Servers" 

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

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

812 IP6ListField("nispservers", [], 

813 length_from=lambda pkt: pkt.optlen)] 

814 

815 

816class DHCP6OptNISDomain(_DHCP6OptGuessPayload): # RFC3898 

817 name = "DHCP6 Option - NIS Domain Name" 

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

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

820 DNSStrField("nisdomain", "", 

821 length_from=lambda pkt: pkt.optlen)] 

822 

823 

824class DHCP6OptNISPDomain(_DHCP6OptGuessPayload): # RFC3898 

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

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

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

828 DNSStrField("nispdomain", "", 

829 length_from=lambda pkt: pkt.optlen)] 

830 

831 

832class DHCP6OptSNTPServers(_DHCP6OptGuessPayload): # RFC4075 

833 name = "DHCP6 option - SNTP Servers" 

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

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

836 IP6ListField("sntpservers", [], 

837 length_from=lambda pkt: pkt.optlen)] 

838 

839 

840IRT_DEFAULT = 86400 

841IRT_MINIMUM = 600 

842 

843 

844class DHCP6OptInfoRefreshTime(_DHCP6OptGuessPayload): # RFC4242 

845 name = "DHCP6 Option - Information Refresh Time" 

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

847 ShortField("optlen", 4), 

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

849 

850 

851class DHCP6OptBCMCSDomains(_DHCP6OptGuessPayload): # RFC4280 

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

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

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

855 DomainNameListField("bcmcsdomains", [], 

856 length_from=lambda pkt: pkt.optlen)] 

857 

858 

859class DHCP6OptBCMCSServers(_DHCP6OptGuessPayload): # RFC4280 

860 name = "DHCP6 Option - BCMCS Addresses List" 

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

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

863 IP6ListField("bcmcsservers", [], 

864 length_from=lambda pkt: pkt.optlen)] 

865 

866 

867_dhcp6_geoconf_what = { 

868 0: "DHCP server", 

869 1: "closest network element", 

870 2: "client" 

871} 

872 

873 

874class DHCP6OptGeoConfElement(Packet): 

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

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

877 StrLenField("CAvalue", "", 

878 length_from=lambda pkt: pkt.CAlength)] 

879 

880 

881class DHCP6OptGeoConf(_DHCP6OptGuessPayload): # RFC 4776 

882 name = "DHCP6 Option - Civic Location" 

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

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

885 adjust=lambda x: x + 3), 

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

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

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

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

890 

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

892 

893 

894class DHCP6OptRemoteID(_DHCP6OptGuessPayload): # RFC4649 

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

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

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

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

899 IntEnumField("enterprisenum", None, 

900 IANA_ENTERPRISE_NUMBERS), 

901 StrLenField("remoteid", "", 

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

903 

904 

905class DHCP6OptSubscriberID(_DHCP6OptGuessPayload): # RFC4580 

906 name = "DHCP6 Option - Subscriber ID" 

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

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

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

910 # but we don't really care 

911 StrLenField("subscriberid", "", 

912 length_from=lambda pkt: pkt.optlen)] 

913 

914 

915class DHCP6OptClientFQDN(_DHCP6OptGuessPayload): # RFC4704 

916 name = "DHCP6 Option - Client FQDN" 

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

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

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

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

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

922 DNSStrField("fqdn", "", 

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

924 

925 

926class DHCP6OptPanaAuthAgent(_DHCP6OptGuessPayload): # RFC5192 

927 name = "DHCP6 PANA Authentication Agent Option" 

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

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

930 IP6ListField("paaaddr", [], 

931 length_from=lambda pkt: pkt.optlen)] 

932 

933 

934class DHCP6OptNewPOSIXTimeZone(_DHCP6OptGuessPayload): # RFC4833 

935 name = "DHCP6 POSIX Timezone Option" 

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

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

938 StrLenField("optdata", "", 

939 length_from=lambda pkt: pkt.optlen)] 

940 

941 

942class DHCP6OptNewTZDBTimeZone(_DHCP6OptGuessPayload): # RFC4833 

943 name = "DHCP6 TZDB Timezone Option" 

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

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

946 StrLenField("optdata", "", 

947 length_from=lambda pkt: pkt.optlen)] 

948 

949 

950class DHCP6OptRelayAgentERO(_DHCP6OptGuessPayload): # RFC4994 

951 name = "DHCP6 Option - RelayRequest Option" 

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

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

954 fmt="!H"), 

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

956 length_from=lambda pkt: pkt.optlen)] 

957 

958 

959class DHCP6OptLQClientLink(_DHCP6OptGuessPayload): # RFC5007 

960 name = "DHCP6 Client Link Option" 

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

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

963 IP6ListField("linkaddress", [], 

964 length_from=lambda pkt: pkt.optlen)] 

965 

966 

967class DHCP6NTPSubOptSrvAddr(Packet): # RFC5908 sect 4.1 

968 name = "DHCP6 NTP Server Address Suboption" 

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

970 ShortField("optlen", 16), 

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

972 

973 def extract_padding(self, s): 

974 return b"", s 

975 

976 

977class DHCP6NTPSubOptMCAddr(Packet): # RFC5908 sect 4.2 

978 name = "DHCP6 NTP Multicast Address Suboption" 

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

980 ShortField("optlen", 16), 

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

982 

983 def extract_padding(self, s): 

984 return b"", s 

985 

986 

987class DHCP6NTPSubOptSrvFQDN(Packet): # RFC5908 sect 4.3 

988 name = "DHCP6 NTP Server FQDN Suboption" 

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

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

991 DNSStrField("fqdn", "", 

992 length_from=lambda pkt: pkt.optlen)] 

993 

994 def extract_padding(self, s): 

995 return b"", s 

996 

997 

998_ntp_subopts = {1: DHCP6NTPSubOptSrvAddr, 

999 2: DHCP6NTPSubOptMCAddr, 

1000 3: DHCP6NTPSubOptSrvFQDN} 

1001 

1002 

1003def _ntp_subopt_dispatcher(p, **kwargs): 

1004 cls = conf.raw_layer 

1005 if len(p) >= 2: 

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

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

1008 return cls(p, **kwargs) 

1009 

1010 

1011class DHCP6OptNTPServer(_DHCP6OptGuessPayload): # RFC5908 

1012 name = "DHCP6 NTP Server Option" 

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

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

1015 fmt="!H"), 

1016 PacketListField("ntpserver", [], 

1017 _ntp_subopt_dispatcher, 

1018 length_from=lambda pkt: pkt.optlen)] 

1019 

1020 

1021class DHCP6OptBootFileUrl(_DHCP6OptGuessPayload): # RFC5970 

1022 name = "DHCP6 Boot File URL Option" 

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

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

1025 StrLenField("optdata", "", 

1026 length_from=lambda pkt: pkt.optlen)] 

1027 

1028 

1029class DHCP6OptClientArchType(_DHCP6OptGuessPayload): # RFC5970 

1030 name = "DHCP6 Client System Architecture Type Option" 

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

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

1033 fmt="!H"), 

1034 FieldListField("archtypes", [], 

1035 ShortField("archtype", 0), 

1036 length_from=lambda pkt: pkt.optlen)] 

1037 

1038 

1039class DHCP6OptClientNetworkInterId(_DHCP6OptGuessPayload): # RFC5970 

1040 name = "DHCP6 Client Network Interface Identifier Option" 

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

1042 ShortField("optlen", 3), 

1043 ByteField("iitype", 0), 

1044 ByteField("iimajor", 0), 

1045 ByteField("iiminor", 0)] 

1046 

1047 

1048class DHCP6OptERPDomain(_DHCP6OptGuessPayload): # RFC6440 

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

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

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

1052 DomainNameListField("erpdomain", [], 

1053 length_from=lambda pkt: pkt.optlen)] 

1054 

1055 

1056class DHCP6OptRelaySuppliedOpt(_DHCP6OptGuessPayload): # RFC6422 

1057 name = "DHCP6 Relay-Supplied Options Option" 

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

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

1060 fmt="!H"), 

1061 PacketListField("relaysupplied", [], 

1062 _DHCP6OptGuessPayloadElt, 

1063 length_from=lambda pkt: pkt.optlen)] 

1064 

1065 

1066# Virtual Subnet selection 

1067class DHCP6OptVSS(_DHCP6OptGuessPayload): # RFC6607 

1068 name = "DHCP6 Option - Virtual Subnet Selection" 

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

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

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

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

1073 StrLenField("data", "", 

1074 length_from=lambda pkt: pkt.optlen)] 

1075 

1076 

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

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

1079class DHCP6OptClientLinkLayerAddr(_DHCP6OptGuessPayload): # RFC6939 

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

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

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

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

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

1085 _LLAddrField("clladdr", ETHER_ANY)] 

1086 

1087 

1088class DHCP6OptCaptivePortal(_DHCP6OptGuessPayload): # RFC8910 

1089 name = "DHCP6 Option - Captive-Portal" 

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

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

1092 StrLenField("URI", "", 

1093 length_from=lambda pkt: pkt.optlen)] 

1094 

1095 

1096class DHCP6OptMudUrl(_DHCP6OptGuessPayload): # RFC8520 

1097 name = "DHCP6 Option - MUD URL" 

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

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

1100 StrLenField("mudstring", "", 

1101 length_from=lambda pkt: pkt.optlen, 

1102 max_length=253, 

1103 )] 

1104 

1105 

1106##################################################################### 

1107# DHCPv6 messages # 

1108##################################################################### 

1109 

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

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

1112DHCP6RelayAgentUnicastAddr = "" 

1113DHCP6RelayHopCount = "" 

1114DHCP6ServerUnicastAddr = "" 

1115DHCP6ClientUnicastAddr = "" 

1116DHCP6ClientIA_TA = "" 

1117DHCP6ClientIA_NA = "" 

1118DHCP6ClientIAID = "" 

1119T1 = "" # Voir 2462 

1120T2 = "" # Voir 2462 

1121DHCP6ServerDUID = "" 

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

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

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

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

1126# les options preference 

1127 

1128# Emitted by : 

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

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

1131# INFORMATION REQUEST 

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

1133 

1134##################################################################### 

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

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

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

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

1139# les clients 

1140 

1141 

1142class DHCP6(_DHCP6OptGuessPayload): 

1143 name = "DHCPv6 Generic Message" 

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

1145 X3BytesField("trid", 0x000000)] 

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

1147 

1148 def hashret(self): 

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

1150 

1151# DHCPv6 Relay Message Option # 

1152 

1153# Relayed message is seen as a payload. 

1154 

1155 

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

1157 name = "DHCP6 Relay Message Option" 

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

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

1160 length_of="message"), 

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

1162 length_from=lambda p: p.optlen)] 

1163 

1164##################################################################### 

1165# Solicit Message : sect 17.1.1 RFC3315 

1166# - sent by client 

1167# - must include a client identifier option 

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

1169# server to assign address 

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

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

1172# assignment of temporary addresses 

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

1174# options the client is interested in receiving (eventually 

1175# including hints) 

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

1177# accept Reconfigure messages from the server. 

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

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

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

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

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

1183# Request. 

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

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

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

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

1188# exchanged during the session 

1189 

1190 

1191class DHCP6_Solicit(DHCP6): 

1192 name = "DHCPv6 Solicit Message" 

1193 msgtype = 1 

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

1195 

1196##################################################################### 

1197# Advertise Message 

1198# - sent by server 

1199# - Includes a server identifier option 

1200# - Includes a client identifier option 

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

1202# - transaction ID must match 

1203 

1204 

1205class DHCP6_Advertise(DHCP6): 

1206 name = "DHCPv6 Advertise Message" 

1207 msgtype = 2 

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

1209 

1210 def answers(self, other): 

1211 return (isinstance(other, DHCP6_Solicit) and 

1212 other.msgtype == 1 and 

1213 self.trid == other.trid) 

1214 

1215##################################################################### 

1216# Request Message 

1217# - sent by clients 

1218# - includes a server identifier option 

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

1220# - includes a client identifier option 

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

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

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

1224# the server (p40) 

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

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

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

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

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

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

1231 

1232 

1233class DHCP6_Request(DHCP6): 

1234 name = "DHCPv6 Request Message" 

1235 msgtype = 3 

1236 

1237##################################################################### 

1238# Confirm Message 

1239# - sent by clients 

1240# - must include a client identifier option 

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

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

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

1244 

1245 

1246class DHCP6_Confirm(DHCP6): 

1247 name = "DHCPv6 Confirm Message" 

1248 msgtype = 4 

1249 

1250##################################################################### 

1251# Renew Message 

1252# - sent by clients 

1253# - must include a server identifier option 

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

1255# - must include a client identifier option 

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

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

1258# those IAs in its confirm messages 

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

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

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

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

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

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

1265# p51 pour le reste. 

1266 

1267 

1268class DHCP6_Renew(DHCP6): 

1269 name = "DHCPv6 Renew Message" 

1270 msgtype = 5 

1271 

1272##################################################################### 

1273# Rebind Message 

1274# - sent by clients 

1275# - must include a client identifier option 

1276# cf p52 

1277 

1278 

1279class DHCP6_Rebind(DHCP6): 

1280 name = "DHCPv6 Rebind Message" 

1281 msgtype = 6 

1282 

1283##################################################################### 

1284# Reply Message 

1285# - sent by servers 

1286# - the message must include a server identifier option 

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

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

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

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

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

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

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

1294# client resends the original message using multicast 

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

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

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

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

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

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

1301# without specifying any addresses or restart the DHCP server 

1302# discovery process. 

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

1304# server's DUID in the Reply message 

1305 

1306 

1307class DHCP6_Reply(DHCP6): 

1308 name = "DHCPv6 Reply Message" 

1309 msgtype = 7 

1310 

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

1312 

1313 def answers(self, other): 

1314 

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

1316 DHCP6_Decline, DHCP6_Request, DHCP6_Release, DHCP6_Renew) 

1317 

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

1319 

1320##################################################################### 

1321# Release Message 

1322# - sent by clients 

1323# - must include a server identifier option 

1324# cf p53 

1325 

1326 

1327class DHCP6_Release(DHCP6): 

1328 name = "DHCPv6 Release Message" 

1329 msgtype = 8 

1330 

1331##################################################################### 

1332# Decline Message 

1333# - sent by clients 

1334# - must include a client identifier option 

1335# - Server identifier option must match server identifier 

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

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

1338# not be in added to the IAs. 

1339# - cf p54 

1340 

1341 

1342class DHCP6_Decline(DHCP6): 

1343 name = "DHCPv6 Decline Message" 

1344 msgtype = 9 

1345 

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

1347# Reconfigure Message 

1348# - sent by servers 

1349# - must be unicast to the client 

1350# - must include a server identifier option 

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

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

1353# must be a valid value 

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

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

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

1357# voir le plus souvent. 

1358 

1359 

1360class DHCP6_Reconf(DHCP6): 

1361 name = "DHCPv6 Reconfigure Message" 

1362 msgtype = 10 

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

1364 

1365 

1366##################################################################### 

1367# Information-Request Message 

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

1369# addresses. 

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

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

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

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

1374# if the message will be authenticated. 

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

1376# (can include hints) 

1377 

1378class DHCP6_InfoRequest(DHCP6): 

1379 name = "DHCPv6 Information Request Message" 

1380 msgtype = 11 

1381 

1382##################################################################### 

1383# sent between Relay Agents and Servers 

1384# 

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

1386# peut en inclure d'autres. 

1387# voir section 7.1 de la 3315 

1388 

1389# Relay-Forward Message 

1390# - sent by relay agents to servers 

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

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

1393# 32. 

1394 

1395 

1396class DHCP6_RelayForward(_DHCP6OptGuessPayload, Packet): 

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

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

1399 ByteField("hopcount", None), 

1400 IP6Field("linkaddr", "::"), 

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

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

1403 

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

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

1406 

1407##################################################################### 

1408# sent between Relay Agents and Servers 

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

1410# peut en inclure d'autres. 

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

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

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

1414# se limite au contenu du champ peer address. 

1415# Voir section 7.2 de la 3315. 

1416 

1417# Relay-Reply Message 

1418# - sent by servers to relay agents 

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

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

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

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

1423# presente en ip source du paquet recu 

1424 

1425 

1426class DHCP6_RelayReply(DHCP6_RelayForward): 

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

1428 msgtype = 13 

1429 

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

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

1432 

1433 def answers(self, other): 

1434 return (isinstance(other, DHCP6_RelayForward) and 

1435 self.hopcount == other.hopcount and 

1436 self.linkaddr == other.linkaddr and 

1437 self.peeraddr == other.peeraddr) 

1438 

1439 

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

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

1442 

1443 

1444class DHCPv6_am(AnsweringMachine): 

1445 function_name = "dhcp6d" 

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

1447 send_function = staticmethod(send) 

1448 

1449 def usage(self): 

1450 msg = """ 

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

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

1453 sipdomains=None, sipservers=None, 

1454 nisdomain=None, nisservers=None, 

1455 nispdomain=None, nispservers=None, 

1456 bcmcsdomains=None, bcmcsservers=None) 

1457 

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

1459 

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

1461 is provided a DUID_LLT is constructed based on the MAC 

1462 address of the sending interface and launch time of dhcp6d 

1463 answering machine. 

1464 

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

1466 conf.iface. 

1467 

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

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

1470 value makes clients stops waiting for further Advertise 

1471 messages from other servers. 

1472 

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

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

1475 option is inactive. See RFC 3646 for details. 

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

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

1478 See RFC 3646 for details. 

1479 

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

1481 it is empty and the associated DHCP6OptSNTPServers option 

1482 is inactive. 

1483 

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

1485 associated DHCP6OptSIPDomains option is inactive. See RFC 3319 

1486 for details. 

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

1488 empty and the associated DHCP6OptSIPDomains option is inactive. 

1489 See RFC 3319 for details. 

1490 

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

1492 associated DHCP6OptNISDomains option is inactive. See RFC 3898 

1493 for details. See RFC 3646 for details. 

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

1495 empty and the associated DHCP6OptNISServers option is inactive. 

1496 See RFC 3646 for details. 

1497 

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

1499 associated DHCP6OptNISPDomains option is inactive. See RFC 3898 

1500 for details. 

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

1502 empty and the associated DHCP6OptNISServers option is inactive. 

1503 See RFC 3898 for details. 

1504 

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

1506 associated DHCP6OptBCMCSDomains option is inactive. See RFC 4280 

1507 for details. 

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

1509 empty and the associated DHCP6OptBCMCSServers option is inactive. 

1510 See RFC 4280 for details. 

1511 

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

1513 print(msg) 

1514 

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

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

1517 sntpservers=None, sipdomains=None, sipservers=None, 

1518 nisdomain=None, nisservers=None, nispdomain=None, 

1519 nispservers=None, bcmcsservers=None, bcmcsdomains=None, 

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

1521 def norm_list(val, param_name): 

1522 if val is None: 

1523 return None 

1524 if isinstance(val, list): 

1525 return val 

1526 elif isinstance(val, str): 

1527 tmp_len = val.split(',') 

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

1529 else: 

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

1531 self.usage() 

1532 return -1 

1533 

1534 if iface is None: 

1535 iface = conf.iface 

1536 

1537 self.debug = debug 

1538 

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

1540 self.dhcpv6_options = {} 

1541 

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

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

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

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

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

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

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

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

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

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

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

1553 

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

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

1556 return False 

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

1558 pass 

1559 else: 

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

1561 

1562 if self.debug: 

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

1564 opts = sorted(self.dhcpv6_options) 

1565 for i in opts: 

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

1567 

1568 # Preference value used in Advertise. 

1569 self.advpref = advpref 

1570 

1571 # IP Pool 

1572 self.startip = startip 

1573 self.endip = endip 

1574 # XXX TODO Check IPs are in same subnet 

1575 

1576 #### 

1577 # The interface we are listening/replying on 

1578 self.iface = iface 

1579 

1580 #### 

1581 # Generate a server DUID 

1582 if duid is not None: 

1583 self.duid = duid 

1584 else: 

1585 # Timeval 

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

1587 delta = time.mktime(epoch) - EPOCH 

1588 timeval = time.time() - delta 

1589 

1590 # Mac Address 

1591 rawmac = get_if_raw_hwaddr(iface)[1] 

1592 mac = ":".join("%.02x" % orb(x) for x in rawmac) 

1593 

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

1595 

1596 if self.debug: 

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

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

1599 

1600 #### 

1601 # Find the source address we will use 

1602 self.src_addr = None 

1603 try: 

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

1605 except (StopIteration, RuntimeError): 

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

1607 return 

1608 else: 

1609 self.src_addr = addr[0] 

1610 

1611 #### 

1612 # Our leases 

1613 self.leases = {} 

1614 

1615 if self.debug: 

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

1617 

1618 def is_request(self, p): 

1619 if IPv6 not in p: 

1620 return False 

1621 

1622 src = p[IPv6].src 

1623 

1624 p = p[IPv6].payload 

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

1626 return False 

1627 

1628 p = p.payload 

1629 if not isinstance(p, DHCP6): 

1630 return False 

1631 

1632 # Message we considered client messages : 

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

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

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

1636 return False 

1637 

1638 # Message validation following section 15 of RFC 3315 

1639 

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

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

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

1643 if ((DHCP6OptClientId not in p) or 

1644 DHCP6OptServerId in p): 

1645 return False 

1646 

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

1648 p.msgtype == 4): # Confirm 

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

1650 # XXX do not support address assignment 

1651 return False 

1652 

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

1654 p.msgtype == 5 or # Renew 

1655 p.msgtype == 8): # Release 

1656 

1657 # Both options must be present 

1658 if ((DHCP6OptServerId not in p) or 

1659 (DHCP6OptClientId not in p)): 

1660 return False 

1661 # provided server DUID must match ours 

1662 duid = p[DHCP6OptServerId].duid 

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

1664 return False 

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

1666 return False 

1667 

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

1669 p.msgtype == 8): # Release 

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

1671 # XXX do not support address assignment 

1672 return False 

1673 

1674 elif p.msgtype == 9: # Decline 

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

1676 if not self.debug: 

1677 return False 

1678 

1679 bo = Color.bold 

1680 g = Color.green + bo 

1681 b = Color.blue + bo 

1682 n = Color.normal 

1683 r = Color.red 

1684 

1685 vendor = in6_addrtovendor(src) 

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

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

1688 else: 

1689 vendor = "" 

1690 src = bo + src + n 

1691 

1692 it = p 

1693 addrs = [] 

1694 while it: 

1695 lst = [] 

1696 if isinstance(it, DHCP6OptIA_NA): 

1697 lst = it.ianaopts 

1698 elif isinstance(it, DHCP6OptIA_TA): 

1699 lst = it.iataopts 

1700 

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

1702 it = it.payload 

1703 

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

1705 if self.debug: 

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

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

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

1709 print(msg) 

1710 

1711 # See RFC 3315 sect 18.1.7 

1712 

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

1714 # one or more addresses assigned to her is already 

1715 # used on the link. 

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

1717 # be sent in return. 

1718 

1719 # - Message must include a Server identifier option 

1720 # - the content of the Server identifier option must 

1721 # match the server's identifier 

1722 # - the message must include a Client Identifier option 

1723 return False 

1724 

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

1726 if DHCP6OptServerId in p: 

1727 duid = p[DHCP6OptServerId].duid 

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

1729 return False 

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

1731 return False 

1732 if ((DHCP6OptIA_NA in p) or 

1733 (DHCP6OptIA_TA in p) or 

1734 (DHCP6OptIA_PD in p)): 

1735 return False 

1736 else: 

1737 return False 

1738 

1739 return True 

1740 

1741 def print_reply(self, req, reply): 

1742 def norm(s): 

1743 if s.startswith("DHCPv6 "): 

1744 s = s[7:] 

1745 if s.endswith(" Message"): 

1746 s = s[:-8] 

1747 return s 

1748 

1749 if reply is None: 

1750 return 

1751 

1752 bo = Color.bold 

1753 g = Color.green + bo 

1754 b = Color.blue + bo 

1755 n = Color.normal 

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

1757 reqsrc = req.getlayer(IPv6).src 

1758 vendor = in6_addrtovendor(reqsrc) 

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

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

1761 else: 

1762 vendor = "" 

1763 reqsrc = bo + reqsrc + n 

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

1765 

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

1767 

1768 def make_reply(self, req): 

1769 p = req[IPv6] 

1770 req_src = p.src 

1771 

1772 p = p.payload.payload 

1773 

1774 msgtype = p.msgtype 

1775 trid = p.trid 

1776 

1777 def _include_options(query, answer): 

1778 """ 

1779 Include options from the DHCPv6 query 

1780 """ 

1781 

1782 # See which options should be included 

1783 reqopts = [] 

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

1785 reqopts = query[DHCP6OptOptReq].reqopts 

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

1787 if o in reqopts: 

1788 answer /= opt 

1789 else: 

1790 # advertise everything we have available 

1791 # Should not happen has clients MUST include 

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

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

1794 answer /= opt 

1795 

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

1797 

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

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

1800 

1801 client_duid = p[DHCP6OptClientId].duid 

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

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

1804 

1805 if p.haslayer(DHCP6OptRapidCommit): 

1806 # construct a Reply packet 

1807 resp /= DHCP6_Reply(trid=trid) 

1808 resp /= DHCP6OptRapidCommit() # See 17.1.2 

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

1810 resp /= DHCP6OptClientId(duid=client_duid) 

1811 

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

1813 

1814 if (p.haslayer(DHCP6OptIA_NA) or 

1815 p.haslayer(DHCP6OptIA_TA)): 

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

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

1818 resp /= DHCP6_Advertise(trid=trid) 

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

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

1821 resp /= DHCP6OptClientId(duid=client_duid) 

1822 

1823 elif p.haslayer(DHCP6OptIA_PD): 

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

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

1826 resp /= DHCP6_Advertise(trid=trid) 

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

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

1829 resp /= DHCP6OptClientId(duid=client_duid) 

1830 

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

1832 resp /= DHCP6_Advertise(trid=trid) 

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

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

1835 resp /= DHCP6OptClientId(duid=client_duid) 

1836 resp /= DHCP6OptReconfAccept() 

1837 

1838 _include_options(p, resp) 

1839 

1840 return resp 

1841 

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

1843 client_duid = p[DHCP6OptClientId].duid 

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

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

1846 resp /= DHCP6_Solicit(trid=trid) 

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

1848 resp /= DHCP6OptClientId(duid=client_duid) 

1849 

1850 _include_options(p, resp) 

1851 

1852 return resp 

1853 

1854 elif msgtype == 4: # CONFIRM 

1855 # see Sect 18.1.2 

1856 

1857 # Client want to check if addresses it was assigned 

1858 # are still appropriate 

1859 

1860 # Server must discard any Confirm messages that 

1861 # do not include a Client Identifier option OR 

1862 # THAT DO INCLUDE a Server Identifier Option 

1863 

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

1865 # a unicast destination address 

1866 

1867 pass 

1868 

1869 elif msgtype == 5: # RENEW 

1870 # see Sect 18.1.3 

1871 

1872 # Clients want to extend lifetime of assigned addresses 

1873 # and update configuration parameters. This message is sent 

1874 # specifically to the server that provided her the info 

1875 

1876 # - Received message must include a Server Identifier 

1877 # option. 

1878 # - the content of server identifier option must match 

1879 # the server's identifier. 

1880 # - the message must include a Client identifier option 

1881 

1882 pass 

1883 

1884 elif msgtype == 6: # REBIND 

1885 # see Sect 18.1.4 

1886 

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

1888 # available server after he received no response 

1889 # to its previous Renew message. 

1890 

1891 # - Message must include a Client Identifier Option 

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

1893 

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

1895 # a unicast destination address 

1896 

1897 pass 

1898 

1899 elif msgtype == 8: # RELEASE 

1900 # See RFC 3315 section 18.1.6 

1901 

1902 # Message is sent to the server to indicate that 

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

1904 # We should parse the message and verify our dictionary 

1905 # to log that fact. 

1906 

1907 # - The message must include a server identifier option 

1908 # - The content of the Server Identifier option must 

1909 # match the server's identifier 

1910 # - the message must include a Client Identifier option 

1911 

1912 pass 

1913 

1914 elif msgtype == 9: # DECLINE 

1915 # See RFC 3315 section 18.1.7 

1916 pass 

1917 

1918 elif msgtype == 11: # INFO-REQUEST 

1919 client_duid = None 

1920 if not p.haslayer(DHCP6OptClientId): 

1921 if self.debug: 

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

1923 else: 

1924 client_duid = p[DHCP6OptClientId].duid 

1925 

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

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

1928 resp /= DHCP6_Reply(trid=trid) 

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

1930 

1931 if client_duid: 

1932 resp /= DHCP6OptClientId(duid=client_duid) 

1933 

1934 # Stack requested options if available 

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

1936 resp /= opt 

1937 

1938 return resp 

1939 

1940 else: 

1941 # what else ? 

1942 pass 

1943 

1944 # - We won't support reemission 

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

1946 # at the beginning