From e51a31726f61c9261ae608db558a25fa626ab82f Mon Sep 17 00:00:00 2001
From: Phil <phil@secdev.org>
Date: Fri, 12 Sep 2008 17:57:20 +0200
Subject: [PATCH] Split DHCP6

---
 scapy/config.py       |    2 +-
 scapy/layers/dhcp6.py | 1728 +++++++++++++++++++++++++++++++++++++++++
 scapy/layers/inet6.py | 1705 ----------------------------------------
 3 files changed, 1729 insertions(+), 1706 deletions(-)
 create mode 100644 scapy/layers/dhcp6.py

diff --git a/scapy/config.py b/scapy/config.py
index a213cd2f..7fb2de0e 100644
--- a/scapy/config.py
+++ b/scapy/config.py
@@ -315,7 +315,7 @@ extensions_paths: path or list of paths where extensions are to be looked for
     netcache = NetCache()
     load_layers = ["l2", "inet", "dhcp", "dns", "dot11", "gprs", "hsrp", "inet6", "ir", "isakmp", "l2tp",
                    "mgcp", "mobileip", "netbios", "netflow", "ntp", "ppp", "radius", "rip", "rtp",
-                   "sebek", "skinny", "smb", "snmp", "tftp", "x509", "bluetooth" ]
+                   "sebek", "skinny", "smb", "snmp", "tftp", "x509", "bluetooth", "dhcp6" ]
     
 
 conf=Conf()
