Following your comments, this 2nd version of the patch adds the ability to 
dissect and generate DHCPv6 packets. I needed it to develop a simple LDRA app 
(as defined in RFC 6221).

Signed-off-by: Olivier DESNOE <[email protected]>
---
diff --git a/ryu/lib/packet/dhcp6.py b/ryu/lib/packet/dhcp6.py
new file mode 100644
index 0000000..f572c71
--- /dev/null
+++ b/ryu/lib/packet/dhcp6.py
@@ -0,0 +1,293 @@
+# Copyright (C) 2016 Bouygues Telecom.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#    http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+# implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+"""
+DHCPv6 packet parser/serializer
+
+RFC 3315
+DHCP packet format
+
+   The following diagram illustrates the format of DHCP messages sent
+   between clients and servers:
+
+       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
+      +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+      |    msg_type   |               transaction_id                  |
+      +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+      |                                                               |
+      .                            options                            .
+      .                           (variable)                          .
+      |                                                               |
+      +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+
+   There are two relay agent messages, which share the following format:
+
+       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
+      +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+      |    msg_type   |   hop_count   |                               |
+      +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+                               |
+      |                                                               |
+      |                         link_address                          |
+      |                                                               |
+      |                               +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-|
+      |                               |                               |
+      +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+                               |
+      |                                                               |
+      |                         peer_address                          |
+      |                                                               |
+      |                               +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-|
+      |                               |                               |
+      +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+                               |
+      .                                                               .
+      .            options (variable number and length)   ....        .
+      |                                                               |
+      +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+
+"""
+import random
+import struct
+
+from . import packet_base
+from ryu.lib import addrconv
+from ryu.lib import stringify
+
+# DHCPv6 message types
+DHCPV6_SOLICIT = 1
+DHCPV6_ADVERTISE = 2
+DHCPV6_REQUEST = 3
+DHCPV6_CONFIRM = 4
+DHCPV6_RENEW = 5
+DHCPV6_REBIND = 6
+DHCPV6_REPLY = 7
+DHCPV6_RELEASE = 8
+DHCPV6_DECLINE = 9
+DHCPV6_RECONFIGURE = 10
+DHCPV6_INFORMATION_REQUEST = 11
+DHCPV6_RELAY_FORW = 12
+DHCPV6_RELAY_REPL = 13
+
+# DHCPv6 option-codes
+DHCPV6_OPTION_CLIENTID = 1
+DHCPV6_OPTION_SERVERID = 2
+DHCPV6_OPTION_IA_NA = 3
+DHCPV6_OPTION_IA_TA = 4
+DHCPV6_OPTION_IAADDR = 5
+DHCPV6_OPTION_ORO = 6
+DHCPV6_OPTION_PREFERENCE = 7
+DHCPV6_OPTION_ELAPSED_TIME = 8
+DHCPV6_OPTION_RELAY_MSG = 9
+DHCPV6_OPTION_AUTH = 11
+DHCPV6_OPTION_UNICAST = 12
+DHCPV6_OPTION_STATUS_CODE = 13
+DHCPV6_OPTION_RAPID_COMMIT = 14
+DHCPV6_OPTION_USER_CLASS = 15
+DHCPV6_OPTION_VENDOR_CLASS = 16
+DHCPV6_OPTION_VENDOR_OPTS = 17
+DHCPV6_OPTION_INTERFACE_ID = 18
+DHCPV6_OPTION_RECONF_MSG = 19
+DHCPV6_OPTION_RECONF_ACCEPT = 20
+
+
+class dhcp6(packet_base.PacketBase):
+    """DHCPv6 (RFC 3315) header encoder/decoder class.
+
+    The serialized packet would looks like the ones described
+    in the following sections.
+
+    * RFC 3315 DHCP packet format
+
+    An instance has the following attributes at least.
+    Most of them are same to the on-wire counterparts but in host byte order.
+    __init__ takes the corresponding args in this order.
+
+
+    ============== ====================
+    Attribute      Description
+    ============== ====================
+    msg_type       Identifies the DHCP message type
+
+    * For unrelayed messages only:
+    transaction_id The transaction ID for this message exchange.
+
+    * For relayed messages only
+    hop_count      Number of relay agents that have relayed this
+                   message.
+    link_address   A global or site-local address that will be used by
+                   the server to identify the link on which the client
+                   is located.
+    peer_address   The address of the client or relay agent from which
+                   the message to be relayed was received.
+
+    * For unrelayed and relayed messages:
+    options        Options carried in this message
+    ============== ====================
+    """
+    _MIN_LEN = 8
+    _DHCPV6_UNPACK_STR = '!I'
+    _DHCPV6_RELAY_UNPACK_STR = '!H16s16s'
+    _DHCPV6_UNPACK_STR_LEN = struct.calcsize(_DHCPV6_UNPACK_STR)
+    _DHCPV6_RELAY_UNPACK_STR_LEN = struct.calcsize(_DHCPV6_RELAY_UNPACK_STR)
+    _DHCPV6_PACK_STR = '!I'
+    _DHCPV6_RELAY_PACK_STR = '!H16s16s'
+
+    def __init__(self, msg_type, options, transaction_id=None, hop_count=0,
+                 link_address='::', peer_address='::'):
+        super(dhcp6, self).__init__()
+        self.msg_type = msg_type
+        self.options = options
+        if transaction_id is None:
+            self.transaction_id = random.randint(0, 0xffffff)
+        else:
+            self.transaction_id = transaction_id
+        self.hop_count = hop_count
+        self.link_address = link_address
+        self.peer_address = peer_address
+
+    @classmethod
+    def parser(cls, buf):
+        (msg_type, ) = struct.unpack_from('!B', buf)
+
+        buf = b'\x00' + buf[1:]  # unpack xid as a 4-byte integer
+        if msg_type == DHCPV6_RELAY_FORW or msg_type == DHCPV6_RELAY_REPL:
+            (hop_count, link_address, peer_address) \
+                = struct.unpack_from(cls._DHCPV6_RELAY_UNPACK_STR, buf)
+            length = struct.calcsize(cls._DHCPV6_RELAY_UNPACK_STR)
+        else:
+            (transaction_id, ) \
+                = struct.unpack_from(cls._DHCPV6_UNPACK_STR, buf)
+            length = struct.calcsize(cls._DHCPV6_UNPACK_STR)
+
+        if len(buf) > length:
+            parse_opt = options.parser(buf[length:])
+            length += parse_opt.options_len
+            if msg_type == DHCPV6_RELAY_FORW or msg_type == DHCPV6_RELAY_REPL:
+                return (cls(msg_type, parse_opt, 0, hop_count,
+                            addrconv.ipv6.bin_to_text(link_address),
+                            addrconv.ipv6.bin_to_text(peer_address)),
+                        None, buf[length:])
+            else:
+                return (cls(msg_type, parse_opt, transaction_id),
+                        None, buf[length:])
+        else:
+            return None, None, buf
+
+    def serialize(self, payload=None, prev=None):
+        seri_opt = self.options.serialize()
+        if (self.msg_type == DHCPV6_RELAY_FORW or
+                self.msg_type == DHCPV6_RELAY_REPL):
+            pack_str = '%s%ds' % (self._DHCPV6_RELAY_PACK_STR,
+                                  self.options.options_len)
+            buf = struct.pack(pack_str, self.hop_count,
+                              addrconv.ipv6.text_to_bin(self.link_address),
+                              addrconv.ipv6.text_to_bin(self.peer_address),
+                              seri_opt)
+        else:
+            pack_str = '%s%ds' % (self._DHCPV6_PACK_STR,
+                                  self.options.options_len)
+            buf = struct.pack(pack_str, self.transaction_id, seri_opt)
+        return struct.pack('!B', self.msg_type) + buf[1:]
+
+
+class options(stringify.StringifyMixin):
+    """DHCP (RFC 3315) options encoder/decoder class.
+
+    This is used with ryu.lib.packet.dhcp6.dhcp6.
+
+    """
+
+    def __init__(self, option_list=None, options_len=0):
+        super(options, self).__init__()
+        if option_list is None:
+            self.option_list = []
+        else:
+            self.option_list = option_list
+        self.options_len = options_len
+
+    @classmethod
+    def parser(cls, buf):
+        opt_parse_list = []
+        offset = 0
+        while len(buf) > offset:
+            opt_buf = buf[offset:]
+            opt = option.parser(opt_buf)
+            opt_parse_list.append(opt)
+            offset += opt.length + 4
+        return cls(opt_parse_list, len(buf))
+
+    def serialize(self):
+        seri_opt = ""
+        for opt in self.option_list:
+            seri_opt += opt.serialize()
+        if self.options_len == 0:
+            self.options_len = len(seri_opt)
+        return seri_opt
+
+
+class option(stringify.StringifyMixin):
+    """DHCP (RFC 3315) options encoder/decoder class.
+
+    This is used with ryu.lib.packet.dhcp6.dhcp6.options.
+
+    An instance has the following attributes at least.
+    Most of them are same to the on-wire counterparts but in host byte order.
+    __init__ takes the corresponding args in this order.
+
+   The format of DHCP options 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
+      +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+      |          option-code          |           option-len          |
+      +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+      |                          option-data                          |
+      |                      (option-len octets)                      |
+      +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+
+      option-code   An unsigned integer identifying the specific option
+                    type carried in this option.
+
+      option-len    An unsigned integer giving the length of the
+                    option-data field in this option in octets.
+
+      option-data   The data for the option; the format of this data
+                    depends on the definition of the option.
+    """
+    _UNPACK_STR = '!H'
+    _UNPACK_STR_LEN = struct.calcsize(_UNPACK_STR)
+    _PACK_STR = '!HH%ds'
+
+    def __init__(self, code, data, length=0):
+        super(option, self).__init__()
+        self.code = code
+        self.data = data
+        self.length = length
+
+    @classmethod
+    def parser(cls, buf):
+        code = struct.unpack_from(cls._UNPACK_STR, buf)[0]
+        buf = buf[cls._UNPACK_STR_LEN:]
+        length = struct.unpack_from(cls._UNPACK_STR, buf)[0]
+        buf = buf[cls._UNPACK_STR_LEN:]
+        value_unpack_str = '%ds' % length
+        data = struct.unpack_from(value_unpack_str, buf)[0]
+        return cls(code, data, length)
+
+    def serialize(self):
+        if self.length == 0:
+            self.length = len(self.data)
+        options_pack_str = self._PACK_STR % self.length
+        return struct.pack(options_pack_str, self.code, self.length, self.data)
diff --git a/ryu/lib/packet/udp.py b/ryu/lib/packet/udp.py
index f39fb90..7d24ee0 100644
--- a/ryu/lib/packet/udp.py
+++ b/ryu/lib/packet/udp.py
@@ -18,6 +18,7 @@ import struct
 from . import packet_base
 from . import packet_utils
 from . import dhcp
+from . import dhcp6
 from . import vxlan
 
 
@@ -52,11 +53,12 @@ class udp(packet_base.PacketBase):
 
     @staticmethod
     def get_packet_type(src_port, dst_port):
-        if ((src_port == 68 and dst_port == 67) or
-            (src_port == 67 and dst_port == 68) or
-            (src_port == 67 and
-             dst_port == 67)):
+        if ((src_port in [67, 68] and dst_port == 67) or
+                (dst_port in [67, 68] and src_port == 67)):
             return dhcp.dhcp
+        if ((src_port in [546, 547] and dst_port == 547) or
+                (dst_port in [546, 547] and src_port == 547)):
+            return dhcp6.dhcp6
         if (dst_port == vxlan.UDP_DST_PORT or
                 dst_port == vxlan.UDP_DST_PORT_OLD):
             return vxlan.vxlan




------------------------------------------------------------------------------
Check out the vibrant tech community on one of the world's most 
engaging tech sites, SlashDot.org! http://sdm.link/slashdot
_______________________________________________
Ryu-devel mailing list
[email protected]
https://lists.sourceforge.net/lists/listinfo/ryu-devel

Reply via email to