summaryrefslogtreecommitdiffstats
path: root/scripts/external_libs/scapy-2.3.1/python3/scapy/layers/dhcp6.py
diff options
context:
space:
mode:
Diffstat (limited to 'scripts/external_libs/scapy-2.3.1/python3/scapy/layers/dhcp6.py')
-rw-r--r--scripts/external_libs/scapy-2.3.1/python3/scapy/layers/dhcp6.py1718
1 files changed, 1718 insertions, 0 deletions
diff --git a/scripts/external_libs/scapy-2.3.1/python3/scapy/layers/dhcp6.py b/scripts/external_libs/scapy-2.3.1/python3/scapy/layers/dhcp6.py
new file mode 100644
index 00000000..a11a4149
--- /dev/null
+++ b/scripts/external_libs/scapy-2.3.1/python3/scapy/layers/dhcp6.py
@@ -0,0 +1,1718 @@
+## 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>
+
+"""
+DHCPv6: Dynamic Host Configuration Protocol for IPv6. [RFC 3315]
+"""
+
+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",b"") ]
+
+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 = conf.raw_layer
+ 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", b"",
+ 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 bytes(i)
+
+ def m2i(self, pkt, x):
+ cls = conf.raw_layer
+ if len(x) > 4:
+ o = struct.unpack("!H", x[:2])[0]
+ cls = get_cls(duid_cls.get(o, conf.raw_layer), conf.raw_layer)
+ 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"
+ 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", b"",
+ length_from = lambda pkt: pkt.optlen - 24) ]
+ def guess_payload_class(self, payload):
+ return conf.padding_layer
+
+class _IANAOptField(PacketListField):
+ def i2len(self, pkt, z):
+ if z is None or z == []:
+ return 0
+ return sum(map(lambda x: len(bytes(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 conf.padding_layer in p:
+ pad = p[conf.padding_layer]
+ 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 y in dhcp6opts:
+ 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 b"".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", b"A"*8, 8), # TODO: XXX
+ StrLenField("authinfo", b"",
+ 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", b"",
+ 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(bytes(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 conf.padding_layer in p:
+ pad = p[conf.padding_layer]
+ remain = pad.load
+ del(pad.underlayer.payload)
+ else:
+ remain = b""
+ 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", b"",
+ length_from = lambda pkt: pkt.len) ]
+ def guess_payload_class(self, payload):
+ return conf.padding_layer
+
+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", b"",
+ length_from = lambda pkt: pkt.optlen) ]
+ def guess_payload_class(self, payload):
+ return conf.padding_layer
+
+# 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", b"",
+ 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] != b'\x00':
+ while x and x[0] != 0:
+ l = (x[0])
+ cur.append(x[1:l+1])
+ x = x[l+1:]
+ res.append(b".".join(cur))
+ if x and x[0] == 0:
+ x = x[1:]
+ return res
+
+ def i2m(self, pkt, x):
+ def conditionalTrailingDot(z):
+ if z and z[-1] == 0:
+ return z
+ return z+b'\x00'
+ res = b""
+ x = [ i.encode('ascii') for i in x if type(i) is str ]
+ tmp = map(lambda y: map((lambda z: chr(len(z)).encode('ascii')+z), y.split(b'.')), x)
+ return b"".join(map(lambda x: conditionalTrailingDot(b"".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", b"",
+ 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):
+ cur = []
+ while x:
+ l = (x[0])
+ cur.append(x[1:1+l])
+ x = x[l+1:]
+ ret_str = b".".join(cur)
+ return ret_str
+
+ def i2m(self, pkt, x):
+ if not x:
+ return b""
+ tmp = b"".join(map(lambda z: chr(len(z)).encode('ascii')+z, x.split(b'.')))
+ return 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", b"",
+ 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", b"",
+ 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((payload[0]))
+ return get_cls(dhcp6opts.get(ord(payload[0]),"DHCP6OptUnknown"), conf.raw_layer)
+ return conf.raw_layer
+
+#####################################################################
+## 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"
+ 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"
+ 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"
+ 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"
+ 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"
+ msgtype = 5
+
+#####################################################################
+# Rebind Message
+# - sent by clients
+# - must include a client identifier option
+# cf p52
+
+class DHCP6_Rebind(DHCP6):
+ name = "DHCPv6 Rebind Message"
+ 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"
+ msgtype = 7
+
+ overload_fields = { UDP: {"sport": 547, "dport": 546} }
+
+ def answers(self, other):
+
+ types = (DHCP6_InfoRequest, DHCP6_Confirm, DHCP6_Rebind, DHCP6_Decline, DHCP6_Request, DHCP6_Release, DHCP6_Renew)
+
+ return (isinstance(other, types) 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"
+ 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"
+ 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"
+ 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"
+ msgtype = 11
+
+#####################################################################
+# 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),
+ ByteField("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)"
+ 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.hopcount == other.hopcount 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 = conf.raw_layer
+ if len(x) >= 2:
+ cls = get_cls(dhcp6_cls_by_type.get((x[0]), "Raw"), conf.raw_layer)
+ 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)
+ mac = ":".join(map(lambda x: "%.02x" % x, 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())
+ l = [ x for x in in6_getifaddr() if x[2] == iface and in6_islladdr(x[0]) ]
+ 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 bytes(duid) != bytes(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)
+ opsaddr = [ x for x in l if isinstance(x, DHCP6OptIAAddress) ]
+ 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 bytes(duid) != bytes(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