1 # Copyright (C) 2012 Nippon Telegraph and Telephone Corporation.
3 # Licensed under the Apache License, Version 2.0 (the "License");
4 # you may not use this file except in compliance with the License.
5 # You may obtain a copy of the License at
7 # http://www.apache.org/licenses/LICENSE-2.0
9 # Unless required by applicable law or agreed to in writing, software
10 # distributed under the License is distributed on an "AS IS" BASIS,
11 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
13 # See the License for the specific language governing permissions and
14 # limitations under the License.
21 from ryu.lib import stringify
22 from . import packet_base
23 from . import packet_utils
25 from . import openflow
29 LOG = logging.getLogger(__name__)
31 # TCP Option Kind Numbers
32 TCP_OPTION_KIND_END_OF_OPTION_LIST = 0 # End of Option List
33 TCP_OPTION_KIND_NO_OPERATION = 1 # No-Operation
34 TCP_OPTION_KIND_MAXIMUM_SEGMENT_SIZE = 2 # Maximum Segment Size
35 TCP_OPTION_KIND_WINDOW_SCALE = 3 # Window Scale
36 TCP_OPTION_KIND_SACK_PERMITTED = 4 # SACK Permitted
37 TCP_OPTION_KIND_SACK = 5 # SACK
38 TCP_OPTION_KIND_TIMESTAMPS = 8 # Timestamps
39 TCP_OPTION_KIND_USER_TIMEOUT = 28 # User Timeout Option
40 TCP_OPTION_KIND_AUTHENTICATION = 29 # TCP Authentication Option (TCP-AO)
53 class tcp(packet_base.PacketBase):
54 """TCP (RFC 793) header encoder/decoder class.
56 An instance has the following attributes at least.
57 Most of them are same to the on-wire counterparts but in host byte order.
58 __init__ takes the corresponding args in this order.
60 ============== ====================
62 ============== ====================
64 dst_port Destination Port
66 ack Acknowledgement Number
68 (0 means automatically-calculate when encoding)
72 (0 means automatically-calculate when encoding)
74 option List of ``TCPOption`` sub-classes or an bytearray
77 ============== ====================
80 _PACK_STR = '!HHIIBBHHH'
81 _MIN_LEN = struct.calcsize(_PACK_STR)
83 def __init__(self, src_port=1, dst_port=1, seq=0, ack=0, offset=0,
84 bits=0, window_size=0, csum=0, urgent=0, option=None):
85 super(tcp, self).__init__()
86 self.src_port = src_port
87 self.dst_port = dst_port
92 self.window_size = window_size
98 return self.offset * 4
100 def has_flags(self, *flags):
101 """Check if flags are set on this packet.
103 returns boolean if all passed flags is set
107 >>> pkt = tcp.tcp(bits=(tcp.TCP_SYN | tcp.TCP_ACK))
108 >>> pkt.has_flags(tcp.TCP_SYN, tcp.TCP_ACK)
113 return (self.bits & mask) == mask
116 def get_payload_type(src_port, dst_port):
117 from ryu.ofproto.ofproto_common import OFP_TCP_PORT, OFP_SSL_PORT_OLD
118 if bgp.TCP_SERVER_PORT in [src_port, dst_port]:
119 return bgp.BGPMessage
120 elif(src_port in [OFP_TCP_PORT, OFP_SSL_PORT_OLD] or
121 dst_port in [OFP_TCP_PORT, OFP_SSL_PORT_OLD]):
122 return openflow.openflow
123 elif src_port == zebra.ZEBRA_PORT:
124 return zebra._ZebraMessageFromZebra
125 elif dst_port == zebra.ZEBRA_PORT:
126 return zebra.ZebraMessage
131 def parser(cls, buf):
132 (src_port, dst_port, seq, ack, offset, bits, window_size,
133 csum, urgent) = struct.unpack_from(cls._PACK_STR, buf)
137 if length > tcp._MIN_LEN:
138 option_buf = buf[tcp._MIN_LEN:length]
142 opt, option_buf = TCPOption.parser(option_buf)
146 'Encounter an error during parsing TCP option field.'
147 'Skip parsing TCP option.')
148 option = buf[tcp._MIN_LEN:length]
151 msg = cls(src_port, dst_port, seq, ack, offset, bits,
152 window_size, csum, urgent, option)
154 return msg, cls.get_payload_type(src_port, dst_port), buf[length:]
156 def serialize(self, payload, prev):
157 offset = self.offset << 4
158 h = bytearray(struct.pack(
159 tcp._PACK_STR, self.src_port, self.dst_port, self.seq,
160 self.ack, offset, self.bits, self.window_size, self.csum,
164 if isinstance(self.option, (list, tuple)):
165 option_buf = bytearray()
166 for opt in self.option:
167 option_buf.extend(opt.serialize())
169 mod = len(option_buf) % 4
171 h.extend(self.option)
172 mod = len(self.option) % 4
174 h.extend(bytearray(4 - mod))
176 offset = self.offset << 2
178 h.extend(bytearray(offset - len(h)))
181 self.offset = len(h) >> 2
182 offset = self.offset << 4
183 struct.pack_into('!B', h, 12, offset)
186 total_length = len(h) + len(payload)
187 self.csum = packet_utils.checksum_ip(prev, total_length,
189 struct.pack_into('!H', h, 16, self.csum)
190 return six.binary_type(h)
193 class TCPOption(stringify.StringifyMixin):
195 _KIND_PACK_STR = '!B' # kind
196 NO_BODY_OFFSET = 1 # kind(1 byte)
197 WITH_BODY_OFFSET = 2 # kind(1 byte) + length(1 byte)
201 def __init__(self, kind=None, length=None):
202 self.kind = self.cls_kind if kind is None else kind
203 self.length = self.cls_length if length is None else length
206 def register(cls, kind, length):
207 def _register(subcls):
208 subcls.cls_kind = kind
209 subcls.cls_length = length
210 cls._KINDS[kind] = subcls
216 # For no body TCP Options
217 return cls(cls.cls_kind, cls.cls_length), buf[cls.cls_length:]
220 def parser(cls, buf):
221 (kind,) = struct.unpack_from(cls._KIND_PACK_STR, buf)
222 subcls = cls._KINDS.get(kind)
224 subcls = TCPOptionUnknown
225 return subcls.parse(buf)
228 # For no body TCP Options
229 return struct.pack(self._KIND_PACK_STR, self.cls_kind)
232 class TCPOptionUnknown(TCPOption):
233 _PACK_STR = '!BB' # kind, length
235 def __init__(self, value, kind, length):
236 super(TCPOptionUnknown, self).__init__(kind, length)
237 self.value = value if value is not None else b''
241 (kind, length) = struct.unpack_from(cls._PACK_STR, buf)
242 value = buf[2:length]
243 return cls(value, kind, length), buf[length:]
246 self.length = self.WITH_BODY_OFFSET + len(self.value)
247 return struct.pack(self._PACK_STR,
248 self.kind, self.length) + self.value
251 @TCPOption.register(TCP_OPTION_KIND_END_OF_OPTION_LIST,
252 TCPOption.NO_BODY_OFFSET)
253 class TCPOptionEndOfOptionList(TCPOption):
257 @TCPOption.register(TCP_OPTION_KIND_NO_OPERATION,
258 TCPOption.NO_BODY_OFFSET)
259 class TCPOptionNoOperation(TCPOption):
263 @TCPOption.register(TCP_OPTION_KIND_MAXIMUM_SEGMENT_SIZE, 4)
264 class TCPOptionMaximumSegmentSize(TCPOption):
265 _PACK_STR = '!BBH' # kind, length, max_seg_size
267 def __init__(self, max_seg_size, kind=None, length=None):
268 super(TCPOptionMaximumSegmentSize, self).__init__(kind, length)
269 self.max_seg_size = max_seg_size
273 (_, _, max_seg_size) = struct.unpack_from(cls._PACK_STR, buf)
274 return cls(max_seg_size,
275 cls.cls_kind, cls.cls_length), buf[cls.cls_length:]
278 return struct.pack(self._PACK_STR,
279 self.kind, self.length, self.max_seg_size)
282 @TCPOption.register(TCP_OPTION_KIND_WINDOW_SCALE, 3)
283 class TCPOptionWindowScale(TCPOption):
284 _PACK_STR = '!BBB' # kind, length, shift_cnt
286 def __init__(self, shift_cnt, kind=None, length=None):
287 super(TCPOptionWindowScale, self).__init__(kind, length)
288 self.shift_cnt = shift_cnt
292 (_, _, shift_cnt) = struct.unpack_from(cls._PACK_STR, buf)
293 return cls(shift_cnt,
294 cls.cls_kind, cls.cls_length), buf[cls.cls_length:]
297 return struct.pack(self._PACK_STR,
298 self.kind, self.length, self.shift_cnt)
301 @TCPOption.register(TCP_OPTION_KIND_SACK_PERMITTED, 2)
302 class TCPOptionSACKPermitted(TCPOption):
303 _PACK_STR = '!BB' # kind, length
306 return struct.pack(self._PACK_STR, self.kind, self.length)
309 @TCPOption.register(TCP_OPTION_KIND_SACK,
310 2) # variable length. 2 is the length except blocks.
311 class TCPOptionSACK(TCPOption):
312 _PACK_STR = '!BB' # kind, length
313 _BLOCK_PACK_STR = '!II' # Left Edge of Block, Right Edge of Block
315 def __init__(self, blocks, kind=None, length=None):
316 super(TCPOptionSACK, self).__init__(kind, length)
317 # blocks is a list of tuple as followings.
319 # ('Left Edge of 1st Block', 'Right Edge of 1st Block'),
321 # ('Left Edge of nth Block', 'Right Edge of nth Block')
327 (_, length) = struct.unpack_from(cls._PACK_STR, buf)
328 blocks_buf = buf[2:length]
331 lr_block = struct.unpack_from(cls._BLOCK_PACK_STR, blocks_buf)
332 blocks.append(lr_block) # (left, right)
333 blocks_buf = blocks_buf[8:]
334 return cls(blocks, cls.cls_kind, length), buf[length:]
338 for left, right in self.blocks:
339 buf += struct.pack(self._BLOCK_PACK_STR, left, right)
340 self.length = self.cls_length + len(buf)
341 return struct.pack(self._PACK_STR, self.kind, self.length) + buf
344 @TCPOption.register(TCP_OPTION_KIND_TIMESTAMPS, 10)
345 class TCPOptionTimestamps(TCPOption):
346 _PACK_STR = '!BBII' # kind, length, ts_val, ts_ecr
348 def __init__(self, ts_val, ts_ecr, kind=None, length=None):
349 super(TCPOptionTimestamps, self).__init__(kind, length)
355 (_, _, ts_val, ts_ecr) = struct.unpack_from(cls._PACK_STR, buf)
356 return cls(ts_val, ts_ecr,
357 cls.cls_kind, cls.cls_length), buf[cls.cls_length:]
360 return struct.pack(self._PACK_STR,
361 self.kind, self.length, self.ts_val, self.ts_ecr)
364 @TCPOption.register(TCP_OPTION_KIND_USER_TIMEOUT, 4)
365 class TCPOptionUserTimeout(TCPOption):
366 _PACK_STR = '!BBH' # kind, length, granularity(1bit)|user_timeout(15bit)
368 def __init__(self, granularity, user_timeout, kind=None, length=None):
369 super(TCPOptionUserTimeout, self).__init__(kind, length)
370 self.granularity = granularity
371 self.user_timeout = user_timeout
375 (_, _, body) = struct.unpack_from(cls._PACK_STR, buf)
376 granularity = body >> 15
377 user_timeout = body & 0x7fff
378 return cls(granularity, user_timeout,
379 cls.cls_kind, cls.cls_length), buf[cls.cls_length:]
382 body = (self.granularity << 15) | self.user_timeout
383 return struct.pack(self._PACK_STR, self.kind, self.length, body)
386 @TCPOption.register(TCP_OPTION_KIND_AUTHENTICATION,
387 4) # variable length. 4 is the length except MAC.
388 class TCPOptionAuthentication(TCPOption):
389 _PACK_STR = '!BBBB' # kind, length, key_id, r_next_key_id
391 def __init__(self, key_id, r_next_key_id, mac, kind=None, length=None):
392 super(TCPOptionAuthentication, self).__init__(kind, length)
394 self.r_next_key_id = r_next_key_id
400 key_id, r_next_key_id) = struct.unpack_from(cls._PACK_STR, buf)
402 return cls(key_id, r_next_key_id, mac,
403 cls.cls_kind, length), buf[length:]
406 self.length = self.cls_length + len(self.mac)
407 return struct.pack(self._PACK_STR, self.kind, self.length,
408 self.key_id, self.r_next_key_id) + self.mac