Repository: secdev/scapy

Issue:
DHCP6 relay message option not decoded
Decoding a DHCPv6 message with a relay message gives this:

`<Ether  dst=00:02:00:00:00:11 src=00:00:00:02:02:02 type=n_802_1Q |<Dot1Q  prio=7 id=0 vlan=2049 type=n_802_1Q |<Dot1Q  prio=7 id=0 vlan=6 type=IPv6 |<IPv6  version=6 tc=192 fl=0 plen=175 nh=UDP hlim=255 src=fe80::10:0:0:1 dst=fe80::202:ff:fe00:11 |<UDP  sport=dhcpv6_server dport=dhcpv6_server len=175 chksum=0xdd39 |<DHCP6_RelayReply  msgtype=RELAY-REPL hopcount=0 linkaddr=:: peeraddr=:: |<DHCP6OptRelayMsg  optcode=RELAY_MSG optlen=76 |<DHCP6OptUnknown  optcode=556 optlen=33418 data='\x00\x02\x00\n\x00\x03\x00\x01j\x13\xff\x00\x00\x00\x00\x01\x00\n\x00\x03\x00\x01\x00\x02\x00\x00\x00\x11\x00\x03\x00(\x00\x00\x00\x01\x00\x00\x07\x08\x00\x00\x0b@\x00\x05\x00\x18:\xaa\x00\x00\n\x00\x00\x12\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x0e\x10\x00\x01Q\x80\x00\x12\x00\x1500:00:00:00:00:01;1;o\x00%\x00\x18\x00\x00\x19\x7falu00:02:00:00:00:11' |>>>>>>>>`

while it should decode the payload of the option as another DHCPv6 message.

Here's an example packet:
[dhcpv6.zip](https://github.com/secdev/scapy/files/1108703/dhcpv6.zip)

Here's the tshark output for the same packet:

    Frame 1: 237 bytes on wire (1896 bits), 237 bytes captured (1896 bits)
        Encapsulation type: Ethernet (1)
        Arrival Time: Jun 28, 2017 14:02:30.035342000 CEST
        [Time shift for this packet: 0.000000000 seconds]
        Epoch Time: 1498651350.035342000 seconds
        [Time delta from previous captured frame: 0.000000000 seconds]
        [Time delta from previous displayed frame: 0.000000000 seconds]
        [Time since reference or first frame: 0.000000000 seconds]
        Frame Number: 1
        Frame Length: 237 bytes (1896 bits)
        Capture Length: 237 bytes (1896 bits)
        [Frame is marked: False]
        [Frame is ignored: False]
        [Protocols in frame: eth:ethertype:vlan:ethertype:vlan:ethertype:ipv6:udp:dhcpv6]
    Ethernet II, Src: 00:00:00_02:02:02 (00:00:00:02:02:02), Dst: NetSys_00:00:11 (00:02:00:00:00:11)
        Destination: NetSys_00:00:11 (00:02:00:00:00:11)
            Address: NetSys_00:00:11 (00:02:00:00:00:11)
            .... ..0. .... .... .... .... = LG bit: Globally unique address (factory default)
            .... ...0 .... .... .... .... = IG bit: Individual address (unicast)
        Source: 00:00:00_02:02:02 (00:00:00:02:02:02)
            Address: 00:00:00_02:02:02 (00:00:00:02:02:02)
            .... ..0. .... .... .... .... = LG bit: Globally unique address (factory default)
            .... ...0 .... .... .... .... = IG bit: Individual address (unicast)
        Type: 802.1Q Virtual LAN (0x8100)
    802.1Q Virtual LAN, PRI: 7, CFI: 0, ID: 2049
        111. .... .... .... = Priority: Network Control (7)
        ...0 .... .... .... = CFI: Canonical (0)
        .... 1000 0000 0001 = ID: 2049
        Type: 802.1Q Virtual LAN (0x8100)
    802.1Q Virtual LAN, PRI: 7, CFI: 0, ID: 6
        111. .... .... .... = Priority: Network Control (7)
        ...0 .... .... .... = CFI: Canonical (0)
        .... 0000 0000 0110 = ID: 6
        Type: IPv6 (0x86dd)
    Internet Protocol Version 6, Src: fe80::10:0:0:1, Dst: fe80::202:ff:fe00:11
        0110 .... = Version: 6
        .... 1100 0000 .... .... .... .... .... = Traffic class: 0xc0 (DSCP: CS6, ECN: Not-ECT)
            .... 1100 00.. .... .... .... .... .... = Differentiated Services Codepoint: Class Selector 6 (48)
            .... .... ..00 .... .... .... .... .... = Explicit Congestion Notification: Not ECN-Capable Transport (0)
        .... .... .... 0000 0000 0000 0000 0000 = Flow label: 0x00000
        Payload length: 175
        Next header: UDP (17)
        Hop limit: 255
        Source: fe80::10:0:0:1
        Destination: fe80::202:ff:fe00:11
        [Destination SA MAC: NetSys_00:00:11 (00:02:00:00:00:11)]
        [Source GeoIP: Unknown]
        [Destination GeoIP: Unknown]
    User Datagram Protocol, Src Port: 547, Dst Port: 547
        Source Port: 547
        Destination Port: 547
        Length: 175
        Checksum: 0xdd39 [unverified]
        [Checksum Status: Unverified]
        [Stream index: 0]
    DHCPv6
        Message type: Relay-reply (13)
        Hopcount: 0
        Link address: ::
        Peer address: ::
        Relay Message
            Option: Relay Message (9)
            Length: 76
            Value: 022c828a0002000a000300016a13ff0000000001000a0003...
            DHCPv6
                Message type: Advertise (2)
                Transaction ID: 0x2c828a
                Server Identifier
                    Option: Server Identifier (2)
                    Length: 10
                    Value: 000300016a13ff000000
                    DUID: 000300016a13ff000000
                    DUID Type: link-layer address (3)
                    Hardware type: Ethernet (1)
                    Link-layer address: 6a:13:ff:00:00:00
                Client Identifier
                    Option: Client Identifier (1)
                    Length: 10
                    Value: 00030001000200000011
                    DUID: 00030001000200000011
                    DUID Type: link-layer address (3)
                    Hardware type: Ethernet (1)
                    Link-layer address: 00:02:00:00:00:11
                Identity Association for Non-temporary Address
                    Option: Identity Association for Non-temporary Address (3)
                    Length: 40
                    Value: 000000010000070800000b40000500183aaa00000a000012...
                    IAID: 00000001
                    T1: 1800
                    T2: 2880
                    IA Address
                        Option: IA Address (5)
                        Length: 24
                        Value: 3aaa00000a000012000000000000000000000e1000015180
                        IPv6 address: 3aaa:0:a00:12::
                        Preferred lifetime: 3600
                        Valid lifetime: 86400
        Interface-Id
            Option: Interface-Id (18)
            Length: 21
            Value: 30303a30303a30303a30303a30303a30313b313b6f
            Interface-ID: 30303a30303a30303a30303a30303a30313b313b6f
        Remote Identifier
            Option: Remote Identifier (37)
            Length: 24
            Value: 0000197f616c7530303a30323a30303a30303a30303a3131
            Enterprise ID: Alcatel-Lucent (formerly 'Panthera Networks, Inc.') (6527)
            Remote-ID: 616c7530303a30323a30303a30303a30303a3131





Discussion:

Resolving patch:
diff --git a/scapy/layers/dhcp6.py b/scapy/layers/dhcp6.py
--- a/scapy/layers/dhcp6.py
+++ b/scapy/layers/dhcp6.py
@@ -23,7 +23,8 @@
 from scapy.fields import BitField, ByteEnumField, ByteField, FieldLenField, \
     FlagsField, IntEnumField, IntField, MACField, PacketField, \
     PacketListField, ShortEnumField, ShortField, StrField, StrFixedLenField, \
-    StrLenField, UTCTimeField, X3BytesField, XIntField, XShortEnumField
+    StrLenField, UTCTimeField, X3BytesField, XIntField, XShortEnumField, \
+    PacketLenField
 from scapy.layers.inet import UDP
 from scapy.layers.inet6 import DomainNameListField, IP6Field, IP6ListField, IPv6
 from scapy.packet import Packet, bind_bottom_up
@@ -443,20 +444,6 @@ class DHCP6OptElapsedTime(_DHCP6OptGuessPayload):# RFC sect 22.9
                     _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
@@ -912,6 +899,17 @@ class DHCP6(_DHCP6OptGuessPayload):
     def hashret(self):
         return struct.pack("!I", self.trid)[1:4]
 
+#### 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), 
+                    FieldLenField("optlen", None, fmt="!H",
+                        length_of="message"),
+                    PacketLenField("message", DHCP6(), DHCP6,
+                        length_from=lambda p: p.optlen) ]
+
 #####################################################################
 # Solicit Message : sect 17.1.1 RFC3315
 # - sent by client


