diff --git a/doc/source/library_packet_ref.rst b/doc/source/library_packet_ref.rst index 29c68a20..56c09f9f 100644 --- a/doc/source/library_packet_ref.rst +++ b/doc/source/library_packet_ref.rst @@ -56,6 +56,9 @@ Protocol Header classes .. automodule:: ryu.lib.packet.vxlan :members: +.. automodule:: ryu.lib.packet.geneve + :members: + .. automodule:: ryu.lib.packet.gre :members: diff --git a/ryu/lib/packet/ether_types.py b/ryu/lib/packet/ether_types.py index bfa78995..e8287d3b 100644 --- a/ryu/lib/packet/ether_types.py +++ b/ryu/lib/packet/ether_types.py @@ -16,6 +16,7 @@ ETH_TYPE_IP = 0x0800 ETH_TYPE_ARP = 0x0806 +ETH_TYPE_TEB = 0x6558 ETH_TYPE_8021Q = 0x8100 ETH_TYPE_IPV6 = 0x86dd ETH_TYPE_SLOW = 0x8809 diff --git a/ryu/lib/packet/geneve.py b/ryu/lib/packet/geneve.py new file mode 100644 index 00000000..6cbba495 --- /dev/null +++ b/ryu/lib/packet/geneve.py @@ -0,0 +1,189 @@ +# Copyright (C) 2016 Nippon Telegraph and Telephone Corporation. +# +# 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. + +""" +Geneve packet parser/serializer +""" + +import struct + +from ryu.lib import stringify +from ryu.lib import type_desc +from . import packet_base +from . import ether_types + + +UDP_DST_PORT = 6081 + + +class geneve(packet_base.PacketBase): + """Geneve (RFC draft-ietf-nvo3-geneve-03) header encoder/decoder class. + + 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 + ============== ======================================================== + version Version. + opt_len The length of the options fields. + flags Flag field for OAM packet and Critical options present. + protocol Protocol Type field. + The Protocol Type is defined as "ETHER TYPES". + vni Identifier for unique element of virtual network. + options List of ``Option*`` instance. + ============== ======================================================== + """ + _HEADER_FMT = "!BBHI" + _MIN_LEN = struct.calcsize(_HEADER_FMT) + + # 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 + # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + # |Ver| Opt Len |O|C| Rsvd. | Protocol Type | + # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + # | Virtual Network Identifier (VNI) | Reserved | + # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + # | Variable Length Options | + # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + + # Flags + OAM_PACKET_FLAG = 1 << 7 + CRITICAL_OPTIONS_FLAG = 1 << 6 + + def __init__(self, version=0, opt_len=0, flags=0, + protocol=ether_types.ETH_TYPE_TEB, vni=None, options=None): + super(geneve, self).__init__() + + self.version = version + self.opt_len = opt_len + assert (flags & 0x3F) == 0 + self.flags = flags + self.protocol = protocol + self.vni = vni + for o in options: + assert isinstance(o, Option) + self.options = options + + @classmethod + def parser(cls, buf): + (ver_opt_len, flags, protocol, + vni) = struct.unpack_from(cls._HEADER_FMT, buf) + version = ver_opt_len >> 6 + # The Opt Len field expressed in four byte multiples. + opt_len = (ver_opt_len & 0x3F) * 4 + + opt_bin = buf[cls._MIN_LEN:cls._MIN_LEN + opt_len] + options = [] + while opt_bin: + option, opt_bin = Option.parser(opt_bin) + options.append(option) + + msg = cls(version, opt_len, flags, protocol, vni >> 8, options) + + from . import ethernet + geneve._TYPES = ethernet.ethernet._TYPES + geneve.register_packet_type(ethernet.ethernet, + ether_types.ETH_TYPE_TEB) + + return (msg, geneve.get_packet_type(protocol), + buf[cls._MIN_LEN + opt_len:]) + + def serialize(self, payload=None, prev=None): + tunnel_options = bytearray() + for o in self.options: + tunnel_options += o.serialize() + self.opt_len = len(tunnel_options) + # The Opt Len field expressed in four byte multiples. + opt_len = self.opt_len // 4 + + return (struct.pack(self._HEADER_FMT, + (self.version << 6) | opt_len, + self.flags, self.protocol, self.vni << 8) + + tunnel_options) + + +class Option(stringify.StringifyMixin, type_desc.TypeDisp): + """ + Tunnel Options + """ + _OPTION_PACK_STR = "!HBB" + _OPTION_LEN = struct.calcsize(_OPTION_PACK_STR) + + # 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 Class | Type |R|R|R| Length | + # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + # | Variable Option Data | + # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + def __init__(self, option_class=None, type_=None, length=0): + super(Option, self).__init__() + if option_class is None or type_ is None: + (option_class, type_) = self._rev_lookup_type(self.__class__) + self.option_class = option_class + self.type = type_ + self.length = length + + @classmethod + def parse_value(cls, buf): + # Sub-classes should override this method, if needed. + return {} + + def serialize_value(self): + # Sub-classes should override this method, if needed. + return b'' + + @classmethod + def parser(cls, buf): + (option_class, type_, + length) = struct.unpack_from(cls._OPTION_PACK_STR, buf) + + # The Length field expressed in four byte multiples. + length *= 4 + subcls = Option._lookup_type((option_class, type_)) + + return ( + subcls(option_class=option_class, type_=type_, length=length, + **subcls.parse_value( + buf[cls._OPTION_LEN:cls._OPTION_LEN + length])), + buf[cls._OPTION_LEN + length:]) + + def serialize(self, _payload=None, _prev=None): + data = self.serialize_value() + self.length = len(data) + # The Length field expressed in four byte multiples. + length = self.length // 4 + + return (struct.pack(self._OPTION_PACK_STR, int(self.option_class), + self.type, length) + data) + + +@Option.register_unknown_type() +class OptionDataUnknown(Option): + """ + Unknown Option Class and Type specific Option + """ + def __init__(self, buf, option_class=None, type_=None, length=0): + super(OptionDataUnknown, self).__init__(option_class=option_class, + type_=type_, + length=length) + self.buf = buf + + @classmethod + def parse_value(cls, buf): + return {"buf": buf} + + def serialize_value(self): + return self.buf diff --git a/ryu/lib/packet/udp.py b/ryu/lib/packet/udp.py index f39fb901..9d3ed3c5 100644 --- a/ryu/lib/packet/udp.py +++ b/ryu/lib/packet/udp.py @@ -19,6 +19,7 @@ from . import packet_base from . import packet_utils from . import dhcp from . import vxlan +from . import geneve class udp(packet_base.PacketBase): @@ -60,6 +61,8 @@ class udp(packet_base.PacketBase): if (dst_port == vxlan.UDP_DST_PORT or dst_port == vxlan.UDP_DST_PORT_OLD): return vxlan.vxlan + if dst_port == geneve.UDP_DST_PORT: + return geneve.geneve return None @classmethod