lib/packet/tcp: Parse TCP Option field

Suggested-by: Ramana Reddy <gtvrreddy@gmail.com>
Signed-off-by: Minoru TAKAHASHI <takahashi.minoru7@gmail.com>
Signed-off-by: IWASE Yusuke <iwase.yusuke0@gmail.com>
Signed-off-by: FUJITA Tomonori <fujita.tomonori@lab.ntt.co.jp>
This commit is contained in:
IWASE Yusuke 2015-12-24 15:55:22 +09:00 committed by FUJITA Tomonori
parent d9ecfccdc8
commit 9521e46181

View File

@ -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
@TCPOption.register(TCP_OPTION_KIND_END_OF_OPTION_LIST,
TCPOption.NO_BODY_OFFSET)
class TCPOptionEndOfOptionList(TCPOption):
pass
@TCPOption.register(TCP_OPTION_KIND_NO_OPERATION,
TCPOption.NO_BODY_OFFSET)
class TCPOptionNoOperation(TCPOption):
pass
@TCPOption.register(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)
@TCPOption.register(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)
@TCPOption.register(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)
@TCPOption.register(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
@TCPOption.register(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)
@TCPOption.register(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)
@TCPOption.register(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