packet: Add VXLAN parser
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 <iwase.yusuke0@gmail.com> Signed-off-by: FUJITA Tomonori <fujita.tomonori@lab.ntt.co.jp>
This commit is contained in:
parent
7f38397442
commit
a6c3d38789
|
@ -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
|
||||
|
|
|
@ -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)
|
|
@ -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)
|
Loading…
Reference in New Issue