Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.8/site-packages/scapy/layers/dhcp6.py: 62%
Shortcuts on this page
r m x toggle line displays
j k next/prev highlighted chunk
0 (zero) top of page
1 (one) first highlighted chunk
Shortcuts on this page
r m x toggle line displays
j k next/prev highlighted chunk
0 (zero) top of page
1 (one) first highlighted chunk
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>
9"""
10DHCPv6: Dynamic Host Configuration Protocol for IPv6. [RFC 3315,8415]
11"""
13import socket
14import struct
15import time
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
39#############################################################################
40# Helpers ##
41#############################################################################
44def get_cls(name, fallback_cls):
45 return globals().get(name, fallback_cls)
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"}
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)
69#############################################################################
70#############################################################################
71# DHCPv6 #
72#############################################################################
73#############################################################################
76All_DHCP_Relay_Agents_and_Servers = "ff02::1:2"
77All_DHCP_Servers = "ff05::1:3" # Site-Local scope : deprecated by 3879
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 }
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 }
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"}
209#####################################################################
210# DHCPv6 DUID related stuff #
211#####################################################################
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"}
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)"}
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)
263class _LLAddrField(MACField):
264 pass
266# XXX We only support Ethernet addresses at the moment. _LLAddrField
267# will be modified when needed. Ask us. --arno
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)]
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", "")]
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)]
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)]
298duid_cls = {1: "DUID_LLT",
299 2: "DUID_EN",
300 3: "DUID_LL",
301 4: "DUID_UUID"}
303#####################################################################
304# DHCPv6 Options classes #
305#####################################################################
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)
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 )
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)
339 def extract_padding(self, s):
340 return b"", s
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)]
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)
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)]
367class DHCP6OptServerId(DHCP6OptClientId): # RFC 8415 sect 21.3
368 name = "DHCP6 Server Identifier Option"
369 optcode = 2
371# Should be encapsulated in the option field of IA_NA or IA_TA options
372# Can only appear at that location.
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)]
390 def guess_payload_class(self, payload):
391 return conf.padding_layer
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)]
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)]
416# DHCPv6 Option Request Option #
418class _OptReqListField(StrLenField):
419 islist = 1
421 def i2h(self, pkt, x):
422 if not x:
423 return []
424 return x
426 def i2len(self, pkt, x):
427 return 2 * len(x)
429 def any2i(self, pkt, x):
430 return x
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)
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
451 def i2m(self, pkt, x):
452 return b"".join(struct.pack('!H', y) for y in x)
454# A client may include an ORO in a solicit, Request, Renew, Rebind,
455# Confirm or Information-request
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)]
466# DHCPv6 Preference Option #
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)]
477# DHCPv6 Elapsed Time Option #
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.)
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)]
493# DHCPv6 Authentication Option #
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.
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}
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)]
555# DHCPv6 Server Unicast Option #
558class _SrvAddrField(IP6Field):
559 def i2h(self, pkt, x):
560 if x is None:
561 return "::"
562 return x
564 def i2m(self, pkt, x):
565 return inet_pton(socket.AF_INET6, self.i2h(pkt, x))
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)]
575# DHCPv6 Status Code Option #
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
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)]
596# DHCPv6 Rapid Commit Option #
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)]
604# DHCPv6 User Class Option #
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)
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
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)]
634 def guess_payload_class(self, payload):
635 return conf.padding_layer
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)]
647# DHCPv6 Vendor Class Option #
649class _VendorClassDataField(_UserClassDataField):
650 pass
653class VENDOR_CLASS_DATA(USER_CLASS_DATA):
654 name = "vendor class data"
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
667# DHCPv6 Vendor-Specific Information Option #
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)]
677 def guess_payload_class(self, payload):
678 return conf.padding_layer
680# The third one that will be used for nothing interesting
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
693# DHCPv6 Interface-ID Option #
695# Repasser sur cette option a la fin. Elle a pas l'air d'etre des
696# masses critique.
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)]
708# DHCPv6 Reconfigure Message Option #
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"})]
721# DHCPv6 Reconfigure Accept Option #
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)]
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)]
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)]
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)]
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)]
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)]
784 def guess_payload_class(self, payload):
785 return conf.padding_layer
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)]
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)]
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)]
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)]
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)]
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)]
840IRT_DEFAULT = 86400
841IRT_MINIMUM = 600
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
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)]
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)]
867_dhcp6_geoconf_what = {
868 0: "DHCP server",
869 1: "closest network element",
870 2: "client"
871}
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)]
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)]
891# TODO: see if we encounter opaque values from vendor devices
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)]
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)]
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)]
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)]
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)]
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)]
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)]
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)]
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", "::")]
973 def extract_padding(self, s):
974 return b"", s
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", "::")]
983 def extract_padding(self, s):
984 return b"", s
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)]
994 def extract_padding(self, s):
995 return b"", s
998_ntp_subopts = {1: DHCP6NTPSubOptSrvAddr,
999 2: DHCP6NTPSubOptMCAddr,
1000 3: DHCP6NTPSubOptSrvFQDN}
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)
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)]
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)]
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)]
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)]
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)]
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)]
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)]
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)]
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)]
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 )]
1106#####################################################################
1107# DHCPv6 messages #
1108#####################################################################
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
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)
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
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}}
1148 def hashret(self):
1149 return struct.pack("!I", self.trid)[1:4]
1151# DHCPv6 Relay Message Option #
1153# Relayed message is seen as a payload.
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)]
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
1191class DHCP6_Solicit(DHCP6):
1192 name = "DHCPv6 Solicit Message"
1193 msgtype = 1
1194 overload_fields = {UDP: {"sport": 546, "dport": 547}}
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
1205class DHCP6_Advertise(DHCP6):
1206 name = "DHCPv6 Advertise Message"
1207 msgtype = 2
1208 overload_fields = {UDP: {"sport": 547, "dport": 546}}
1210 def answers(self, other):
1211 return (isinstance(other, DHCP6_Solicit) and
1212 other.msgtype == 1 and
1213 self.trid == other.trid)
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.
1233class DHCP6_Request(DHCP6):
1234 name = "DHCPv6 Request Message"
1235 msgtype = 3
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
1246class DHCP6_Confirm(DHCP6):
1247 name = "DHCPv6 Confirm Message"
1248 msgtype = 4
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.
1268class DHCP6_Renew(DHCP6):
1269 name = "DHCPv6 Renew Message"
1270 msgtype = 5
1272#####################################################################
1273# Rebind Message
1274# - sent by clients
1275# - must include a client identifier option
1276# cf p52
1279class DHCP6_Rebind(DHCP6):
1280 name = "DHCPv6 Rebind Message"
1281 msgtype = 6
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
1307class DHCP6_Reply(DHCP6):
1308 name = "DHCPv6 Reply Message"
1309 msgtype = 7
1311 overload_fields = {UDP: {"sport": 547, "dport": 546}}
1313 def answers(self, other):
1315 types = (DHCP6_Solicit, DHCP6_InfoRequest, DHCP6_Confirm, DHCP6_Rebind,
1316 DHCP6_Decline, DHCP6_Request, DHCP6_Release, DHCP6_Renew)
1318 return (isinstance(other, types) and self.trid == other.trid)
1320#####################################################################
1321# Release Message
1322# - sent by clients
1323# - must include a server identifier option
1324# cf p53
1327class DHCP6_Release(DHCP6):
1328 name = "DHCPv6 Release Message"
1329 msgtype = 8
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
1342class DHCP6_Decline(DHCP6):
1343 name = "DHCPv6 Decline Message"
1344 msgtype = 9
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.
1360class DHCP6_Reconf(DHCP6):
1361 name = "DHCPv6 Reconfigure Message"
1362 msgtype = 10
1363 overload_fields = {UDP: {"sport": 547, "dport": 546}}
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)
1378class DHCP6_InfoRequest(DHCP6):
1379 name = "DHCPv6 Information Request Message"
1380 msgtype = 11
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
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.
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}}
1404 def hashret(self): # we filter on peer address field
1405 return inet_pton(socket.AF_INET6, self.peeraddr)
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.
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
1426class DHCP6_RelayReply(DHCP6_RelayForward):
1427 name = "DHCPv6 Relay Reply Message (Relay Agent/Server Message)"
1428 msgtype = 13
1430 def hashret(self): # We filter on peer address field.
1431 return inet_pton(socket.AF_INET6, self.peeraddr)
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)
1440bind_bottom_up(UDP, _dhcp6_dispatcher, {"dport": 547})
1441bind_bottom_up(UDP, _dhcp6_dispatcher, {"dport": 546})
1444class DHCPv6_am(AnsweringMachine):
1445 function_name = "dhcp6d"
1446 filter = "udp and port 546 and port 547"
1447 send_function = staticmethod(send)
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)
1458 debug : When set, additional debugging information is printed.
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.
1465 iface : the interface to listen/reply on if you do not want to use
1466 conf.iface.
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.
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.
1480 sntpservers : a list of SNTP servers IPv6 addresses. By default,
1481 it is empty and the associated DHCP6OptSNTPServers option
1482 is inactive.
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.
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.
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.
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.
1512 If you have a need for others, just ask ... or provide a patch."""
1513 print(msg)
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
1534 if iface is None:
1535 iface = conf.iface
1537 self.debug = debug
1539 # Dictionary of provided DHCPv6 options, keyed by option type
1540 self.dhcpv6_options = {}
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
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)
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])))
1568 # Preference value used in Advertise.
1569 self.advpref = advpref
1571 # IP Pool
1572 self.startip = startip
1573 self.endip = endip
1574 # XXX TODO Check IPs are in same subnet
1576 ####
1577 # The interface we are listening/replying on
1578 self.iface = iface
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
1590 # Mac Address
1591 rawmac = get_if_raw_hwaddr(iface)[1]
1592 mac = ":".join("%.02x" % orb(x) for x in rawmac)
1594 self.duid = DUID_LLT(timeval=timeval, lladdr=mac)
1596 if self.debug:
1597 print("\n[+] Our server DUID:")
1598 self.duid.show(label_lvl=" " * 4)
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]
1611 ####
1612 # Our leases
1613 self.leases = {}
1615 if self.debug:
1616 print("\n[+] Starting DHCPv6 service on %s:" % self.iface)
1618 def is_request(self, p):
1619 if IPv6 not in p:
1620 return False
1622 src = p[IPv6].src
1624 p = p[IPv6].payload
1625 if not isinstance(p, UDP) or p.sport != 546 or p.dport != 547:
1626 return False
1628 p = p.payload
1629 if not isinstance(p, DHCP6):
1630 return False
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
1638 # Message validation following section 15 of RFC 3315
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
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
1653 elif (p.msgtype == 3 or # Request
1654 p.msgtype == 5 or # Renew
1655 p.msgtype == 8): # Release
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
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
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
1679 bo = Color.bold
1680 g = Color.green + bo
1681 b = Color.blue + bo
1682 n = Color.normal
1683 r = Color.red
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
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
1701 addrs += [x.addr for x in lst if isinstance(x, DHCP6OptIAAddress)] # noqa: E501
1702 it = it.payload
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)
1711 # See RFC 3315 sect 18.1.7
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.
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
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
1739 return True
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
1749 if reply is None:
1750 return
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
1766 print("Sent %s answering to %s from %s%s" % (reptype, reqtype, reqsrc, vendor)) # noqa: E501
1768 def make_reply(self, req):
1769 p = req[IPv6]
1770 req_src = p.src
1772 p = p.payload.payload
1774 msgtype = p.msgtype
1775 trid = p.trid
1777 def _include_options(query, answer):
1778 """
1779 Include options from the DHCPv6 query
1780 """
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
1796 if msgtype == 1: # SOLICIT (See Sect 17.1 and 17.2 of RFC 3315)
1798 # XXX We don't support address or prefix assignment
1799 # XXX We also do not support relay function --arno
1801 client_duid = p[DHCP6OptClientId].duid
1802 resp = IPv6(src=self.src_addr, dst=req_src)
1803 resp /= UDP(sport=547, dport=546)
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)
1812 else: # No Rapid Commit in the packet. Reply with an Advertise
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)
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)
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()
1838 _include_options(p, resp)
1840 return resp
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)
1850 _include_options(p, resp)
1852 return resp
1854 elif msgtype == 4: # CONFIRM
1855 # see Sect 18.1.2
1857 # Client want to check if addresses it was assigned
1858 # are still appropriate
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
1864 # XXX we must discard the SOLICIT if it is received with
1865 # a unicast destination address
1867 pass
1869 elif msgtype == 5: # RENEW
1870 # see Sect 18.1.3
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
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
1882 pass
1884 elif msgtype == 6: # REBIND
1885 # see Sect 18.1.4
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.
1891 # - Message must include a Client Identifier Option
1892 # - Message can't include a Server identifier option
1894 # XXX we must discard the SOLICIT if it is received with
1895 # a unicast destination address
1897 pass
1899 elif msgtype == 8: # RELEASE
1900 # See RFC 3315 section 18.1.6
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.
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
1912 pass
1914 elif msgtype == 9: # DECLINE
1915 # See RFC 3315 section 18.1.7
1916 pass
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
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)
1931 if client_duid:
1932 resp /= DHCP6OptClientId(duid=client_duid)
1934 # Stack requested options if available
1935 for o, opt in self.dhcpv6_options.items():
1936 resp /= opt
1938 return resp
1940 else:
1941 # what else ?
1942 pass
1944 # - We won't support reemission
1945 # - We won't support relay role, nor relay forwarded messages
1946 # at the beginning