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