diff --git a/scapy/layers/dhcp6.py b/scapy/layers/dhcp6.py
new file mode 100644
index 00000000..59430ad9
--- /dev/null
+++ b/scapy/layers/dhcp6.py
@@ -0,0 +1,1728 @@
+## This file is part of Scapy
+## See http://www.secdev.org/projects/scapy for more informations
+## Copyright (C) Philippe Biondi <phil@secdev.org>
+## This program is published under a GPLv2 license
+
+## Copyright (C) 2005  Guillaume Valadon <guedou@hongo.wide.ad.jp>
+##                     Arnaud Ebalard <arnaud.ebalard@eads.net>
+
+import socket
+from scapy.packet import *
+from scapy.fields import *
+from scapy.utils6 import *
+from scapy.layers.inet6 import *
+from scapy.ansmachine import AnsweringMachine
+
+#############################################################################
+# Helpers                                                                  ##
+#############################################################################
+
+def get_cls(name, fallback_cls):
+    return globals().get(name, fallback_cls)
+
+
+#############################################################################
+#############################################################################
+###                                DHCPv6                                 ###
+#############################################################################
+#############################################################################
+
+All_DHCP_Relay_Agents_and_Servers = "ff02::1:2" 
+All_DHCP_Servers = "ff05::1:3"  # Site-Local scope : deprecated by 3879
+
+dhcp6opts = { 1: "CLIENTID",  
+              2: "SERVERID",
+              3: "IA_NA",
+              4: "IA_TA",
+              5: "IAADDR",
+              6: "ORO",
+              7: "PREFERENCE",
+              8: "ELAPSED_TIME",
+              9: "RELAY_MSG",
+             11: "AUTH",
+             12: "UNICAST",
+             13: "STATUS_CODE",
+             14: "RAPID_COMMIT",
+             15: "USER_CLASS",
+             16: "VENDOR_CLASS",
+             17: "VENDOR_OPTS",
+             18: "INTERFACE_ID",
+             19: "RECONF_MSG",
+             20: "RECONF_ACCEPT",
+             21: "SIP Servers Domain Name List",     #RFC3319
+             22: "SIP Servers IPv6 Address List",    #RFC3319
+             23: "DNS Recursive Name Server Option", #RFC3646
+             24: "Domain Search List option",        #RFC3646
+             25: "OPTION_IA_PD",                     #RFC3633
+             26: "OPTION_IAPREFIX",                  #RFC3633
+             27: "OPTION_NIS_SERVERS",               #RFC3898
+             28: "OPTION_NISP_SERVERS",              #RFC3898
+             29: "OPTION_NIS_DOMAIN_NAME",           #RFC3898
+             30: "OPTION_NISP_DOMAIN_NAME",          #RFC3898
+             31: "OPTION_SNTP_SERVERS",              #RFC4075
+             32: "OPTION_INFORMATION_REFRESH_TIME",  #RFC4242
+             33: "OPTION_BCMCS_SERVER_D",            #RFC4280         
+             34: "OPTION_BCMCS_SERVER_A",            #RFC4280
+             36: "OPTION_GEOCONF_CIVIC",             #RFC-ietf-geopriv-dhcp-civil-09.txt
+             37: "OPTION_REMOTE_ID",                 #RFC4649
+             38: "OPTION_SUBSCRIBER_ID",             #RFC4580
+             39: "OPTION_CLIENT_FQDN" }              #RFC4704
+
+dhcp6opts_by_code = {  1: "DHCP6OptClientId", 
+                       2: "DHCP6OptServerId",
+                       3: "DHCP6OptIA_NA",
+                       4: "DHCP6OptIA_TA",
+                       5: "DHCP6OptIAAddress",
+                       6: "DHCP6OptOptReq",
+                       7: "DHCP6OptPref",
+                       8: "DHCP6OptElapsedTime",
+                       9: "DHCP6OptRelayMsg",
+                       11: "DHCP6OptAuth",
+                       12: "DHCP6OptServerUnicast",
+                       13: "DHCP6OptStatusCode",
+                       14: "DHCP6OptRapidCommit",
+                       15: "DHCP6OptUserClass",
+                       16: "DHCP6OptVendorClass",
+                       17: "DHCP6OptVendorSpecificInfo",
+                       18: "DHCP6OptIfaceId",
+                       19: "DHCP6OptReconfMsg",
+                       20: "DHCP6OptReconfAccept",
+                       21: "DHCP6OptSIPDomains",          #RFC3319
+                       22: "DHCP6OptSIPServers",          #RFC3319
+                       23: "DHCP6OptDNSServers",          #RFC3646
+                       24: "DHCP6OptDNSDomains",          #RFC3646
+                       25: "DHCP6OptIA_PD",               #RFC3633
+                       26: "DHCP6OptIAPrefix",            #RFC3633
+                       27: "DHCP6OptNISServers",          #RFC3898
+                       28: "DHCP6OptNISPServers",         #RFC3898
+                       29: "DHCP6OptNISDomain",           #RFC3898
+                       30: "DHCP6OptNISPDomain",          #RFC3898
+                       31: "DHCP6OptSNTPServers",         #RFC4075
+                       32: "DHCP6OptInfoRefreshTime",     #RFC4242
+                       33: "DHCP6OptBCMCSDomains",        #RFC4280         
+                       34: "DHCP6OptBCMCSServers",        #RFC4280
+                       #36: "DHCP6OptGeoConf",            #RFC-ietf-geopriv-dhcp-civil-09.txt
+                       37: "DHCP6OptRemoteID",            #RFC4649
+                       38: "DHCP6OptSubscriberID",        #RFC4580
+                       39: "DHCP6OptClientFQDN",          #RFC4704
+                       #40: "DHCP6OptPANAAgent",          #RFC-ietf-dhc-paa-option-05.txt
+                       #41: "DHCP6OptNewPOSIXTimeZone,    #RFC4833
+                       #42: "DHCP6OptNewTZDBTimeZone,     #RFC4833
+                       43: "DHCP6OptRelayAgentERO"        #RFC4994
+                       #44: "DHCP6OptLQQuery",            #RFC5007
+                       #45: "DHCP6OptLQClientData",       #RFC5007
+                       #46: "DHCP6OptLQClientTime",       #RFC5007
+                       #47: "DHCP6OptLQRelayData",        #RFC5007
+                       #48: "DHCP6OptLQClientLink",       #RFC5007
+}
+
+
+# sect 5.3 RFC 3315 : DHCP6 Messages types
+dhcp6types = {   1:"SOLICIT",
+                 2:"ADVERTISE",
+                 3:"REQUEST",
+                 4:"CONFIRM",
+                 5:"RENEW",
+                 6:"REBIND",
+                 7:"REPLY",
+                 8:"RELEASE",
+                 9:"DECLINE",
+                10:"RECONFIGURE",
+                11:"INFORMATION-REQUEST",
+                12:"RELAY-FORW",
+                13:"RELAY-REPL" }
+
+
+#####################################################################
+###                  DHCPv6 DUID related stuff                    ###
+#####################################################################
+
+duidtypes = { 1: "Link-layer address plus time", 
+              2: "Vendor-assigned unique ID based on Enterprise Number",
+              3: "Link-layer Address" }
+
+# DUID hardware types - RFC 826 - Extracted from 
+# http://www.iana.org/assignments/arp-parameters on 31/10/06
+# We should add the length of every kind of address.
+duidhwtypes = {  0: "NET/ROM pseudo", # Not referenced by IANA
+                 1: "Ethernet (10Mb)",
+                 2: "Experimental Ethernet (3Mb)",
+                 3: "Amateur Radio AX.25",
+                 4: "Proteon ProNET Token Ring",
+                 5: "Chaos",
+                 6: "IEEE 802 Networks",
+                 7: "ARCNET",
+                 8: "Hyperchannel",
+                 9: "Lanstar",
+                10: "Autonet Short Address",
+                11: "LocalTalk",
+                12: "LocalNet (IBM PCNet or SYTEK LocalNET)",
+                13: "Ultra link",
+                14: "SMDS",
+                15: "Frame Relay",
+                16: "Asynchronous Transmission Mode (ATM)",
+                17: "HDLC",
+                18: "Fibre Channel",
+                19: "Asynchronous Transmission Mode (ATM)",
+                20: "Serial Line",
+                21: "Asynchronous Transmission Mode (ATM)",
+                22: "MIL-STD-188-220",
+                23: "Metricom",
+                24: "IEEE 1394.1995",
+                25: "MAPOS",
+                26: "Twinaxial",
+                27: "EUI-64",
+                28: "HIPARP",
+                29: "IP and ARP over ISO 7816-3",
+                30: "ARPSec",
+                31: "IPsec tunnel",
+                32: "InfiniBand (TM)",
+                33: "TIA-102 Project 25 Common Air Interface (CAI)" }
+
+class UTCTimeField(IntField):
+    epoch = (2000, 1, 1, 0, 0, 0, 5, 1, 0) # required Epoch
+    def i2repr(self, pkt, x):
+        x = self.i2h(pkt, x)
+        from time import gmtime, strftime, mktime
+        delta = mktime(self.epoch) - mktime(gmtime(0))
+        x = x + delta
+        t = strftime("%a, %d %b %Y %H:%M:%S +0000", gmtime(x))
+        return "%s (%d)" % (t, x)
+
+class _LLAddrField(MACField):
+    pass
+
+# XXX We only support Ethernet addresses at the moment. _LLAddrField 
+#     will be modified when needed. Ask us. --arno
+class DUID_LLT(Packet):  # sect 9.2 RFC 3315
+    name = "DUID - Link-layer address plus time"
+    fields_desc = [ ShortEnumField("type", 1, duidtypes),
+                    XShortEnumField("hwtype", 1, duidhwtypes), 
+                    UTCTimeField("timeval", 0), # i.e. 01 Jan 2000
+                    _LLAddrField("lladdr", ETHER_ANY) ]
+
+# In fact, IANA enterprise-numbers file available at 
+# http//www.iana.org/asignments/enterprise-numbers)
+# is simply huge (more than 2Mo and 600Ko in bz2). I'll
+# add only most common vendors, and encountered values.
+# -- arno
+iana_enterprise_num = {    9: "ciscoSystems",
+                          35: "Nortel Networks",
+                          43: "3Com",
+                         311: "Microsoft",
+                        2636: "Juniper Networks, Inc.",
+                        4526: "Netgear",
+                        5771: "Cisco Systems, Inc.",
+                        5842: "Cisco Systems",
+                       16885: "Nortel Networks" }
+
+class DUID_EN(Packet):  # sect 9.3 RFC 3315
+    name = "DUID - Assigned by Vendor Based on Enterprise Number"
+    fields_desc = [ ShortEnumField("type", 2, duidtypes),
+                    IntEnumField("enterprisenum", 311, iana_enterprise_num),
+                    StrField("id","") ] 
+
+class DUID_LL(Packet):  # sect 9.4 RFC 3315
+    name = "DUID - Based on Link-layer Address"
+    fields_desc = [ ShortEnumField("type", 3, duidtypes),
+                    XShortEnumField("hwtype", 1, duidhwtypes), 
+                    _LLAddrField("lladdr", ETHER_ANY) ]
+
+duid_cls = { 1: "DUID_LLT",
+             2: "DUID_EN",
+             3: "DUID_LL"}
+
+#####################################################################
+###                   DHCPv6 Options classes                      ###
+#####################################################################
+
+class _DHCP6OptGuessPayload(Packet):
+    def guess_payload_class(self, payload):
+        cls = Raw
+        if len(payload) > 2 :
+            opt = struct.unpack("!H", payload[:2])[0]
+            cls = get_cls(dhcp6opts_by_code.get(opt, "DHCP6OptUnknown"), DHCP6OptUnknown)
+        return cls
+
+class DHCP6OptUnknown(_DHCP6OptGuessPayload): # A generic DHCPv6 Option
+    name = "Unknown DHCPv6 OPtion"
+    fields_desc = [ ShortEnumField("optcode", 0, dhcp6opts), 
+                    FieldLenField("optlen", None, length_of="data", fmt="!H"),
+                    StrLenField("data", "",
+                                length_from = lambda pkt: pkt.optlen)]
+
+class _DUIDField(PacketField):
+    holds_packets=1
+    def __init__(self, name, default, length_from=None):
+        StrField.__init__(self, name, default)
+        self.length_from = length_from
+
+    def i2m(self, pkt, i):
+        return str(i)
+
+    def m2i(self, pkt, x):
+        cls = Raw 
+        if len(x) > 4:
+            o = struct.unpack("!H", x[:2])[0]
+            cls = get_cls(duid_cls.get(o, Raw), "Raw")
+        return cls(x)
+
+    def getfield(self, pkt, s):
+        l = self.length_from(pkt)
+        return s[l:], self.m2i(pkt,s[:l])
+ 
+
+class DHCP6OptClientId(_DHCP6OptGuessPayload):     # RFC sect 22.2
+    name = "DHCP6 Client Identifier Option"
+    fields_desc = [ ShortEnumField("optcode", 1, dhcp6opts), 
+                    FieldLenField("optlen", None, length_of="duid", fmt="!H"),
+                    _DUIDField("duid", "",
+                               length_from = lambda pkt: pkt.optlen) ]
+
+
+class DHCP6OptServerId(DHCP6OptClientId):     # RFC sect 22.3
+    name = "DHCP6 Server Identifier Option"
+    __metaclass__ = NewDefaultValues
+    optcode = 2
+
+# Should be encapsulated in the option field of IA_NA or IA_TA options
+# Can only appear at that location.
+# TODO : last field IAaddr-options is not defined in the reference document
+class DHCP6OptIAAddress(_DHCP6OptGuessPayload):    # RFC sect 22.6
+    name = "DHCP6 IA Address Option (IA_TA or IA_NA suboption)"
+    fields_desc = [ ShortEnumField("optcode", 5, dhcp6opts), 
+                    FieldLenField("optlen", None, length_of="iaaddropts",
+                                  fmt="!H", adjust = lambda pkt,x: x+24),
+                    IP6Field("addr", "::"),
+                    IntField("preflft", 0),
+                    IntField("validlft", 0),
+                    XIntField("iaid", None),
+                    StrLenField("iaaddropts", "",
+                                length_from  = lambda pkt: pkt.optlen - 24) ]
+    def guess_payload_class(self, payload):
+        return Padding
+
+class _IANAOptField(PacketListField):
+    def i2len(self, pkt, z):
+        if z is None or z == []:
+            return 0
+        return sum(map(lambda x: len(str(x)) ,z))
+
+    def getfield(self, pkt, s):
+        l = self.length_from(pkt)
+        lst = []
+        remain, payl = s[:l], s[l:]
+        while len(remain)>0:
+            p = self.m2i(pkt,remain)
+            if Padding in p:
+                pad = p[Padding]
+                remain = pad.load
+                del(pad.underlayer.payload)
+            else:
+                remain = ""
+            lst.append(p)
+        return payl,lst
+
+class DHCP6OptIA_NA(_DHCP6OptGuessPayload):         # RFC sect 22.4
+    name = "DHCP6 Identity Association for Non-temporary Addresses Option"
+    fields_desc = [ ShortEnumField("optcode", 3, dhcp6opts), 
+                    FieldLenField("optlen", None, length_of="ianaopts",
+                                  fmt="!H", adjust = lambda pkt,x: x+12),
+                    XIntField("iaid", None),
+                    IntField("T1", None),
+                    IntField("T2", None),
+                    _IANAOptField("ianaopts", [], DHCP6OptIAAddress,
+                                  length_from = lambda pkt: pkt.optlen-12) ]
+
+class _IATAOptField(_IANAOptField):
+    pass
+
+class DHCP6OptIA_TA(_DHCP6OptGuessPayload):         # RFC sect 22.5
+    name = "DHCP6 Identity Association for Temporary Addresses Option"
+    fields_desc = [ ShortEnumField("optcode", 4, dhcp6opts), 
+                    FieldLenField("optlen", None, length_of="iataopts",
+                                  fmt="!H", adjust = lambda pkt,x: x+4),
+                    XIntField("iaid", None),
+                    _IATAOptField("iataopts", [], DHCP6OptIAAddress,
+                                  length_from = lambda pkt: pkt.optlen-4) ]
+
+
+#### DHCPv6 Option Request Option ###################################
+
+class _OptReqListField(StrLenField):
+    islist = 1
+    def i2h(self, pkt, x):
+        if x is None:
+            return []
+        return x
+
+    def i2len(self, pkt, x):
+        return 2*len(x)
+
+    def any2i(self, pkt, x):
+        return x
+
+    def i2repr(self, pkt, x):
+        s = []
+        for y in self.i2h(pkt, x):
+            if dhcp6opts.has_key(y):
+                s.append(dhcp6opts[y])
+            else:
+                s.append("%d" % y)
+        return "[%s]" % ", ".join(s) 
+
+    def m2i(self, pkt, x):
+        r = []
+        while len(x) != 0:
+            if len(x)<2:
+                warning("Odd length for requested option field. Rejecting last byte")
+                return r
+            r.append(struct.unpack("!H", x[:2])[0])
+            x = x[2:]
+        return r
+    
+    def i2m(self, pkt, x):
+        return "".join(map(lambda y: struct.pack("!H", y), x))
+
+# A client may include an ORO in a solicit, Request, Renew, Rebind,
+# Confirm or Information-request
+class DHCP6OptOptReq(_DHCP6OptGuessPayload):       # RFC sect 22.7
+    name = "DHCP6 Option Request Option"
+    fields_desc = [ ShortEnumField("optcode", 6, dhcp6opts),
+                    FieldLenField("optlen", None, length_of="reqopts", fmt="!H"),
+                    _OptReqListField("reqopts", [23, 24],
+                                     length_from = lambda pkt: pkt.optlen) ]
+
+
+#### DHCPv6 Preference Option #######################################
+
+# emise par un serveur pour affecter le choix fait par le client. Dans
+# les messages Advertise, a priori
+class DHCP6OptPref(_DHCP6OptGuessPayload):       # RFC sect 22.8
+    name = "DHCP6 Preference Option"
+    fields_desc = [ ShortEnumField("optcode", 7, dhcp6opts), 
+                    ShortField("optlen", 1 ),
+                    ByteField("prefval",255) ]
+
+
+#### DHCPv6 Elapsed Time Option #####################################
+
+class _ElapsedTimeField(ShortField):
+    def i2repr(self, pkt, x):
+        if x == 0xffff:
+            return "infinity (0xffff)"
+        return "%.2f sec" % (self.i2h(pkt, x)/100.)
+
+class DHCP6OptElapsedTime(_DHCP6OptGuessPayload):# RFC sect 22.9
+    name = "DHCP6 Elapsed Time Option"
+    fields_desc = [ ShortEnumField("optcode", 8, dhcp6opts), 
+                    ShortField("optlen", 2),
+                    _ElapsedTimeField("elapsedtime", 0) ]
+
+
+#### DHCPv6 Relay Message Option ####################################
+
+# Relayed message is seen as a payload.
+class DHCP6OptRelayMsg(_DHCP6OptGuessPayload):# RFC sect 22.10
+    name = "DHCP6 Relay Message Option"
+    fields_desc = [ ShortEnumField("optcode", 9, dhcp6opts), 
+                    ShortField("optlen", None ) ]
+    def post_build(self, p, pay):
+        if self.optlen is None:
+            l = len(pay) 
+            p = p[:2]+struct.pack("!H", l)
+        return p + pay
+
+
+#### DHCPv6 Authentication Option ###################################
+
+#    The following fields are set in an Authentication option for the
+#    Reconfigure Key Authentication Protocol:
+#
+#       protocol    3
+#
+#       algorithm   1
+#
+#       RDM         0
+#
+#    The format of the Authentication information for the Reconfigure Key
+#    Authentication Protocol is:
+#
+#      0                   1                   2                   3
+#      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
+#     +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+#     |     Type      |                 Value (128 bits)              |
+#     +-+-+-+-+-+-+-+-+                                               |
+#     .                                                               .
+#     .                                                               .
+#     .                                               +-+-+-+-+-+-+-+-+
+#     |                                               |
+#     +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+#
+#       Type    Type of data in Value field carried in this option:
+#
+#                  1   Reconfigure Key value (used in Reply message).
+#
+#                  2   HMAC-MD5 digest of the message (used in Reconfigure
+#                      message).
+#
+#       Value   Data as defined by field.
+
+
+# TODO : Decoding only at the moment
+class DHCP6OptAuth(_DHCP6OptGuessPayload):    # RFC sect 22.11
+    name = "DHCP6 Option - Authentication"
+    fields_desc = [ ShortEnumField("optcode", 11, dhcp6opts), 
+                    FieldLenField("optlen", None, length_of="authinfo",
+                                  adjust = lambda pkt,x: x+11),
+                    ByteField("proto", 3), # TODO : XXX
+                    ByteField("alg", 1), # TODO : XXX
+                    ByteField("rdm", 0), # TODO : XXX
+                    StrFixedLenField("replay", "A"*8, 8), # TODO: XXX
+                    StrLenField("authinfo", "",
+                                length_from = lambda pkt: pkt.optlen - 11) ]
+
+#### DHCPv6 Server Unicast Option ###################################
+
+class _SrvAddrField(IP6Field):
+    def i2h(self, pkt, x):
+        if x is None:
+            return "::"
+        return x
+    
+    def i2m(self, pkt, x):
+        return inet_pton(socket.AF_INET6, self.i2h(pkt,x))
+
+class DHCP6OptServerUnicast(_DHCP6OptGuessPayload):# RFC sect 22.12
+    name = "DHCP6 Server Unicast Option"
+    fields_desc = [ ShortEnumField("optcode", 12, dhcp6opts), 
+                    ShortField("optlen", 16 ),
+                    _SrvAddrField("srvaddr",None) ]
+
+
+#### DHCPv6 Status Code Option ######################################
+
+dhcp6statuscodes = { 0:"Success",      # sect 24.4
+                     1:"UnspecFail",
+                     2:"NoAddrsAvail",
+                     3:"NoBinding",
+                     4:"NotOnLink",
+                     5:"UseMulticast",
+                     6:"NoPrefixAvail"} # From RFC3633
+
+class DHCP6OptStatusCode(_DHCP6OptGuessPayload):# RFC sect 22.13
+    name = "DHCP6 Status Code Option"
+    fields_desc = [ ShortEnumField("optcode", 13, dhcp6opts), 
+                    FieldLenField("optlen", None, length_of="statusmsg",
+                                  fmt="!H", adjust = lambda pkt,x:x+2),
+                    ShortEnumField("statuscode",None,dhcp6statuscodes),
+                    StrLenField("statusmsg", "",
+                                length_from = lambda pkt: pkt.optlen-2) ]
+
+
+#### DHCPv6 Rapid Commit Option #####################################
+
+class DHCP6OptRapidCommit(_DHCP6OptGuessPayload):   # RFC sect 22.14
+    name = "DHCP6 Rapid Commit Option"
+    fields_desc = [ ShortEnumField("optcode", 14, dhcp6opts),
+                    ShortField("optlen", 0)]
+
+
+#### DHCPv6 User Class Option #######################################
+
+class _UserClassDataField(PacketListField):
+    def i2len(self, pkt, z):
+        if z is None or z == []:
+            return 0
+        return sum(map(lambda x: len(str(x)) ,z))
+
+    def getfield(self, pkt, s):
+        l = self.length_from(pkt)
+        lst = []
+        remain, payl = s[:l], s[l:]
+        while len(remain)>0:
+            p = self.m2i(pkt,remain)
+            if Padding in p:
+                pad = p[Padding]
+                remain = pad.load
+                del(pad.underlayer.payload)
+            else:
+                remain = ""
+            lst.append(p)
+        return payl,lst
+
+
+class USER_CLASS_DATA(Packet):
+    name = "user class data"
+    fields_desc = [ FieldLenField("len", None, length_of="data"),
+                    StrLenField("data", "",
+                                length_from = lambda pkt: pkt.len) ]
+    def guess_payload_class(self, payload):
+        return Padding
+
+class DHCP6OptUserClass(_DHCP6OptGuessPayload):# RFC sect 22.15
+    name = "DHCP6 User Class Option"
+    fields_desc = [ ShortEnumField("optcode", 15, dhcp6opts), 
+                    FieldLenField("optlen", None, fmt="!H",
+                                  length_of="userclassdata"),
+                    _UserClassDataField("userclassdata", [], USER_CLASS_DATA,
+                                        length_from = lambda pkt: pkt.optlen) ]
+
+
+#### DHCPv6 Vendor Class Option #####################################
+
+class _VendorClassDataField(_UserClassDataField):
+    pass
+
+class VENDOR_CLASS_DATA(USER_CLASS_DATA):
+    name = "vendor class data"
+
+class DHCP6OptVendorClass(_DHCP6OptGuessPayload):# RFC sect 22.16
+    name = "DHCP6 Vendor Class Option"
+    fields_desc = [ ShortEnumField("optcode", 16, dhcp6opts), 
+                    FieldLenField("optlen", None, length_of="vcdata", fmt="!H",
+                                  adjust = lambda pkt,x: x+4),
+                    IntEnumField("enterprisenum",None , iana_enterprise_num ),
+                    _VendorClassDataField("vcdata", [], VENDOR_CLASS_DATA,
+                                          length_from = lambda pkt: pkt.optlen-4) ]
+
+#### DHCPv6 Vendor-Specific Information Option ######################
+
+class VENDOR_SPECIFIC_OPTION(_DHCP6OptGuessPayload):
+    name = "vendor specific option data"
+    fields_desc = [ ShortField("optcode", None),
+                    FieldLenField("optlen", None, length_of="optdata"),
+                    StrLenField("optdata", "",
+                                length_from = lambda pkt: pkt.optlen) ]
+    def guess_payload_class(self, payload):
+        return Padding
+
+# The third one that will be used for nothing interesting
+class DHCP6OptVendorSpecificInfo(_DHCP6OptGuessPayload):# RFC sect 22.17
+    name = "DHCP6 Vendor-specific Information Option"
+    fields_desc = [ ShortEnumField("optcode", 17, dhcp6opts), 
+                    FieldLenField("optlen", None, length_of="vso", fmt="!H",
+                                  adjust = lambda pkt,x: x+4),
+                    IntEnumField("enterprisenum",None , iana_enterprise_num),
+                    _VendorClassDataField("vso", [], VENDOR_SPECIFIC_OPTION,
+                                          length_from = lambda pkt: pkt.optlen-4) ]
+
+#### DHCPv6 Interface-ID Option #####################################
+
+# Repasser sur cette option a la fin. Elle a pas l'air d'etre des
+# masses critique.
+class DHCP6OptIfaceId(_DHCP6OptGuessPayload):# RFC sect 22.18
+    name = "DHCP6 Interface-Id Option"
+    fields_desc = [ ShortEnumField("optcode", 18, dhcp6opts),
+                    FieldLenField("optlen", None, fmt="!H",
+                                  length_of="ifaceid"),
+                    StrLenField("ifaceid", "",
+                                length_from = lambda pkt: pkt.optlen) ]
+
+
+#### DHCPv6 Reconfigure Message Option ##############################
+
+# A server includes a Reconfigure Message option in a Reconfigure
+# message to indicate to the client whether the client responds with a
+# renew message or an Informatiion-request message.
+class DHCP6OptReconfMsg(_DHCP6OptGuessPayload):       # RFC sect 22.19
+    name = "DHCP6 Reconfigure Message Option"
+    fields_desc = [ ShortEnumField("optcode", 19, dhcp6opts), 
+                    ShortField("optlen", 1 ),
+                    ByteEnumField("msgtype", 11, {  5:"Renew Message", 
+                                                   11:"Information Request"}) ]
+
+
+#### DHCPv6 Reconfigure Accept Option ###############################
+
+# A client uses the Reconfigure Accept option to announce to the
+# server whether the client is willing to accept Recoonfigure
+# messages, and a server uses this option to tell the client whether
+# or not to accept Reconfigure messages. The default behavior in the
+# absence of this option, means unwillingness to accept reconfigure
+# messages, or instruction not to accept Reconfigure messages, for the
+# client and server messages, respectively.
+class DHCP6OptReconfAccept(_DHCP6OptGuessPayload):   # RFC sect 22.20
+    name = "DHCP6 Reconfigure Accept Option"
+    fields_desc = [ ShortEnumField("optcode", 20, dhcp6opts),
+                    ShortField("optlen", 0)]
+
+# As required in Sect 8. of RFC 3315, Domain Names must be encoded as 
+# described in section 3.1 of RFC 1035
+# XXX Label should be at most 63 octets in length : we do not enforce it
+#     Total length of domain should be 255 : we do not enforce it either
+class DomainNameListField(StrLenField):
+    islist = 1
+
+    def i2len(self, pkt, x):
+        return len(self.i2m(pkt, x))
+
+    def m2i(self, pkt, x):
+        res = []
+        while x:
+            cur = []
+            while x and x[0] != '\x00':
+                l = ord(x[0])
+                cur.append(x[1:l+1])
+                x = x[l+1:]
+            res.append(".".join(cur))
+            if x and x[0] == '\x00':
+                x = x[1:]
+        return res
+
+    def i2m(self, pkt, x):
+        def conditionalTrailingDot(z):
+            if z and z[-1] == '\x00':
+                return z
+            return z+'\x00'
+        res = ""
+        tmp = map(lambda y: map((lambda z: chr(len(z))+z), y.split('.')), x)
+        return "".join(map(lambda x: conditionalTrailingDot("".join(x)), tmp))
+
+class DHCP6OptSIPDomains(_DHCP6OptGuessPayload):       #RFC3319
+    name = "DHCP6 Option - SIP Servers Domain Name List"
+    fields_desc = [ ShortEnumField("optcode", 21, dhcp6opts),
+                    FieldLenField("optlen", None, length_of="sipdomains"),
+                    DomainNameListField("sipdomains", [],
+                                        length_from = lambda pkt: pkt.optlen) ]
+
+class DHCP6OptSIPServers(_DHCP6OptGuessPayload):          #RFC3319
+    name = "DHCP6 Option - SIP Servers IPv6 Address List"
+    fields_desc = [ ShortEnumField("optcode", 22, dhcp6opts),
+                    FieldLenField("optlen", None, length_of="sipservers"),
+                    IP6ListField("sipservers", [], 
+                                 length_from = lambda pkt: pkt.optlen) ]
+
+class DHCP6OptDNSServers(_DHCP6OptGuessPayload):          #RFC3646
+    name = "DHCP6 Option - DNS Recursive Name Server"
+    fields_desc = [ ShortEnumField("optcode", 23, dhcp6opts),
+                    FieldLenField("optlen", None, length_of="dnsservers"),
+                    IP6ListField("dnsservers", [],
+                                 length_from = lambda pkt: pkt.optlen) ]
+
+class DHCP6OptDNSDomains(_DHCP6OptGuessPayload): #RFC3646
+    name = "DHCP6 Option - Domain Search List option"
+    fields_desc = [ ShortEnumField("optcode", 24, dhcp6opts),
+                    FieldLenField("optlen", None, length_of="dnsdomains"),
+                    DomainNameListField("dnsdomains", [],
+                                        length_from = lambda pkt: pkt.optlen) ]
+
+# TODO: Implement iaprefopts correctly when provided with more 
+#       information about it.
+class DHCP6OptIAPrefix(_DHCP6OptGuessPayload):                    #RFC3633
+    name = "DHCP6 Option - IA_PD Prefix option"
+    fields_desc = [ ShortEnumField("optcode", 26, dhcp6opts),
+                    FieldLenField("optlen", None, length_of="iaprefopts",
+                                  adjust = lambda pkt,x: x+26),
+                    IntField("preflft", 0),
+                    IntField("validlft", 0),
+                    ByteField("plen", 48),  # TODO: Challenge that default value
+                    IP6Field("prefix", "2001:db8::"), # At least, global and won't hurt
+                    StrLenField("iaprefopts", "",
+                                length_from = lambda pkt: pkt.optlen-26) ]
+
+class DHCP6OptIA_PD(_DHCP6OptGuessPayload):                       #RFC3633
+    name = "DHCP6 Option - Identity Association for Prefix Delegation"
+    fields_desc = [ ShortEnumField("optcode", 25, dhcp6opts),
+                    FieldLenField("optlen", None, length_of="iapdopt",
+                                  adjust = lambda pkt,x: x+12),
+                    IntField("iaid", 0),
+                    IntField("T1", 0),
+                    IntField("T2", 0),
+                    PacketListField("iapdopt", [], DHCP6OptIAPrefix,
+                                    length_from = lambda pkt: pkt.optlen-12) ]
+
+class DHCP6OptNISServers(_DHCP6OptGuessPayload):                 #RFC3898
+    name = "DHCP6 Option - NIS Servers"
+    fields_desc = [ ShortEnumField("optcode", 27, dhcp6opts),
+                    FieldLenField("optlen", None, length_of="nisservers"),
+                    IP6ListField("nisservers", [],
+                                 length_from = lambda pkt: pkt.optlen) ]
+
+class DHCP6OptNISPServers(_DHCP6OptGuessPayload):                #RFC3898
+    name = "DHCP6 Option - NIS+ Servers"
+    fields_desc = [ ShortEnumField("optcode", 28, dhcp6opts),
+                    FieldLenField("optlen", None, length_of="nispservers"),
+                    IP6ListField("nispservers", [],
+                                 length_from = lambda pkt: pkt.optlen) ]
+
+class DomainNameField(StrLenField):
+    def getfield(self, pkt, s):
+        l = self.length_from(pkt)
+        return s[l:], self.m2i(pkt,s[:l])
+
+    def i2len(self, pkt, x):
+        return len(self.i2m(pkt, x))
+
+    def m2i(self, pkt, x):
+        save = x
+        cur = []
+        while x and x[0] != '\x00':
+            l = ord(x[0])
+            cur.append(x[1:1+l])
+            x = x[l+1:]
+        if x[0] != '\x00':
+            print "Found weird domain: '%s'. Keeping %s" % (save, x)
+        return ".".join(cur)
+
+    def i2m(self, pkt, x):
+        def conditionalTrailingDot(z):
+            if (z and z[-1] == '\x00'):
+                return z
+            return z+'\x00'
+        if not x:
+            return ""
+        tmp = "".join(map(lambda z: chr(len(z))+z, x.split('.')))
+        return conditionalTrailingDot(tmp)
+
+class DHCP6OptNISDomain(_DHCP6OptGuessPayload):             #RFC3898
+    name = "DHCP6 Option - NIS Domain Name"
+    fields_desc = [ ShortEnumField("optcode", 29, dhcp6opts),
+                    FieldLenField("optlen", None, length_of="nisdomain"),
+                    DomainNameField("nisdomain", "",
+                                    length_from = lambda pkt: pkt.optlen) ]
+
+class DHCP6OptNISPDomain(_DHCP6OptGuessPayload):            #RFC3898
+    name = "DHCP6 Option - NIS+ Domain Name"
+    fields_desc = [ ShortEnumField("optcode", 30, dhcp6opts),
+                    FieldLenField("optlen", None, length_of="nispdomain"),
+                    DomainNameField("nispdomain", "",
+                                    length_from= lambda pkt: pkt.optlen) ]
+
+class DHCP6OptSNTPServers(_DHCP6OptGuessPayload):                #RFC4075
+    name = "DHCP6 option - SNTP Servers"
+    fields_desc = [ ShortEnumField("optcode", 31, dhcp6opts),
+                    FieldLenField("optlen", None, length_of="sntpservers"),
+                    IP6ListField("sntpservers", [],
+                                 length_from = lambda pkt: pkt.optlen) ]
+
+IRT_DEFAULT=86400
+IRT_MINIMUM=600
+class DHCP6OptInfoRefreshTime(_DHCP6OptGuessPayload):    #RFC4242
+    name = "DHCP6 Option - Information Refresh Time"
+    fields_desc = [ ShortEnumField("optcode", 32, dhcp6opts),
+                    ShortField("optlen", 4),
+                    IntField("reftime", IRT_DEFAULT)] # One day
+
+class DHCP6OptBCMCSDomains(_DHCP6OptGuessPayload):              #RFC4280         
+    name = "DHCP6 Option - BCMCS Domain Name List"
+    fields_desc = [ ShortEnumField("optcode", 33, dhcp6opts),
+                    FieldLenField("optlen", None, length_of="bcmcsdomains"),
+                    DomainNameListField("bcmcsdomains", [],
+                                        length_from = lambda pkt: pkt.optlen) ]
+
+class DHCP6OptBCMCSServers(_DHCP6OptGuessPayload):              #RFC4280
+    name = "DHCP6 Option - BCMCS Addresses List"
+    fields_desc = [ ShortEnumField("optcode", 34, dhcp6opts),
+                    FieldLenField("optlen", None, length_of="bcmcsservers"),
+                    IP6ListField("bcmcsservers", [],
+                                 length_from= lambda pkt: pkt.optlen) ]
+
+# TODO : Does Nothing at the moment
+class DHCP6OptGeoConf(_DHCP6OptGuessPayload):               #RFC-ietf-geopriv-dhcp-civil-09.txt
+    name = ""
+    fields_desc = [ ShortEnumField("optcode", 36, dhcp6opts),
+                    FieldLenField("optlen", None, length_of="optdata"),
+                    StrLenField("optdata", "",
+                                length_from = lambda pkt: pkt.optlen) ]
+
+# TODO: see if we encounter opaque values from vendor devices
+class DHCP6OptRemoteID(_DHCP6OptGuessPayload):                   #RFC4649
+    name = "DHCP6 Option - Relay Agent Remote-ID"
+    fields_desc = [ ShortEnumField("optcode", 37, dhcp6opts),
+                    FieldLenField("optlen", None, length_of="remoteid",
+                                  adjust = lambda pkt,x: x+4),
+                    IntEnumField("enterprisenum", None, iana_enterprise_num),
+                    StrLenField("remoteid", "",
+                                length_from = lambda pkt: pkt.optlen-4) ]
+
+# TODO : 'subscriberid' default value should be at least 1 byte long
+class DHCP6OptSubscriberID(_DHCP6OptGuessPayload):               #RFC4580
+    name = "DHCP6 Option - Subscriber ID"
+    fields_desc = [ ShortEnumField("optcode", 38, dhcp6opts),
+                    FieldLenField("optlen", None, length_of="subscriberid"),
+                    StrLenField("subscriberid", "",
+                                length_from = lambda pkt: pkt.optlen) ]
+
+# TODO :  "The data in the Domain Name field MUST be encoded
+#          as described in Section 8 of [5]"
+class DHCP6OptClientFQDN(_DHCP6OptGuessPayload):                 #RFC4704
+    name = "DHCP6 Option - Client FQDN"
+    fields_desc = [ ShortEnumField("optcode", 39, dhcp6opts),
+                    FieldLenField("optlen", None, length_of="fqdn",
+                                  adjust = lambda pkt,x: x+1),
+                    BitField("res", 0, 5),
+                    FlagsField("flags", 0, 3, "SON" ),
+                    DomainNameField("fqdn", "",
+                                    length_from = lambda pkt: pkt.optlen-1) ]
+
+class DHCP6OptRelayAgentERO(_DHCP6OptGuessPayload):       # RFC4994
+    name = "DHCP6 Option - RelayRequest Option"
+    fields_desc = [ ShortEnumField("optcode", 43, dhcp6opts),
+                    FieldLenField("optlen", None, length_of="reqopts", fmt="!H"),
+                    _OptReqListField("reqopts", [23, 24],
+                                     length_from = lambda pkt: pkt.optlen) ]
+
+#####################################################################
+###                        DHCPv6 messages                        ###
+#####################################################################
+
+# Some state parameters of the protocols that should probably be 
+# useful to have in the configuration (and keep up-to-date)
+DHCP6RelayAgentUnicastAddr=""
+DHCP6RelayHopCount=""
+DHCP6ServerUnicastAddr=""
+DHCP6ClientUnicastAddr=""
+DHCP6ClientIA_TA=""
+DHCP6ClientIA_NA=""
+DHCP6ClientIAID=""
+T1="" # Voir 2462
+T2="" # Voir 2462
+DHCP6ServerDUID=""
+DHCP6CurrentTransactionID="" # devrait etre utilise pour matcher une
+# reponse et mis a jour en mode client par une valeur aleatoire pour
+# laquelle on attend un retour de la part d'un serveur.
+DHCP6PrefVal="" # la valeur de preference a utiliser dans
+# les options preference
+
+# Emitted by :
+# - server : ADVERTISE, REPLY, RECONFIGURE, RELAY-REPL (vers relay)
+# - client : SOLICIT, REQUEST, CONFIRM, RENEW, REBIND, RELEASE, DECLINE,
+#            INFORMATION REQUEST
+# - relay  : RELAY-FORW (toward server)
+
+class _DHCP6GuessPayload(Packet):
+    def guess_payload_class(self, payload):
+        if len(payload) > 1 :
+            print ord(payload[0])
+            return get_cls(dhcp6opts.get(ord(payload[0]),"DHCP6OptUnknown"), Raw)
+        return Raw
+
+#####################################################################
+## DHCPv6 messages sent between Clients and Servers (types 1 to 11)
+# Comme specifie en section 15.1 de la RFC 3315, les valeurs de
+# transaction id sont selectionnees de maniere aleatoire par le client
+# a chaque emission et doivent matcher dans les reponses faites par
+# les clients
+class DHCP6(_DHCP6OptGuessPayload):
+    name = "DHCPv6 Generic Message)"
+    fields_desc = [ ByteEnumField("msgtype",None,dhcp6types),
+                    X3BytesField("trid",0x000000) ]
+    overload_fields = { UDP: {"sport": 546, "dport": 547} }
+
+    def hashret(self):
+        return struct.pack("!I", self.trid)[1:4]
+
+#####################################################################
+# Solicit Message : sect 17.1.1 RFC3315
+# - sent by client
+# - must include a client identifier option
+# - the client may include IA options for any IAs to which it wants the
+#   server to assign address
+# - The client use IA_NA options to request the assignment of
+#   non-temporary addresses and uses IA_TA options to request the
+#   assignment of temporary addresses
+# - The client should include an Option Request option to indicate the
+#   options the client is interested in receiving (eventually
+#   including hints)
+# - The client includes a Reconfigure Accept option if is willing to
+#   accept Reconfigure messages from the server.
+# Le cas du send and reply est assez particulier car suivant la
+# presence d'une option rapid commit dans le solicit, l'attente
+# s'arrete au premier message de reponse recu ou alors apres un
+# timeout. De la meme maniere, si un message Advertise arrive avec une
+# valeur de preference de 255, il arrete l'attente et envoie une
+# Request.
+# - The client announces its intention to use DHCP authentication by
+# including an Authentication option in its solicit message. The
+# server selects a key for the client based on the client's DUID. The
+# client and server use that key to authenticate all DHCP messages
+# exchanged during the session
+
+class DHCP6_Solicit(DHCP6):
+    name = "DHCPv6 Solicit Message"
+    __metaclass__ = NewDefaultValues
+    msgtype = 1
+    overload_fields = { UDP: {"sport": 546, "dport": 547} }
+
+#####################################################################
+# Advertise Message
+# - sent by server
+# - Includes a server identifier option
+# - Includes a client identifier option
+# - the client identifier option must match the client's DUID
+# - transaction ID must match
+
+class DHCP6_Advertise(DHCP6):
+    name = "DHCPv6 Advertise Message"
+    __metaclass__ = NewDefaultValues
+    msgtype = 2
+    overload_fields = { UDP: {"sport": 547, "dport": 546} }
+    
+    def answers(self, other):
+        return (isinstance(other,DHCP6_Solicit) and 
+                other.msgtype == 1 and
+                self.trid == other.trid)
+
+#####################################################################
+# Request Message
+# - sent by clients
+# - includes a server identifier option
+# - the content of Server Identifier option must match server's DUID
+# - includes a client identifier option
+# - must include an ORO Option (even with hints) p40
+# - can includes a reconfigure Accept option indicating whether or
+#   not the client is willing to accept Reconfigure messages from
+#   the server (p40)
+# - When the server receives a Request message via unicast from a
+# client to which the server has not sent a unicast option, the server
+# discards the Request message and responds with a Reply message
+# containinig Status Code option with the value UseMulticast, a Server
+# Identifier Option containing the server's DUID, the client
+# Identifier option from the client message and no other option.
+
+class DHCP6_Request(DHCP6):
+    name = "DHCPv6 Request Message"
+    __metaclass__ = NewDefaultValues
+    msgtype = 3
+
+#####################################################################
+# Confirm Message
+# - sent by clients
+# - must include a clien identifier option
+# - When the server receives a Confirm Message, the server determines
+# whether the addresses in the Confirm message are appropriate for the
+# link to which the client is attached. cf p50
+
+class DHCP6_Confirm(DHCP6):
+    name = "DHCPv6 Confirm Message"
+    __metaclass__ = NewDefaultValues
+    msgtype = 4
+    
+#####################################################################
+# Renew Message
+# - sent by clients
+# - must include a server identifier option
+# - content of server identifier option must match the server's identifier
+# - must include a client identifier option
+# - the clients includes any IA assigned to the interface that may
+# have moved to a new link, along with the addresses associated with
+# those IAs in its confirm messages
+# - When the server receives a Renew message that contains an IA
+# option from a client, it locates the client's binding and verifies
+# that the information in the IA from the client matches the
+# information for that client. If the server cannot find a client
+# entry for the IA the server returns the IA containing no addresses
+# with a status code option est to NoBinding in the Reply message. cf
+# p51 pour le reste.
+
+class DHCP6_Renew(DHCP6):
+    name = "DHCPv6 Renew Message"
+    __metaclass__ = NewDefaultValues
+    msgtype = 5
+    
+#####################################################################
+# Rebind Message
+# - sent by clients
+# - must include a client identifier option
+# cf p52
+
+class DHCP6_Rebind(DHCP6):
+    name = "DHCPv6 Rebind Message"
+    __metaclass__ = NewDefaultValues
+    msgtype = 6
+    
+#####################################################################
+# Reply Message
+# - sent by servers
+# - the message must include a server identifier option
+# - transaction-id field must match the value of original message
+# The server includes a Rapid Commit option in the Reply message to
+# indicate that the reply is in response to a solicit message
+# - if the client receives a reply message with a Status code option
+# with the value UseMulticast, the client records the receipt of the
+# message and sends subsequent messages to the server through the
+# interface on which the message was received using multicast. The
+# client resends the original message using multicast
+# - When the client receives a NotOnLink status from the server in
+# response to a Confirm message, the client performs DHCP server
+# solicitation as described in section 17 and client-initiated
+# configuration as descrribed in section 18 (RFC 3315)
+# - when the client receives a NotOnLink status from the server in
+# response to a Request, the client can either re-issue the Request
+# without specifying any addresses or restart the DHCP server
+# discovery process.
+# - the server must include a server identifier option containing the
+# server's DUID in the Reply message
+
+class DHCP6_Reply(DHCP6):
+    name = "DHCPv6 Reply Message"
+    __metaclass__ = NewDefaultValues
+    msgtype = 7
+    
+    def answers(self, other):
+        return (isinstance(other, DHCP6_InfoRequest) and
+                self.trid == other.trid)
+
+#####################################################################
+# Release Message
+# - sent by clients
+# - must include a server identifier option
+# cf p53
+
+class DHCP6_Release(DHCP6):
+    name = "DHCPv6 Release Message"
+    __metaclass__ = NewDefaultValues
+    msgtype = 8
+    
+#####################################################################
+# Decline Message
+# - sent by clients
+# - must include a client identifier option
+# - Server identifier option must match server identifier
+# - The addresses to be declined must be included in the IAs. Any
+# addresses for the IAs the client wishes to continue to use should
+# not be in added to the IAs.
+# - cf p54 
+
+class DHCP6_Decline(DHCP6):
+    name = "DHCPv6 Decline Message"
+    __metaclass__ = NewDefaultValues
+    msgtype = 9
+    
+#####################################################################
+# Reconfigure Message
+# - sent by servers
+# - must be unicast to the client
+# - must include a server identifier option
+# - must include a client identifier option that contains the client DUID
+# - must contain a Reconfigure Message Option and the message type
+#   must be a valid value
+# - the server sets the transaction-id to 0
+# - The server must use DHCP Authentication in the Reconfigure
+# message. Autant dire que ca va pas etre le type de message qu'on va
+# voir le plus souvent.
+
+class DHCP6_Reconf(DHCP6):
+    name = "DHCPv6 Reconfigure Message"
+    __metaclass__ = NewDefaultValues
+    msgtype = 10
+    overload_fields = { UDP: { "sport": 547, "dport": 546 } }
+
+    
+#####################################################################
+# Information-Request Message
+# - sent by clients when needs configuration information but no
+# addresses. 
+# - client should include a client identifier option to identify
+# itself. If it doesn't the server is not able to return client
+# specific options or the server can choose to not respond to the
+# message at all. The client must include a client identifier option
+# if the message will be authenticated.
+# - client must include an ORO of option she's interested in receiving
+# (can include hints)
+
+class DHCP6_InfoRequest(DHCP6):
+    name = "DHCPv6 Information Request Message"    
+    __metaclass__ = NewDefaultValues
+    msgtype = 11 
+    
+    def hashret(self): 
+        return struct.pack("!I", self.trid)[1:3]
+
+#####################################################################
+# sent between Relay Agents and Servers 
+#
+# Normalement, doit inclure une option "Relay Message Option"
+# peut en inclure d'autres.
+# voir section 7.1 de la 3315
+
+# Relay-Forward Message
+# - sent by relay agents to servers
+# If the relay agent relays messages to the All_DHCP_Servers multicast
+# address or other multicast addresses, it sets the Hop Limit field to
+# 32. 
+
+class DHCP6_RelayForward(_DHCP6GuessPayload,Packet):
+    name = "DHCPv6 Relay Forward Message (Relay Agent/Server Message)"
+    fields_desc = [ ByteEnumField("msgtype", 12, dhcp6types),
+                    ShortField("hopcount", None),
+                    IP6Field("linkaddr", "::"),
+                    IP6Field("peeraddr", "::") ]
+    def hashret(self): # we filter on peer address field
+        return inet_pton(socket.AF_INET6, self.peeraddr)
+
+#####################################################################
+# sent between Relay Agents and Servers 
+# Normalement, doit inclure une option "Relay Message Option"
+# peut en inclure d'autres.
+# Les valeurs des champs hop-count, link-addr et peer-addr
+# sont copiees du messsage Forward associe. POur le suivi de session.
+# Pour le moment, comme decrit dans le commentaire, le hashret
+# se limite au contenu du champ peer address.
+# Voir section 7.2 de la 3315.
+
+# Relay-Reply Message
+# - sent by servers to relay agents
+# - if the solicit message was received in a Relay-Forward message,
+# the server constructs a relay-reply message with the Advertise
+# message in the payload of a relay-message. cf page 37/101. Envoie de
+# ce message en unicast au relay-agent. utilisation de l'adresse ip
+# presente en ip source du paquet recu
+
+class DHCP6_RelayReply(DHCP6_RelayForward):
+    name = "DHCPv6 Relay Reply Message (Relay Agent/Server Message)"
+    __metaclass__= NewDefaultValues
+    msgtype = 13
+    def hashret(self): # We filter on peer address field.
+        return inet_pton(socket.AF_INET6, self.peeraddr)
+    def answers(self, other):
+        return (isinstance(other, DHCP6_RelayForward) and
+                self.count == other.count and
+                self.linkaddr == other.linkaddr and
+                self.peeraddr == other.peeraddr )
+
+
+dhcp6_cls_by_type = {  1: "DHCP6_Solicit",
+                       2: "DHCP6_Advertise",
+                       3: "DHCP6_Request",
+                       4: "DHCP6_Confirm",
+                       5: "DHCP6_Renew",
+                       6: "DHCP6_Rebind",
+                       7: "DHCP6_Reply",
+                       8: "DHCP6_Release",
+                       9: "DHCP6_Decline",
+                      10: "DHCP6_Reconf",
+                      11: "DHCP6_InfoRequest",
+                      12: "DHCP6_RelayForward",
+                      13: "DHCP6_RelayReply" }
+
+def _dhcp6_dispatcher(x, *args, **kargs):
+    cls = Raw
+    if len(x) >= 2:
+        cls = get_cls(dhcp6_cls_by_type.get(ord(x[0]), "Raw"), Raw)
+    return cls(x, *args, **kargs)
+
+bind_bottom_up(UDP, _dhcp6_dispatcher, { "dport": 547 } )
+bind_bottom_up(UDP, _dhcp6_dispatcher, { "dport": 546 } )
+
+
+
+class DHCPv6_am(AnsweringMachine):
+    function_name = "dhcp6d"
+    filter = "udp and port 546 and port 547" 
+    send_function = staticmethod(send)
+    def usage(self):
+        msg = """
+dhcp6d( dns="2001:500::1035", domain="localdomain, local", duid=None)
+        iface=conf.iface6, advpref=255, sntpservers=None, 
+        sipdomains=None, sipservers=None, 
+        nisdomain=None, nisservers=None, 
+        nispdomain=None, nispservers=None,
+        bcmcsdomain=None, bcmcsservers=None)
+
+   debug : When set, additional debugging information is printed. 
+
+   duid   : some DUID class (DUID_LLT, DUID_LL or DUID_EN). If none
+            is provided a DUID_LLT is constructed based on the MAC 
+            address of the sending interface and launch time of dhcp6d 
+            answering machine. 
+  
+   iface : the interface to listen/reply on if you do not want to use 
+           conf.iface6.
+
+   advpref : Value in [0,255] given to Advertise preference field.
+             By default, 255 is used. Be aware that this specific
+             value makes clients stops waiting for further Advertise
+             messages from other servers.
+
+   dns : list of recursive DNS servers addresses (as a string or list). 
+         By default, it is set empty and the associated DHCP6OptDNSServers
+         option is inactive. See RFC 3646 for details.
+   domain : a list of DNS search domain (as a string or list). By default, 
+         it is empty and the associated DHCP6OptDomains option is inactive.
+         See RFC 3646 for details.
+
+   sntpservers : a list of SNTP servers IPv6 addresses. By default,
+         it is empty and the associated DHCP6OptSNTPServers option 
+         is inactive. 
+
+   sipdomains : a list of SIP domains. By default, it is empty and the
+         associated DHCP6OptSIPDomains option is inactive. See RFC 3319
+         for details.
+   sipservers : a list of SIP servers IPv6 addresses. By default, it is 
+         empty and the associated DHCP6OptSIPDomains option is inactive. 
+         See RFC 3319 for details.
+
+   nisdomain : a list of NIS domains. By default, it is empty and the
+         associated DHCP6OptNISDomains option is inactive. See RFC 3898
+         for details. See RFC 3646 for details.
+   nisservers : a list of NIS servers IPv6 addresses. By default, it is 
+         empty and the associated DHCP6OptNISServers option is inactive.
+         See RFC 3646 for details.
+
+   nispdomain : a list of NIS+ domains. By default, it is empty and the
+         associated DHCP6OptNISPDomains option is inactive. See RFC 3898
+         for details.
+   nispservers : a list of NIS+ servers IPv6 addresses. By default, it is 
+         empty and the associated DHCP6OptNISServers option is inactive.
+         See RFC 3898 for details.
+
+   bcmcsdomain : a list of BCMCS domains. By default, it is empty and the
+         associated DHCP6OptBCMCSDomains option is inactive. See RFC 4280
+         for details.
+   bcmcsservers : a list of BCMCS servers IPv6 addresses. By default, it is 
+         empty and the associated DHCP6OptBCMCSServers option is inactive.
+         See RFC 4280 for details.
+
+   If you have a need for others, just ask ... or provide a patch."""
+        print msg
+
+    def parse_options(self, dns="2001:500::1035", domain="localdomain, local",
+                      startip="2001:db8::1", endip="2001:db8::20", duid=None,
+                      sntpservers=None, sipdomains=None, sipservers=None, 
+                      nisdomain=None, nisservers=None, nispdomain=None,
+                      nispservers=None, bcmcsservers=None, bcmcsdomains=None,
+                      iface=None, debug=0, advpref=255):
+        def norm_list(val, param_name):
+            if val is None:
+                return None
+            if type(val) is list:
+                return val
+            elif type(val) is str:
+                l = val.split(',')
+                return map(lambda x: x.strip(), l)
+            else:
+                print "Bad '%s' parameter provided." % param_name
+                self.usage()
+                return -1
+
+        if iface is None:
+            iface = conf.iface6
+        
+        self.debug = debug
+
+        # Dictionary of provided DHCPv6 options, keyed by option type
+        self.dhcpv6_options={}
+
+        for o in [(dns, "dns", 23, lambda x: DHCP6OptDNSServers(dnsservers=x)), 
+                  (domain, "domain", 24, lambda x: DHCP6OptDNSDomains(dnsdomains=x)), 
+                  (sntpservers, "sntpservers", 31, lambda x: DHCP6OptSNTPServers(sntpservers=x)),
+                  (sipservers, "sipservers", 22, lambda x: DHCP6OptSIPServers(sipservers=x)),
+                  (sipdomains, "sipdomains", 21, lambda x: DHCP6OptSIPDomains(sipdomains=x)),
+                  (nisservers, "nisservers", 27, lambda x: DHCP6OptNISServers(nisservers=x)),
+                  (nisdomain, "nisdomain", 29, lambda x: DHCP6OptNISDomain(nisdomain=(x+[""])[0])),
+                  (nispservers, "nispservers", 28, lambda x: DHCP6OptNISPServers(nispservers=x)), 
+                  (nispdomain, "nispdomain", 30, lambda x: DHCP6OptNISPDomain(nispdomain=(x+[""])[0])),
+                  (bcmcsservers, "bcmcsservers", 33, lambda x: DHCP6OptBCMCSServers(bcmcsservers=x)),
+                  (bcmcsdomains, "bcmcsdomains", 34, lambda x: DHCP6OptBCMCSDomains(bcmcsdomains=x))]:
+
+            opt = norm_list(o[0], o[1])
+            if opt == -1: # Usage() was triggered
+                return False
+            elif opt is None: # We won't return that option
+                pass
+            else:
+                self.dhcpv6_options[o[2]] = o[3](opt)
+
+        if self.debug:
+            print "\n[+] List of active DHCPv6 options:"
+            opts = self.dhcpv6_options.keys()
+            opts.sort()
+            for i in opts:
+                print "    %d: %s" % (i, repr(self.dhcpv6_options[i]))
+
+        # Preference value used in Advertise. 
+        self.advpref = advpref
+
+        # IP Pool
+        self.startip = startip
+        self.endip   = endip
+        # XXX TODO Check IPs are in same subnet
+
+        ####
+        # The interface we are listening/replying on
+        self.iface = iface
+
+        ####        
+        # Generate a server DUID
+        if duid is not None:
+            self.duid = duid
+        else:
+            # Timeval
+            from time import gmtime, strftime, mktime
+            epoch = (2000, 1, 1, 0, 0, 0, 5, 1, 0)
+            delta = mktime(epoch) - mktime(gmtime(0))
+            timeval = time.time() - delta
+
+            # Mac Address
+            rawmac = get_if_raw_hwaddr(iface)[1]
+            mac = ":".join(map(lambda x: "%.02x" % ord(x), list(rawmac)))
+
+            self.duid = DUID_LLT(timeval = timeval, lladdr = mac)
+            
+        if self.debug:
+            print "\n[+] Our server DUID:" 
+            self.duid.show(label_lvl=" "*4)
+
+        ####
+        # Find the source address we will use
+        l = filter(lambda x: x[2] == iface and in6_islladdr(x[0]), 
+                   in6_getifaddr())
+        if not l:
+            warning("Unable to get a Link-Local address")
+            return 
+        
+        self.src_addr = l[0][0]
+
+        ####
+        # Our leases
+        self.leases = {}
+        
+
+        if self.debug:
+            print "\n[+] Starting DHCPv6 service on %s:" % self.iface 
+
+    def is_request(self, p):
+        if not IPv6 in p:
+            return False
+
+        src = p[IPv6].src
+        dst = p[IPv6].dst
+
+        p = p[IPv6].payload 
+        if not isinstance(p, UDP) or p.sport != 546 or p.dport != 547 :
+            return False
+
+        p = p.payload
+        if not isinstance(p, DHCP6):
+            return False
+
+        # Message we considered client messages :
+        # Solicit (1), Request (3), Confirm (4), Renew (5), Rebind (6)
+        # Decline (9), Release (8), Information-request (11),
+        if not (p.msgtype in [1, 3, 4, 5, 6, 8, 9, 11]):
+            return False
+
+        # Message validation following section 15 of RFC 3315
+
+        if ((p.msgtype == 1) or # Solicit 
+            (p.msgtype == 6) or # Rebind
+            (p.msgtype == 4)):  # Confirm
+            if ((not DHCP6OptClientId in p) or
+                DHCP6OptServerId in p):
+                return False
+
+            if (p.msgtype == 6 or # Rebind
+                p.msgtype == 4):  # Confirm   
+                # XXX We do not reply to Confirm or Rebind as we 
+                # XXX do not support address assignment            
+                return False
+
+        elif (p.msgtype == 3 or # Request
+              p.msgtype == 5 or # Renew
+              p.msgtype == 8):  # Release
+        
+            # Both options must be present
+            if ((not DHCP6OptServerId in p) or
+                (not DHCP6OptClientId in p)):
+                return False
+            # provided server DUID must match ours
+            duid = p[DHCP6OptServerId].duid
+            if (type(duid) != type(self.duid)):
+                return False
+            if str(duid) != str(self.duid):
+                return False
+
+            if (p.msgtype == 5 or # Renew
+                p.msgtype == 8):  # Release
+                # XXX We do not reply to Renew or Release as we 
+                # XXX do not support address assignment            
+                return False
+
+        elif p.msgtype == 9: # Decline
+            # XXX We should check if we are tracking that client
+            if not self.debug:
+                return False
+
+            bo = Color.bold
+            g = Color.green + bo
+            b = Color.blue + bo
+            n = Color.normal
+            r = Color.red
+
+            vendor  = in6_addrtovendor(src)
+            if (vendor and vendor != "UNKNOWN"):
+                vendor = " [" + b + vendor + n + "]"
+            else:
+                vendor = ""
+            src  = bo + src + n
+
+            it = p
+            addrs = []
+            while it:
+                l = []
+                if isinstance(it, DHCP6OptIA_NA):
+                    l = it.ianaopts
+                elif isinstance(it, DHCP6OptIA_TA):
+                    l = it.iataopts
+
+                opsaddr = filter(lambda x: isinstance(x, DHCP6OptIAAddress),l)
+                a=map(lambda x: x.addr,  opsaddr)
+                addrs += a
+                it = it.payload
+                    
+            addrs = map(lambda x: bo + x + n, addrs)
+            if debug:
+                msg = r + "[DEBUG]" + n + " Received " + g + "Decline" + n 
+                msg += " from " + bo + src + vendor + " for "
+                msg += ", ".join(addrs)+ n
+                print msg
+
+            # See sect 18.1.7
+
+            # Sent by a client to warn us she has determined
+            # one or more addresses assigned to her is already
+            # used on the link.
+            # We should simply log that fact. No messaged should
+            # be sent in return.
+
+            # - Message must include a Server identifier option
+            # - the content of the Server identifier option must 
+            #   match the server's identifier
+            # - the message must include a Client Identifier option
+            return False
+
+        elif p.msgtype == 11: # Information-Request
+            if DHCP6OptServerId in p:
+                duid = p[DHCP6OptServerId].duid
+                if (type(duid) != type(self.duid)):
+                    return False
+                if str(duid) != str(self.duid):
+                    return False
+            if ((DHCP6OptIA_NA in p) or 
+                (DHCP6OptIA_TA in p) or
+                (DHCP6OptIA_PD in p)):
+                    return False
+        else:
+            return False
+
+        return True
+
+    def print_reply(self, req, reply):
+        def norm(s):
+            if s.startswith("DHCPv6 "):
+                s = s[7:]
+            if s.endswith(" Message"):
+                s = s[:-8]
+            return s
+        
+        if reply is None:
+            return
+
+        bo = Color.bold
+        g = Color.green + bo
+        b = Color.blue + bo
+        n = Color.normal
+        reqtype = g + norm(req.getlayer(UDP).payload.name) + n
+        reqsrc  = req.getlayer(IPv6).src
+        vendor  = in6_addrtovendor(reqsrc)
+        if (vendor and vendor != "UNKNOWN"):
+            vendor = " [" + b + vendor + n + "]"
+        else:
+            vendor = ""
+        reqsrc  = bo + reqsrc + n
+        reptype = g + norm(reply.getlayer(UDP).payload.name) + n
+
+        print "Sent %s answering to %s from %s%s" % (reptype, reqtype, reqsrc, vendor)
+
+    def make_reply(self, req):
+        req_mac_src = req.src
+        req_mac_dst = req.dst
+
+        p = req[IPv6]
+        req_src = p.src
+        req_dst = p.dst
+
+        p = p.payload.payload
+
+        msgtype = p.msgtype
+        trid = p.trid
+
+        if msgtype == 1: # SOLICIT (See Sect 17.1 and 17.2 of RFC 3315)
+            
+            # XXX We don't support address or prefix assignment
+            # XXX We also do not support relay function           --arno
+
+            client_duid = p[DHCP6OptClientId].duid
+            resp  = IPv6(src=self.src_addr, dst=req_src)
+            resp /= UDP(sport=547, dport=546)
+            
+            if p.haslayer(DHCP6OptRapidCommit):
+                # construct a Reply packet 
+                resp /= DHCP6_Reply(trid=trid)
+                resp /= DHCP6OptRapidCommit() # See 17.1.2
+                resp /= DHCP6OptServerId(duid = self.duid)
+                resp /= DHCP6OptClientId(duid = client_duid)
+                
+            else: # No Rapid Commit in the packet. Reply with an Advertise                
+                
+                if (p.haslayer(DHCP6OptIA_NA) or
+                    p.haslayer(DHCP6OptIA_TA)):
+                    # XXX We don't assign addresses at the moment
+                    msg = "Scapy6 dhcp6d does not support address assignment"
+                    resp /= DHCP6_Advertise(trid = trid)
+                    resp /= DHCP6OptStatusCode(statuscode=2, statusmsg=msg)
+                    resp /= DHCP6OptServerId(duid = self.duid)
+                    resp /= DHCP6OptClientId(duid = client_duid)                  
+
+                elif p.haslayer(DHCP6OptIA_PD):
+                    # XXX We don't assign prefixes at the moment
+                    msg = "Scapy6 dhcp6d does not support prefix assignment"
+                    resp /= DHCP6_Advertise(trid = trid)
+                    resp /= DHCP6OptStatusCode(statuscode=6, statusmsg=msg)
+                    resp /= DHCP6OptServerId(duid = self.duid)
+                    resp /= DHCP6OptClientId(duid = client_duid)                  
+
+                else: # Usual case, no request for prefixes or addresse
+                    resp /= DHCP6_Advertise(trid = trid)
+                    resp /= DHCP6OptPref(prefval = self.advpref)
+                    resp /= DHCP6OptServerId(duid = self.duid)
+                    resp /= DHCP6OptClientId(duid = client_duid)
+                    resp /= DHCP6OptReconfAccept()
+                    
+                    # See which options should be included
+                    reqopts = []
+                    if p.haslayer(DHCP6OptOptReq): # add only asked ones
+                        reqopts = p[DHCP6OptOptReq].reqopts
+                        for o in self.dhcpv6_options.keys():
+                            if o in reqopts:
+                                resp /= self.dhcpv6_options[o]
+                    else: # advertise everything we have available
+                        for o in self.dhcpv6_options.keys():
+                            resp /= self.dhcpv6_options[o]                    
+
+            return resp
+
+        elif msgtype == 3: #REQUEST (INFO-REQUEST is further below)
+            client_duid = p[DHCP6OptClientId].duid
+            resp  = IPv6(src=self.src_addr, dst=req_src)
+            resp /= UDP(sport=547, dport=546)
+            resp /= DHCP6_Solicit(trid=trid)
+            resp /= DHCP6OptServerId(duid = self.duid)
+            resp /= DHCP6OptClientId(duid = client_duid)
+
+            # See which options should be included
+            reqopts = []
+            if p.haslayer(DHCP6OptOptReq): # add only asked ones
+                reqopts = p[DHCP6OptOptReq].reqopts
+                for o in self.dhcpv6_options.keys():
+                    if o in reqopts:
+                        resp /= self.dhcpv6_options[o]
+            else: 
+                # advertise everything we have available.
+                # Should not happen has clients MUST include 
+                # and ORO in requests (sec 18.1.1)   -- arno
+                for o in self.dhcpv6_options.keys():
+                    resp /= self.dhcpv6_options[o]          
+
+            return resp            
+        
+        elif msgtype == 4: # CONFIRM
+            # see Sect 18.1.2
+            
+            # Client want to check if addresses it was assigned
+            # are still appropriate
+
+            # Server must discard any Confirm messages that
+            # do not include a Client Identifier option OR
+            # THAT DO INCLUDE a Server Identifier Option
+
+            # XXX we must discard the SOLICIT if it is received with
+            #     a unicast destination address
+
+            pass
+
+        elif msgtype == 5: # RENEW
+            # see Sect 18.1.3
+            
+            # Clients want to extend lifetime of assigned addresses
+            # and update configuration parameters. This message is sent
+            # specifically to the server that provided her the info
+
+            # - Received message must include a Server Identifier
+            #   option.
+            # - the content of server identifier option must match
+            #   the server's identifier.
+            # - the message must include a Client identifier option
+
+            pass
+        
+        elif msgtype == 6: # REBIND
+            # see Sect 18.1.4
+            
+            # Same purpose as the Renew message but sent to any
+            # available server after he received no response
+            # to its previous Renew message.
+
+            
+            # - Message must include a Client Identifier Option
+            # - Message can't include a Server identifier option
+
+            # XXX we must discard the SOLICIT if it is received with
+            #     a unicast destination address
+
+            pass
+
+        elif msgtype == 8: # RELEASE
+            # See section 18.1.6
+
+            # Message is sent to the server to indicate that 
+            # she will no longer use the addresses that was assigned
+            # We should parse the message and verify our dictionary
+            # to log that fact.
+
+
+            # - The message must include a server identifier option
+            # - The content of the Server Identifier option must
+            #   match the server's identifier
+            # - the message must include a Client Identifier option
+
+            pass
+
+        elif msgtype == 9: # DECLINE
+            # See section 18.1.7            
+            pass
+
+        elif msgtype == 11: # INFO-REQUEST
+            client_duid = None
+            if not p.haslayer(DHCP6OptClientId):
+                if self.debug:
+                    warning("Received Info Request message without Client Id option")
+            else:
+                client_duid = p[DHCP6OptClientId].duid
+
+            resp  = IPv6(src=self.src_addr, dst=req_src)
+            resp /= UDP(sport=547, dport=546)
+            resp /= DHCP6_Reply(trid=trid)
+            resp /= DHCP6OptServerId(duid = self.duid)
+
+            if client_duid:
+                resp /= DHCP6OptClientId(duid = client_duid)
+                
+            # Stack requested options if available
+            reqopts = []
+            if p.haslayer(DHCP6OptOptReq):
+                reqopts = p[DHCP6OptOptReq].reqopts
+            for o in self.dhcpv6_options.keys():
+                resp /= self.dhcpv6_options[o]
+
+            return resp
+
+        else:
+            # what else ?
+            pass
+
+        # - We won't support reemission
+        # - We won't support relay role, nor relay forwarded messages
+        #   at the beginning
diff --git a/scapy/layers/inet6.py b/scapy/layers/inet6.py
index 55d971bf..46a72980 100644
--- a/scapy/layers/inet6.py
+++ b/scapy/layers/inet6.py
@@ -2279,1711 +2279,6 @@ def _niquery_guesser(p):
             cls = ICMPv6NIReplyUnknown
     return cls
 
