IANA has assigned the value 4789 for the VXLAN UDP destination port, this patch registers dst_port 4789 into UDP packet parser. Additionally, early adopters might be using UDP dst_port 8472, we register this number for the backward compatibility.
Signed-off-by: IWASE Yusuke <[email protected]> --- ryu/lib/packet/udp.py | 11 +++-- ryu/lib/packet/vxlan.py | 90 +++++++++++++++++++++++++++++++++++++ ryu/tests/unit/packet/test_vxlan.py | 75 +++++++++++++++++++++++++++++++ 3 files changed, 173 insertions(+), 3 deletions(-) create mode 100644 ryu/lib/packet/vxlan.py create mode 100644 ryu/tests/unit/packet/test_vxlan.py diff --git a/ryu/lib/packet/udp.py b/ryu/lib/packet/udp.py index bae6d73..b67bd1a 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 vxlan class udp(packet_base.PacketBase): @@ -49,10 +50,14 @@ class udp(packet_base.PacketBase): self.total_length = total_length self.csum = csum - @classmethod - def get_packet_type(cls, src_port, dst_port): - if (src_port == 68 and dst_port == 67) or (src_port == 67 and dst_port == 68): + @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)): return dhcp.dhcp + if (dst_port == vxlan.UDP_DST_PORT or + dst_port == vxlan.UDP_DST_PORT_OLD): + return vxlan.vxlan return None @classmethod diff --git a/ryu/lib/packet/vxlan.py b/ryu/lib/packet/vxlan.py new file mode 100644 index 0000000..d68b9b6 --- /dev/null +++ b/ryu/lib/packet/vxlan.py @@ -0,0 +1,90 @@ +# 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. + +""" +VXLAN packet parser/serializer + +RFC 7348 +VXLAN Header: ++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +|R|R|R|R|I|R|R|R| Reserved | ++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +| VXLAN Network Identifier (VNI) | Reserved | ++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + +- Flags (8 bits): where the I flag MUST be set to 1 for a valid + VXLAN Network ID (VNI). The other 7 bits (designated "R") are + reserved fields and MUST be set to zero on transmission and + ignored on receipt. + +- VXLAN Segment ID/VXLAN Network Identifier (VNI): this is a + 24-bit value used to designate the individual VXLAN overlay + network on which the communicating VMs are situated. VMs in + different VXLAN overlay networks cannot communicate with each + other. + +- Reserved fields (24 bits and 8 bits): MUST be set to zero on + transmission and ignored on receipt. +""" + +import struct +import logging + +from . import packet_base + + +LOG = logging.getLogger(__name__) + +UDP_DST_PORT = 4789 +UDP_DST_PORT_OLD = 8472 # for backward compatibility like Linux + + +class vxlan(packet_base.PacketBase): + """VXLAN (RFC 7348) 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 + ============== ==================== + vni VXLAN Network Identifier + ============== ==================== + """ + + # Note: Python has no format character for 24 bits field. + # we use uint32 format character instead and bit-shift at serializing. + _PACK_STR = '!II' + _MIN_LEN = struct.calcsize(_PACK_STR) + + def __init__(self, vni): + super(vxlan, self).__init__() + self.vni = vni + + @classmethod + def parser(cls, buf): + (flags_reserved, vni_rserved) = struct.unpack_from(cls._PACK_STR, buf) + + # Check VXLAN flags is valid + assert (1 << 3) == (flags_reserved >> 24) + + # Note: To avoid cyclic import, import ethernet module here + from ryu.lib.packet import ethernet + return cls(vni_rserved >> 8), ethernet.ethernet, buf[cls._MIN_LEN:] + + def serialize(self, payload, prev): + return struct.pack(self._PACK_STR, + 1 << (3 + 24), self.vni << 8) diff --git a/ryu/tests/unit/packet/test_vxlan.py b/ryu/tests/unit/packet/test_vxlan.py new file mode 100644 index 0000000..fe418ff --- /dev/null +++ b/ryu/tests/unit/packet/test_vxlan.py @@ -0,0 +1,75 @@ +# 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. + +import logging +import unittest + +from nose.tools import eq_ +from nose.tools import raises + +from ryu.lib.packet import ethernet +from ryu.lib.packet import vxlan + + +LOG = logging.getLogger(__name__) + + +class Test_vxlan(unittest.TestCase): + """ + Test case for VXLAN (RFC 7348) header encoder/decoder class. + """ + + vni = 0x123456 + buf = ( + b'\x08\x00\x00\x00' # flags = R|R|R|R|I|R|R|R (8 bits) + b'\x12\x34\x56\x00' # vni = 0x123456 (24 bits) + b'test_payload' # for test + ) + pkt = vxlan.vxlan(vni) + jsondict = { + 'vxlan': { + 'vni': vni + } + } + + def test_init(self): + eq_(self.vni, self.pkt.vni) + + def test_parser(self): + parsed_pkt, next_proto_cls, rest_buf = vxlan.vxlan.parser(self.buf) + eq_(self.vni, parsed_pkt.vni) + eq_(ethernet.ethernet, next_proto_cls) + eq_(b'test_payload', rest_buf) + + @raises(AssertionError) + def test_invalid_flags(self): + invalid_flags_bug = ( + b'\x00\x00\x00\x00' # all bits are set to zero + b'\x12\x34\x56\x00' # vni = 0x123456 (24 bits) + ) + vxlan.vxlan.parser(invalid_flags_bug) + + def test_serialize(self): + serialized_buf = self.pkt.serialize(payload=None, prev=None) + eq_(self.buf[:vxlan.vxlan._MIN_LEN], serialized_buf) + + def test_from_jsondict(self): + pkt_from_json = vxlan.vxlan.from_jsondict( + self.jsondict[vxlan.vxlan.__name__]) + eq_(self.vni, pkt_from_json.vni) + + def test_to_jsondict(self): + jsondict_from_pkt = self.pkt.to_jsondict() + eq_(self.jsondict, jsondict_from_pkt) -- 2.7.4 ------------------------------------------------------------------------------ Mobile security can be enabling, not merely restricting. Employees who bring their own devices (BYOD) to work are irked by the imposition of MDM restrictions. Mobile Device Manager Plus allows you to control only the apps on BYO-devices by containerizing them, leaving personal data untouched! https://ad.doubleclick.net/ddm/clk/304595813;131938128;j _______________________________________________ Ryu-devel mailing list [email protected] https://lists.sourceforge.net/lists/listinfo/ryu-devel
