Suggested-by: Ramana Reddy <[email protected]>
Signed-off-by: Minoru TAKAHASHI <[email protected]>
Signed-off-by: IWASE Yusuke <[email protected]>
---
 ryu/lib/packet/tcp.py | 264 ++++++++++++++++++++++++++++++++++++++++++++++++--
 1 file changed, 258 insertions(+), 6 deletions(-)

diff --git a/ryu/lib/packet/tcp.py b/ryu/lib/packet/tcp.py
index 7262e24..7b5a7c7 100644
--- a/ryu/lib/packet/tcp.py
+++ b/ryu/lib/packet/tcp.py
@@ -15,9 +15,25 @@
 
 import six
 import struct
+import logging
 
 from . import packet_base
 from . import packet_utils
+from ryu.lib import stringify
+
+
+LOG = logging.getLogger(__name__)
+
+# TCP Option Kind Numbers
+TCP_OPTION_KIND_END_OF_OPTION_LIST = 0    # End of Option List
+TCP_OPTION_KIND_NO_OPERATION = 1          # No-Operation
+TCP_OPTION_KIND_MAXIMUM_SEGMENT_SIZE = 2  # Maximum Segment Size
+TCP_OPTION_KIND_WINDOW_SCALE = 3          # Window Scale
+TCP_OPTION_KIND_SACK_PERMITTED = 4        # SACK Permitted
+TCP_OPTION_KIND_SACK = 5                  # SACK
+TCP_OPTION_KIND_TIMESTAMPS = 8            # Timestamps
+TCP_OPTION_KIND_USER_TIMEOUT = 28         # User Timeout Option
+TCP_OPTION_KIND_AUTHENTICATION = 29       # TCP Authentication Option (TCP-AO)
 
 
 class tcp(packet_base.PacketBase):
@@ -41,7 +57,8 @@ class tcp(packet_base.PacketBase):
     csum           Checksum \
                    (0 means automatically-calculate when encoding)
     urgent         Urgent Pointer
