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