Test patch:
diff --git a/test/regression.uts b/test/regression.uts
--- a/test/regression.uts
+++ b/test/regression.uts
@@ -4338,7 +4338,7 @@ a.msgtype == 12 and a.hopcount == 0 and a.linkaddr == "::" and a.peeraddr == "::
 
 = DHCP6_RelayForward - Dissection with options
 a = DHCP6_RelayForward(b'\x0c\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\t\x00\x04\x00\x01\x00\x00')
-a.msgtype == 12 and DHCP6OptRelayMsg in a and DHCP6OptClientId in a
+a.msgtype == 12 and DHCP6OptRelayMsg in a and isinstance(a.message, DHCP6)
 
 
 ############
@@ -4346,12 +4346,16 @@ a.msgtype == 12 and DHCP6OptRelayMsg in a and DHCP6OptClientId in a
 + Test DHCP6 Messages - DHCP6OptRelayMsg
 
 = DHCP6OptRelayMsg - Basic Instantiation
-str(DHCP6OptRelayMsg(optcode=37)) == b'\x00%\x00\x00'
+str(DHCP6OptRelayMsg(optcode=37)) == b'\x00%\x00\x04\x00\x00\x00\x00'
 
 = DHCP6OptRelayMsg - Basic Dissection
 a=DHCP6OptRelayMsg(b'\x00\r\x00\x00')
 assert a.optcode == 13
 
+= DHCP6OptRelayMsg - Embedded DHCP6 packet
+p = DHCP6OptRelayMsg(b'\x00\t\x00\x04\x00\x00\x00\x00')
+isinstance(p.message, DHCP6)
+
 ############
 ############
 + Test DHCP6 Messages - DHCP6_RelayReply