diff options
-rw-r--r-- | ryu/lib/packet/ethernet.py | 1 | ||||
-rw-r--r-- | ryu/lib/packet/vlan.py | 76 | ||||
-rw-r--r-- | ryu/ofproto/ether.py | 3 | ||||
-rw-r--r-- | ryu/tests/unit/packet/test_vlan.py | 118 |
4 files changed, 178 insertions, 20 deletions
diff --git a/ryu/lib/packet/ethernet.py b/ryu/lib/packet/ethernet.py index 349a352c..e63ffee8 100644 --- a/ryu/lib/packet/ethernet.py +++ b/ryu/lib/packet/ethernet.py @@ -76,4 +76,5 @@ class ethernet(packet_base.PacketBase): # copy vlan _TYPES ethernet._TYPES = vlan.vlan._TYPES ethernet.register_packet_type(vlan.vlan, ether.ETH_TYPE_8021Q) +ethernet.register_packet_type(vlan.svlan, ether.ETH_TYPE_8021AD) ethernet.register_packet_type(mpls.mpls, ether.ETH_TYPE_MPLS) diff --git a/ryu/lib/packet/vlan.py b/ryu/lib/packet/vlan.py index 8930197b..2f0e2565 100644 --- a/ryu/lib/packet/vlan.py +++ b/ryu/lib/packet/vlan.py @@ -13,6 +13,7 @@ # See the License for the specific language governing permissions and # limitations under the License. +import abc import struct from . import packet_base from . import arp @@ -24,7 +25,35 @@ from . import llc from ryu.ofproto import ether -class vlan(packet_base.PacketBase): +class _vlan(packet_base.PacketBase): + + __metaclass__ = abc.ABCMeta + _PACK_STR = "!HH" + _MIN_LEN = struct.calcsize(_PACK_STR) + + @abc.abstractmethod + def __init__(self, pcp, cfi, vid, ethertype): + super(_vlan, self).__init__() + self.pcp = pcp + self.cfi = cfi + self.vid = vid + self.ethertype = ethertype + + @classmethod + def parser(cls, buf): + tci, ethertype = struct.unpack_from(cls._PACK_STR, buf) + pcp = tci >> 13 + cfi = (tci >> 12) & 1 + vid = tci & ((1 << 12) - 1) + return (cls(pcp, cfi, vid, ethertype), + vlan.get_packet_type(ethertype), buf[vlan._MIN_LEN:]) + + def serialize(self, payload, prev): + tci = self.pcp << 13 | self.cfi << 12 | self.vid + return struct.pack(vlan._PACK_STR, tci, self.ethertype) + + +class vlan(_vlan): """VLAN (IEEE 802.1Q) header encoder/decoder class. An instance has the following attributes at least. @@ -41,15 +70,8 @@ class vlan(packet_base.PacketBase): ============== ==================== """ - _PACK_STR = "!HH" - _MIN_LEN = struct.calcsize(_PACK_STR) - def __init__(self, pcp=0, cfi=0, vid=0, ethertype=ether.ETH_TYPE_IP): - super(vlan, self).__init__() - self.pcp = pcp - self.cfi = cfi - self.vid = vid - self.ethertype = ethertype + super(vlan, self).__init__(pcp, cfi, vid, ethertype) @classmethod def get_packet_type(cls, type_): @@ -63,18 +85,32 @@ class vlan(packet_base.PacketBase): type_ = ether.ETH_TYPE_IEEE802_3 return cls._TYPES.get(type_) + +class svlan(_vlan): + """S-VLAN (IEEE 802.1ad) 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 + cfi Canonical Format Indicator + vid VLAN Identifier + ethertype EtherType + ============== ==================== + """ + + def __init__(self, pcp=0, cfi=0, vid=0, ethertype=ether.ETH_TYPE_8021Q): + super(svlan, self).__init__(pcp, cfi, vid, ethertype) + @classmethod - def parser(cls, buf): - tci, ethertype = struct.unpack_from(cls._PACK_STR, buf) - pcp = tci >> 13 - cfi = (tci >> 12) & 1 - vid = tci & ((1 << 12) - 1) - return (cls(pcp, cfi, vid, ethertype), - vlan.get_packet_type(ethertype), buf[vlan._MIN_LEN:]) + def get_packet_type(cls, type_): + return cls._TYPES.get(type_) - def serialize(self, payload, prev): - tci = self.pcp << 13 | self.cfi << 12 | self.vid - return struct.pack(vlan._PACK_STR, tci, self.ethertype) vlan.register_packet_type(arp.arp, ether.ETH_TYPE_ARP) vlan.register_packet_type(ipv4.ipv4, ether.ETH_TYPE_IP) @@ -82,3 +118,5 @@ vlan.register_packet_type(ipv6.ipv6, ether.ETH_TYPE_IPV6) vlan.register_packet_type(lldp.lldp, ether.ETH_TYPE_LLDP) 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) diff --git a/ryu/ofproto/ether.py b/ryu/ofproto/ether.py index f4883a39..9e1539c0 100644 --- a/ryu/ofproto/ether.py +++ b/ryu/ofproto/ether.py @@ -18,7 +18,8 @@ ETH_TYPE_IP = 0x0800 ETH_TYPE_ARP = 0x0806 ETH_TYPE_8021Q = 0x8100 ETH_TYPE_IPV6 = 0x86dd -ETH_TYPE_MPLS = 0x8847 ETH_TYPE_SLOW = 0x8809 +ETH_TYPE_MPLS = 0x8847 +ETH_TYPE_8021AD = 0x88a8 ETH_TYPE_LLDP = 0x88cc ETH_TYPE_IEEE802_3 = 0x05dc diff --git a/ryu/tests/unit/packet/test_vlan.py b/ryu/tests/unit/packet/test_vlan.py index 69835078..c9aa1862 100644 --- a/ryu/tests/unit/packet/test_vlan.py +++ b/ryu/tests/unit/packet/test_vlan.py @@ -26,6 +26,7 @@ from ryu.lib.packet.ethernet import ethernet from ryu.lib.packet.packet import Packet from ryu.lib.packet.ipv4 import ipv4 from ryu.lib.packet.vlan import vlan +from ryu.lib.packet.vlan import svlan LOG = logging.getLogger('test_vlan') @@ -136,3 +137,120 @@ class Test_vlan(unittest.TestCase): def test_malformed_vlan(self): m_short_buf = self.buf[1:vlan._MIN_LEN] vlan.parser(m_short_buf) + + +class Test_svlan(unittest.TestCase): + + pcp = 0 + cfi = 0 + vid = 32 + tci = pcp << 15 | cfi << 12 | vid + ethertype = ether.ETH_TYPE_8021Q + + buf = pack(svlan._PACK_STR, tci, ethertype) + + sv = svlan(pcp, cfi, vid, ethertype) + + def setUp(self): + pass + + def tearDown(self): + pass + + def find_protocol(self, pkt, name): + for p in pkt.protocols: + if p.protocol_name == name: + return p + + def test_init(self): + eq_(self.pcp, self.sv.pcp) + eq_(self.cfi, self.sv.cfi) + eq_(self.vid, self.sv.vid) + eq_(self.ethertype, self.sv.ethertype) + + def test_parser(self): + res, ptype, _ = self.sv.parser(self.buf) + + eq_(res.pcp, self.pcp) + eq_(res.cfi, self.cfi) + eq_(res.vid, self.vid) + eq_(res.ethertype, self.ethertype) + eq_(ptype, vlan) + + def test_serialize(self): + data = bytearray() + prev = None + buf = self.sv.serialize(data, prev) + + fmt = svlan._PACK_STR + res = struct.unpack(fmt, buf) + + eq_(res[0], self.tci) + eq_(res[1], self.ethertype) + + def _build_svlan(self): + src_mac = '00:07:0d:af:f4:54' + dst_mac = '00:00:00:00:00:00' + ethertype = ether.ETH_TYPE_8021AD + e = ethernet(dst_mac, src_mac, ethertype) + + pcp = 0 + cfi = 0 + vid = 32 + tci = pcp << 15 | cfi << 12 | vid + ethertype = ether.ETH_TYPE_IP + v = vlan(pcp, cfi, vid, 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(version, header_length, tos, total_length, identification, + flags, offset, ttl, proto, csum, src, dst, option) + + p = Packet() + + p.add_protocol(e) + p.add_protocol(self.sv) + p.add_protocol(v) + p.add_protocol(ip) + p.serialize() + + return p + + def test_build_svlan(self): + p = self._build_svlan() + + e = self.find_protocol(p, "ethernet") + ok_(e) + eq_(e.ethertype, ether.ETH_TYPE_8021AD) + + sv = self.find_protocol(p, "svlan") + ok_(sv) + eq_(sv.ethertype, ether.ETH_TYPE_8021Q) + + v = self.find_protocol(p, "vlan") + ok_(v) + eq_(v.ethertype, ether.ETH_TYPE_IP) + + ip = self.find_protocol(p, "ipv4") + ok_(ip) + + eq_(sv.pcp, self.pcp) + eq_(sv.cfi, self.cfi) + eq_(sv.vid, self.vid) + eq_(sv.ethertype, self.ethertype) + + @raises(Exception) + def test_malformed_svlan(self): + m_short_buf = self.buf[1:svlan._MIN_LEN] + svlan.parser(m_short_buf) |