-    option         An bytearray containing Options and following Padding. \
+    option         List of ``TCPOption`` sub-classes or an bytearray
+                   containing options. \
                    None if no options.
     ============== ====================
     """
@@ -70,11 +87,21 @@ class tcp(packet_base.PacketBase):
     def parser(cls, buf):
         (src_port, dst_port, seq, ack, offset, bits, window_size,
          csum, urgent) = struct.unpack_from(cls._PACK_STR, buf)
-        offset = offset >> 4
-        bits = bits & 0x3f
+        offset >>= 4
+        bits &= 0x3f
         length = offset * 4
         if length > tcp._MIN_LEN:
-            option = buf[tcp._MIN_LEN:length]
+            option_buf = buf[tcp._MIN_LEN:length]
+            try:
+                option = []
+                while option_buf:
+                    opt, option_buf = TCPOption.parser(option_buf)
+                    option.append(opt)
+            except struct.error:
+                LOG.warning(
+                    'Encounter an error during parsing TCP option field.'
+                    'Skip parsing TCP option.')
+                option = buf[tcp._MIN_LEN:length]
         else:
             option = None
         msg = cls(src_port, dst_port, seq, ack, offset, bits,
@@ -90,8 +117,15 @@ class tcp(packet_base.PacketBase):
             self.urgent))
 
         if self.option:
-            h.extend(self.option)
-            mod = len(self.option) % 4
+            if isinstance(self.option, (list, tuple)):
+                option_buf = bytearray()
+                for opt in self.option:
+                    option_buf.extend(opt.serialize())
+                h.extend(option_buf)
+                mod = len(option_buf) % 4
+            else:
+                h.extend(self.option)
+                mod = len(self.option) % 4
             if mod:
                 h.extend(bytearray(4 - mod))
             if self.offset:
@@ -110,3 +144,221 @@ class tcp(packet_base.PacketBase):
                                                  h + payload)
             struct.pack_into('!H', h, 16, self.csum)
         return six.binary_type(h)
+
+
+class TCPOption(stringify.StringifyMixin):
+    _KINDS = {}
+    _KIND_PACK_STR = '!B'  # kind
+    NO_BODY_OFFSET = 1     # kind(1 byte)
+    WITH_BODY_OFFSET = 2   # kind(1 byte) + length(1 byte)
+    cls_kind = None
+    cls_length = None
+
+    def __init__(self, kind=None, length=None):
+        self.kind = self.cls_kind if kind is None else kind
+        self.length = self.cls_length if length is None else length
+
+    @classmethod
+    def register(cls, kind, length):
+        def _register(subcls):
+            subcls.cls_kind = kind
+            subcls.cls_length = length
+            cls._KINDS[kind] = subcls
+            return subcls
+        return _register
+
+    @classmethod
+    def parse(cls, buf):
+        # For no body TCP Options
+        return cls(cls.cls_kind, cls.cls_length), buf[cls.cls_length:]
+
+    @classmethod
+    def parser(cls, buf):
+        (kind,) = struct.unpack_from(cls._KIND_PACK_STR, buf)
+        subcls = cls._KINDS.get(kind)
+        if not subcls:
+            subcls = TCPOptionUnknown
+        return subcls.parse(buf)
+
+    def serialize(self):
+        # For no body TCP Options
+        return struct.pack(self._KIND_PACK_STR, self.cls_kind)
+
+
+class TCPOptionUnknown(TCPOption):
+    _PACK_STR = '!BB'  # kind, length
+
+    def __init__(self, value, kind, length):
+        super(TCPOptionUnknown, self).__init__(kind, length)
+        self.value = value if value is not None else b''
+
+    @classmethod
+    def parse(cls, buf):
+        (kind, length) = struct.unpack_from(cls._PACK_STR, buf)
+        value = buf[2:length]
+        return cls(value, kind, length), buf[length:]
+
+    def serialize(self):
+        self.length = self.WITH_BODY_OFFSET + len(self.value)
+        return struct.pack(self._PACK_STR,
+                           self.kind, self.length) + self.value
+
+
[email protected](TCP_OPTION_KIND_END_OF_OPTION_LIST,
+                    TCPOption.NO_BODY_OFFSET)
+class TCPOptionEndOfOptionList(TCPOption):
+    pass
+
+
[email protected](TCP_OPTION_KIND_NO_OPERATION,
+                    TCPOption.NO_BODY_OFFSET)
+class TCPOptionNoOperation(TCPOption):
+    pass
+
+
[email protected](TCP_OPTION_KIND_MAXIMUM_SEGMENT_SIZE, 4)
+class TCPOptionMaximumSegmentSize(TCPOption):
+    _PACK_STR = '!BBH'  # kind, length, max_seg_size
+
+    def __init__(self, max_seg_size, kind=None, length=None):
+        super(TCPOptionMaximumSegmentSize, self).__init__(kind, length)
+        self.max_seg_size = max_seg_size
+
+    @classmethod
+    def parse(cls, buf):
+        (_, _, max_seg_size) = struct.unpack_from(cls._PACK_STR, buf)
+        return cls(max_seg_size,
+                   cls.cls_kind, cls.cls_length), buf[cls.cls_length:]
+
+    def serialize(self):
+        return struct.pack(self._PACK_STR,
+                           self.kind, self.length, self.max_seg_size)
+
+
[email protected](TCP_OPTION_KIND_WINDOW_SCALE, 3)
+class TCPOptionWindowScale(TCPOption):
+    _PACK_STR = '!BBB'  # kind, length, shift_cnt
+
+    def __init__(self, shift_cnt, kind=None, length=None):
+        super(TCPOptionWindowScale, self).__init__(kind, length)
+        self.shift_cnt = shift_cnt
+
+    @classmethod
+    def parse(cls, buf):
+        (_, _, shift_cnt) = struct.unpack_from(cls._PACK_STR, buf)
+        return cls(shift_cnt,
+                   cls.cls_kind, cls.cls_length), buf[cls.cls_length:]
+
+    def serialize(self):
+        return struct.pack(self._PACK_STR,
+                           self.kind, self.length, self.shift_cnt)
+
+
[email protected](TCP_OPTION_KIND_SACK_PERMITTED, 2)
+class TCPOptionSACKPermitted(TCPOption):
+    _PACK_STR = '!BB'  # kind, length
+
+    def serialize(self):
+        return struct.pack(self._PACK_STR, self.kind, self.length)
+
+
[email protected](TCP_OPTION_KIND_SACK,
+                    2)  # variable length. 2 is the length except blocks.
+class TCPOptionSACK(TCPOption):
+    _PACK_STR = '!BB'        # kind, length
+    _BLOCK_PACK_STR = '!II'  # Left Edge of Block, Right Edge of Block
+
+    def __init__(self, blocks, kind=None, length=None):
+        super(TCPOptionSACK, self).__init__(kind, length)
+        # blocks is a list of tuple as followings.
+        # self.blocks = [
+        #     ('Left Edge of 1st Block', 'Right Edge of 1st Block'),
+        #     ...
+        #     ('Left Edge of nth Block', 'Right Edge of nth Block')
+        # ]
+        self.blocks = blocks
+
+    @classmethod
+    def parse(cls, buf):
+        (_, length) = struct.unpack_from(cls._PACK_STR, buf)
+        blocks_buf = buf[2:length]
+        blocks = []
+        while blocks_buf:
+            lr_block = struct.unpack_from(cls._BLOCK_PACK_STR, blocks_buf)
+            blocks.append(lr_block)  # (left, right)
+            blocks_buf = blocks_buf[8:]
+        return cls(blocks, cls.cls_kind, length), buf[length:]
+
+    def serialize(self):
+        buf = bytearray()
+        for left, right in self.blocks:
+            buf += struct.pack(self._BLOCK_PACK_STR, left, right)
+        self.length = self.cls_length + len(buf)
+        return struct.pack(self._PACK_STR, self.kind, self.length) + buf
+
+
[email protected](TCP_OPTION_KIND_TIMESTAMPS, 10)
+class TCPOptionTimestamps(TCPOption):
+    _PACK_STR = '!BBII'  # kind, length, ts_val, ts_ecr
+
+    def __init__(self, ts_val, ts_ecr, kind=None, length=None):
+        super(TCPOptionTimestamps, self).__init__(kind, length)
+        self.ts_val = ts_val
+        self.ts_ecr = ts_ecr
+
+    @classmethod
+    def parse(cls, buf):
+        (_, _, ts_val, ts_ecr) = struct.unpack_from(cls._PACK_STR, buf)
+        return cls(ts_val, ts_ecr,
+                   cls.cls_kind, cls.cls_length), buf[cls.cls_length:]
+
+    def serialize(self):
+        return struct.pack(self._PACK_STR,
+                           self.kind, self.length, self.ts_val, self.ts_ecr)
+
+
[email protected](TCP_OPTION_KIND_USER_TIMEOUT, 4)
+class TCPOptionUserTimeout(TCPOption):
+    _PACK_STR = '!BBH'  # kind, length, granularity(1bit)|user_timeout(15bit)
+
+    def __init__(self, granularity, user_timeout, kind=None, length=None):
+        super(TCPOptionUserTimeout, self).__init__(kind, length)
+        self.granularity = granularity
+        self.user_timeout = user_timeout
+
+    @classmethod
+    def parse(cls, buf):
+        (_, _, body) = struct.unpack_from(cls._PACK_STR, buf)
+        granularity = body >> 15
+        user_timeout = body & 0x7fff
+        return cls(granularity, user_timeout,
+                   cls.cls_kind, cls.cls_length), buf[cls.cls_length:]
+
+    def serialize(self):
+        body = (self.granularity << 15) | self.user_timeout
+        return struct.pack(self._PACK_STR, self.kind, self.length, body)
+
+
[email protected](TCP_OPTION_KIND_AUTHENTICATION,
+                    4)  # variable length. 4 is the length except MAC.
+class TCPOptionAuthentication(TCPOption):
+    _PACK_STR = '!BBBB'  # kind, length, key_id, r_next_key_id
+
+    def __init__(self, key_id, r_next_key_id, mac, kind=None, length=None):
+        super(TCPOptionAuthentication, self).__init__(kind, length)
+        self.key_id = key_id
+        self.r_next_key_id = r_next_key_id
+        self.mac = mac
+
+    @classmethod
+    def parse(cls, buf):
+        (_, length,
+         key_id, r_next_key_id) = struct.unpack_from(cls._PACK_STR, buf)
+        mac = buf[4:length]
+        return cls(key_id, r_next_key_id, mac,
+                   cls.cls_kind, length), buf[length:]
+
+    def serialize(self):
+        self.length = self.cls_length + len(self.mac)
+        return struct.pack(self._PACK_STR, self.kind, self.length,
+                           self.key_id, self.r_next_key_id) + self.mac
-- 
1.9.1


------------------------------------------------------------------------------
_______________________________________________
Ryu-devel mailing list
[email protected]
https://lists.sourceforge.net/lists/listinfo/ryu-devel

Reply via email to