-#############################################################################
-#############################################################################
-###                                DHCPv6                                 ###
-#############################################################################
-#############################################################################
-
-All_DHCP_Relay_Agents_and_Servers = "ff02::1:2" 
-All_DHCP_Servers = "ff05::1:3"  # Site-Local scope : deprecated by 3879
-
-dhcp6opts = { 1: "CLIENTID",  
-              2: "SERVERID",
-              3: "IA_NA",
-              4: "IA_TA",
-              5: "IAADDR",
-              6: "ORO",
-              7: "PREFERENCE",
-              8: "ELAPSED_TIME",
-              9: "RELAY_MSG",
-             11: "AUTH",
-             12: "UNICAST",
-             13: "STATUS_CODE",
-             14: "RAPID_COMMIT",
-             15: "USER_CLASS",
-             16: "VENDOR_CLASS",
-             17: "VENDOR_OPTS",
-             18: "INTERFACE_ID",
-             19: "RECONF_MSG",
-             20: "RECONF_ACCEPT",
-             21: "SIP Servers Domain Name List",     #RFC3319
-             22: "SIP Servers IPv6 Address List",    #RFC3319
-             23: "DNS Recursive Name Server Option", #RFC3646
-             24: "Domain Search List option",        #RFC3646
-             25: "OPTION_IA_PD",                     #RFC3633
-             26: "OPTION_IAPREFIX",                  #RFC3633
-             27: "OPTION_NIS_SERVERS",               #RFC3898
-             28: "OPTION_NISP_SERVERS",              #RFC3898
-             29: "OPTION_NIS_DOMAIN_NAME",           #RFC3898
-             30: "OPTION_NISP_DOMAIN_NAME",          #RFC3898
-             31: "OPTION_SNTP_SERVERS",              #RFC4075
-             32: "OPTION_INFORMATION_REFRESH_TIME",  #RFC4242
-             33: "OPTION_BCMCS_SERVER_D",            #RFC4280         
-             34: "OPTION_BCMCS_SERVER_A",            #RFC4280
-             36: "OPTION_GEOCONF_CIVIC",             #RFC-ietf-geopriv-dhcp-civil-09.txt
-             37: "OPTION_REMOTE_ID",                 #RFC4649
-             38: "OPTION_SUBSCRIBER_ID",             #RFC4580
-             39: "OPTION_CLIENT_FQDN" }              #RFC4704
-
-dhcp6opts_by_code = {  1: "DHCP6OptClientId", 
-                       2: "DHCP6OptServerId",
-                       3: "DHCP6OptIA_NA",
-                       4: "DHCP6OptIA_TA",
-                       5: "DHCP6OptIAAddress",
-                       6: "DHCP6OptOptReq",
-                       7: "DHCP6OptPref",
-                       8: "DHCP6OptElapsedTime",
-                       9: "DHCP6OptRelayMsg",
-                       11: "DHCP6OptAuth",
-                       12: "DHCP6OptServerUnicast",
-                       13: "DHCP6OptStatusCode",
-                       14: "DHCP6OptRapidCommit",
-                       15: "DHCP6OptUserClass",
-                       16: "DHCP6OptVendorClass",
-                       17: "DHCP6OptVendorSpecificInfo",
-                       18: "DHCP6OptIfaceId",
-                       19: "DHCP6OptReconfMsg",
-                       20: "DHCP6OptReconfAccept",
-                       21: "DHCP6OptSIPDomains",          #RFC3319
-                       22: "DHCP6OptSIPServers",          #RFC3319
-                       23: "DHCP6OptDNSServers",          #RFC3646
-                       24: "DHCP6OptDNSDomains",          #RFC3646
-                       25: "DHCP6OptIA_PD",               #RFC3633
-                       26: "DHCP6OptIAPrefix",            #RFC3633
-                       27: "DHCP6OptNISServers",          #RFC3898
-                       28: "DHCP6OptNISPServers",         #RFC3898
-                       29: "DHCP6OptNISDomain",           #RFC3898
-                       30: "DHCP6OptNISPDomain",          #RFC3898
-                       31: "DHCP6OptSNTPServers",         #RFC4075
-                       32: "DHCP6OptInfoRefreshTime",     #RFC4242
-                       33: "DHCP6OptBCMCSDomains",        #RFC4280         
-                       34: "DHCP6OptBCMCSServers",        #RFC4280
-                       #36: "DHCP6OptGeoConf",            #RFC-ietf-geopriv-dhcp-civil-09.txt
-                       37: "DHCP6OptRemoteID",            #RFC4649
-                       38: "DHCP6OptSubscriberID",        #RFC4580
-                       39: "DHCP6OptClientFQDN",          #RFC4704
-                       #40: "DHCP6OptPANAAgent",          #RFC-ietf-dhc-paa-option-05.txt
-                       #41: "DHCP6OptNewPOSIXTimeZone,    #RFC4833
-                       #42: "DHCP6OptNewTZDBTimeZone,     #RFC4833
-                       43: "DHCP6OptRelayAgentERO"        #RFC4994
-                       #44: "DHCP6OptLQQuery",            #RFC5007
-                       #45: "DHCP6OptLQClientData",       #RFC5007
-                       #46: "DHCP6OptLQClientTime",       #RFC5007
-                       #47: "DHCP6OptLQRelayData",        #RFC5007
-                       #48: "DHCP6OptLQClientLink",       #RFC5007
-}
-
-
-# sect 5.3 RFC 3315 : DHCP6 Messages types
-dhcp6types = {   1:"SOLICIT",
-                 2:"ADVERTISE",
-                 3:"REQUEST",
-                 4:"CONFIRM",
-                 5:"RENEW",
-                 6:"REBIND",
-                 7:"REPLY",
-                 8:"RELEASE",
-                 9:"DECLINE",
-                10:"RECONFIGURE",
-                11:"INFORMATION-REQUEST",
-                12:"RELAY-FORW",
-                13:"RELAY-REPL" }
-
-
-#####################################################################
-###                  DHCPv6 DUID related stuff                    ###
-#####################################################################
-
-duidtypes = { 1: "Link-layer address plus time", 
-              2: "Vendor-assigned unique ID based on Enterprise Number",
-              3: "Link-layer Address" }
-
-# DUID hardware types - RFC 826 - Extracted from 
-# http://www.iana.org/assignments/arp-parameters on 31/10/06
-# We should add the length of every kind of address.
-duidhwtypes = {  0: "NET/ROM pseudo", # Not referenced by IANA
-                 1: "Ethernet (10Mb)",
-                 2: "Experimental Ethernet (3Mb)",
-                 3: "Amateur Radio AX.25",
-                 4: "Proteon ProNET Token Ring",
-                 5: "Chaos",
-                 6: "IEEE 802 Networks",
-                 7: "ARCNET",
-                 8: "Hyperchannel",
-                 9: "Lanstar",
-                10: "Autonet Short Address",
-                11: "LocalTalk",
-                12: "LocalNet (IBM PCNet or SYTEK LocalNET)",
-                13: "Ultra link",
-                14: "SMDS",
-                15: "Frame Relay",
-                16: "Asynchronous Transmission Mode (ATM)",
-                17: "HDLC",
-                18: "Fibre Channel",
-                19: "Asynchronous Transmission Mode (ATM)",
-                20: "Serial Line",
-                21: "Asynchronous Transmission Mode (ATM)",
-                22: "MIL-STD-188-220",
-                23: "Metricom",
-                24: "IEEE 1394.1995",
-                25: "MAPOS",
-                26: "Twinaxial",
-                27: "EUI-64",
-                28: "HIPARP",
-                29: "IP and ARP over ISO 7816-3",
-                30: "ARPSec",
-                31: "IPsec tunnel",
-                32: "InfiniBand (TM)",
-                33: "TIA-102 Project 25 Common Air Interface (CAI)" }
-
-class UTCTimeField(IntField):
-    epoch = (2000, 1, 1, 0, 0, 0, 5, 1, 0) # required Epoch
-    def i2repr(self, pkt, x):
-        x = self.i2h(pkt, x)
-        from time import gmtime, strftime, mktime
-        delta = mktime(self.epoch) - mktime(gmtime(0))
-        x = x + delta
-        t = strftime("%a, %d %b %Y %H:%M:%S +0000", gmtime(x))
-        return "%s (%d)" % (t, x)
-
-class _LLAddrField(MACField):
-    pass
-
-# XXX We only support Ethernet addresses at the moment. _LLAddrField 
-#     will be modified when needed. Ask us. --arno
-class DUID_LLT(Packet):  # sect 9.2 RFC 3315
-    name = "DUID - Link-layer address plus time"
-    fields_desc = [ ShortEnumField("type", 1, duidtypes),
-                    XShortEnumField("hwtype", 1, duidhwtypes), 
-                    UTCTimeField("timeval", 0), # i.e. 01 Jan 2000
-                    _LLAddrField("lladdr", ETHER_ANY) ]
-
-# In fact, IANA enterprise-numbers file available at 
-# http//www.iana.org/asignments/enterprise-numbers)
-# is simply huge (more than 2Mo and 600Ko in bz2). I'll
-# add only most common vendors, and encountered values.
-# -- arno
-iana_enterprise_num = {    9: "ciscoSystems",
-                          35: "Nortel Networks",
-                          43: "3Com",
-                         311: "Microsoft",
-                        2636: "Juniper Networks, Inc.",
-                        4526: "Netgear",
-                        5771: "Cisco Systems, Inc.",
-                        5842: "Cisco Systems",
-                       16885: "Nortel Networks" }
-
-class DUID_EN(Packet):  # sect 9.3 RFC 3315
-    name = "DUID - Assigned by Vendor Based on Enterprise Number"
-    fields_desc = [ ShortEnumField("type", 2, duidtypes),
-                    IntEnumField("enterprisenum", 311, iana_enterprise_num),
-                    StrField("id","") ] 
-
-class DUID_LL(Packet):  # sect 9.4 RFC 3315
-    name = "DUID - Based on Link-layer Address"
-    fields_desc = [ ShortEnumField("type", 3, duidtypes),
-                    XShortEnumField("hwtype", 1, duidhwtypes), 
-                    _LLAddrField("lladdr", ETHER_ANY) ]
-
-duid_cls = { 1: "DUID_LLT",
-             2: "DUID_EN",
-             3: "DUID_LL"}
-
-#####################################################################
-###                   DHCPv6 Options classes                      ###
-#####################################################################
-
-class _DHCP6OptGuessPayload(Packet):
-    def guess_payload_class(self, payload):
-        cls = Raw
-        if len(payload) > 2 :
-            opt = struct.unpack("!H", payload[:2])[0]
-            cls = get_cls(dhcp6opts_by_code.get(opt, "DHCP6OptUnknown"), DHCP6OptUnknown)
-        return cls
-
-class DHCP6OptUnknown(_DHCP6OptGuessPayload): # A generic DHCPv6 Option
-    name = "Unknown DHCPv6 OPtion"
-    fields_desc = [ ShortEnumField("optcode", 0, dhcp6opts), 
-                    FieldLenField("optlen", None, length_of="data", fmt="!H"),
-                    StrLenField("data", "",
-                                length_from = lambda pkt: pkt.optlen)]
-
-class _DUIDField(PacketField):
-    holds_packets=1
-    def __init__(self, name, default, length_from=None):
-        StrField.__init__(self, name, default)
-        self.length_from = length_from
-
-    def i2m(self, pkt, i):
-        return str(i)
-
-    def m2i(self, pkt, x):
-        cls = Raw 
-        if len(x) > 4:
-            o = struct.unpack("!H", x[:2])[0]
-            cls = get_cls(duid_cls.get(o, Raw), "Raw")
-        return cls(x)
-
-    def getfield(self, pkt, s):
-        l = self.length_from(pkt)
-        return s[l:], self.m2i(pkt,s[:l])
- 
-
-class DHCP6OptClientId(_DHCP6OptGuessPayload):     # RFC sect 22.2
-    name = "DHCP6 Client Identifier Option"
-    fields_desc = [ ShortEnumField("optcode", 1, dhcp6opts), 
-                    FieldLenField("optlen", None, length_of="duid", fmt="!H"),
-                    _DUIDField("duid", "",
-                               length_from = lambda pkt: pkt.optlen) ]
-
-
-class DHCP6OptServerId(DHCP6OptClientId):     # RFC sect 22.3
-    name = "DHCP6 Server Identifier Option"
-    __metaclass__ = NewDefaultValues
-    optcode = 2
-
-# Should be encapsulated in the option field of IA_NA or IA_TA options
-# Can only appear at that location.
-# TODO : last field IAaddr-options is not defined in the reference document
-class DHCP6OptIAAddress(_DHCP6OptGuessPayload):    # RFC sect 22.6
-    name = "DHCP6 IA Address Option (IA_TA or IA_NA suboption)"
-    fields_desc = [ ShortEnumField("optcode", 5, dhcp6opts), 
-                    FieldLenField("optlen", None, length_of="iaaddropts",
-                                  fmt="!H", adjust = lambda pkt,x: x+24),
-                    IP6Field("addr", "::"),
-                    IntField("preflft", 0),
-                    IntField("validlft", 0),
-                    XIntField("iaid", None),
-                    StrLenField("iaaddropts", "",
-                                length_from  = lambda pkt: pkt.optlen - 24) ]
-    def guess_payload_class(self, payload):
-        return Padding
-
-class _IANAOptField(PacketListField):
-    def i2len(self, pkt, z):
-        if z is None or z == []:
-            return 0
-        return sum(map(lambda x: len(str(x)) ,z))
-
-    def getfield(self, pkt, s):
-        l = self.length_from(pkt)
-        lst = []
-        remain, payl = s[:l], s[l:]
-        while len(remain)>0:
-            p = self.m2i(pkt,remain)
-            if Padding in p:
-                pad = p[Padding]
-                remain = pad.load
-                del(pad.underlayer.payload)
-            else:
-                remain = ""
-            lst.append(p)
-        return payl,lst
-
-class DHCP6OptIA_NA(_DHCP6OptGuessPayload):         # RFC sect 22.4
-    name = "DHCP6 Identity Association for Non-temporary Addresses Option"
-    fields_desc = [ ShortEnumField("optcode", 3, dhcp6opts), 
-                    FieldLenField("optlen", None, length_of="ianaopts",
-                                  fmt="!H", adjust = lambda pkt,x: x+12),
-                    XIntField("iaid", None),
-                    IntField("T1", None),
-                    IntField("T2", None),
-                    _IANAOptField("ianaopts", [], DHCP6OptIAAddress,
-                                  length_from = lambda pkt: pkt.optlen-12) ]
-
-class _IATAOptField(_IANAOptField):
-    pass
-
-class DHCP6OptIA_TA(_DHCP6OptGuessPayload):         # RFC sect 22.5
-    name = "DHCP6 Identity Association for Temporary Addresses Option"
-    fields_desc = [ ShortEnumField("optcode", 4, dhcp6opts), 
-                    FieldLenField("optlen", None, length_of="iataopts",
-                                  fmt="!H", adjust = lambda pkt,x: x+4),
-                    XIntField("iaid", None),
-                    _IATAOptField("iataopts", [], DHCP6OptIAAddress,
-                                  length_from = lambda pkt: pkt.optlen-4) ]
-
-
-#### DHCPv6 Option Request Option ###################################
-
-class _OptReqListField(StrLenField):
-    islist = 1
-    def i2h(self, pkt, x):
-        if x is None:
-            return []
-        return x
-
-    def i2len(self, pkt, x):
-        return 2*len(x)
-
-    def any2i(self, pkt, x):
-        return x
-
-    def i2repr(self, pkt, x):
-        s = []
-        for y in self.i2h(pkt, x):
-            if dhcp6opts.has_key(y):
-                s.append(dhcp6opts[y])
-            else:
-                s.append("%d" % y)
-        return "[%s]" % ", ".join(s) 
-
-    def m2i(self, pkt, x):
-        r = []
-        while len(x) != 0:
-            if len(x)<2:
-                warning("Odd length for requested option field. Rejecting last byte")
-                return r
-            r.append(struct.unpack("!H", x[:2])[0])
-            x = x[2:]
-        return r
-    
-    def i2m(self, pkt, x):
-        return "".join(map(lambda y: struct.pack("!H", y), x))
-
-# A client may include an ORO in a solicit, Request, Renew, Rebind,
-# Confirm or Information-request
-class DHCP6OptOptReq(_DHCP6OptGuessPayload):       # RFC sect 22.7
-    name = "DHCP6 Option Request Option"
-    fields_desc = [ ShortEnumField("optcode", 6, dhcp6opts),
-                    FieldLenField("optlen", None, length_of="reqopts", fmt="!H"),
-                    _OptReqListField("reqopts", [23, 24],
-                                     length_from = lambda pkt: pkt.optlen) ]
-
-
-#### DHCPv6 Preference Option #######################################
-
-# emise par un serveur pour affecter le choix fait par le client. Dans
-# les messages Advertise, a priori
-class DHCP6OptPref(_DHCP6OptGuessPayload):       # RFC sect 22.8
-    name = "DHCP6 Preference Option"
-    fields_desc = [ ShortEnumField("optcode", 7, dhcp6opts), 
-                    ShortField("optlen", 1 ),
-                    ByteField("prefval",255) ]
-
-
-#### DHCPv6 Elapsed Time Option #####################################
-
-class _ElapsedTimeField(ShortField):
-    def i2repr(self, pkt, x):
-        if x == 0xffff:
-            return "infinity (0xffff)"
-        return "%.2f sec" % (self.i2h(pkt, x)/100.)
-
-class DHCP6OptElapsedTime(_DHCP6OptGuessPayload):# RFC sect 22.9
-    name = "DHCP6 Elapsed Time Option"
-    fields_desc = [ ShortEnumField("optcode", 8, dhcp6opts), 
-                    ShortField("optlen", 2),
-                    _ElapsedTimeField("elapsedtime", 0) ]
-
-
-#### DHCPv6 Relay Message Option ####################################
-
-# Relayed message is seen as a payload.
-class DHCP6OptRelayMsg(_DHCP6OptGuessPayload):# RFC sect 22.10
-    name = "DHCP6 Relay Message Option"
-    fields_desc = [ ShortEnumField("optcode", 9, dhcp6opts), 
-                    ShortField("optlen", None ) ]
-    def post_build(self, p, pay):
-        if self.optlen is None:
-            l = len(pay) 
-            p = p[:2]+struct.pack("!H", l)
-        return p + pay
-
-
-#### DHCPv6 Authentication Option ###################################
-
-#    The following fields are set in an Authentication option for the
-#    Reconfigure Key Authentication Protocol:
-#
-#       protocol    3
-#
-#       algorithm   1
-#
-#       RDM         0
-#
-#    The format of the Authentication information for the Reconfigure Key
-#    Authentication Protocol is:
-#
-#      0                   1                   2                   3
-#      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
-#     +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
-#     |     Type      |                 Value (128 bits)              |
-#     +-+-+-+-+-+-+-+-+                                               |
-#     .                                                               .
-#     .                                                               .
-#     .                                               +-+-+-+-+-+-+-+-+
-#     |                                               |
-#     +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
-#
-#       Type    Type of data in Value field carried in this option:
-#
-#                  1   Reconfigure Key value (used in Reply message).
-#
-#                  2   HMAC-MD5 digest of the message (used in Reconfigure
-#                      message).
-#
-#       Value   Data as defined by field.
-
-
-# TODO : Decoding only at the moment
-class DHCP6OptAuth(_DHCP6OptGuessPayload):    # RFC sect 22.11
-    name = "DHCP6 Option - Authentication"
-    fields_desc = [ ShortEnumField("optcode", 11, dhcp6opts), 
-                    FieldLenField("optlen", None, length_of="authinfo",
-                                  adjust = lambda pkt,x: x+11),
-                    ByteField("proto", 3), # TODO : XXX
-                    ByteField("alg", 1), # TODO : XXX
-                    ByteField("rdm", 0), # TODO : XXX
-                    StrFixedLenField("replay", "A"*8, 8), # TODO: XXX
-                    StrLenField("authinfo", "",
-                                length_from = lambda pkt: pkt.optlen - 11) ]
-
-#### DHCPv6 Server Unicast Option ###################################
-
-class _SrvAddrField(IP6Field):
-    def i2h(self, pkt, x):
-        if x is None:
-            return "::"
-        return x
-    
-    def i2m(self, pkt, x):
-        return inet_pton(socket.AF_INET6, self.i2h(pkt,x))
-
-class DHCP6OptServerUnicast(_DHCP6OptGuessPayload):# RFC sect 22.12
-    name = "DHCP6 Server Unicast Option"
-    fields_desc = [ ShortEnumField("optcode", 12, dhcp6opts), 
-                    ShortField("optlen", 16 ),
-                    _SrvAddrField("srvaddr",None) ]
-
-
-#### DHCPv6 Status Code Option ######################################
-
-dhcp6statuscodes = { 0:"Success",      # sect 24.4
-                     1:"UnspecFail",
-                     2:"NoAddrsAvail",
-                     3:"NoBinding",
-                     4:"NotOnLink",
-                     5:"UseMulticast",
-                     6:"NoPrefixAvail"} # From RFC3633
-
-class DHCP6OptStatusCode(_DHCP6OptGuessPayload):# RFC sect 22.13
-    name = "DHCP6 Status Code Option"
-    fields_desc = [ ShortEnumField("optcode", 13, dhcp6opts), 
-                    FieldLenField("optlen", None, length_of="statusmsg",
-                                  fmt="!H", adjust = lambda pkt,x:x+2),
-                    ShortEnumField("statuscode",None,dhcp6statuscodes),
-                    StrLenField("statusmsg", "",
-                                length_from = lambda pkt: pkt.optlen-2) ]
-
-
-#### DHCPv6 Rapid Commit Option #####################################
-
-class DHCP6OptRapidCommit(_DHCP6OptGuessPayload):   # RFC sect 22.14
-    name = "DHCP6 Rapid Commit Option"
-    fields_desc = [ ShortEnumField("optcode", 14, dhcp6opts),
-                    ShortField("optlen", 0)]
-
-
-#### DHCPv6 User Class Option #######################################
-
-class _UserClassDataField(PacketListField):
-    def i2len(self, pkt, z):
-        if z is None or z == []:
-            return 0
-        return sum(map(lambda x: len(str(x)) ,z))
-
-    def getfield(self, pkt, s):
-        l = self.length_from(pkt)
-        lst = []
-        remain, payl = s[:l], s[l:]
-        while len(remain)>0:
-            p = self.m2i(pkt,remain)
-            if Padding in p:
-                pad = p[Padding]
-                remain = pad.load
-                del(pad.underlayer.payload)
-            else:
-                remain = ""
-            lst.append(p)
-        return payl,lst
-
-
-class USER_CLASS_DATA(Packet):
-    name = "user class data"
-    fields_desc = [ FieldLenField("len", None, length_of="data"),
-                    StrLenField("data", "",
-                                length_from = lambda pkt: pkt.len) ]
-    def guess_payload_class(self, payload):
-        return Padding
-
-class DHCP6OptUserClass(_DHCP6OptGuessPayload):# RFC sect 22.15
-    name = "DHCP6 User Class Option"
-    fields_desc = [ ShortEnumField("optcode", 15, dhcp6opts), 
-                    FieldLenField("optlen", None, fmt="!H",
-                                  length_of="userclassdata"),
-                    _UserClassDataField("userclassdata", [], USER_CLASS_DATA,
-                                        length_from = lambda pkt: pkt.optlen) ]
-
-
-#### DHCPv6 Vendor Class Option #####################################
-
-class _VendorClassDataField(_UserClassDataField):
-    pass
-
-class VENDOR_CLASS_DATA(USER_CLASS_DATA):
-    name = "vendor class data"
-
-class DHCP6OptVendorClass(_DHCP6OptGuessPayload):# RFC sect 22.16
-    name = "DHCP6 Vendor Class Option"
-    fields_desc = [ ShortEnumField("optcode", 16, dhcp6opts), 
-                    FieldLenField("optlen", None, length_of="vcdata", fmt="!H",
-                                  adjust = lambda pkt,x: x+4),
-                    IntEnumField("enterprisenum",None , iana_enterprise_num ),
-                    _VendorClassDataField("vcdata", [], VENDOR_CLASS_DATA,
-                                          length_from = lambda pkt: pkt.optlen-4) ]
-
-#### DHCPv6 Vendor-Specific Information Option ######################
-
-class VENDOR_SPECIFIC_OPTION(_DHCP6OptGuessPayload):
-    name = "vendor specific option data"
-    fields_desc = [ ShortField("optcode", None),
-                    FieldLenField("optlen", None, length_of="optdata"),
-                    StrLenField("optdata", "",
-                                length_from = lambda pkt: pkt.optlen) ]
-    def guess_payload_class(self, payload):
-        return Padding
-
-# The third one that will be used for nothing interesting
-class DHCP6OptVendorSpecificInfo(_DHCP6OptGuessPayload):# RFC sect 22.17
-    name = "DHCP6 Vendor-specific Information Option"
-    fields_desc = [ ShortEnumField("optcode", 17, dhcp6opts), 
-                    FieldLenField("optlen", None, length_of="vso", fmt="!H",
-                                  adjust = lambda pkt,x: x+4),
-                    IntEnumField("enterprisenum",None , iana_enterprise_num),
-                    _VendorClassDataField("vso", [], VENDOR_SPECIFIC_OPTION,
-                                          length_from = lambda pkt: pkt.optlen-4) ]
-
-#### DHCPv6 Interface-ID Option #####################################
-
-# Repasser sur cette option a la fin. Elle a pas l'air d'etre des
-# masses critique.
-class DHCP6OptIfaceId(_DHCP6OptGuessPayload):# RFC sect 22.18
-    name = "DHCP6 Interface-Id Option"
-    fields_desc = [ ShortEnumField("optcode", 18, dhcp6opts),
-                    FieldLenField("optlen", None, fmt="!H",
-                                  length_of="ifaceid"),
-                    StrLenField("ifaceid", "",
-                                length_from = lambda pkt: pkt.optlen) ]
-
-
-#### DHCPv6 Reconfigure Message Option ##############################
-
-# A server includes a Reconfigure Message option in a Reconfigure
-# message to indicate to the client whether the client responds with a
-# renew message or an Informatiion-request message.
-class DHCP6OptReconfMsg(_DHCP6OptGuessPayload):       # RFC sect 22.19
-    name = "DHCP6 Reconfigure Message Option"
-    fields_desc = [ ShortEnumField("optcode", 19, dhcp6opts), 
-                    ShortField("optlen", 1 ),
-                    ByteEnumField("msgtype", 11, {  5:"Renew Message", 
-                                                   11:"Information Request"}) ]
-
-
-#### DHCPv6 Reconfigure Accept Option ###############################
-
-# A client uses the Reconfigure Accept option to announce to the
-# server whether the client is willing to accept Recoonfigure
-# messages, and a server uses this option to tell the client whether
-# or not to accept Reconfigure messages. The default behavior in the
-# absence of this option, means unwillingness to accept reconfigure
-# messages, or instruction not to accept Reconfigure messages, for the
-# client and server messages, respectively.
-class DHCP6OptReconfAccept(_DHCP6OptGuessPayload):   # RFC sect 22.20
-    name = "DHCP6 Reconfigure Accept Option"
-    fields_desc = [ ShortEnumField("optcode", 20, dhcp6opts),
-                    ShortField("optlen", 0)]
-
-# As required in Sect 8. of RFC 3315, Domain Names must be encoded as 
-# described in section 3.1 of RFC 1035
-# XXX Label should be at most 63 octets in length : we do not enforce it
-#     Total length of domain should be 255 : we do not enforce it either
-class DomainNameListField(StrLenField):
-    islist = 1
-
-    def i2len(self, pkt, x):
-        return len(self.i2m(pkt, x))
-
-    def m2i(self, pkt, x):
-        res = []
-        while x:
-            cur = []
-            while x and x[0] != '\x00':
-                l = ord(x[0])
-                cur.append(x[1:l+1])
-                x = x[l+1:]
-            res.append(".".join(cur))
-            if x and x[0] == '\x00':
-                x = x[1:]
-        return res
-
-    def i2m(self, pkt, x):
-        def conditionalTrailingDot(z):
-            if z and z[-1] == '\x00':
-                return z
-            return z+'\x00'
-        res = ""
-        tmp = map(lambda y: map((lambda z: chr(len(z))+z), y.split('.')), x)
-        return "".join(map(lambda x: conditionalTrailingDot("".join(x)), tmp))
-
-class DHCP6OptSIPDomains(_DHCP6OptGuessPayload):       #RFC3319
-    name = "DHCP6 Option - SIP Servers Domain Name List"
-    fields_desc = [ ShortEnumField("optcode", 21, dhcp6opts),
-                    FieldLenField("optlen", None, length_of="sipdomains"),
-                    DomainNameListField("sipdomains", [],
-                                        length_from = lambda pkt: pkt.optlen) ]
-
-class DHCP6OptSIPServers(_DHCP6OptGuessPayload):          #RFC3319
-    name = "DHCP6 Option - SIP Servers IPv6 Address List"
-    fields_desc = [ ShortEnumField("optcode", 22, dhcp6opts),
-                    FieldLenField("optlen", None, length_of="sipservers"),
-                    IP6ListField("sipservers", [], 
-                                 length_from = lambda pkt: pkt.optlen) ]
-
-class DHCP6OptDNSServers(_DHCP6OptGuessPayload):          #RFC3646
-    name = "DHCP6 Option - DNS Recursive Name Server"
-    fields_desc = [ ShortEnumField("optcode", 23, dhcp6opts),
-                    FieldLenField("optlen", None, length_of="dnsservers"),
-                    IP6ListField("dnsservers", [],
-                                 length_from = lambda pkt: pkt.optlen) ]
-
-class DHCP6OptDNSDomains(_DHCP6OptGuessPayload): #RFC3646
-    name = "DHCP6 Option - Domain Search List option"
-    fields_desc = [ ShortEnumField("optcode", 24, dhcp6opts),
-                    FieldLenField("optlen", None, length_of="dnsdomains"),
-                    DomainNameListField("dnsdomains", [],
-                                        length_from = lambda pkt: pkt.optlen) ]
-
-# TODO: Implement iaprefopts correctly when provided with more 
-#       information about it.
-class DHCP6OptIAPrefix(_DHCP6OptGuessPayload):                    #RFC3633
-    name = "DHCP6 Option - IA_PD Prefix option"
-    fields_desc = [ ShortEnumField("optcode", 26, dhcp6opts),
-                    FieldLenField("optlen", None, length_of="iaprefopts",
-                                  adjust = lambda pkt,x: x+26),
-                    IntField("preflft", 0),
-                    IntField("validlft", 0),
-                    ByteField("plen", 48),  # TODO: Challenge that default value
-                    IP6Field("prefix", "2001:db8::"), # At least, global and won't hurt
-                    StrLenField("iaprefopts", "",
-                                length_from = lambda pkt: pkt.optlen-26) ]
-
-class DHCP6OptIA_PD(_DHCP6OptGuessPayload):                       #RFC3633
-    name = "DHCP6 Option - Identity Association for Prefix Delegation"
-    fields_desc = [ ShortEnumField("optcode", 25, dhcp6opts),
-                    FieldLenField("optlen", None, length_of="iapdopt",
-                                  adjust = lambda pkt,x: x+12),
-                    IntField("iaid", 0),
-                    IntField("T1", 0),
-                    IntField("T2", 0),
-                    PacketListField("iapdopt", [], DHCP6OptIAPrefix,
-                                    length_from = lambda pkt: pkt.optlen-12) ]
-
-class DHCP6OptNISServers(_DHCP6OptGuessPayload):                 #RFC3898
-    name = "DHCP6 Option - NIS Servers"
-    fields_desc = [ ShortEnumField("optcode", 27, dhcp6opts),
-                    FieldLenField("optlen", None, length_of="nisservers"),
-                    IP6ListField("nisservers", [],
-                                 length_from = lambda pkt: pkt.optlen) ]
-
-class DHCP6OptNISPServers(_DHCP6OptGuessPayload):                #RFC3898
-    name = "DHCP6 Option - NIS+ Servers"
-    fields_desc = [ ShortEnumField("optcode", 28, dhcp6opts),
-                    FieldLenField("optlen", None, length_of="nispservers"),
-                    IP6ListField("nispservers", [],
-                                 length_from = lambda pkt: pkt.optlen) ]
-
-class DomainNameField(StrLenField):
-    def getfield(self, pkt, s):
-        l = self.length_from(pkt)
-        return s[l:], self.m2i(pkt,s[:l])
-
-    def i2len(self, pkt, x):
-        return len(self.i2m(pkt, x))
-
-    def m2i(self, pkt, x):
-        save = x
-        cur = []
-        while x and x[0] != '\x00':
-            l = ord(x[0])
-            cur.append(x[1:1+l])
-            x = x[l+1:]
-        if x[0] != '\x00':
-            print "Found weird domain: '%s'. Keeping %s" % (save, x)
-        return ".".join(cur)
-
-    def i2m(self, pkt, x):
-        def conditionalTrailingDot(z):
-            if (z and z[-1] == '\x00'):
-                return z
-            return z+'\x00'
-        if not x:
-            return ""
-        tmp = "".join(map(lambda z: chr(len(z))+z, x.split('.')))
-        return conditionalTrailingDot(tmp)
-
-class DHCP6OptNISDomain(_DHCP6OptGuessPayload):             #RFC3898
-    name = "DHCP6 Option - NIS Domain Name"
-    fields_desc = [ ShortEnumField("optcode", 29, dhcp6opts),
-                    FieldLenField("optlen", None, length_of="nisdomain"),
-                    DomainNameField("nisdomain", "",
-                                    length_from = lambda pkt: pkt.optlen) ]
-
-class DHCP6OptNISPDomain(_DHCP6OptGuessPayload):            #RFC3898
-    name = "DHCP6 Option - NIS+ Domain Name"
-    fields_desc = [ ShortEnumField("optcode", 30, dhcp6opts),
-                    FieldLenField("optlen", None, length_of="nispdomain"),
-                    DomainNameField("nispdomain", "",
-                                    length_from= lambda pkt: pkt.optlen) ]
-
-class DHCP6OptSNTPServers(_DHCP6OptGuessPayload):                #RFC4075
-    name = "DHCP6 option - SNTP Servers"
-    fields_desc = [ ShortEnumField("optcode", 31, dhcp6opts),
-                    FieldLenField("optlen", None, length_of="sntpservers"),
-                    IP6ListField("sntpservers", [],
-                                 length_from = lambda pkt: pkt.optlen) ]
-
-IRT_DEFAULT=86400
-IRT_MINIMUM=600
-class DHCP6OptInfoRefreshTime(_DHCP6OptGuessPayload):    #RFC4242
-    name = "DHCP6 Option - Information Refresh Time"
-    fields_desc = [ ShortEnumField("optcode", 32, dhcp6opts),
-                    ShortField("optlen", 4),
-                    IntField("reftime", IRT_DEFAULT)] # One day
-
-class DHCP6OptBCMCSDomains(_DHCP6OptGuessPayload):              #RFC4280         
-    name = "DHCP6 Option - BCMCS Domain Name List"
-    fields_desc = [ ShortEnumField("optcode", 33, dhcp6opts),
-                    FieldLenField("optlen", None, length_of="bcmcsdomains"),
-                    DomainNameListField("bcmcsdomains", [],
-                                        length_from = lambda pkt: pkt.optlen) ]
-
-class DHCP6OptBCMCSServers(_DHCP6OptGuessPayload):              #RFC4280
-    name = "DHCP6 Option - BCMCS Addresses List"
-    fields_desc = [ ShortEnumField("optcode", 34, dhcp6opts),
-                    FieldLenField("optlen", None, length_of="bcmcsservers"),
-                    IP6ListField("bcmcsservers", [],
-                                 length_from= lambda pkt: pkt.optlen) ]
-
-# TODO : Does Nothing at the moment
-class DHCP6OptGeoConf(_DHCP6OptGuessPayload):               #RFC-ietf-geopriv-dhcp-civil-09.txt
-    name = ""
-    fields_desc = [ ShortEnumField("optcode", 36, dhcp6opts),
-                    FieldLenField("optlen", None, length_of="optdata"),
-                    StrLenField("optdata", "",
-                                length_from = lambda pkt: pkt.optlen) ]
-
-# TODO: see if we encounter opaque values from vendor devices
-class DHCP6OptRemoteID(_DHCP6OptGuessPayload):                   #RFC4649
-    name = "DHCP6 Option - Relay Agent Remote-ID"
-    fields_desc = [ ShortEnumField("optcode", 37, dhcp6opts),
-                    FieldLenField("optlen", None, length_of="remoteid",
-                                  adjust = lambda pkt,x: x+4),
-                    IntEnumField("enterprisenum", None, iana_enterprise_num),
-                    StrLenField("remoteid", "",
-                                length_from = lambda pkt: pkt.optlen-4) ]
-
-# TODO : 'subscriberid' default value should be at least 1 byte long
-class DHCP6OptSubscriberID(_DHCP6OptGuessPayload):               #RFC4580
-    name = "DHCP6 Option - Subscriber ID"
-    fields_desc = [ ShortEnumField("optcode", 38, dhcp6opts),
-                    FieldLenField("optlen", None, length_of="subscriberid"),
-                    StrLenField("subscriberid", "",
-                                length_from = lambda pkt: pkt.optlen) ]
-
-# TODO :  "The data in the Domain Name field MUST be encoded
-#          as described in Section 8 of [5]"
-class DHCP6OptClientFQDN(_DHCP6OptGuessPayload):                 #RFC4704
-    name = "DHCP6 Option - Client FQDN"
-    fields_desc = [ ShortEnumField("optcode", 39, dhcp6opts),
-                    FieldLenField("optlen", None, length_of="fqdn",
-                                  adjust = lambda pkt,x: x+1),
-                    BitField("res", 0, 5),
-                    FlagsField("flags", 0, 3, "SON" ),
-                    DomainNameField("fqdn", "",
-                                    length_from = lambda pkt: pkt.optlen-1) ]
-
-class DHCP6OptRelayAgentERO(_DHCP6OptGuessPayload):       # RFC4994
-    name = "DHCP6 Option - RelayRequest Option"
-    fields_desc = [ ShortEnumField("optcode", 43, dhcp6opts),
-                    FieldLenField("optlen", None, length_of="reqopts", fmt="!H"),
-                    _OptReqListField("reqopts", [23, 24],
-                                     length_from = lambda pkt: pkt.optlen) ]
-
-#####################################################################
-###                        DHCPv6 messages                        ###
-#####################################################################
-
-# Some state parameters of the protocols that should probably be 
-# useful to have in the configuration (and keep up-to-date)
-DHCP6RelayAgentUnicastAddr=""
-DHCP6RelayHopCount=""
-DHCP6ServerUnicastAddr=""
-DHCP6ClientUnicastAddr=""
-DHCP6ClientIA_TA=""
-DHCP6ClientIA_NA=""
-DHCP6ClientIAID=""
-T1="" # Voir 2462
-T2="" # Voir 2462
-DHCP6ServerDUID=""
-DHCP6CurrentTransactionID="" # devrait etre utilise pour matcher une
-# reponse et mis a jour en mode client par une valeur aleatoire pour
-# laquelle on attend un retour de la part d'un serveur.
-DHCP6PrefVal="" # la valeur de preference a utiliser dans
-# les options preference
-
-# Emitted by :
-# - server : ADVERTISE, REPLY, RECONFIGURE, RELAY-REPL (vers relay)
-# - client : SOLICIT, REQUEST, CONFIRM, RENEW, REBIND, RELEASE, DECLINE,
-#            INFORMATION REQUEST
-# - relay  : RELAY-FORW (toward server)
-
-class _DHCP6GuessPayload(Packet):
-    def guess_payload_class(self, payload):
-        if len(payload) > 1 :
-            print ord(payload[0])
-            return get_cls(dhcp6opts.get(ord(payload[0]),"DHCP6OptUnknown"), Raw)
-        return Raw
-
-#####################################################################
-## DHCPv6 messages sent between Clients and Servers (types 1 to 11)
-# Comme specifie en section 15.1 de la RFC 3315, les valeurs de
-# transaction id sont selectionnees de maniere aleatoire par le client
-# a chaque emission et doivent matcher dans les reponses faites par
-# les clients
-class DHCP6(_DHCP6OptGuessPayload):
-    name = "DHCPv6 Generic Message)"
-    fields_desc = [ ByteEnumField("msgtype",None,dhcp6types),
-                    X3BytesField("trid",0x000000) ]
-    overload_fields = { UDP: {"sport": 546, "dport": 547} }
-
-    def hashret(self):
-        return struct.pack("!I", self.trid)[1:4]
-
-#####################################################################
-# Solicit Message : sect 17.1.1 RFC3315
-# - sent by client
-# - must include a client identifier option
-# - the client may include IA options for any IAs to which it wants the
-#   server to assign address
-# - The client use IA_NA options to request the assignment of
-#   non-temporary addresses and uses IA_TA options to request the
-#   assignment of temporary addresses
-# - The client should include an Option Request option to indicate the
-#   options the client is interested in receiving (eventually
-#   including hints)
-# - The client includes a Reconfigure Accept option if is willing to
-#   accept Reconfigure messages from the server.
-# Le cas du send and reply est assez particulier car suivant la
-# presence d'une option rapid commit dans le solicit, l'attente
-# s'arrete au premier message de reponse recu ou alors apres un
-# timeout. De la meme maniere, si un message Advertise arrive avec une
-# valeur de preference de 255, il arrete l'attente et envoie une
-# Request.
-# - The client announces its intention to use DHCP authentication by
-# including an Authentication option in its solicit message. The
-# server selects a key for the client based on the client's DUID. The
-# client and server use that key to authenticate all DHCP messages
-# exchanged during the session
-
-class DHCP6_Solicit(DHCP6):
-    name = "DHCPv6 Solicit Message"
-    __metaclass__ = NewDefaultValues
-    msgtype = 1
-    overload_fields = { UDP: {"sport": 546, "dport": 547} }
-
-#####################################################################
-# Advertise Message
-# - sent by server
-# - Includes a server identifier option
-# - Includes a client identifier option
-# - the client identifier option must match the client's DUID
-# - transaction ID must match
-
-class DHCP6_Advertise(DHCP6):
-    name = "DHCPv6 Advertise Message"
-    __metaclass__ = NewDefaultValues
-    msgtype = 2
-    overload_fields = { UDP: {"sport": 547, "dport": 546} }
-    
-    def answers(self, other):
-        return (isinstance(other,DHCP6_Solicit) and 
-                other.msgtype == 1 and
-                self.trid == other.trid)
-
-#####################################################################
-# Request Message
-# - sent by clients
-# - includes a server identifier option
-# - the content of Server Identifier option must match server's DUID
-# - includes a client identifier option
-# - must include an ORO Option (even with hints) p40
-# - can includes a reconfigure Accept option indicating whether or
-#   not the client is willing to accept Reconfigure messages from
-#   the server (p40)
-# - When the server receives a Request message via unicast from a
-# client to which the server has not sent a unicast option, the server
-# discards the Request message and responds with a Reply message
-# containinig Status Code option with the value UseMulticast, a Server
-# Identifier Option containing the server's DUID, the client
-# Identifier option from the client message and no other option.
-
-class DHCP6_Request(DHCP6):
-    name = "DHCPv6 Request Message"
-    __metaclass__ = NewDefaultValues
-    msgtype = 3
-
-#####################################################################
-# Confirm Message
-# - sent by clients
-# - must include a clien identifier option
-# - When the server receives a Confirm Message, the server determines
-# whether the addresses in the Confirm message are appropriate for the
-# link to which the client is attached. cf p50
-
-class DHCP6_Confirm(DHCP6):
-    name = "DHCPv6 Confirm Message"
-    __metaclass__ = NewDefaultValues
-    msgtype = 4
-    
-#####################################################################
-# Renew Message
-# - sent by clients
-# - must include a server identifier option
-# - content of server identifier option must match the server's identifier
-# - must include a client identifier option
-# - the clients includes any IA assigned to the interface that may
-# have moved to a new link, along with the addresses associated with
-# those IAs in its confirm messages
-# - When the server receives a Renew message that contains an IA
-# option from a client, it locates the client's binding and verifies
-# that the information in the IA from the client matches the
-# information for that client. If the server cannot find a client
-# entry for the IA the server returns the IA containing no addresses
-# with a status code option est to NoBinding in the Reply message. cf
-# p51 pour le reste.
-
-class DHCP6_Renew(DHCP6):
-    name = "DHCPv6 Renew Message"
-    __metaclass__ = NewDefaultValues
-    msgtype = 5
-    
-#####################################################################
-# Rebind Message
-# - sent by clients
-# - must include a client identifier option
-# cf p52
-
-class DHCP6_Rebind(DHCP6):
-    name = "DHCPv6 Rebind Message"
-    __metaclass__ = NewDefaultValues
-    msgtype = 6
-    
-#####################################################################
-# Reply Message
-# - sent by servers
-# - the message must include a server identifier option
-# - transaction-id field must match the value of original message
-# The server includes a Rapid Commit option in the Reply message to
-# indicate that the reply is in response to a solicit message
-# - if the client receives a reply message with a Status code option
-# with the value UseMulticast, the client records the receipt of the
-# message and sends subsequent messages to the server through the
-# interface on which the message was received using multicast. The
-# client resends the original message using multicast
-# - When the client receives a NotOnLink status from the server in
-# response to a Confirm message, the client performs DHCP server
-# solicitation as described in section 17 and client-initiated
-# configuration as descrribed in section 18 (RFC 3315)
-# - when the client receives a NotOnLink status from the server in
-# response to a Request, the client can either re-issue the Request
-# without specifying any addresses or restart the DHCP server
-# discovery process.
-# - the server must include a server identifier option containing the
-# server's DUID in the Reply message
-
-class DHCP6_Reply(DHCP6):
-    name = "DHCPv6 Reply Message"
-    __metaclass__ = NewDefaultValues
-    msgtype = 7
-    
-    def answers(self, other):
-        return (isinstance(other, DHCP6_InfoRequest) and
-                self.trid == other.trid)
-
-#####################################################################
-# Release Message
-# - sent by clients
-# - must include a server identifier option
-# cf p53
-
-class DHCP6_Release(DHCP6):
-    name = "DHCPv6 Release Message"
-    __metaclass__ = NewDefaultValues
-    msgtype = 8
-    
-#####################################################################
-# Decline Message
-# - sent by clients
-# - must include a client identifier option
-# - Server identifier option must match server identifier
-# - The addresses to be declined must be included in the IAs. Any
-# addresses for the IAs the client wishes to continue to use should
-# not be in added to the IAs.
-# - cf p54 
-
-class DHCP6_Decline(DHCP6):
-    name = "DHCPv6 Decline Message"
-    __metaclass__ = NewDefaultValues
-    msgtype = 9
-    
-#####################################################################
-# Reconfigure Message
-# - sent by servers
-# - must be unicast to the client
-# - must include a server identifier option
-# - must include a client identifier option that contains the client DUID
-# - must contain a Reconfigure Message Option and the message type
-#   must be a valid value
-# - the server sets the transaction-id to 0
-# - The server must use DHCP Authentication in the Reconfigure
-# message. Autant dire que ca va pas etre le type de message qu'on va
-# voir le plus souvent.
-
-class DHCP6_Reconf(DHCP6):
-    name = "DHCPv6 Reconfigure Message"
-    __metaclass__ = NewDefaultValues
-    msgtype = 10
-    overload_fields = { UDP: { "sport": 547, "dport": 546 } }
-
-    
-#####################################################################
-# Information-Request Message
-# - sent by clients when needs configuration information but no
-# addresses. 
-# - client should include a client identifier option to identify
-# itself. If it doesn't the server is not able to return client
-# specific options or the server can choose to not respond to the
-# message at all. The client must include a client identifier option
-# if the message will be authenticated.
-# - client must include an ORO of option she's interested in receiving
-# (can include hints)
-
-class DHCP6_InfoRequest(DHCP6):
-    name = "DHCPv6 Information Request Message"    
-    __metaclass__ = NewDefaultValues
-    msgtype = 11 
-    
-    def hashret(self): 
-        return struct.pack("!I", self.trid)[1:3]
-
-#####################################################################
-# sent between Relay Agents and Servers 
-#
-# Normalement, doit inclure une option "Relay Message Option"
-# peut en inclure d'autres.
-# voir section 7.1 de la 3315
-
-# Relay-Forward Message
-# - sent by relay agents to servers
-# If the relay agent relays messages to the All_DHCP_Servers multicast
-# address or other multicast addresses, it sets the Hop Limit field to
-# 32. 
-
-class DHCP6_RelayForward(_DHCP6GuessPayload,Packet):
-    name = "DHCPv6 Relay Forward Message (Relay Agent/Server Message)"
-    fields_desc = [ ByteEnumField("msgtype", 12, dhcp6types),
-                    ShortField("hopcount", None),
-                    IP6Field("linkaddr", "::"),
-                    IP6Field("peeraddr", "::") ]
-    def hashret(self): # we filter on peer address field
-        return inet_pton(socket.AF_INET6, self.peeraddr)
-
-#####################################################################
-# sent between Relay Agents and Servers 
-# Normalement, doit inclure une option "Relay Message Option"
-# peut en inclure d'autres.
-# Les valeurs des champs hop-count, link-addr et peer-addr
-# sont copiees du messsage Forward associe. POur le suivi de session.
-# Pour le moment, comme decrit dans le commentaire, le hashret
-# se limite au contenu du champ peer address.
-# Voir section 7.2 de la 3315.
-
-# Relay-Reply Message
-# - sent by servers to relay agents
-# - if the solicit message was received in a Relay-Forward message,
-# the server constructs a relay-reply message with the Advertise
-# message in the payload of a relay-message. cf page 37/101. Envoie de
-# ce message en unicast au relay-agent. utilisation de l'adresse ip
-# presente en ip source du paquet recu
-
-class DHCP6_RelayReply(DHCP6_RelayForward):
-    name = "DHCPv6 Relay Reply Message (Relay Agent/Server Message)"
-    __metaclass__= NewDefaultValues
-    msgtype = 13
-    def hashret(self): # We filter on peer address field.
-        return inet_pton(socket.AF_INET6, self.peeraddr)
-    def answers(self, other):
-        return (isinstance(other, DHCP6_RelayForward) and
-                self.count == other.count and
-                self.linkaddr == other.linkaddr and
-                self.peeraddr == other.peeraddr )
-
-
-dhcp6_cls_by_type = {  1: "DHCP6_Solicit",
-                       2: "DHCP6_Advertise",
-                       3: "DHCP6_Request",
-                       4: "DHCP6_Confirm",
-                       5: "DHCP6_Renew",
-                       6: "DHCP6_Rebind",
-                       7: "DHCP6_Reply",
-                       8: "DHCP6_Release",
-                       9: "DHCP6_Decline",
-                      10: "DHCP6_Reconf",
-                      11: "DHCP6_InfoRequest",
-                      12: "DHCP6_RelayForward",
-                      13: "DHCP6_RelayReply" }
-
-def _dhcp6_dispatcher(x, *args, **kargs):
-    cls = Raw
-    if len(x) >= 2:
-        cls = get_cls(dhcp6_cls_by_type.get(ord(x[0]), "Raw"), Raw)
-    return cls(x, *args, **kargs)
-
-bind_bottom_up(UDP, _dhcp6_dispatcher, { "dport": 547 } )
-bind_bottom_up(UDP, _dhcp6_dispatcher, { "dport": 546 } )
-
-
-
-class DHCPv6_am(AnsweringMachine):
-    function_name = "dhcp6d"
-    filter = "udp and port 546 and port 547" 
-    send_function = staticmethod(send)
-    def usage(self):
-        msg = """
-dhcp6d( dns="2001:500::1035", domain="localdomain, local", duid=None)
-        iface=conf.iface6, advpref=255, sntpservers=None, 
-        sipdomains=None, sipservers=None, 
-        nisdomain=None, nisservers=None, 
-        nispdomain=None, nispservers=None,
-        bcmcsdomain=None, bcmcsservers=None)
-
-   debug : When set, additional debugging information is printed. 
-
-   duid   : some DUID class (DUID_LLT, DUID_LL or DUID_EN). If none
-            is provided a DUID_LLT is constructed based on the MAC 
-            address of the sending interface and launch time of dhcp6d 
-            answering machine. 
-  
-   iface : the interface to listen/reply on if you do not want to use 
-           conf.iface6.
-
-   advpref : Value in [0,255] given to Advertise preference field.
-             By default, 255 is used. Be aware that this specific
-             value makes clients stops waiting for further Advertise
-             messages from other servers.
-
-   dns : list of recursive DNS servers addresses (as a string or list). 
-         By default, it is set empty and the associated DHCP6OptDNSServers
-         option is inactive. See RFC 3646 for details.
-   domain : a list of DNS search domain (as a string or list). By default, 
-         it is empty and the associated DHCP6OptDomains option is inactive.
-         See RFC 3646 for details.
-
-   sntpservers : a list of SNTP servers IPv6 addresses. By default,
-         it is empty and the associated DHCP6OptSNTPServers option 
-         is inactive. 
-
-   sipdomains : a list of SIP domains. By default, it is empty and the
-         associated DHCP6OptSIPDomains option is inactive. See RFC 3319
-         for details.
-   sipservers : a list of SIP servers IPv6 addresses. By default, it is 
-         empty and the associated DHCP6OptSIPDomains option is inactive. 
-         See RFC 3319 for details.
-
-   nisdomain : a list of NIS domains. By default, it is empty and the
-         associated DHCP6OptNISDomains option is inactive. See RFC 3898
-         for details. See RFC 3646 for details.
-   nisservers : a list of NIS servers IPv6 addresses. By default, it is 
-         empty and the associated DHCP6OptNISServers option is inactive.
-         See RFC 3646 for details.
-
-   nispdomain : a list of NIS+ domains. By default, it is empty and the
-         associated DHCP6OptNISPDomains option is inactive. See RFC 3898
-         for details.
-   nispservers : a list of NIS+ servers IPv6 addresses. By default, it is 
-         empty and the associated DHCP6OptNISServers option is inactive.
-         See RFC 3898 for details.
-
-   bcmcsdomain : a list of BCMCS domains. By default, it is empty and the
-         associated DHCP6OptBCMCSDomains option is inactive. See RFC 4280
-         for details.
-   bcmcsservers : a list of BCMCS servers IPv6 addresses. By default, it is 
-         empty and the associated DHCP6OptBCMCSServers option is inactive.
-         See RFC 4280 for details.
-
-   If you have a need for others, just ask ... or provide a patch."""
-        print msg
-
-    def parse_options(self, dns="2001:500::1035", domain="localdomain, local",
-                      startip="2001:db8::1", endip="2001:db8::20", duid=None,
-                      sntpservers=None, sipdomains=None, sipservers=None, 
-                      nisdomain=None, nisservers=None, nispdomain=None,
-                      nispservers=None, bcmcsservers=None, bcmcsdomains=None,
-                      iface=None, debug=0, advpref=255):
-        def norm_list(val, param_name):
-            if val is None:
-                return None
-            if type(val) is list:
-                return val
-            elif type(val) is str:
-                l = val.split(',')
-                return map(lambda x: x.strip(), l)
-            else:
-                print "Bad '%s' parameter provided." % param_name
-                self.usage()
-                return -1
-
-        if iface is None:
-            iface = conf.iface6
-        
-        self.debug = debug
-
-        # Dictionary of provided DHCPv6 options, keyed by option type
-        self.dhcpv6_options={}
-
-        for o in [(dns, "dns", 23, lambda x: DHCP6OptDNSServers(dnsservers=x)), 
-                  (domain, "domain", 24, lambda x: DHCP6OptDNSDomains(dnsdomains=x)), 
-                  (sntpservers, "sntpservers", 31, lambda x: DHCP6OptSNTPServers(sntpservers=x)),
-                  (sipservers, "sipservers", 22, lambda x: DHCP6OptSIPServers(sipservers=x)),
-                  (sipdomains, "sipdomains", 21, lambda x: DHCP6OptSIPDomains(sipdomains=x)),
-                  (nisservers, "nisservers", 27, lambda x: DHCP6OptNISServers(nisservers=x)),
-                  (nisdomain, "nisdomain", 29, lambda x: DHCP6OptNISDomain(nisdomain=(x+[""])[0])),
-                  (nispservers, "nispservers", 28, lambda x: DHCP6OptNISPServers(nispservers=x)), 
-                  (nispdomain, "nispdomain", 30, lambda x: DHCP6OptNISPDomain(nispdomain=(x+[""])[0])),
-                  (bcmcsservers, "bcmcsservers", 33, lambda x: DHCP6OptBCMCSServers(bcmcsservers=x)),
-                  (bcmcsdomains, "bcmcsdomains", 34, lambda x: DHCP6OptBCMCSDomains(bcmcsdomains=x))]:
-
-            opt = norm_list(o[0], o[1])
-            if opt == -1: # Usage() was triggered
-                return False
-            elif opt is None: # We won't return that option
-                pass
-            else:
-                self.dhcpv6_options[o[2]] = o[3](opt)
-
-        if self.debug:
-            print "\n[+] List of active DHCPv6 options:"
-            opts = self.dhcpv6_options.keys()
-            opts.sort()
-            for i in opts:
-                print "    %d: %s" % (i, repr(self.dhcpv6_options[i]))
-
-        # Preference value used in Advertise. 
-        self.advpref = advpref
-
-        # IP Pool
-        self.startip = startip
-        self.endip   = endip
-        # XXX TODO Check IPs are in same subnet
-
-        ####
-        # The interface we are listening/replying on
-        self.iface = iface
-
-        ####        
-        # Generate a server DUID
-        if duid is not None:
-            self.duid = duid
-        else:
-            # Timeval
-            from time import gmtime, strftime, mktime
-            epoch = (2000, 1, 1, 0, 0, 0, 5, 1, 0)
-            delta = mktime(epoch) - mktime(gmtime(0))
-            timeval = time.time() - delta
-
-            # Mac Address
-            rawmac = get_if_raw_hwaddr(iface)[1]
-            mac = ":".join(map(lambda x: "%.02x" % ord(x), list(rawmac)))
-
-            self.duid = DUID_LLT(timeval = timeval, lladdr = mac)
-            
-        if self.debug:
-            print "\n[+] Our server DUID:" 
-            self.duid.show(label_lvl=" "*4)
-
-        ####
-        # Find the source address we will use
-        l = filter(lambda x: x[2] == iface and in6_islladdr(x[0]), 
-                   in6_getifaddr())
-        if not l:
-            warning("Unable to get a Link-Local address")
-            return 
-        
-        self.src_addr = l[0][0]
-
-        ####
-        # Our leases
-        self.leases = {}
-        
-
-        if self.debug:
-            print "\n[+] Starting DHCPv6 service on %s:" % self.iface 
-
-    def is_request(self, p):
-        if not IPv6 in p:
-            return False
-
-        src = p[IPv6].src
-        dst = p[IPv6].dst
-
-        p = p[IPv6].payload 
-        if not isinstance(p, UDP) or p.sport != 546 or p.dport != 547 :
-            return False
-
-        p = p.payload
-        if not isinstance(p, DHCP6):
-            return False
-
-        # Message we considered client messages :
-        # Solicit (1), Request (3), Confirm (4), Renew (5), Rebind (6)
-        # Decline (9), Release (8), Information-request (11),
-        if not (p.msgtype in [1, 3, 4, 5, 6, 8, 9, 11]):
-            return False
-
-        # Message validation following section 15 of RFC 3315
-
-        if ((p.msgtype == 1) or # Solicit 
-            (p.msgtype == 6) or # Rebind
-            (p.msgtype == 4)):  # Confirm
-            if ((not DHCP6OptClientId in p) or
-                DHCP6OptServerId in p):
-                return False
-
-            if (p.msgtype == 6 or # Rebind
-                p.msgtype == 4):  # Confirm   
-                # XXX We do not reply to Confirm or Rebind as we 
-                # XXX do not support address assignment            
-                return False
-
-        elif (p.msgtype == 3 or # Request
-              p.msgtype == 5 or # Renew
-              p.msgtype == 8):  # Release
-        
-            # Both options must be present
-            if ((not DHCP6OptServerId in p) or
-                (not DHCP6OptClientId in p)):
-                return False
-            # provided server DUID must match ours
-            duid = p[DHCP6OptServerId].duid
-            if (type(duid) != type(self.duid)):
-                return False
-            if str(duid) != str(self.duid):
-                return False
-
-            if (p.msgtype == 5 or # Renew
-                p.msgtype == 8):  # Release
-                # XXX We do not reply to Renew or Release as we 
-                # XXX do not support address assignment            
-                return False
-
-        elif p.msgtype == 9: # Decline
-            # XXX We should check if we are tracking that client
-            if not self.debug:
-                return False
-
-            bo = Color.bold
-            g = Color.green + bo
-            b = Color.blue + bo
-            n = Color.normal
-            r = Color.red
-
-            vendor  = in6_addrtovendor(src)
-            if (vendor and vendor != "UNKNOWN"):
-                vendor = " [" + b + vendor + n + "]"
-            else:
-                vendor = ""
-            src  = bo + src + n
-
-            it = p
-            addrs = []
-            while it:
-                l = []
-                if isinstance(it, DHCP6OptIA_NA):
-                    l = it.ianaopts
-                elif isinstance(it, DHCP6OptIA_TA):
-                    l = it.iataopts
-
-                opsaddr = filter(lambda x: isinstance(x, DHCP6OptIAAddress),l)
-                a=map(lambda x: x.addr,  opsaddr)
-                addrs += a
-                it = it.payload
-                    
-            addrs = map(lambda x: bo + x + n, addrs)
-            if debug:
-                msg = r + "[DEBUG]" + n + " Received " + g + "Decline" + n 
-                msg += " from " + bo + src + vendor + " for "
-                msg += ", ".join(addrs)+ n
-                print msg
-
-            # See sect 18.1.7
-
-            # Sent by a client to warn us she has determined
-            # one or more addresses assigned to her is already
-            # used on the link.
-            # We should simply log that fact. No messaged should
-            # be sent in return.
-
-            # - Message must include a Server identifier option
-            # - the content of the Server identifier option must 
-            #   match the server's identifier
-            # - the message must include a Client Identifier option
-            return False
-
-        elif p.msgtype == 11: # Information-Request
-            if DHCP6OptServerId in p:
-                duid = p[DHCP6OptServerId].duid
-                if (type(duid) != type(self.duid)):
-                    return False
-                if str(duid) != str(self.duid):
-                    return False
-            if ((DHCP6OptIA_NA in p) or 
-                (DHCP6OptIA_TA in p) or
-                (DHCP6OptIA_PD in p)):
-                    return False
-        else:
-            return False
-
-        return True
-
-    def print_reply(self, req, reply):
-        def norm(s):
-            if s.startswith("DHCPv6 "):
-                s = s[7:]
-            if s.endswith(" Message"):
-                s = s[:-8]
-            return s
-        
-        if reply is None:
-            return
-
-        bo = Color.bold
-        g = Color.green + bo
-        b = Color.blue + bo
-        n = Color.normal
-        reqtype = g + norm(req.getlayer(UDP).payload.name) + n
-        reqsrc  = req.getlayer(IPv6).src
-        vendor  = in6_addrtovendor(reqsrc)
-        if (vendor and vendor != "UNKNOWN"):
-            vendor = " [" + b + vendor + n + "]"
-        else:
-            vendor = ""
-        reqsrc  = bo + reqsrc + n
-        reptype = g + norm(reply.getlayer(UDP).payload.name) + n
-
-        print "Sent %s answering to %s from %s%s" % (reptype, reqtype, reqsrc, vendor)
-
-    def make_reply(self, req):
-        req_mac_src = req.src
-        req_mac_dst = req.dst
-
-        p = req[IPv6]
-        req_src = p.src
-        req_dst = p.dst
-
-        p = p.payload.payload
-
-        msgtype = p.msgtype
-        trid = p.trid
-
-        if msgtype == 1: # SOLICIT (See Sect 17.1 and 17.2 of RFC 3315)
-            
-            # XXX We don't support address or prefix assignment
-            # XXX We also do not support relay function           --arno
-
-            client_duid = p[DHCP6OptClientId].duid
-            resp  = IPv6(src=self.src_addr, dst=req_src)
-            resp /= UDP(sport=547, dport=546)
-            
-            if p.haslayer(DHCP6OptRapidCommit):
-                # construct a Reply packet 
-                resp /= DHCP6_Reply(trid=trid)
-                resp /= DHCP6OptRapidCommit() # See 17.1.2
-                resp /= DHCP6OptServerId(duid = self.duid)
-                resp /= DHCP6OptClientId(duid = client_duid)
-                
-            else: # No Rapid Commit in the packet. Reply with an Advertise                
-                
-                if (p.haslayer(DHCP6OptIA_NA) or
-                    p.haslayer(DHCP6OptIA_TA)):
-                    # XXX We don't assign addresses at the moment
-                    msg = "Scapy6 dhcp6d does not support address assignment"
-                    resp /= DHCP6_Advertise(trid = trid)
-                    resp /= DHCP6OptStatusCode(statuscode=2, statusmsg=msg)
-                    resp /= DHCP6OptServerId(duid = self.duid)
-                    resp /= DHCP6OptClientId(duid = client_duid)                  
-
-                elif p.haslayer(DHCP6OptIA_PD):
-                    # XXX We don't assign prefixes at the moment
-                    msg = "Scapy6 dhcp6d does not support prefix assignment"
-                    resp /= DHCP6_Advertise(trid = trid)
-                    resp /= DHCP6OptStatusCode(statuscode=6, statusmsg=msg)
-                    resp /= DHCP6OptServerId(duid = self.duid)
-                    resp /= DHCP6OptClientId(duid = client_duid)                  
-
-                else: # Usual case, no request for prefixes or addresse
-                    resp /= DHCP6_Advertise(trid = trid)
-                    resp /= DHCP6OptPref(prefval = self.advpref)
-                    resp /= DHCP6OptServerId(duid = self.duid)
-                    resp /= DHCP6OptClientId(duid = client_duid)
-                    resp /= DHCP6OptReconfAccept()
-                    
-                    # See which options should be included
-                    reqopts = []
-                    if p.haslayer(DHCP6OptOptReq): # add only asked ones
-                        reqopts = p[DHCP6OptOptReq].reqopts
-                        for o in self.dhcpv6_options.keys():
-                            if o in reqopts:
-                                resp /= self.dhcpv6_options[o]
-                    else: # advertise everything we have available
-                        for o in self.dhcpv6_options.keys():
-                            resp /= self.dhcpv6_options[o]                    
-
-            return resp
-
-        elif msgtype == 3: #REQUEST (INFO-REQUEST is further below)
-            client_duid = p[DHCP6OptClientId].duid
-            resp  = IPv6(src=self.src_addr, dst=req_src)
-            resp /= UDP(sport=547, dport=546)
-            resp /= DHCP6_Solicit(trid=trid)
-            resp /= DHCP6OptServerId(duid = self.duid)
-            resp /= DHCP6OptClientId(duid = client_duid)
-
-            # See which options should be included
-            reqopts = []
-            if p.haslayer(DHCP6OptOptReq): # add only asked ones
-                reqopts = p[DHCP6OptOptReq].reqopts
-                for o in self.dhcpv6_options.keys():
-                    if o in reqopts:
-                        resp /= self.dhcpv6_options[o]
-            else: 
-                # advertise everything we have available.
-                # Should not happen has clients MUST include 
-                # and ORO in requests (sec 18.1.1)   -- arno
-                for o in self.dhcpv6_options.keys():
-                    resp /= self.dhcpv6_options[o]          
-
-            return resp            
-        
-        elif msgtype == 4: # CONFIRM
-            # see Sect 18.1.2
-            
-            # Client want to check if addresses it was assigned
-            # are still appropriate
-
-            # Server must discard any Confirm messages that
-            # do not include a Client Identifier option OR
-            # THAT DO INCLUDE a Server Identifier Option
-
-            # XXX we must discard the SOLICIT if it is received with
-            #     a unicast destination address
-
-            pass
-
-        elif msgtype == 5: # RENEW
-            # see Sect 18.1.3
-            
-            # Clients want to extend lifetime of assigned addresses
-            # and update configuration parameters. This message is sent
-            # specifically to the server that provided her the info
-
-            # - Received message must include a Server Identifier
-            #   option.
-            # - the content of server identifier option must match
-            #   the server's identifier.
-            # - the message must include a Client identifier option
-
-            pass
-        
-        elif msgtype == 6: # REBIND
-            # see Sect 18.1.4
-            
-            # Same purpose as the Renew message but sent to any
-            # available server after he received no response
-            # to its previous Renew message.
-
-            
-            # - Message must include a Client Identifier Option
-            # - Message can't include a Server identifier option
-
-            # XXX we must discard the SOLICIT if it is received with
-            #     a unicast destination address
-
-            pass
-
-        elif msgtype == 8: # RELEASE
-            # See section 18.1.6
-
-            # Message is sent to the server to indicate that 
-            # she will no longer use the addresses that was assigned
-            # We should parse the message and verify our dictionary
-            # to log that fact.
-
-
-            # - The message must include a server identifier option
-            # - The content of the Server Identifier option must
-            #   match the server's identifier
-            # - the message must include a Client Identifier option
-
-            pass
-
-        elif msgtype == 9: # DECLINE
-            # See section 18.1.7            
-            pass
-
-        elif msgtype == 11: # INFO-REQUEST
-            client_duid = None
-            if not p.haslayer(DHCP6OptClientId):
-                if self.debug:
-                    warning("Received Info Request message without Client Id option")
-            else:
-                client_duid = p[DHCP6OptClientId].duid
-
-            resp  = IPv6(src=self.src_addr, dst=req_src)
-            resp /= UDP(sport=547, dport=546)
-            resp /= DHCP6_Reply(trid=trid)
-            resp /= DHCP6OptServerId(duid = self.duid)
-
-            if client_duid:
-                resp /= DHCP6OptClientId(duid = client_duid)
-                
-            # Stack requested options if available
-            reqopts = []
-            if p.haslayer(DHCP6OptOptReq):
-                reqopts = p[DHCP6OptOptReq].reqopts
-            for o in self.dhcpv6_options.keys():
-                resp /= self.dhcpv6_options[o]
-
-            return resp
-
-        else:
-            # what else ?
-            pass
-
-        # - We won't support reemission
-        # - We won't support relay role, nor relay forwarded messages
-        #   at the beginning
 
 #############################################################################
 #############################################################################
-- 
GitLab