1# SPDX-License-Identifier: GPL-2.0-only
2# This file is part of Scapy
3# See https://scapy.net/ for more information
4# Copyright (C) Philippe Biondi <phil@secdev.org>
5# Copyright (C) Philippe Biondi <phil@secdev.org>
6# Copyright (C) 2005 Guillaume Valadon <guedou@hongo.wide.ad.jp>
7# Arnaud Ebalard <arnaud.ebalard@eads.net>
8
9"""
10DHCPv6: Dynamic Host Configuration Protocol for IPv6. [RFC 3315,8415]
11"""
12
13import socket
14import struct
15import time
16
17from scapy.ansmachine import AnsweringMachine
18from scapy.arch import get_if_hwaddr, in6_getifaddr
19from scapy.config import conf
20from scapy.data import EPOCH, ETHER_ANY
21from scapy.compat import raw, orb
22from scapy.error import warning
23from scapy.fields import BitField, ByteEnumField, ByteField, FieldLenField, \
24 FlagsField, IntEnumField, IntField, MACField, \
25 PacketListField, ShortEnumField, ShortField, StrField, StrFixedLenField, \
26 StrLenField, UTCTimeField, X3BytesField, XIntField, XShortEnumField, \
27 PacketLenField, UUIDField, FieldListField
28from scapy.data import IANA_ENTERPRISE_NUMBERS
29from scapy.layers.dns import DNSStrField
30from scapy.layers.inet import UDP
31from scapy.layers.inet6 import DomainNameListField, IP6Field, IP6ListField, \
32 IPv6
33from scapy.packet import Packet, bind_bottom_up
34from scapy.pton_ntop import inet_pton
35from scapy.sendrecv import send
36from scapy.themes import Color
37from scapy.utils6 import in6_addrtovendor, in6_islladdr
38
39#############################################################################
40# Helpers ##
41#############################################################################
42
43
44def get_cls(name, fallback_cls):
45 return globals().get(name, fallback_cls)
46
47
48dhcp6_cls_by_type = {1: "DHCP6_Solicit",
49 2: "DHCP6_Advertise",
50 3: "DHCP6_Request",
51 4: "DHCP6_Confirm",
52 5: "DHCP6_Renew",
53 6: "DHCP6_Rebind",
54 7: "DHCP6_Reply",
55 8: "DHCP6_Release",
56 9: "DHCP6_Decline",
57 10: "DHCP6_Reconf",
58 11: "DHCP6_InfoRequest",
59 12: "DHCP6_RelayForward",
60 13: "DHCP6_RelayReply",
61 36: "DHCP6_AddrRegInform",
62 37: "DHCP6_AddrRegReply",
63 }
64
65
66def _dhcp6_dispatcher(x, *args, **kargs):
67 cls = conf.raw_layer
68 if len(x) >= 2:
69 cls = get_cls(dhcp6_cls_by_type.get(orb(x[0]), "Raw"), conf.raw_layer)
70 return cls(x, *args, **kargs)
71
72#############################################################################
73#############################################################################
74# DHCPv6 #
75#############################################################################
76#############################################################################
77
78
79All_DHCP_Relay_Agents_and_Servers = "ff02::1:2"
80All_DHCP_Servers = "ff05::1:3" # Site-Local scope : deprecated by 3879
81
82dhcp6opts = {1: "CLIENTID",
83 2: "SERVERID",
84 3: "IA_NA",
85 4: "IA_TA",
86 5: "IAADDR",
87 6: "ORO",
88 7: "PREFERENCE",
89 8: "ELAPSED_TIME",
90 9: "RELAY_MSG",
91 11: "AUTH",
92 12: "UNICAST",
93 13: "STATUS_CODE",
94 14: "RAPID_COMMIT",
95 15: "USER_CLASS",
96 16: "VENDOR_CLASS",
97 17: "VENDOR_OPTS",
98 18: "INTERFACE_ID",
99 19: "RECONF_MSG",
100 20: "RECONF_ACCEPT",
101 21: "SIP Servers Domain Name List", # RFC3319
102 22: "SIP Servers IPv6 Address List", # RFC3319
103 23: "DNS Recursive Name Server Option", # RFC3646
104 24: "Domain Search List option", # RFC3646
105 25: "OPTION_IA_PD", # RFC3633
106 26: "OPTION_IAPREFIX", # RFC3633
107 27: "OPTION_NIS_SERVERS", # RFC3898
108 28: "OPTION_NISP_SERVERS", # RFC3898
109 29: "OPTION_NIS_DOMAIN_NAME", # RFC3898
110 30: "OPTION_NISP_DOMAIN_NAME", # RFC3898
111 31: "OPTION_SNTP_SERVERS", # RFC4075
112 32: "OPTION_INFORMATION_REFRESH_TIME", # RFC4242
113 33: "OPTION_BCMCS_SERVER_D", # RFC4280
114 34: "OPTION_BCMCS_SERVER_A", # RFC4280
115 36: "OPTION_GEOCONF_CIVIC", # RFC-ietf-geopriv-dhcp-civil-09.txt
116 37: "OPTION_REMOTE_ID", # RFC4649
117 38: "OPTION_SUBSCRIBER_ID", # RFC4580
118 39: "OPTION_CLIENT_FQDN", # RFC4704
119 40: "OPTION_PANA_AGENT", # RFC5192
120 41: "OPTION_NEW_POSIX_TIMEZONE", # RFC4833
121 42: "OPTION_NEW_TZDB_TIMEZONE", # RFC4833
122 48: "OPTION_LQ_CLIENT_LINK", # RFC5007
123 56: "OPTION_NTP_SERVER", # RFC5908
124 59: "OPT_BOOTFILE_URL", # RFC5970
125 60: "OPT_BOOTFILE_PARAM", # RFC5970
126 61: "OPTION_CLIENT_ARCH_TYPE", # RFC5970
127 62: "OPTION_NII", # RFC5970
128 65: "OPTION_ERP_LOCAL_DOMAIN_NAME", # RFC6440
129 66: "OPTION_RELAY_SUPPLIED_OPTIONS", # RFC6422
130 68: "OPTION_VSS", # RFC6607
131 79: "OPTION_CLIENT_LINKLAYER_ADDR", # RFC6939
132 103: "OPTION_CAPTIVE_PORTAL", # RFC8910
133 112: "OPTION_MUD_URL", # RFC8520
134 148: "OPTION_ADDR_REG_ENABLE", # RFC9686
135 }
136
137dhcp6opts_by_code = {1: "DHCP6OptClientId",
138 2: "DHCP6OptServerId",
139 3: "DHCP6OptIA_NA",
140 4: "DHCP6OptIA_TA",
141 5: "DHCP6OptIAAddress",
142 6: "DHCP6OptOptReq",
143 7: "DHCP6OptPref",
144 8: "DHCP6OptElapsedTime",
145 9: "DHCP6OptRelayMsg",
146 11: "DHCP6OptAuth",
147 12: "DHCP6OptServerUnicast",
148 13: "DHCP6OptStatusCode",
149 14: "DHCP6OptRapidCommit",
150 15: "DHCP6OptUserClass",
151 16: "DHCP6OptVendorClass",
152 17: "DHCP6OptVendorSpecificInfo",
153 18: "DHCP6OptIfaceId",
154 19: "DHCP6OptReconfMsg",
155 20: "DHCP6OptReconfAccept",
156 21: "DHCP6OptSIPDomains", # RFC3319
157 22: "DHCP6OptSIPServers", # RFC3319
158 23: "DHCP6OptDNSServers", # RFC3646
159 24: "DHCP6OptDNSDomains", # RFC3646
160 25: "DHCP6OptIA_PD", # RFC3633
161 26: "DHCP6OptIAPrefix", # RFC3633
162 27: "DHCP6OptNISServers", # RFC3898
163 28: "DHCP6OptNISPServers", # RFC3898
164 29: "DHCP6OptNISDomain", # RFC3898
165 30: "DHCP6OptNISPDomain", # RFC3898
166 31: "DHCP6OptSNTPServers", # RFC4075
167 32: "DHCP6OptInfoRefreshTime", # RFC4242
168 33: "DHCP6OptBCMCSDomains", # RFC4280
169 34: "DHCP6OptBCMCSServers", # RFC4280
170 # 36: "DHCP6OptGeoConf", #RFC-ietf-geopriv-dhcp-civil-09.txt # noqa: E501
171 37: "DHCP6OptRemoteID", # RFC4649
172 38: "DHCP6OptSubscriberID", # RFC4580
173 39: "DHCP6OptClientFQDN", # RFC4704
174 40: "DHCP6OptPanaAuthAgent", # RFC-ietf-dhc-paa-option-05.txt # noqa: E501
175 41: "DHCP6OptNewPOSIXTimeZone", # RFC4833
176 42: "DHCP6OptNewTZDBTimeZone", # RFC4833
177 43: "DHCP6OptRelayAgentERO", # RFC4994
178 # 44: "DHCP6OptLQQuery", #RFC5007
179 # 45: "DHCP6OptLQClientData", #RFC5007
180 # 46: "DHCP6OptLQClientTime", #RFC5007
181 # 47: "DHCP6OptLQRelayData", #RFC5007
182 48: "DHCP6OptLQClientLink", # RFC5007
183 56: "DHCP6OptNTPServer", # RFC5908
184 59: "DHCP6OptBootFileUrl", # RFC5790
185 60: "DHCP6OptBootFileParam", # RFC5970
186 61: "DHCP6OptClientArchType", # RFC5970
187 62: "DHCP6OptClientNetworkInterId", # RFC5970
188 65: "DHCP6OptERPDomain", # RFC6440
189 66: "DHCP6OptRelaySuppliedOpt", # RFC6422
190 68: "DHCP6OptVSS", # RFC6607
191 79: "DHCP6OptClientLinkLayerAddr", # RFC6939
192 103: "DHCP6OptCaptivePortal", # RFC8910
193 112: "DHCP6OptMudUrl", # RFC8520
194 148: "DHCP6OptAddrRegEnable", # RFC9686
195 }
196
197
198# sect 7.3 RFC 8415 : DHCP6 Messages types
199# also RFC 9686
200dhcp6types = {1: "SOLICIT",
201 2: "ADVERTISE",
202 3: "REQUEST",
203 4: "CONFIRM",
204 5: "RENEW",
205 6: "REBIND",
206 7: "REPLY",
207 8: "RELEASE",
208 9: "DECLINE",
209 10: "RECONFIGURE",
210 11: "INFORMATION-REQUEST",
211 12: "RELAY-FORW",
212 13: "RELAY-REPL",
213 36: "ADDR-REG-INFORM",
214 37: "ADDR-REG-REPLY",
215 }
216
217
218#####################################################################
219# DHCPv6 DUID related stuff #
220#####################################################################
221
222duidtypes = {1: "Link-layer address plus time",
223 2: "Vendor-assigned unique ID based on Enterprise Number",
224 3: "Link-layer Address",
225 4: "UUID"}
226
227# DUID hardware types - RFC 826 - Extracted from
228# http://www.iana.org/assignments/arp-parameters on 31/10/06
229# We should add the length of every kind of address.
230duidhwtypes = {0: "NET/ROM pseudo", # Not referenced by IANA
231 1: "Ethernet (10Mb)",
232 2: "Experimental Ethernet (3Mb)",
233 3: "Amateur Radio AX.25",
234 4: "Proteon ProNET Token Ring",
235 5: "Chaos",
236 6: "IEEE 802 Networks",
237 7: "ARCNET",
238 8: "Hyperchannel",
239 9: "Lanstar",
240 10: "Autonet Short Address",
241 11: "LocalTalk",
242 12: "LocalNet (IBM PCNet or SYTEK LocalNET)",
243 13: "Ultra link",
244 14: "SMDS",
245 15: "Frame Relay",
246 16: "Asynchronous Transmission Mode (ATM)",
247 17: "HDLC",
248 18: "Fibre Channel",
249 19: "Asynchronous Transmission Mode (ATM)",
250 20: "Serial Line",
251 21: "Asynchronous Transmission Mode (ATM)",
252 22: "MIL-STD-188-220",
253 23: "Metricom",
254 24: "IEEE 1394.1995",
255 25: "MAPOS",
256 26: "Twinaxial",
257 27: "EUI-64",
258 28: "HIPARP",
259 29: "IP and ARP over ISO 7816-3",
260 30: "ARPSec",
261 31: "IPsec tunnel",
262 32: "InfiniBand (TM)",
263 33: "TIA-102 Project 25 Common Air Interface (CAI)"}
264
265
266class _UTCTimeField(UTCTimeField):
267 def __init__(self, *args, **kargs):
268 epoch_2000 = (2000, 1, 1, 0, 0, 0, 5, 1, 0) # required Epoch
269 UTCTimeField.__init__(self, epoch=epoch_2000, *args, **kargs)
270
271
272class _LLAddrField(MACField):
273 pass
274
275# XXX We only support Ethernet addresses at the moment. _LLAddrField
276# will be modified when needed. Ask us. --arno
277
278
279class DUID_LLT(Packet): # sect 9.2 RFC 3315
280 name = "DUID - Link-layer address plus time"
281 fields_desc = [ShortEnumField("type", 1, duidtypes),
282 XShortEnumField("hwtype", 1, duidhwtypes),
283 _UTCTimeField("timeval", 0), # i.e. 01 Jan 2000
284 _LLAddrField("lladdr", ETHER_ANY)]
285
286
287class DUID_EN(Packet): # sect 9.3 RFC 3315
288 name = "DUID - Assigned by Vendor Based on Enterprise Number"
289 fields_desc = [ShortEnumField("type", 2, duidtypes),
290 IntEnumField("enterprisenum", 311, IANA_ENTERPRISE_NUMBERS),
291 StrField("id", "")]
292
293
294class DUID_LL(Packet): # sect 9.4 RFC 3315
295 name = "DUID - Based on Link-layer Address"
296 fields_desc = [ShortEnumField("type", 3, duidtypes),
297 XShortEnumField("hwtype", 1, duidhwtypes),
298 _LLAddrField("lladdr", ETHER_ANY)]
299
300
301class DUID_UUID(Packet): # RFC 6355
302 name = "DUID - Based on UUID"
303 fields_desc = [ShortEnumField("type", 4, duidtypes),
304 UUIDField("uuid", None, uuid_fmt=UUIDField.FORMAT_BE)]
305
306
307duid_cls = {1: "DUID_LLT",
308 2: "DUID_EN",
309 3: "DUID_LL",
310 4: "DUID_UUID"}
311
312#####################################################################
313# DHCPv6 Options classes #
314#####################################################################
315
316
317class _DHCP6OptGuessPayload(Packet):
318 @staticmethod
319 def _just_guess_payload_class(cls, payload):
320 # try to guess what option is in the payload
321 if len(payload) <= 2:
322 return conf.raw_layer
323 opt = struct.unpack("!H", payload[:2])[0]
324 clsname = dhcp6opts_by_code.get(opt, None)
325 if clsname is None:
326 return cls
327 return get_cls(clsname, cls)
328
329 def guess_payload_class(self, payload):
330 # this method is used in case of all derived classes
331 # from _DHCP6OptGuessPayload in this file
332 return _DHCP6OptGuessPayload._just_guess_payload_class(
333 DHCP6OptUnknown,
334 payload
335 )
336
337
338class _DHCP6OptGuessPayloadElt(_DHCP6OptGuessPayload):
339 """
340 Same than _DHCP6OptGuessPayload but made for lists
341 in case of list of different suboptions
342 e.g. in ianaopts in DHCP6OptIA_NA
343 """
344 @classmethod
345 def dispatch_hook(cls, payload=None, *args, **kargs):
346 return cls._just_guess_payload_class(conf.raw_layer, payload)
347
348 def extract_padding(self, s):
349 return b"", s
350
351
352class DHCP6OptUnknown(_DHCP6OptGuessPayload): # A generic DHCPv6 Option
353 name = "Unknown DHCPv6 Option"
354 fields_desc = [ShortEnumField("optcode", 0, dhcp6opts),
355 FieldLenField("optlen", None, length_of="data", fmt="!H"),
356 StrLenField("data", "",
357 length_from=lambda pkt: pkt.optlen)]
358
359
360def _duid_dispatcher(x):
361 cls = conf.raw_layer
362 if len(x) > 4:
363 o = struct.unpack("!H", x[:2])[0]
364 cls = get_cls(duid_cls.get(o, conf.raw_layer), conf.raw_layer)
365 return cls(x)
366
367
368class DHCP6OptClientId(_DHCP6OptGuessPayload): # RFC 8415 sect 21.2
369 name = "DHCP6 Client Identifier Option"
370 fields_desc = [ShortEnumField("optcode", 1, dhcp6opts),
371 FieldLenField("optlen", None, length_of="duid", fmt="!H"),
372 PacketLenField("duid", "", _duid_dispatcher,
373 length_from=lambda pkt: pkt.optlen)]
374
375
376class DHCP6OptServerId(DHCP6OptClientId): # RFC 8415 sect 21.3
377 name = "DHCP6 Server Identifier Option"
378 optcode = 2
379
380# Should be encapsulated in the option field of IA_NA or IA_TA options
381# Can only appear at that location.
382
383
384class DHCP6OptIAAddress(_DHCP6OptGuessPayload): # RFC 8415 sect 21.6
385 name = "DHCP6 IA Address Option (IA_TA or IA_NA suboption)"
386 fields_desc = [ShortEnumField("optcode", 5, dhcp6opts),
387 FieldLenField("optlen", None, length_of="iaaddropts",
388 fmt="!H", adjust=lambda pkt, x: x + 24),
389 IP6Field("addr", "::"),
390 IntEnumField("preflft", 0, {0xffffffff: "infinity"}),
391 IntEnumField("validlft", 0, {0xffffffff: "infinity"}),
392 # last field IAaddr-options is not defined in the
393 # reference document. We copy what wireshark does: read
394 # more dhcp6 options and excpect failures
395 PacketListField("iaaddropts", [],
396 _DHCP6OptGuessPayloadElt,
397 length_from=lambda pkt: pkt.optlen - 24)]
398
399 def guess_payload_class(self, payload):
400 return conf.padding_layer
401
402
403class DHCP6OptIA_NA(_DHCP6OptGuessPayload): # RFC 8415 sect 21.4
404 name = "DHCP6 Identity Association for Non-temporary Addresses Option"
405 fields_desc = [ShortEnumField("optcode", 3, dhcp6opts),
406 FieldLenField("optlen", None, length_of="ianaopts",
407 fmt="!H", adjust=lambda pkt, x: x + 12),
408 XIntField("iaid", None),
409 IntField("T1", None),
410 IntField("T2", None),
411 PacketListField("ianaopts", [], _DHCP6OptGuessPayloadElt,
412 length_from=lambda pkt: pkt.optlen - 12)]
413
414
415class DHCP6OptIA_TA(_DHCP6OptGuessPayload): # RFC 8415 sect 21.5
416 name = "DHCP6 Identity Association for Temporary Addresses Option"
417 fields_desc = [ShortEnumField("optcode", 4, dhcp6opts),
418 FieldLenField("optlen", None, length_of="iataopts",
419 fmt="!H", adjust=lambda pkt, x: x + 4),
420 XIntField("iaid", None),
421 PacketListField("iataopts", [], _DHCP6OptGuessPayloadElt,
422 length_from=lambda pkt: pkt.optlen - 4)]
423
424
425# DHCPv6 Option Request Option #
426
427class _OptReqListField(StrLenField):
428 islist = 1
429
430 def i2h(self, pkt, x):
431 if not x:
432 return []
433 return x
434
435 def i2len(self, pkt, x):
436 return 2 * len(x)
437
438 def any2i(self, pkt, x):
439 return x
440
441 def i2repr(self, pkt, x):
442 s = []
443 for y in self.i2h(pkt, x):
444 if y in dhcp6opts:
445 s.append(dhcp6opts[y])
446 else:
447 s.append("%d" % y)
448 return "[%s]" % ", ".join(s)
449
450 def m2i(self, pkt, x):
451 r = []
452 while len(x) != 0:
453 if len(x) < 2:
454 warning("Odd length for requested option field. Rejecting last byte") # noqa: E501
455 return r
456 r.append(struct.unpack("!H", x[:2])[0])
457 x = x[2:]
458 return r
459
460 def i2m(self, pkt, x):
461 return b"".join(struct.pack('!H', y) for y in x)
462
463# A client may include an ORO in a solicit, Request, Renew, Rebind,
464# Confirm or Information-request
465
466
467class DHCP6OptOptReq(_DHCP6OptGuessPayload): # RFC 8415 sect 21.7
468 name = "DHCP6 Option Request Option"
469 fields_desc = [ShortEnumField("optcode", 6, dhcp6opts),
470 FieldLenField("optlen", None, length_of="reqopts", fmt="!H"), # noqa: E501
471 _OptReqListField("reqopts", [23, 24],
472 length_from=lambda pkt: pkt.optlen)]
473
474
475# DHCPv6 Preference Option #
476
477# emise par un serveur pour affecter le choix fait par le client. Dans
478# les messages Advertise, a priori
479class DHCP6OptPref(_DHCP6OptGuessPayload): # RFC 8415 sect 21.8
480 name = "DHCP6 Preference Option"
481 fields_desc = [ShortEnumField("optcode", 7, dhcp6opts),
482 ShortField("optlen", 1),
483 ByteField("prefval", 255)]
484
485
486# DHCPv6 Elapsed Time Option #
487
488class _ElapsedTimeField(ShortField):
489 def i2repr(self, pkt, x):
490 if x == 0xffff:
491 return "infinity (0xffff)"
492 return "%.2f sec" % (self.i2h(pkt, x) / 100.)
493
494
495class DHCP6OptElapsedTime(_DHCP6OptGuessPayload): # RFC 8415 sect 21.9
496 name = "DHCP6 Elapsed Time Option"
497 fields_desc = [ShortEnumField("optcode", 8, dhcp6opts),
498 ShortField("optlen", 2),
499 _ElapsedTimeField("elapsedtime", 0)]
500
501
502# DHCPv6 Authentication Option #
503
504# The following fields are set in an Authentication option for the
505# Reconfigure Key Authentication Protocol:
506#
507# protocol 3
508#
509# algorithm 1
510#
511# RDM 0
512#
513# The format of the Authentication information for the Reconfigure Key
514# Authentication Protocol is:
515#
516# 0 1 2 3
517# 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
518# +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
519# | Type | Value (128 bits) |
520# +-+-+-+-+-+-+-+-+ |
521# . .
522# . .
523# . +-+-+-+-+-+-+-+-+
524# | |
525# +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
526#
527# Type Type of data in Value field carried in this option:
528#
529# 1 Reconfigure Key value (used in Reply message).
530#
531# 2 HMAC-MD5 digest of the message (used in Reconfigure
532# message).
533#
534# Value Data as defined by field.
535
536# https://www.iana.org/assignments/auth-namespaces
537_dhcp6_auth_proto = {
538 0: "configuration token",
539 1: "delayed authentication",
540 2: "delayed authentication (obsolete)",
541 3: "reconfigure key",
542}
543_dhcp6_auth_alg = {
544 0: "configuration token",
545 1: "HMAC-MD5",
546}
547_dhcp6_auth_rdm = {
548 0: "use of a monotonically increasing value"
549}
550
551
552class DHCP6OptAuth(_DHCP6OptGuessPayload): # RFC 8415 sect 21.11
553 name = "DHCP6 Option - Authentication"
554 fields_desc = [ShortEnumField("optcode", 11, dhcp6opts),
555 FieldLenField("optlen", None, length_of="authinfo",
556 fmt="!H", adjust=lambda pkt, x: x + 11),
557 ByteEnumField("proto", 3, _dhcp6_auth_proto),
558 ByteEnumField("alg", 1, _dhcp6_auth_alg),
559 ByteEnumField("rdm", 0, _dhcp6_auth_rdm),
560 StrFixedLenField("replay", b"\x00" * 8, 8),
561 StrLenField("authinfo", "",
562 length_from=lambda pkt: pkt.optlen - 11)]
563
564# DHCPv6 Server Unicast Option #
565
566
567class _SrvAddrField(IP6Field):
568 def i2h(self, pkt, x):
569 if x is None:
570 return "::"
571 return x
572
573 def i2m(self, pkt, x):
574 return inet_pton(socket.AF_INET6, self.i2h(pkt, x))
575
576
577class DHCP6OptServerUnicast(_DHCP6OptGuessPayload): # RFC 8415 sect 21.12
578 name = "DHCP6 Server Unicast Option"
579 fields_desc = [ShortEnumField("optcode", 12, dhcp6opts),
580 ShortField("optlen", 16),
581 _SrvAddrField("srvaddr", None)]
582
583
584# DHCPv6 Status Code Option #
585
586dhcp6statuscodes = {0: "Success", # RFC 8415 sect 21.13
587 1: "UnspecFail",
588 2: "NoAddrsAvail",
589 3: "NoBinding",
590 4: "NotOnLink",
591 5: "UseMulticast",
592 6: "NoPrefixAvail"} # From RFC3633
593
594
595class DHCP6OptStatusCode(_DHCP6OptGuessPayload): # RFC 8415 sect 21.13
596 name = "DHCP6 Status Code Option"
597 fields_desc = [ShortEnumField("optcode", 13, dhcp6opts),
598 FieldLenField("optlen", None, length_of="statusmsg",
599 fmt="!H", adjust=lambda pkt, x:x + 2),
600 ShortEnumField("statuscode", None, dhcp6statuscodes),
601 StrLenField("statusmsg", "",
602 length_from=lambda pkt: pkt.optlen - 2)]
603
604
605# DHCPv6 Rapid Commit Option #
606
607class DHCP6OptRapidCommit(_DHCP6OptGuessPayload): # RFC 8415 sect 21.14
608 name = "DHCP6 Rapid Commit Option"
609 fields_desc = [ShortEnumField("optcode", 14, dhcp6opts),
610 ShortField("optlen", 0)]
611
612
613# DHCPv6 User Class Option #
614
615class _UserClassDataField(PacketListField):
616 def i2len(self, pkt, z):
617 if z is None or z == []:
618 return 0
619 return sum(len(raw(x)) for x in z)
620
621 def getfield(self, pkt, s):
622 tmp_len = self.length_from(pkt)
623 lst = []
624 remain, payl = s[:tmp_len], s[tmp_len:]
625 while len(remain) > 0:
626 p = self.m2i(pkt, remain)
627 if conf.padding_layer in p:
628 pad = p[conf.padding_layer]
629 remain = pad.load
630 del pad.underlayer.payload
631 else:
632 remain = ""
633 lst.append(p)
634 return payl, lst
635
636
637class USER_CLASS_DATA(Packet):
638 name = "user class data"
639 fields_desc = [FieldLenField("len", None, length_of="data"),
640 StrLenField("data", "",
641 length_from=lambda pkt: pkt.len)]
642
643 def guess_payload_class(self, payload):
644 return conf.padding_layer
645
646
647class DHCP6OptUserClass(_DHCP6OptGuessPayload): # RFC 8415 sect 21.15
648 name = "DHCP6 User Class Option"
649 fields_desc = [ShortEnumField("optcode", 15, dhcp6opts),
650 FieldLenField("optlen", None, fmt="!H",
651 length_of="userclassdata"),
652 _UserClassDataField("userclassdata", [], USER_CLASS_DATA,
653 length_from=lambda pkt: pkt.optlen)]
654
655
656# DHCPv6 Vendor Class Option #
657
658class _VendorClassDataField(_UserClassDataField):
659 pass
660
661
662class VENDOR_CLASS_DATA(USER_CLASS_DATA):
663 name = "vendor class data"
664
665
666class DHCP6OptVendorClass(_DHCP6OptGuessPayload): # RFC 8415 sect 21.16
667 name = "DHCP6 Vendor Class Option"
668 fields_desc = [ShortEnumField("optcode", 16, dhcp6opts),
669 FieldLenField("optlen", None, length_of="vcdata", fmt="!H",
670 adjust=lambda pkt, x: x + 4),
671 IntEnumField("enterprisenum", None,
672 IANA_ENTERPRISE_NUMBERS),
673 _VendorClassDataField("vcdata", [], VENDOR_CLASS_DATA,
674 length_from=lambda pkt: pkt.optlen - 4)] # noqa: E501
675
676# DHCPv6 Vendor-Specific Information Option #
677
678
679class VENDOR_SPECIFIC_OPTION(_DHCP6OptGuessPayload):
680 name = "vendor specific option data"
681 fields_desc = [ShortField("optcode", None),
682 FieldLenField("optlen", None, length_of="optdata"),
683 StrLenField("optdata", "",
684 length_from=lambda pkt: pkt.optlen)]
685
686 def guess_payload_class(self, payload):
687 return conf.padding_layer
688
689# The third one that will be used for nothing interesting
690
691
692class DHCP6OptVendorSpecificInfo(_DHCP6OptGuessPayload): # RFC 8415 sect 21.17
693 name = "DHCP6 Vendor-specific Information Option"
694 fields_desc = [ShortEnumField("optcode", 17, dhcp6opts),
695 FieldLenField("optlen", None, length_of="vso", fmt="!H",
696 adjust=lambda pkt, x: x + 4),
697 IntEnumField("enterprisenum", None,
698 IANA_ENTERPRISE_NUMBERS),
699 _VendorClassDataField("vso", [], VENDOR_SPECIFIC_OPTION,
700 length_from=lambda pkt: pkt.optlen - 4)] # noqa: E501
701
702# DHCPv6 Interface-ID Option #
703
704# Repasser sur cette option a la fin. Elle a pas l'air d'etre des
705# masses critique.
706
707
708class DHCP6OptIfaceId(_DHCP6OptGuessPayload): # RFC 8415 sect 21.18
709 name = "DHCP6 Interface-Id Option"
710 fields_desc = [ShortEnumField("optcode", 18, dhcp6opts),
711 FieldLenField("optlen", None, fmt="!H",
712 length_of="ifaceid"),
713 StrLenField("ifaceid", "",
714 length_from=lambda pkt: pkt.optlen)]
715
716
717# DHCPv6 Reconfigure Message Option #
718
719# A server includes a Reconfigure Message option in a Reconfigure
720# message to indicate to the client whether the client responds with a
721# renew message or an Information-request message.
722class DHCP6OptReconfMsg(_DHCP6OptGuessPayload): # RFC 8415 sect 21.19
723 name = "DHCP6 Reconfigure Message Option"
724 fields_desc = [ShortEnumField("optcode", 19, dhcp6opts),
725 ShortField("optlen", 1),
726 ByteEnumField("msgtype", 11, {5: "Renew Message",
727 11: "Information Request"})]
728
729
730# DHCPv6 Reconfigure Accept Option #
731
732# A client uses the Reconfigure Accept option to announce to the
733# server whether the client is willing to accept Recoonfigure
734# messages, and a server uses this option to tell the client whether
735# or not to accept Reconfigure messages. The default behavior in the
736# absence of this option, means unwillingness to accept reconfigure
737# messages, or instruction not to accept Reconfigure messages, for the
738# client and server messages, respectively.
739class DHCP6OptReconfAccept(_DHCP6OptGuessPayload): # RFC 8415 sect 21.20
740 name = "DHCP6 Reconfigure Accept Option"
741 fields_desc = [ShortEnumField("optcode", 20, dhcp6opts),
742 ShortField("optlen", 0)]
743
744
745class DHCP6OptSIPDomains(_DHCP6OptGuessPayload): # RFC3319
746 name = "DHCP6 Option - SIP Servers Domain Name List"
747 fields_desc = [ShortEnumField("optcode", 21, dhcp6opts),
748 FieldLenField("optlen", None, length_of="sipdomains"),
749 DomainNameListField("sipdomains", [],
750 length_from=lambda pkt: pkt.optlen)]
751
752
753class DHCP6OptSIPServers(_DHCP6OptGuessPayload): # RFC3319
754 name = "DHCP6 Option - SIP Servers IPv6 Address List"
755 fields_desc = [ShortEnumField("optcode", 22, dhcp6opts),
756 FieldLenField("optlen", None, length_of="sipservers"),
757 IP6ListField("sipservers", [],
758 length_from=lambda pkt: pkt.optlen)]
759
760
761class DHCP6OptDNSServers(_DHCP6OptGuessPayload): # RFC3646
762 name = "DHCP6 Option - DNS Recursive Name Server"
763 fields_desc = [ShortEnumField("optcode", 23, dhcp6opts),
764 FieldLenField("optlen", None, length_of="dnsservers"),
765 IP6ListField("dnsservers", [],
766 length_from=lambda pkt: pkt.optlen)]
767
768
769class DHCP6OptDNSDomains(_DHCP6OptGuessPayload): # RFC3646
770 name = "DHCP6 Option - Domain Search List option"
771 fields_desc = [ShortEnumField("optcode", 24, dhcp6opts),
772 FieldLenField("optlen", None, length_of="dnsdomains"),
773 DomainNameListField("dnsdomains", [],
774 length_from=lambda pkt: pkt.optlen)]
775
776
777class DHCP6OptIAPrefix(_DHCP6OptGuessPayload): # RFC 8415 sect 21.22
778 name = "DHCP6 Option - IA Prefix option"
779 fields_desc = [ShortEnumField("optcode", 26, dhcp6opts),
780 FieldLenField("optlen", None, length_of="iaprefopts",
781 adjust=lambda pkt, x: x + 25),
782 IntEnumField("preflft", 0, {0xffffffff: "infinity"}),
783 IntEnumField("validlft", 0, {0xffffffff: "infinity"}),
784 ByteField("plen", 48), # TODO: Challenge that default value
785 # See RFC 8168
786 IP6Field("prefix", "2001:db8::"), # At least, global and won't hurt # noqa: E501
787 # We copy what wireshark does: read more dhcp6 options and
788 # expect failures
789 PacketListField("iaprefopts", [],
790 _DHCP6OptGuessPayloadElt,
791 length_from=lambda pkt: pkt.optlen - 25)]
792
793 def guess_payload_class(self, payload):
794 return conf.padding_layer
795
796
797class DHCP6OptIA_PD(_DHCP6OptGuessPayload): # RFC 8415 sect 21.21
798 name = "DHCP6 Option - Identity Association for Prefix Delegation"
799 fields_desc = [ShortEnumField("optcode", 25, dhcp6opts),
800 FieldLenField("optlen", None, length_of="iapdopt",
801 fmt="!H", adjust=lambda pkt, x: x + 12),
802 XIntField("iaid", None),
803 IntField("T1", None),
804 IntField("T2", None),
805 PacketListField("iapdopt", [], _DHCP6OptGuessPayloadElt,
806 length_from=lambda pkt: pkt.optlen - 12)]
807
808
809class DHCP6OptNISServers(_DHCP6OptGuessPayload): # RFC3898
810 name = "DHCP6 Option - NIS Servers"
811 fields_desc = [ShortEnumField("optcode", 27, dhcp6opts),
812 FieldLenField("optlen", None, length_of="nisservers"),
813 IP6ListField("nisservers", [],
814 length_from=lambda pkt: pkt.optlen)]
815
816
817class DHCP6OptNISPServers(_DHCP6OptGuessPayload): # RFC3898
818 name = "DHCP6 Option - NIS+ Servers"
819 fields_desc = [ShortEnumField("optcode", 28, dhcp6opts),
820 FieldLenField("optlen", None, length_of="nispservers"),
821 IP6ListField("nispservers", [],
822 length_from=lambda pkt: pkt.optlen)]
823
824
825class DHCP6OptNISDomain(_DHCP6OptGuessPayload): # RFC3898
826 name = "DHCP6 Option - NIS Domain Name"
827 fields_desc = [ShortEnumField("optcode", 29, dhcp6opts),
828 FieldLenField("optlen", None, length_of="nisdomain"),
829 DNSStrField("nisdomain", "",
830 length_from=lambda pkt: pkt.optlen)]
831
832
833class DHCP6OptNISPDomain(_DHCP6OptGuessPayload): # RFC3898
834 name = "DHCP6 Option - NIS+ Domain Name"
835 fields_desc = [ShortEnumField("optcode", 30, dhcp6opts),
836 FieldLenField("optlen", None, length_of="nispdomain"),
837 DNSStrField("nispdomain", "",
838 length_from=lambda pkt: pkt.optlen)]
839
840
841class DHCP6OptSNTPServers(_DHCP6OptGuessPayload): # RFC4075
842 name = "DHCP6 option - SNTP Servers"
843 fields_desc = [ShortEnumField("optcode", 31, dhcp6opts),
844 FieldLenField("optlen", None, length_of="sntpservers"),
845 IP6ListField("sntpservers", [],
846 length_from=lambda pkt: pkt.optlen)]
847
848
849IRT_DEFAULT = 86400
850IRT_MINIMUM = 600
851
852
853class DHCP6OptInfoRefreshTime(_DHCP6OptGuessPayload): # RFC4242
854 name = "DHCP6 Option - Information Refresh Time"
855 fields_desc = [ShortEnumField("optcode", 32, dhcp6opts),
856 ShortField("optlen", 4),
857 IntField("reftime", IRT_DEFAULT)] # One day
858
859
860class DHCP6OptBCMCSDomains(_DHCP6OptGuessPayload): # RFC4280
861 name = "DHCP6 Option - BCMCS Domain Name List"
862 fields_desc = [ShortEnumField("optcode", 33, dhcp6opts),
863 FieldLenField("optlen", None, length_of="bcmcsdomains"),
864 DomainNameListField("bcmcsdomains", [],
865 length_from=lambda pkt: pkt.optlen)]
866
867
868class DHCP6OptBCMCSServers(_DHCP6OptGuessPayload): # RFC4280
869 name = "DHCP6 Option - BCMCS Addresses List"
870 fields_desc = [ShortEnumField("optcode", 34, dhcp6opts),
871 FieldLenField("optlen", None, length_of="bcmcsservers"),
872 IP6ListField("bcmcsservers", [],
873 length_from=lambda pkt: pkt.optlen)]
874
875
876_dhcp6_geoconf_what = {
877 0: "DHCP server",
878 1: "closest network element",
879 2: "client"
880}
881
882
883class DHCP6OptGeoConfElement(Packet):
884 fields_desc = [ByteField("CAtype", 0),
885 FieldLenField("CAlength", None, length_of="CAvalue"),
886 StrLenField("CAvalue", "",
887 length_from=lambda pkt: pkt.CAlength)]
888
889
890class DHCP6OptGeoConf(_DHCP6OptGuessPayload): # RFC 4776
891 name = "DHCP6 Option - Civic Location"
892 fields_desc = [ShortEnumField("optcode", 36, dhcp6opts),
893 FieldLenField("optlen", None, length_of="ca_elts",
894 adjust=lambda x: x + 3),
895 ByteEnumField("what", 2, _dhcp6_geoconf_what),
896 StrFixedLenField("country_code", "FR", 2),
897 PacketListField("ca_elts", [], DHCP6OptGeoConfElement,
898 length_from=lambda pkt: pkt.optlen - 3)]
899
900# TODO: see if we encounter opaque values from vendor devices
901
902
903class DHCP6OptRemoteID(_DHCP6OptGuessPayload): # RFC4649
904 name = "DHCP6 Option - Relay Agent Remote-ID"
905 fields_desc = [ShortEnumField("optcode", 37, dhcp6opts),
906 FieldLenField("optlen", None, length_of="remoteid",
907 adjust=lambda pkt, x: x + 4),
908 IntEnumField("enterprisenum", None,
909 IANA_ENTERPRISE_NUMBERS),
910 StrLenField("remoteid", "",
911 length_from=lambda pkt: pkt.optlen - 4)]
912
913
914class DHCP6OptSubscriberID(_DHCP6OptGuessPayload): # RFC4580
915 name = "DHCP6 Option - Subscriber ID"
916 fields_desc = [ShortEnumField("optcode", 38, dhcp6opts),
917 FieldLenField("optlen", None, length_of="subscriberid"),
918 # subscriberid default value should be at least 1 byte long
919 # but we don't really care
920 StrLenField("subscriberid", "",
921 length_from=lambda pkt: pkt.optlen)]
922
923
924class DHCP6OptClientFQDN(_DHCP6OptGuessPayload): # RFC4704
925 name = "DHCP6 Option - Client FQDN"
926 fields_desc = [ShortEnumField("optcode", 39, dhcp6opts),
927 FieldLenField("optlen", None, length_of="fqdn",
928 adjust=lambda pkt, x: x + 1),
929 BitField("res", 0, 5),
930 FlagsField("flags", 0, 3, "SON"),
931 DNSStrField("fqdn", "",
932 length_from=lambda pkt: pkt.optlen - 1)]
933
934
935class DHCP6OptPanaAuthAgent(_DHCP6OptGuessPayload): # RFC5192
936 name = "DHCP6 PANA Authentication Agent Option"
937 fields_desc = [ShortEnumField("optcode", 40, dhcp6opts),
938 FieldLenField("optlen", None, length_of="paaaddr"),
939 IP6ListField("paaaddr", [],
940 length_from=lambda pkt: pkt.optlen)]
941
942
943class DHCP6OptNewPOSIXTimeZone(_DHCP6OptGuessPayload): # RFC4833
944 name = "DHCP6 POSIX Timezone Option"
945 fields_desc = [ShortEnumField("optcode", 41, dhcp6opts),
946 FieldLenField("optlen", None, length_of="optdata"),
947 StrLenField("optdata", "",
948 length_from=lambda pkt: pkt.optlen)]
949
950
951class DHCP6OptNewTZDBTimeZone(_DHCP6OptGuessPayload): # RFC4833
952 name = "DHCP6 TZDB Timezone Option"
953 fields_desc = [ShortEnumField("optcode", 42, dhcp6opts),
954 FieldLenField("optlen", None, length_of="optdata"),
955 StrLenField("optdata", "",
956 length_from=lambda pkt: pkt.optlen)]
957
958
959class DHCP6OptRelayAgentERO(_DHCP6OptGuessPayload): # RFC4994
960 name = "DHCP6 Option - RelayRequest Option"
961 fields_desc = [ShortEnumField("optcode", 43, dhcp6opts),
962 FieldLenField("optlen", None, length_of="reqopts",
963 fmt="!H"),
964 _OptReqListField("reqopts", [23, 24],
965 length_from=lambda pkt: pkt.optlen)]
966
967
968class DHCP6OptLQClientLink(_DHCP6OptGuessPayload): # RFC5007
969 name = "DHCP6 Client Link Option"
970 fields_desc = [ShortEnumField("optcode", 48, dhcp6opts),
971 FieldLenField("optlen", None, length_of="linkaddress"),
972 IP6ListField("linkaddress", [],
973 length_from=lambda pkt: pkt.optlen)]
974
975
976class DHCP6NTPSubOptSrvAddr(Packet): # RFC5908 sect 4.1
977 name = "DHCP6 NTP Server Address Suboption"
978 fields_desc = [ShortField("optcode", 1),
979 ShortField("optlen", 16),
980 IP6Field("addr", "::")]
981
982 def extract_padding(self, s):
983 return b"", s
984
985
986class DHCP6NTPSubOptMCAddr(Packet): # RFC5908 sect 4.2
987 name = "DHCP6 NTP Multicast Address Suboption"
988 fields_desc = [ShortField("optcode", 2),
989 ShortField("optlen", 16),
990 IP6Field("addr", "::")]
991
992 def extract_padding(self, s):
993 return b"", s
994
995
996class DHCP6NTPSubOptSrvFQDN(Packet): # RFC5908 sect 4.3
997 name = "DHCP6 NTP Server FQDN Suboption"
998 fields_desc = [ShortField("optcode", 3),
999 FieldLenField("optlen", None, length_of="fqdn"),
1000 DNSStrField("fqdn", "",
1001 length_from=lambda pkt: pkt.optlen)]
1002
1003 def extract_padding(self, s):
1004 return b"", s
1005
1006
1007_ntp_subopts = {1: DHCP6NTPSubOptSrvAddr,
1008 2: DHCP6NTPSubOptMCAddr,
1009 3: DHCP6NTPSubOptSrvFQDN}
1010
1011
1012def _ntp_subopt_dispatcher(p, **kwargs):
1013 cls = conf.raw_layer
1014 if len(p) >= 2:
1015 o = struct.unpack("!H", p[:2])[0]
1016 cls = _ntp_subopts.get(o, conf.raw_layer)
1017 return cls(p, **kwargs)
1018
1019
1020class DHCP6OptNTPServer(_DHCP6OptGuessPayload): # RFC5908
1021 name = "DHCP6 NTP Server Option"
1022 fields_desc = [ShortEnumField("optcode", 56, dhcp6opts),
1023 FieldLenField("optlen", None, length_of="ntpserver",
1024 fmt="!H"),
1025 PacketListField("ntpserver", [],
1026 _ntp_subopt_dispatcher,
1027 length_from=lambda pkt: pkt.optlen)]
1028
1029
1030class DHCP6OptBootFileUrl(_DHCP6OptGuessPayload): # RFC5970
1031 name = "DHCP6 Boot File URL Option"
1032 fields_desc = [ShortEnumField("optcode", 59, dhcp6opts),
1033 FieldLenField("optlen", None, length_of="optdata"),
1034 StrLenField("optdata", "",
1035 length_from=lambda pkt: pkt.optlen)]
1036
1037
1038class DHCP6OptClientArchType(_DHCP6OptGuessPayload): # RFC5970
1039 name = "DHCP6 Client System Architecture Type Option"
1040 fields_desc = [ShortEnumField("optcode", 61, dhcp6opts),
1041 FieldLenField("optlen", None, length_of="archtypes",
1042 fmt="!H"),
1043 FieldListField("archtypes", [],
1044 ShortField("archtype", 0),
1045 length_from=lambda pkt: pkt.optlen)]
1046
1047
1048class DHCP6OptClientNetworkInterId(_DHCP6OptGuessPayload): # RFC5970
1049 name = "DHCP6 Client Network Interface Identifier Option"
1050 fields_desc = [ShortEnumField("optcode", 62, dhcp6opts),
1051 ShortField("optlen", 3),
1052 ByteField("iitype", 0),
1053 ByteField("iimajor", 0),
1054 ByteField("iiminor", 0)]
1055
1056
1057class DHCP6OptERPDomain(_DHCP6OptGuessPayload): # RFC6440
1058 name = "DHCP6 Option - ERP Domain Name List"
1059 fields_desc = [ShortEnumField("optcode", 65, dhcp6opts),
1060 FieldLenField("optlen", None, length_of="erpdomain"),
1061 DomainNameListField("erpdomain", [],
1062 length_from=lambda pkt: pkt.optlen)]
1063
1064
1065class DHCP6OptRelaySuppliedOpt(_DHCP6OptGuessPayload): # RFC6422
1066 name = "DHCP6 Relay-Supplied Options Option"
1067 fields_desc = [ShortEnumField("optcode", 66, dhcp6opts),
1068 FieldLenField("optlen", None, length_of="relaysupplied",
1069 fmt="!H"),
1070 PacketListField("relaysupplied", [],
1071 _DHCP6OptGuessPayloadElt,
1072 length_from=lambda pkt: pkt.optlen)]
1073
1074
1075# Virtual Subnet selection
1076class DHCP6OptVSS(_DHCP6OptGuessPayload): # RFC6607
1077 name = "DHCP6 Option - Virtual Subnet Selection"
1078 fields_desc = [ShortEnumField("optcode", 68, dhcp6opts),
1079 FieldLenField("optlen", None, length_of="data",
1080 adjust=lambda pkt, x: x + 1),
1081 ByteField("type", 255), # Default Global/default table
1082 StrLenField("data", "",
1083 length_from=lambda pkt: pkt.optlen)]
1084
1085
1086# "Client link-layer address type. The link-layer type MUST be a valid hardware # noqa: E501
1087# type assigned by the IANA, as described in [RFC0826]
1088class DHCP6OptClientLinkLayerAddr(_DHCP6OptGuessPayload): # RFC6939
1089 name = "DHCP6 Option - Client Link Layer address"
1090 fields_desc = [ShortEnumField("optcode", 79, dhcp6opts),
1091 FieldLenField("optlen", None, length_of="clladdr",
1092 adjust=lambda pkt, x: x + 2),
1093 ShortField("lltype", 1), # ethernet
1094 _LLAddrField("clladdr", ETHER_ANY)]
1095
1096
1097class DHCP6OptCaptivePortal(_DHCP6OptGuessPayload): # RFC8910
1098 name = "DHCP6 Option - Captive-Portal"
1099 fields_desc = [ShortEnumField("optcode", 103, dhcp6opts),
1100 FieldLenField("optlen", None, length_of="URI"),
1101 StrLenField("URI", "",
1102 length_from=lambda pkt: pkt.optlen)]
1103
1104
1105class DHCP6OptMudUrl(_DHCP6OptGuessPayload): # RFC8520
1106 name = "DHCP6 Option - MUD URL"
1107 fields_desc = [ShortEnumField("optcode", 112, dhcp6opts),
1108 FieldLenField("optlen", None, length_of="mudstring"),
1109 StrLenField("mudstring", "",
1110 length_from=lambda pkt: pkt.optlen,
1111 max_length=253,
1112 )]
1113
1114
1115class DHCP6OptAddrRegEnable(_DHCP6OptGuessPayload): # RFC 9686 sect 4.1
1116 name = "DHCP6 Address Registration Option"
1117 fields_desc = [ShortEnumField("optcode", 148, dhcp6opts),
1118 ShortField("optlen", 0)]
1119
1120
1121#####################################################################
1122# DHCPv6 messages #
1123#####################################################################
1124
1125# Some state parameters of the protocols that should probably be
1126# useful to have in the configuration (and keep up-to-date)
1127DHCP6RelayAgentUnicastAddr = ""
1128DHCP6RelayHopCount = ""
1129DHCP6ServerUnicastAddr = ""
1130DHCP6ClientUnicastAddr = ""
1131DHCP6ClientIA_TA = ""
1132DHCP6ClientIA_NA = ""
1133DHCP6ClientIAID = ""
1134T1 = "" # Voir 2462
1135T2 = "" # Voir 2462
1136DHCP6ServerDUID = ""
1137DHCP6CurrentTransactionID = "" # devrait etre utilise pour matcher une
1138# reponse et mis a jour en mode client par une valeur aleatoire pour
1139# laquelle on attend un retour de la part d'un serveur.
1140DHCP6PrefVal = "" # la valeur de preference a utiliser dans
1141# les options preference
1142
1143# Emitted by :
1144# - server : ADVERTISE, REPLY, RECONFIGURE, RELAY-REPL (vers relay)
1145# - client : SOLICIT, REQUEST, CONFIRM, RENEW, REBIND, RELEASE, DECLINE,
1146# INFORMATION REQUEST
1147# - relay : RELAY-FORW (toward server)
1148
1149#####################################################################
1150# DHCPv6 messages sent between Clients and Servers (types 1 to 11)
1151# Comme specifie en section 15.1 de la RFC 3315, les valeurs de
1152# transaction id sont selectionnees de maniere aleatoire par le client
1153# a chaque emission et doivent matcher dans les reponses faites par
1154# les clients
1155
1156
1157class DHCP6(_DHCP6OptGuessPayload):
1158 name = "DHCPv6 Generic Message"
1159 fields_desc = [ByteEnumField("msgtype", None, dhcp6types),
1160 X3BytesField("trid", 0x000000)]
1161 overload_fields = {UDP: {"sport": 546, "dport": 547}}
1162
1163 def hashret(self):
1164 return struct.pack("!I", self.trid)[1:4]
1165
1166# DHCPv6 Relay Message Option #
1167
1168# Relayed message is seen as a payload.
1169
1170
1171class DHCP6OptRelayMsg(_DHCP6OptGuessPayload): # RFC 8415 sect 21.10
1172 name = "DHCP6 Relay Message Option"
1173 fields_desc = [ShortEnumField("optcode", 9, dhcp6opts),
1174 FieldLenField("optlen", None, fmt="!H",
1175 length_of="message"),
1176 PacketLenField("message", DHCP6(), _dhcp6_dispatcher,
1177 length_from=lambda p: p.optlen)]
1178
1179#####################################################################
1180# Solicit Message : sect 17.1.1 RFC3315
1181# - sent by client
1182# - must include a client identifier option
1183# - the client may include IA options for any IAs to which it wants the
1184# server to assign address
1185# - The client use IA_NA options to request the assignment of
1186# non-temporary addresses and uses IA_TA options to request the
1187# assignment of temporary addresses
1188# - The client should include an Option Request option to indicate the
1189# options the client is interested in receiving (eventually
1190# including hints)
1191# - The client includes a Reconfigure Accept option if is willing to
1192# accept Reconfigure messages from the server.
1193# Le cas du send and reply est assez particulier car suivant la
1194# presence d'une option rapid commit dans le solicit, l'attente
1195# s'arrete au premier message de reponse recu ou alors apres un
1196# timeout. De la meme maniere, si un message Advertise arrive avec une
1197# valeur de preference de 255, il arrete l'attente et envoie une
1198# Request.
1199# - The client announces its intention to use DHCP authentication by
1200# including an Authentication option in its solicit message. The
1201# server selects a key for the client based on the client's DUID. The
1202# client and server use that key to authenticate all DHCP messages
1203# exchanged during the session
1204
1205
1206class DHCP6_Solicit(DHCP6):
1207 name = "DHCPv6 Solicit Message"
1208 msgtype = 1
1209 overload_fields = {UDP: {"sport": 546, "dport": 547}}
1210
1211#####################################################################
1212# Advertise Message
1213# - sent by server
1214# - Includes a server identifier option
1215# - Includes a client identifier option
1216# - the client identifier option must match the client's DUID
1217# - transaction ID must match
1218
1219
1220class DHCP6_Advertise(DHCP6):
1221 name = "DHCPv6 Advertise Message"
1222 msgtype = 2
1223 overload_fields = {UDP: {"sport": 547, "dport": 546}}
1224
1225 def answers(self, other):
1226 return (isinstance(other, DHCP6_Solicit) and
1227 other.msgtype == 1 and
1228 self.trid == other.trid)
1229
1230#####################################################################
1231# Request Message
1232# - sent by clients
1233# - includes a server identifier option
1234# - the content of Server Identifier option must match server's DUID
1235# - includes a client identifier option
1236# - must include an ORO Option (even with hints) p40
1237# - can includes a reconfigure Accept option indicating whether or
1238# not the client is willing to accept Reconfigure messages from
1239# the server (p40)
1240# - When the server receives a Request message via unicast from a
1241# client to which the server has not sent a unicast option, the server
1242# discards the Request message and responds with a Reply message
1243# containing Status Code option with the value UseMulticast, a Server
1244# Identifier Option containing the server's DUID, the client
1245# Identifier option from the client message and no other option.
1246
1247
1248class DHCP6_Request(DHCP6):
1249 name = "DHCPv6 Request Message"
1250 msgtype = 3
1251
1252#####################################################################
1253# Confirm Message
1254# - sent by clients
1255# - must include a client identifier option
1256# - When the server receives a Confirm Message, the server determines
1257# whether the addresses in the Confirm message are appropriate for the
1258# link to which the client is attached. cf p50
1259
1260
1261class DHCP6_Confirm(DHCP6):
1262 name = "DHCPv6 Confirm Message"
1263 msgtype = 4
1264
1265#####################################################################
1266# Renew Message
1267# - sent by clients
1268# - must include a server identifier option
1269# - content of server identifier option must match the server's identifier
1270# - must include a client identifier option
1271# - the clients includes any IA assigned to the interface that may
1272# have moved to a new link, along with the addresses associated with
1273# those IAs in its confirm messages
1274# - When the server receives a Renew message that contains an IA
1275# option from a client, it locates the client's binding and verifies
1276# that the information in the IA from the client matches the
1277# information for that client. If the server cannot find a client
1278# entry for the IA the server returns the IA containing no addresses
1279# with a status code option est to NoBinding in the Reply message. cf
1280# p51 pour le reste.
1281
1282
1283class DHCP6_Renew(DHCP6):
1284 name = "DHCPv6 Renew Message"
1285 msgtype = 5
1286
1287#####################################################################
1288# Rebind Message
1289# - sent by clients
1290# - must include a client identifier option
1291# cf p52
1292
1293
1294class DHCP6_Rebind(DHCP6):
1295 name = "DHCPv6 Rebind Message"
1296 msgtype = 6
1297
1298#####################################################################
1299# Reply Message
1300# - sent by servers
1301# - the message must include a server identifier option
1302# - transaction-id field must match the value of original message
1303# The server includes a Rapid Commit option in the Reply message to
1304# indicate that the reply is in response to a solicit message
1305# - if the client receives a reply message with a Status code option
1306# with the value UseMulticast, the client records the receipt of the
1307# message and sends subsequent messages to the server through the
1308# interface on which the message was received using multicast. The
1309# client resends the original message using multicast
1310# - When the client receives a NotOnLink status from the server in
1311# response to a Confirm message, the client performs DHCP server
1312# solicitation as described in section 17 and client-initiated
1313# configuration as descrribed in section 18 (RFC 3315)
1314# - when the client receives a NotOnLink status from the server in
1315# response to a Request, the client can either re-issue the Request
1316# without specifying any addresses or restart the DHCP server
1317# discovery process.
1318# - the server must include a server identifier option containing the
1319# server's DUID in the Reply message
1320
1321
1322class DHCP6_Reply(DHCP6):
1323 name = "DHCPv6 Reply Message"
1324 msgtype = 7
1325
1326 overload_fields = {UDP: {"sport": 547, "dport": 546}}
1327
1328 def answers(self, other):
1329
1330 types = (DHCP6_Solicit, DHCP6_InfoRequest, DHCP6_Confirm, DHCP6_Rebind,
1331 DHCP6_Decline, DHCP6_Request, DHCP6_Release, DHCP6_Renew)
1332
1333 return (isinstance(other, types) and self.trid == other.trid)
1334
1335#####################################################################
1336# Release Message
1337# - sent by clients
1338# - must include a server identifier option
1339# cf p53
1340
1341
1342class DHCP6_Release(DHCP6):
1343 name = "DHCPv6 Release Message"
1344 msgtype = 8
1345
1346#####################################################################
1347# Decline Message
1348# - sent by clients
1349# - must include a client identifier option
1350# - Server identifier option must match server identifier
1351# - The addresses to be declined must be included in the IAs. Any
1352# addresses for the IAs the client wishes to continue to use should
1353# not be in added to the IAs.
1354# - cf p54
1355
1356
1357class DHCP6_Decline(DHCP6):
1358 name = "DHCPv6 Decline Message"
1359 msgtype = 9
1360
1361#####################################################################
1362# Reconfigure Message
1363# - sent by servers
1364# - must be unicast to the client
1365# - must include a server identifier option
1366# - must include a client identifier option that contains the client DUID
1367# - must contain a Reconfigure Message Option and the message type
1368# must be a valid value
1369# - the server sets the transaction-id to 0
1370# - The server must use DHCP Authentication in the Reconfigure
1371# message. Autant dire que ca va pas etre le type de message qu'on va
1372# voir le plus souvent.
1373
1374
1375class DHCP6_Reconf(DHCP6):
1376 name = "DHCPv6 Reconfigure Message"
1377 msgtype = 10
1378 overload_fields = {UDP: {"sport": 547, "dport": 546}}
1379
1380
1381#####################################################################
1382# Information-Request Message
1383# - sent by clients when needs configuration information but no
1384# addresses.
1385# - client should include a client identifier option to identify
1386# itself. If it doesn't the server is not able to return client
1387# specific options or the server can choose to not respond to the
1388# message at all. The client must include a client identifier option
1389# if the message will be authenticated.
1390# - client must include an ORO of option she's interested in receiving
1391# (can include hints)
1392
1393class DHCP6_InfoRequest(DHCP6):
1394 name = "DHCPv6 Information Request Message"
1395 msgtype = 11
1396
1397#####################################################################
1398# sent between Relay Agents and Servers
1399#
1400# Normalement, doit inclure une option "Relay Message Option"
1401# peut en inclure d'autres.
1402# voir section 7.1 de la 3315
1403
1404# Relay-Forward Message
1405# - sent by relay agents to servers
1406# If the relay agent relays messages to the All_DHCP_Servers multicast
1407# address or other multicast addresses, it sets the Hop Limit field to
1408# 32.
1409
1410
1411class DHCP6_RelayForward(_DHCP6OptGuessPayload, Packet):
1412 name = "DHCPv6 Relay Forward Message (Relay Agent/Server Message)"
1413 fields_desc = [ByteEnumField("msgtype", 12, dhcp6types),
1414 ByteField("hopcount", None),
1415 IP6Field("linkaddr", "::"),
1416 IP6Field("peeraddr", "::")]
1417 overload_fields = {UDP: {"sport": 547, "dport": 547}}
1418
1419 def hashret(self): # we filter on peer address field
1420 return inet_pton(socket.AF_INET6, self.peeraddr)
1421
1422#####################################################################
1423# sent between Relay Agents and Servers
1424# Normalement, doit inclure une option "Relay Message Option"
1425# peut en inclure d'autres.
1426# Les valeurs des champs hop-count, link-addr et peer-addr
1427# sont copiees du message Forward associe. POur le suivi de session.
1428# Pour le moment, comme decrit dans le commentaire, le hashret
1429# se limite au contenu du champ peer address.
1430# Voir section 7.2 de la 3315.
1431
1432# Relay-Reply Message
1433# - sent by servers to relay agents
1434# - if the solicit message was received in a Relay-Forward message,
1435# the server constructs a relay-reply message with the Advertise
1436# message in the payload of a relay-message. cf page 37/101. Envoie de
1437# ce message en unicast au relay-agent. utilisation de l'adresse ip
1438# presente en ip source du paquet recu
1439
1440
1441class DHCP6_RelayReply(DHCP6_RelayForward):
1442 name = "DHCPv6 Relay Reply Message (Relay Agent/Server Message)"
1443 msgtype = 13
1444
1445 def hashret(self): # We filter on peer address field.
1446 return inet_pton(socket.AF_INET6, self.peeraddr)
1447
1448 def answers(self, other):
1449 return (isinstance(other, DHCP6_RelayForward) and
1450 self.hopcount == other.hopcount and
1451 self.linkaddr == other.linkaddr and
1452 self.peeraddr == other.peeraddr)
1453
1454
1455#####################################################################
1456# Address Registration-Inform Message (RFC 9686)
1457# - sent by clients who generated their own address and need it registered
1458
1459class DHCP6_AddrRegInform(DHCP6):
1460 name = "DHCPv6 Information Request Message"
1461 msgtype = 36
1462
1463#####################################################################
1464# Address Registration-Reply Message (RFC 9686)
1465# - sent by servers who respond to the address registration-inform message
1466
1467
1468class DHCP6_AddrRegReply(DHCP6):
1469 name = "DHCPv6 Information Reply Message"
1470 msgtype = 37
1471
1472
1473bind_bottom_up(UDP, _dhcp6_dispatcher, {"dport": 547})
1474bind_bottom_up(UDP, _dhcp6_dispatcher, {"dport": 546})
1475
1476
1477class DHCPv6_am(AnsweringMachine):
1478 function_name = "dhcp6d"
1479 filter = "udp and port 546 and port 547"
1480 send_function = staticmethod(send)
1481
1482 def usage(self):
1483 msg = """
1484DHCPv6_am.parse_options( dns="2001:500::1035", domain="localdomain, local",
1485 duid=None, iface=conf.iface, advpref=255, sntpservers=None,
1486 sipdomains=None, sipservers=None,
1487 nisdomain=None, nisservers=None,
1488 nispdomain=None, nispservers=None,
1489 bcmcsdomains=None, bcmcsservers=None)
1490
1491 debug : When set, additional debugging information is printed.
1492
1493 duid : some DUID class (DUID_LLT, DUID_LL or DUID_EN). If none
1494 is provided a DUID_LLT is constructed based on the MAC
1495 address of the sending interface and launch time of dhcp6d
1496 answering machine.
1497
1498 iface : the interface to listen/reply on if you do not want to use
1499 conf.iface.
1500
1501 advpref : Value in [0,255] given to Advertise preference field.
1502 By default, 255 is used. Be aware that this specific
1503 value makes clients stops waiting for further Advertise
1504 messages from other servers.
1505
1506 dns : list of recursive DNS servers addresses (as a string or list).
1507 By default, it is set empty and the associated DHCP6OptDNSServers
1508 option is inactive. See RFC 3646 for details.
1509 domain : a list of DNS search domain (as a string or list). By default,
1510 it is empty and the associated DHCP6OptDomains option is inactive.
1511 See RFC 3646 for details.
1512
1513 sntpservers : a list of SNTP servers IPv6 addresses. By default,
1514 it is empty and the associated DHCP6OptSNTPServers option
1515 is inactive.
1516
1517 sipdomains : a list of SIP domains. By default, it is empty and the
1518 associated DHCP6OptSIPDomains option is inactive. See RFC 3319
1519 for details.
1520 sipservers : a list of SIP servers IPv6 addresses. By default, it is
1521 empty and the associated DHCP6OptSIPDomains option is inactive.
1522 See RFC 3319 for details.
1523
1524 nisdomain : a list of NIS domains. By default, it is empty and the
1525 associated DHCP6OptNISDomains option is inactive. See RFC 3898
1526 for details. See RFC 3646 for details.
1527 nisservers : a list of NIS servers IPv6 addresses. By default, it is
1528 empty and the associated DHCP6OptNISServers option is inactive.
1529 See RFC 3646 for details.
1530
1531 nispdomain : a list of NIS+ domains. By default, it is empty and the
1532 associated DHCP6OptNISPDomains option is inactive. See RFC 3898
1533 for details.
1534 nispservers : a list of NIS+ servers IPv6 addresses. By default, it is
1535 empty and the associated DHCP6OptNISServers option is inactive.
1536 See RFC 3898 for details.
1537
1538 bcmcsdomain : a list of BCMCS domains. By default, it is empty and the
1539 associated DHCP6OptBCMCSDomains option is inactive. See RFC 4280
1540 for details.
1541 bcmcsservers : a list of BCMCS servers IPv6 addresses. By default, it is
1542 empty and the associated DHCP6OptBCMCSServers option is inactive.
1543 See RFC 4280 for details.
1544
1545 If you have a need for others, just ask ... or provide a patch."""
1546 print(msg)
1547
1548 def parse_options(self, dns="2001:500::1035", domain="localdomain, local",
1549 startip="2001:db8::1", endip="2001:db8::20", duid=None,
1550 sntpservers=None, sipdomains=None, sipservers=None,
1551 nisdomain=None, nisservers=None, nispdomain=None,
1552 nispservers=None, bcmcsservers=None, bcmcsdomains=None,
1553 iface=None, debug=0, advpref=255):
1554 def norm_list(val, param_name):
1555 if val is None:
1556 return None
1557 if isinstance(val, list):
1558 return val
1559 elif isinstance(val, str):
1560 tmp_len = val.split(',')
1561 return [x.strip() for x in tmp_len]
1562 else:
1563 print("Bad '%s' parameter provided." % param_name)
1564 self.usage()
1565 return -1
1566
1567 if iface is None:
1568 iface = conf.iface
1569
1570 self.debug = debug
1571
1572 # Dictionary of provided DHCPv6 options, keyed by option type
1573 self.dhcpv6_options = {}
1574
1575 for o in [(dns, "dns", 23, lambda x: DHCP6OptDNSServers(dnsservers=x)),
1576 (domain, "domain", 24, lambda x: DHCP6OptDNSDomains(dnsdomains=x)), # noqa: E501
1577 (sntpservers, "sntpservers", 31, lambda x: DHCP6OptSNTPServers(sntpservers=x)), # noqa: E501
1578 (sipservers, "sipservers", 22, lambda x: DHCP6OptSIPServers(sipservers=x)), # noqa: E501
1579 (sipdomains, "sipdomains", 21, lambda x: DHCP6OptSIPDomains(sipdomains=x)), # noqa: E501
1580 (nisservers, "nisservers", 27, lambda x: DHCP6OptNISServers(nisservers=x)), # noqa: E501
1581 (nisdomain, "nisdomain", 29, lambda x: DHCP6OptNISDomain(nisdomain=(x + [""])[0])), # noqa: E501
1582 (nispservers, "nispservers", 28, lambda x: DHCP6OptNISPServers(nispservers=x)), # noqa: E501
1583 (nispdomain, "nispdomain", 30, lambda x: DHCP6OptNISPDomain(nispdomain=(x + [""])[0])), # noqa: E501
1584 (bcmcsservers, "bcmcsservers", 33, lambda x: DHCP6OptBCMCSServers(bcmcsservers=x)), # noqa: E501
1585 (bcmcsdomains, "bcmcsdomains", 34, lambda x: DHCP6OptBCMCSDomains(bcmcsdomains=x))]: # noqa: E501
1586
1587 opt = norm_list(o[0], o[1])
1588 if opt == -1: # Usage() was triggered
1589 return False
1590 elif opt is None: # We won't return that option
1591 pass
1592 else:
1593 self.dhcpv6_options[o[2]] = o[3](opt)
1594
1595 if self.debug:
1596 print("\n[+] List of active DHCPv6 options:")
1597 opts = sorted(self.dhcpv6_options)
1598 for i in opts:
1599 print(" %d: %s" % (i, repr(self.dhcpv6_options[i])))
1600
1601 # Preference value used in Advertise.
1602 self.advpref = advpref
1603
1604 # IP Pool
1605 self.startip = startip
1606 self.endip = endip
1607 # XXX TODO Check IPs are in same subnet
1608
1609 ####
1610 # The interface we are listening/replying on
1611 self.iface = iface
1612
1613 ####
1614 # Generate a server DUID
1615 if duid is not None:
1616 self.duid = duid
1617 else:
1618 # Timeval
1619 epoch = (2000, 1, 1, 0, 0, 0, 5, 1, 0)
1620 delta = time.mktime(epoch) - EPOCH
1621 timeval = time.time() - delta
1622
1623 # Mac Address
1624 mac = get_if_hwaddr(iface)
1625
1626 self.duid = DUID_LLT(timeval=timeval, lladdr=mac)
1627
1628 if self.debug:
1629 print("\n[+] Our server DUID:")
1630 self.duid.show(label_lvl=" " * 4)
1631
1632 ####
1633 # Find the source address we will use
1634 self.src_addr = None
1635 try:
1636 addr = next(x for x in in6_getifaddr() if x[2] == iface and in6_islladdr(x[0])) # noqa: E501
1637 except (StopIteration, RuntimeError):
1638 warning("Unable to get a Link-Local address")
1639 return
1640 else:
1641 self.src_addr = addr[0]
1642
1643 ####
1644 # Our leases
1645 self.leases = {}
1646
1647 if self.debug:
1648 print("\n[+] Starting DHCPv6 service on %s:" % self.iface)
1649
1650 def is_request(self, p):
1651 if IPv6 not in p:
1652 return False
1653
1654 src = p[IPv6].src
1655
1656 p = p[IPv6].payload
1657 if not isinstance(p, UDP) or p.sport != 546 or p.dport != 547:
1658 return False
1659
1660 p = p.payload
1661 if not isinstance(p, DHCP6):
1662 return False
1663
1664 # Message we considered client messages :
1665 # Solicit (1), Request (3), Confirm (4), Renew (5), Rebind (6)
1666 # Decline (9), Release (8), Information-request (11),
1667 if not (p.msgtype in [1, 3, 4, 5, 6, 8, 9, 11]):
1668 return False
1669
1670 # Message validation following section 15 of RFC 3315
1671
1672 if ((p.msgtype == 1) or # Solicit
1673 (p.msgtype == 6) or # Rebind
1674 (p.msgtype == 4)): # Confirm
1675 if ((DHCP6OptClientId not in p) or
1676 DHCP6OptServerId in p):
1677 return False
1678
1679 if (p.msgtype == 6 or # Rebind
1680 p.msgtype == 4): # Confirm
1681 # XXX We do not reply to Confirm or Rebind as we
1682 # XXX do not support address assignment
1683 return False
1684
1685 elif (p.msgtype == 3 or # Request
1686 p.msgtype == 5 or # Renew
1687 p.msgtype == 8): # Release
1688
1689 # Both options must be present
1690 if ((DHCP6OptServerId not in p) or
1691 (DHCP6OptClientId not in p)):
1692 return False
1693 # provided server DUID must match ours
1694 duid = p[DHCP6OptServerId].duid
1695 if not isinstance(duid, type(self.duid)):
1696 return False
1697 if raw(duid) != raw(self.duid):
1698 return False
1699
1700 if (p.msgtype == 5 or # Renew
1701 p.msgtype == 8): # Release
1702 # XXX We do not reply to Renew or Release as we
1703 # XXX do not support address assignment
1704 return False
1705
1706 elif p.msgtype == 9: # Decline
1707 # XXX We should check if we are tracking that client
1708 if not self.debug:
1709 return False
1710
1711 bo = Color.bold
1712 g = Color.green + bo
1713 b = Color.blue + bo
1714 n = Color.normal
1715 r = Color.red
1716
1717 vendor = in6_addrtovendor(src)
1718 if (vendor and vendor != "UNKNOWN"):
1719 vendor = " [" + b + vendor + n + "]"
1720 else:
1721 vendor = ""
1722 src = bo + src + n
1723
1724 it = p
1725 addrs = []
1726 while it:
1727 lst = []
1728 if isinstance(it, DHCP6OptIA_NA):
1729 lst = it.ianaopts
1730 elif isinstance(it, DHCP6OptIA_TA):
1731 lst = it.iataopts
1732
1733 addrs += [x.addr for x in lst if isinstance(x, DHCP6OptIAAddress)] # noqa: E501
1734 it = it.payload
1735
1736 addrs = [bo + x + n for x in addrs]
1737 if self.debug:
1738 msg = r + "[DEBUG]" + n + " Received " + g + "Decline" + n
1739 msg += " from " + bo + src + vendor + " for "
1740 msg += ", ".join(addrs) + n
1741 print(msg)
1742
1743 # See RFC 3315 sect 18.1.7
1744
1745 # Sent by a client to warn us she has determined
1746 # one or more addresses assigned to her is already
1747 # used on the link.
1748 # We should simply log that fact. No messaged should
1749 # be sent in return.
1750
1751 # - Message must include a Server identifier option
1752 # - the content of the Server identifier option must
1753 # match the server's identifier
1754 # - the message must include a Client Identifier option
1755 return False
1756
1757 elif p.msgtype == 11: # Information-Request
1758 if DHCP6OptServerId in p:
1759 duid = p[DHCP6OptServerId].duid
1760 if not isinstance(duid, type(self.duid)):
1761 return False
1762 if raw(duid) != raw(self.duid):
1763 return False
1764 if ((DHCP6OptIA_NA in p) or
1765 (DHCP6OptIA_TA in p) or
1766 (DHCP6OptIA_PD in p)):
1767 return False
1768 else:
1769 return False
1770
1771 return True
1772
1773 def print_reply(self, req, reply):
1774 def norm(s):
1775 if s.startswith("DHCPv6 "):
1776 s = s[7:]
1777 if s.endswith(" Message"):
1778 s = s[:-8]
1779 return s
1780
1781 if reply is None:
1782 return
1783
1784 bo = Color.bold
1785 g = Color.green + bo
1786 b = Color.blue + bo
1787 n = Color.normal
1788 reqtype = g + norm(req.getlayer(UDP).payload.name) + n
1789 reqsrc = req.getlayer(IPv6).src
1790 vendor = in6_addrtovendor(reqsrc)
1791 if (vendor and vendor != "UNKNOWN"):
1792 vendor = " [" + b + vendor + n + "]"
1793 else:
1794 vendor = ""
1795 reqsrc = bo + reqsrc + n
1796 reptype = g + norm(reply.getlayer(UDP).payload.name) + n
1797
1798 print("Sent %s answering to %s from %s%s" % (reptype, reqtype, reqsrc, vendor)) # noqa: E501
1799
1800 def make_reply(self, req):
1801 p = req[IPv6]
1802 req_src = p.src
1803
1804 p = p.payload.payload
1805
1806 msgtype = p.msgtype
1807 trid = p.trid
1808
1809 def _include_options(query, answer):
1810 """
1811 Include options from the DHCPv6 query
1812 """
1813
1814 # See which options should be included
1815 reqopts = []
1816 if query.haslayer(DHCP6OptOptReq): # add only asked ones
1817 reqopts = query[DHCP6OptOptReq].reqopts
1818 for o, opt in self.dhcpv6_options.items():
1819 if o in reqopts:
1820 answer /= opt
1821 else:
1822 # advertise everything we have available
1823 # Should not happen has clients MUST include
1824 # and ORO in requests (sec 18.1.1) -- arno
1825 for o, opt in self.dhcpv6_options.items():
1826 answer /= opt
1827
1828 if msgtype == 1: # SOLICIT (See Sect 17.1 and 17.2 of RFC 3315)
1829
1830 # XXX We don't support address or prefix assignment
1831 # XXX We also do not support relay function --arno
1832
1833 client_duid = p[DHCP6OptClientId].duid
1834 resp = IPv6(src=self.src_addr, dst=req_src)
1835 resp /= UDP(sport=547, dport=546)
1836
1837 if p.haslayer(DHCP6OptRapidCommit):
1838 # construct a Reply packet
1839 resp /= DHCP6_Reply(trid=trid)
1840 resp /= DHCP6OptRapidCommit() # See 17.1.2
1841 resp /= DHCP6OptServerId(duid=self.duid)
1842 resp /= DHCP6OptClientId(duid=client_duid)
1843
1844 else: # No Rapid Commit in the packet. Reply with an Advertise
1845
1846 if (p.haslayer(DHCP6OptIA_NA) or
1847 p.haslayer(DHCP6OptIA_TA)):
1848 # XXX We don't assign addresses at the moment
1849 msg = "Scapy6 dhcp6d does not support address assignment"
1850 resp /= DHCP6_Advertise(trid=trid)
1851 resp /= DHCP6OptStatusCode(statuscode=2, statusmsg=msg)
1852 resp /= DHCP6OptServerId(duid=self.duid)
1853 resp /= DHCP6OptClientId(duid=client_duid)
1854
1855 elif p.haslayer(DHCP6OptIA_PD):
1856 # XXX We don't assign prefixes at the moment
1857 msg = "Scapy6 dhcp6d does not support prefix assignment"
1858 resp /= DHCP6_Advertise(trid=trid)
1859 resp /= DHCP6OptStatusCode(statuscode=6, statusmsg=msg)
1860 resp /= DHCP6OptServerId(duid=self.duid)
1861 resp /= DHCP6OptClientId(duid=client_duid)
1862
1863 else: # Usual case, no request for prefixes or addresse
1864 resp /= DHCP6_Advertise(trid=trid)
1865 resp /= DHCP6OptPref(prefval=self.advpref)
1866 resp /= DHCP6OptServerId(duid=self.duid)
1867 resp /= DHCP6OptClientId(duid=client_duid)
1868 resp /= DHCP6OptReconfAccept()
1869
1870 _include_options(p, resp)
1871
1872 return resp
1873
1874 elif msgtype == 3: # REQUEST (INFO-REQUEST is further below)
1875 client_duid = p[DHCP6OptClientId].duid
1876 resp = IPv6(src=self.src_addr, dst=req_src)
1877 resp /= UDP(sport=547, dport=546)
1878 resp /= DHCP6_Solicit(trid=trid)
1879 resp /= DHCP6OptServerId(duid=self.duid)
1880 resp /= DHCP6OptClientId(duid=client_duid)
1881
1882 _include_options(p, resp)
1883
1884 return resp
1885
1886 elif msgtype == 4: # CONFIRM
1887 # see Sect 18.1.2
1888
1889 # Client want to check if addresses it was assigned
1890 # are still appropriate
1891
1892 # Server must discard any Confirm messages that
1893 # do not include a Client Identifier option OR
1894 # THAT DO INCLUDE a Server Identifier Option
1895
1896 # XXX we must discard the SOLICIT if it is received with
1897 # a unicast destination address
1898
1899 pass
1900
1901 elif msgtype == 5: # RENEW
1902 # see Sect 18.1.3
1903
1904 # Clients want to extend lifetime of assigned addresses
1905 # and update configuration parameters. This message is sent
1906 # specifically to the server that provided her the info
1907
1908 # - Received message must include a Server Identifier
1909 # option.
1910 # - the content of server identifier option must match
1911 # the server's identifier.
1912 # - the message must include a Client identifier option
1913
1914 pass
1915
1916 elif msgtype == 6: # REBIND
1917 # see Sect 18.1.4
1918
1919 # Same purpose as the Renew message but sent to any
1920 # available server after he received no response
1921 # to its previous Renew message.
1922
1923 # - Message must include a Client Identifier Option
1924 # - Message can't include a Server identifier option
1925
1926 # XXX we must discard the SOLICIT if it is received with
1927 # a unicast destination address
1928
1929 pass
1930
1931 elif msgtype == 8: # RELEASE
1932 # See RFC 3315 section 18.1.6
1933
1934 # Message is sent to the server to indicate that
1935 # she will no longer use the addresses that was assigned
1936 # We should parse the message and verify our dictionary
1937 # to log that fact.
1938
1939 # - The message must include a server identifier option
1940 # - The content of the Server Identifier option must
1941 # match the server's identifier
1942 # - the message must include a Client Identifier option
1943
1944 pass
1945
1946 elif msgtype == 9: # DECLINE
1947 # See RFC 3315 section 18.1.7
1948 pass
1949
1950 elif msgtype == 11: # INFO-REQUEST
1951 client_duid = None
1952 if not p.haslayer(DHCP6OptClientId):
1953 if self.debug:
1954 warning("Received Info Request message without Client Id option") # noqa: E501
1955 else:
1956 client_duid = p[DHCP6OptClientId].duid
1957
1958 resp = IPv6(src=self.src_addr, dst=req_src)
1959 resp /= UDP(sport=547, dport=546)
1960 resp /= DHCP6_Reply(trid=trid)
1961 resp /= DHCP6OptServerId(duid=self.duid)
1962
1963 if client_duid:
1964 resp /= DHCP6OptClientId(duid=client_duid)
1965
1966 # Stack requested options if available
1967 for o, opt in self.dhcpv6_options.items():
1968 resp /= opt
1969
1970 return resp
1971
1972 else:
1973 # what else ?
1974 pass
1975
1976 # - We won't support reemission
1977 # - We won't support relay role, nor relay forwarded messages
1978 # at the beginning