From 3ba951f403bb1962f9d07e4bcca891191a28f540 Mon Sep 17 00:00:00 2001 From: Yuichi Ito Date: Tue, 29 Oct 2013 11:28:37 +0900 Subject: [PATCH] packet lib: support Provider Backbone Bridges (PBB, IEEE 802.1ah) Signed-off-by: Yuichi Ito Signed-off-by: FUJITA Tomonori --- doc/source/library_packet_ref.rst | 3 + ryu/lib/packet/pbb.py | 61 +++++++++++ ryu/lib/packet/vlan.py | 6 +- ryu/ofproto/ether.py | 1 + ryu/tests/unit/packet/test_pbb.py | 167 ++++++++++++++++++++++++++++++ 5 files changed, 237 insertions(+), 1 deletion(-) create mode 100644 ryu/lib/packet/pbb.py create mode 100644 ryu/tests/unit/packet/test_pbb.py diff --git a/doc/source/library_packet_ref.rst b/doc/source/library_packet_ref.rst index 13ceec6e..4426cfd3 100644 --- a/doc/source/library_packet_ref.rst +++ b/doc/source/library_packet_ref.rst @@ -29,6 +29,9 @@ Protocol Header classes .. automodule:: ryu.lib.packet.vlan :members: +.. automodule:: ryu.lib.packet.pbb + :members: + .. automodule:: ryu.lib.packet.mpls :members: diff --git a/ryu/lib/packet/pbb.py b/ryu/lib/packet/pbb.py new file mode 100644 index 00000000..6e655e9d --- /dev/null +++ b/ryu/lib/packet/pbb.py @@ -0,0 +1,61 @@ +# Copyright (C) 2013 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 struct +from ryu.lib.packet import packet_base +from ryu.ofproto import ether + + +class itag(packet_base.PacketBase): + """I-TAG (IEEE 802.1ah-2008) 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 + ============== ==================== + pcp Priority Code Point + dei Drop Eligible Indication + uca Use Customer Address + sid Service Instance ID + ============== ==================== + """ + + _PACK_STR = "!I" + _MIN_LEN = struct.calcsize(_PACK_STR) + + def __init__(self, pcp=0, dei=0, uca=0, sid=0): + super(itag, self).__init__() + self.pcp = pcp + self.dei = dei + self.uca = uca + self.sid = sid + + @classmethod + def parser(cls, buf): + (data, ) = struct.unpack_from(cls._PACK_STR, buf) + pcp = data >> 29 + dei = data >> 28 & 1 + uca = data >> 27 & 1 + sid = data & 0x00ffffff + from ryu.lib.packet import ethernet + return (cls(pcp, dei, uca, sid), ethernet.ethernet, + buf[cls._MIN_LEN:]) + + def serialize(self, payload, prev): + data = self.pcp << 29 | self.dei << 28 | self.uca << 27 | self.sid + return struct.pack(self._PACK_STR, data) diff --git a/ryu/lib/packet/vlan.py b/ryu/lib/packet/vlan.py index 2f0e2565..1ee7dd38 100644 --- a/ryu/lib/packet/vlan.py +++ b/ryu/lib/packet/vlan.py @@ -22,6 +22,7 @@ from . import ipv6 from . import lldp from . import slow from . import llc +from . import pbb from ryu.ofproto import ether @@ -98,7 +99,9 @@ class svlan(_vlan): Attribute Description ============== ==================== pcp Priority Code Point - cfi Canonical Format Indicator + cfi Canonical Format Indicator. + In a case to be used as B-TAG, + this field means DEI(Drop Eligible Indication). vid VLAN Identifier ethertype EtherType ============== ==================== @@ -120,3 +123,4 @@ vlan.register_packet_type(slow.slow, ether.ETH_TYPE_SLOW) vlan.register_packet_type(llc.llc, ether.ETH_TYPE_IEEE802_3) svlan.register_packet_type(vlan, ether.ETH_TYPE_8021Q) +svlan.register_packet_type(pbb.itag, ether.ETH_TYPE_8021AH) diff --git a/ryu/ofproto/ether.py b/ryu/ofproto/ether.py index 9e1539c0..f17f7be6 100644 --- a/ryu/ofproto/ether.py +++ b/ryu/ofproto/ether.py @@ -22,4 +22,5 @@ ETH_TYPE_SLOW = 0x8809 ETH_TYPE_MPLS = 0x8847 ETH_TYPE_8021AD = 0x88a8 ETH_TYPE_LLDP = 0x88cc +ETH_TYPE_8021AH = 0x88e7 ETH_TYPE_IEEE802_3 = 0x05dc diff --git a/ryu/tests/unit/packet/test_pbb.py b/ryu/tests/unit/packet/test_pbb.py new file mode 100644 index 00000000..88bcf95d --- /dev/null +++ b/ryu/tests/unit/packet/test_pbb.py @@ -0,0 +1,167 @@ +# Copyright (C) 2013 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 struct +import unittest + +from nose.tools import eq_ +from nose.tools import ok_ +from nose.tools import raises +from ryu.ofproto import ether +from ryu.ofproto import inet +from ryu.lib.packet import ethernet +from ryu.lib.packet import packet +from ryu.lib.packet import ipv4 +from ryu.lib.packet import vlan +from ryu.lib.packet import pbb + + +LOG = logging.getLogger(__name__) + + +class Test_itag(unittest.TestCase): + + pcp = 3 + dei = 0 + uca = 1 + sid = 16770000 + data = pcp << 29 | dei << 28 | uca << 27 | sid + buf = struct.pack(pbb.itag._PACK_STR, data) + it = pbb.itag(pcp, dei, uca, sid) + + def setUp(self): + pass + + def tearDown(self): + pass + + def test_init(self): + eq_(self.pcp, self.it.pcp) + eq_(self.dei, self.it.dei) + eq_(self.uca, self.it.uca) + eq_(self.sid, self.it.sid) + + def test_parser(self): + _res = pbb.itag.parser(self.buf) + if type(_res) is tuple: + res = _res[0] + else: + res = _res + eq_(res.pcp, self.pcp) + eq_(res.dei, self.dei) + eq_(res.uca, self.uca) + eq_(res.sid, self.sid) + + def test_serialize(self): + data = bytearray() + prev = None + buf = self.it.serialize(data, prev) + res = struct.unpack(pbb.itag._PACK_STR, buf) + eq_(res[0], self.data) + + def _build_itag(self): + b_src_mac = '00:07:0d:af:f4:54' + b_dst_mac = '00:00:00:00:00:00' + b_ethertype = ether.ETH_TYPE_8021AD + e1 = ethernet.ethernet(b_dst_mac, b_src_mac, b_ethertype) + + b_pcp = 0 + b_cfi = 0 + b_vid = 32 + b_ethertype = ether.ETH_TYPE_8021Q + bt = vlan.svlan(b_pcp, b_cfi, b_vid, b_ethertype) + + c_src_mac = '11:11:11:11:11:11' + c_dst_mac = 'aa:aa:aa:aa:aa:aa' + c_ethertype = ether.ETH_TYPE_8021AD + e2 = ethernet.ethernet(c_dst_mac, c_src_mac, c_ethertype) + + s_pcp = 0 + s_cfi = 0 + s_vid = 32 + s_ethertype = ether.ETH_TYPE_8021Q + st = vlan.svlan(s_pcp, s_cfi, s_vid, s_ethertype) + + c_pcp = 0 + c_cfi = 0 + c_vid = 32 + c_ethertype = ether.ETH_TYPE_IP + ct = vlan.vlan(c_pcp, c_cfi, c_vid, c_ethertype) + + version = 4 + header_length = 20 + tos = 0 + total_length = 24 + identification = 0x8a5d + flags = 0 + offset = 1480 + ttl = 64 + proto = inet.IPPROTO_ICMP + csum = 0xa7f2 + src = '131.151.32.21' + dst = '131.151.32.129' + option = 'TEST' + ip = ipv4.ipv4(version, header_length, tos, total_length, + identification, flags, offset, ttl, proto, csum, + src, dst, option) + + p = packet.Packet() + + p.add_protocol(e1) + p.add_protocol(bt) + p.add_protocol(self.it) + p.add_protocol(e2) + p.add_protocol(st) + p.add_protocol(ct) + p.add_protocol(ip) + p.serialize() + + return p + + def test_build_itag(self): + p = self._build_itag() + + e = p.get_protocols(ethernet.ethernet) + ok_(e) + ok_(isinstance(e, list)) + eq_(e[0].ethertype, ether.ETH_TYPE_8021AD) + eq_(e[1].ethertype, ether.ETH_TYPE_8021AD) + + sv = p.get_protocols(vlan.svlan) + ok_(sv) + ok_(isinstance(sv, list)) + eq_(sv[0].ethertype, ether.ETH_TYPE_8021Q) + eq_(sv[1].ethertype, ether.ETH_TYPE_8021Q) + + it = p.get_protocol(pbb.itag) + ok_(it) + + v = p.get_protocol(vlan.vlan) + ok_(v) + eq_(v.ethertype, ether.ETH_TYPE_IP) + + ip = p.get_protocol(ipv4.ipv4) + ok_(ip) + + eq_(it.pcp, self.pcp) + eq_(it.dei, self.dei) + eq_(it.uca, self.uca) + eq_(it.sid, self.sid) + + @raises(Exception) + def test_malformed_itag(self): + m_short_buf = self.buf[1:pbb.itag._MIN_LEN] + pbb.itag.parser(m_short_buf)