See "ryu/tests/unit/packet/test_lldp.py" to use this library.

This patch is based on Yamahata's topology discovery patch series.
http://thread.gmane.org/gmane.network.ryu.devel/467

Signed-off-by: YAMADA Hideki <[email protected]>
---
 ryu/lib/packet/lldp.py             |  496 ++++++++++++++++++++++++++++++++++++
 ryu/lib/packet/vlan.py             |    2 +
 ryu/ofproto/ether.py               |    1 +
 ryu/tests/unit/packet/test_lldp.py |  260 +++++++++++++++++++
 4 files changed, 759 insertions(+), 0 deletions(-)
 create mode 100644 ryu/lib/packet/lldp.py
 create mode 100644 ryu/tests/unit/packet/test_lldp.py

diff --git a/ryu/lib/packet/lldp.py b/ryu/lib/packet/lldp.py
new file mode 100644
index 0000000..5ff258f
--- /dev/null
+++ b/ryu/lib/packet/lldp.py
@@ -0,0 +1,496 @@
+# Copyright (C) 2012 Nippon Telegraph and Telephone Corporation.
+# Copyright (C) 2012 Isaku Yamahata <yamahata at private email ne jp>
+#
+# 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.
+
+"""
+Link Layer Discovery Protocol(LLDP, IEEE 802.1AB)
+http://standards.ieee.org/getieee802/download/802.1AB-2009.pdf
+
+
+basic TLV format
+
+octets | 1          | 2             | 3 ...             n + 2 |
+       --------------------------------------------------------
+       | TLV type | TLV information | TLV information string  |
+       | (7bits)  | string length   | ( 0 <= n <= 511 octets) |
+       |          | (9bits)         |                         |
+       --------------------------------------------------------
+bits   |8        2|1|8             1|
+
+
+LLDPDU format
+
+ ------------------------------------------------------------------------
+ | Chassis ID | Port ID | TTL | optional TLV | ... | optional TLV | End |
+ ------------------------------------------------------------------------
+
+Chasis ID, Port ID, TTL, End are mandatory
+optional TLV may be inserted in any order
+"""
+
+import logging
+import struct
+from ryu.lib.packet import packet_base
+
+
+LOG = logging.getLogger(__name__)
+
+
+# LLDP destination MAC address
+LLDP_MAC_NEAREST_BRIDGE = '\x01\x80\xc2\x00\x00\x0e'
+LLDP_MAC_NEAREST_NON_TPMR_BRIDGE = '\x01\x80\xc2\x00\x00\x03'
+LLDP_MAC_NEAREST_CUSTOMER_BRIDGE = '\x01\x80\xc2\x00\x00\x00'
+
+
+LLDP_TLV_TYPELEN_STR = '!H'
+LLDP_TLV_SIZE = 2
+LLDP_TLV_TYPE_MASK = 0xfe00
+LLDP_TLV_TYPE_SHIFT = 9
+LLDP_TLV_LENGTH_MASK = 0x01ff
+
+
+# LLDP TLV type
+LLDP_TLV_END = 0                        # End of LLDPDU
+LLDP_TLV_CHASSIS_ID = 1                 # Chassis ID
+LLDP_TLV_PORT_ID = 2                    # Port ID
+LLDP_TLV_TTL = 3                        # Time To Live
+LLDP_TLV_PORT_DESCRIPTION = 4           # Port Description
+LLDP_TLV_SYSTEM_NAME = 5                # System Name
+LLDP_TLV_SYSTEM_DESCRIPTION = 6         # System Description
+LLDP_TLV_SYSTEM_CAPABILITIES = 7        # System Capabilities
+LLDP_TLV_MANAGEMENT_ADDRESS = 8         # Management Address
+LLDP_TLV_ORGANIZATIONALLY_SPECIFIC = 127  # organizationally Specific TLVs
+
+
+class LLDPBasicTLV(object):
+    _LEN_MIN = 0
+    _LEN_MAX = 511
+    tlv_type = None
+
+    def __init__(self, buf=None, *args, **kwargs):
+        if buf:
+            (self.typelen, ) = struct.unpack(
+                LLDP_TLV_TYPELEN_STR, buf[:LLDP_TLV_SIZE])
+            tlv_type = \
+                (self.typelen & LLDP_TLV_TYPE_MASK) >> LLDP_TLV_TYPE_SHIFT
+            assert self.tlv_type == tlv_type
+
+            self.len = self.typelen & LLDP_TLV_LENGTH_MASK
+            assert len(buf) >= self.len + LLDP_TLV_SIZE
+
+            self.tlv_info = buf[LLDP_TLV_SIZE:]
+            self.tlv_info = self.tlv_info[:self.len]
+
+    @staticmethod
+    def get_type(buf):
+        (typelen, ) = struct.unpack(LLDP_TLV_TYPELEN_STR, buf[:LLDP_TLV_SIZE])
+        return (typelen & LLDP_TLV_TYPE_MASK) >> LLDP_TLV_TYPE_SHIFT
+
+    @staticmethod
+    def set_tlv_type(subcls, tlv_type):
+        assert issubclass(subcls, LLDPBasicTLV)
+        subcls.tlv_type = tlv_type
+
+    def _len_valid(self):
+        return self._LEN_MIN <= self.len and self.len <= self._LEN_MAX
+
+    def __str__(self):
+        self._typelen()
+        return super(LLDPBasicTLV, self).__str__()
+
+
+class lldp(packet_base.PacketBase):
+    _tlv_parsers = {}
+    tlvs = []
+
+    def __init__(self, tlvs):
+        self.tlvs = tlvs
+        length = 0
+        for tlv in tlvs:
+            length += LLDP_TLV_SIZE + tlv.len
+
+        self.length = length
+
+    # at least it must have chassis id, port id, ttl and end
+    def _tlvs_len_valid(self):
+        return len(self.tlvs) >= 4
+
+    # chassis id, port id, ttl and end
+    def _tlvs_valid(self):
+        return (self.tlvs[0].tlv_type == LLDP_TLV_CHASSIS_ID and
+                self.tlvs[1].tlv_type == LLDP_TLV_PORT_ID and
+                self.tlvs[2].tlv_type == LLDP_TLV_TTL and
+                self.tlvs[-1].tlv_type == LLDP_TLV_END)
+
+    def __str__(self):
+        assert self._tlvs_len_valid()
+        assert self._tlvs_valid()
+        return ''.join(str(tlv) for tlv in self.tlvs)
+
+    @classmethod
+    def parser(cls, buf):
+        tlvs = []
+
+        while buf:
+            tlv_type = LLDPBasicTLV.get_type(buf)
+            tlv = cls._tlv_parsers[tlv_type](buf)
+            tlvs.append(tlv)
+            offset = LLDP_TLV_SIZE + tlv.len
+            buf = buf[offset:]
+            assert (len(buf) > 0 and tlv.tlv_type != LLDP_TLV_END) or \
+                   (len(buf) == 0 and tlv.tlv_type == LLDP_TLV_END)
+
+        lldp_pkt = cls(tlvs)
+
+        assert lldp_pkt._tlvs_len_valid()
+        assert lldp_pkt._tlvs_valid()
+
+        return lldp_pkt, None
+
+    def serialize(self, payload, prev):
+        data = bytearray()
+        for tlv in self.tlvs:
+            data += tlv.serialize()
+
+        return data
+
+    @classmethod
+    def set_type(cls, tlv_cls):
+        cls._tlv_parsers[tlv_cls.tlv_type] = tlv_cls
+
+    @classmethod
+    def get_type(cls, tlv_type):
+        return cls._tlv_parsers[tlv_type]
+
+    @classmethod
+    def set_tlv_type(cls, tlv_type):
+        def _set_type(tlv_cls):
+            tlv_cls.set_tlv_type(tlv_cls, tlv_type)
+            cls.set_type(tlv_cls)
+            return tlv_cls
+        return _set_type
+
+
[email protected]_tlv_type(LLDP_TLV_END)
+class End(LLDPBasicTLV):
+    def __init__(self, buf=None, *args, **kwargs):
+        super(End, self).__init__(buf, *args, **kwargs)
+        if buf:
+            pass
+        else:
+            self.len = 0
+            self.typelen = 0
+
+    def serialize(self):
+        return struct.pack('!H', self.typelen)
+
+
[email protected]_tlv_type(LLDP_TLV_CHASSIS_ID)
+class ChassisID(LLDPBasicTLV):
+    _PACK_STR = '!B'
+    _PACK_SIZE = struct.calcsize(_PACK_STR)
+    # subtype id(1 octet) + chassis id length(1 - 255 octet)
+    _LEN_MIN = 2
+    _LEN_MAX = 256
+
+    # Chassis ID subtype
+    SUB_CHASSIS_COMPONENT = 1   # EntPhysicalAlias (IETF RFC 4133)
+    SUB_INTERFACE_ALIAS = 2     # IfAlias (IETF RFC 2863)
+    SUB_PORT_COMPONENT = 3      # EntPhysicalAlias (IETF RFC 4133)
+    SUB_MAC_ADDRESS = 4         # MAC address (IEEE std 802)
+    SUB_NETWORK_ADDRESS = 5     # networkAddress
+    SUB_INTERFACE_NAME = 6      # IfName (IETF RFC 2863)
+    SUB_LOCALLY_ASSIGNED = 7    # local
+
+    def __init__(self, buf=None, *args, **kwargs):
+        super(ChassisID, self).__init__(buf, *args, **kwargs)
+        if buf:
+            (self.subtype, ) = struct.unpack(
+                self._PACK_STR, self.tlv_info[:self._PACK_SIZE])
+            self.chassis_id = self.tlv_info[self._PACK_SIZE:]
+        else:
+            self.subtype = kwargs['subtype']
+            self.chassis_id = kwargs['chassis_id']
+            self.len = self._PACK_SIZE + len(self.chassis_id)
+            assert self._len_valid()
+            self.typelen = (self.tlv_type << LLDP_TLV_TYPE_SHIFT) | self.len
+
+    def serialize(self):
+        return struct.pack('!HB', self.typelen, self.subtype) + self.chassis_id
+
+
[email protected]_tlv_type(LLDP_TLV_PORT_ID)
+class PortID(LLDPBasicTLV):
+    _PACK_STR = '!B'
+    _PACK_SIZE = struct.calcsize(_PACK_STR)
+
+    # subtype id(1 octet) + port id length(1 - 255 octet)
+    _LEN_MIN = 2
+    _LEN_MAX = 256
+
+    # Port ID subtype
+    SUB_INTERFACE_ALIAS = 1     # ifAlias (IETF RFC 2863)
+    SUB_PORT_COMPONENT = 2      # entPhysicalAlias (IETF RFC 4133)
+    SUB_MAC_ADDRESS = 3         # MAC address (IEEE Std 802)
+    SUB_NETWORK_ADDRESS = 4     # networkAddress
+    SUB_INTERFACE_NAME = 5      # ifName (IETF RFC 2863)
+    SUB_AGENT_CIRCUIT_ID = 6    # agent circuit ID(IETF RFC 3046)
+    SUB_LOCALLY_ASSIGNED = 7    # local
+
+    def __init__(self, buf=None, *args, **kwargs):
+        super(PortID, self).__init__(buf, *args, **kwargs)
+        if buf:
+            (self.subtype, ) = struct.unpack(
+                self._PACK_STR, self.tlv_info[:self._PACK_SIZE])
+            self.port_id = self.tlv_info[self._PACK_SIZE:]
+        else:
+            self.subtype = kwargs['subtype']
+            self.port_id = kwargs['port_id']
+            self.len = self._PACK_SIZE + len(self.port_id)
+            assert self._len_valid()
+            self.typelen = (self.tlv_type << LLDP_TLV_TYPE_SHIFT) | self.len
+
+    def serialize(self):
+        return struct.pack('!HB', self.typelen, self.subtype) + self.port_id
+
+
[email protected]_tlv_type(LLDP_TLV_TTL)
+class TTL(LLDPBasicTLV):
+    _PACK_STR = '!H'
+    _PACK_SIZE = struct.calcsize(_PACK_STR)
+    _LEN_MIN = _PACK_SIZE
+    _LEN_MAX = _PACK_SIZE
+
+    def __init__(self, buf=None, *args, **kwargs):
+        super(TTL, self).__init__(buf, *args, **kwargs)
+        if buf:
+            (self.ttl, ) = struct.unpack(
+                self._PACK_STR, self.tlv_info[:self._PACK_SIZE])
+        else:
+            self.ttl = kwargs['ttl']
+            self.len = self._PACK_SIZE
+            assert self._len_valid()
+            self.typelen = (self.tlv_type << LLDP_TLV_TYPE_SHIFT) | self.len
+
+    def serialize(self):
+        return struct.pack('!HH', self.typelen, self.ttl)
+
+
[email protected]_tlv_type(LLDP_TLV_PORT_DESCRIPTION)
+class PortDescription(LLDPBasicTLV):
+    _LEN_MAX = 255
+
+    def __init__(self, buf=None, *args, **kwargs):
+        super(PortDescription, self).__init__(buf, *args, **kwargs)
+        if buf:
+            pass
+        else:
+            self.port_description = kwargs['port_description']
+            self.len = len(self.port_description)
+            assert self._len_valid()
+            self.typelen = (self.tlv_type << LLDP_TLV_TYPE_SHIFT) | self.len
+
+    def serialize(self):
+        return struct.pack('!H', self.typelen) + self.port_description
+
+    @property
+    def port_description(self):
+        return self.tlv_info
+
+    @port_description.setter
+    def port_description(self, value):
+        self.tlv_info = value
+
+
[email protected]_tlv_type(LLDP_TLV_SYSTEM_NAME)
+class SystemName(LLDPBasicTLV):
+    _LEN_MAX = 255
+
+    def __init__(self, buf=None, *args, **kwargs):
+        super(SystemName, self).__init__(buf, *args, **kwargs)
+        if buf:
+            pass
+        else:
+            self.system_name = kwargs['system_name']
+            self.len = len(self.system_name)
+            assert self._len_valid()
+            self.typelen = (self.tlv_type << LLDP_TLV_TYPE_SHIFT) | self.len
+
+    def serialize(self):
+        return struct.pack('!H', self.typelen) + self.tlv_info
+
+    @property
+    def system_name(self):
+        return self.tlv_info
+
+    @system_name.setter
+    def system_name(self, value):
+        self.tlv_info = value
+
+
[email protected]_tlv_type(LLDP_TLV_SYSTEM_DESCRIPTION)
+class SystemDescription(LLDPBasicTLV):
+    _LEN_MAX = 255
+
+    def __init__(self, buf=None, *args, **kwargs):
+        super(SystemDescription, self).__init__(buf, *args, **kwargs)
+        if buf:
+            pass
+        else:
+            self.system_description = kwargs['system_description']
+            self.len = len(self.system_description)
+            assert self._len_valid()
+            self.typelen = (self.tlv_type << LLDP_TLV_TYPE_SHIFT) | self.len
+
+    def serialize(self):
+        return struct.pack('!H', self.typelen) + self.tlv_info
+
+    @property
+    def system_description(self):
+        return self.tlv_info
+
+    @system_description.setter
+    def system_description(self, value):
+        self.tlv_info = value
+
+
[email protected]_tlv_type(LLDP_TLV_SYSTEM_CAPABILITIES)
+class SystemCapabilities(LLDPBasicTLV):
+    # chassis subtype(1) + system cap(2) + enabled cap(2)
+    _PACK_STR = '!BHH'
+    _PACK_SIZE = struct.calcsize(_PACK_STR)
+    _LEN_MIN = _PACK_SIZE
+    _LEN_MAX = _PACK_SIZE
+
+    # System Capabilities
+    CAP_REPEATER = (1 << 1)             # IETF RFC 2108
+    CAP_MAC_BRIDGE = (1 << 2)           # IEEE Std 802.1D
+    CAP_WLAN_ACCESS_POINT = (1 << 3)    # IEEE Std 802.11 MIB
+    CAP_ROUTER = (1 << 4)               # IETF RFC 1812
+    CAP_TELEPHONE = (1 << 5)            # IETF RFC 4293
+    CAP_DOCSIS = (1 << 6)               # IETF RFC 4639 and IETF RFC 4546
+    CAP_STATION_ONLY = (1 << 7)         # IETF RFC 4293
+    CAP_CVLAN = (1 << 8)                # IEEE Std 802.1Q
+    CAP_SVLAN = (1 << 9)                # IEEE Std 802.1Q
+    CAP_TPMR = (1 << 10)                # IEEE Std 802.1Q
+
+    def __init__(self, buf=None, *args, **kwargs):
+        super(SystemCapabilities, self).__init__(buf, *args, **kwargs)
+        if buf:
+            (self.subtype, self.system_cap, self.enabled_cap) = \
+                struct.unpack(self._PACK_STR, self.tlv_info[:self._PACK_SIZE])
+        else:
+            self.subtype = kwargs['subtype']
+            self.system_cap = kwargs['system_cap']
+            self.enabled_cap = kwargs['enabled_cap']
+            self.len = self._PACK_SIZE
+            assert self._len_valid()
+            self.typelen = (self.tlv_type << LLDP_TLV_TYPE_SHIFT) | self.len
+
+    def serialize(self):
+        return struct.pack('!HBHH',
+                           self.typelen, self.subtype,
+                           self.system_cap, self.enabled_cap)
+
+
[email protected]_tlv_type(LLDP_TLV_MANAGEMENT_ADDRESS)
+class ManagementAddress(LLDPBasicTLV):
+    _LEN_MIN = 9
+    _LEN_MAX = 167
+
+    _ADDR_PACK_STR = '!BB'    # address string length, address subtype
+    _ADDR_PACK_SIZE = struct.calcsize(_ADDR_PACK_STR)
+    _ADDR_LEN_MIN = 1
+    _ADDR_LEN_MAX = 31
+
+    _INTF_PACK_STR = '!BIB'   # interface subtype, interface number, oid length
+    _INTF_PACK_SIZE = struct.calcsize(_INTF_PACK_STR)
+    _OID_LEN_MIN = 0
+    _OID_LEN_MAX = 128
+
+    def __init__(self, buf=None, *args, **kwargs):
+        super(ManagementAddress, self).__init__(buf, *args, **kwargs)
+        if buf:
+            (self.addr_len, self.addr_subtype) = struct.unpack(
+                self._ADDR_PACK_STR, self.tlv_info[:self._ADDR_PACK_SIZE])
+            assert self._addr_len_valid()
+            offset = self._ADDR_PACK_SIZE + self.addr_len - 1
+            self.addr = self.tlv_info[self._ADDR_PACK_SIZE:offset]
+
+            (self.intf_subtype, self.intf_num, self.oid_len) = struct.unpack(
+                self._INTF_PACK_STR,
+                self.tlv_info[offset:offset + self._INTF_PACK_SIZE])
+            assert self._oid_len_valid()
+
+            offset = offset + self._INTF_PACK_SIZE
+            self.oid = self.tlv_info[offset:]
+        else:
+            self.addr_subtype = kwargs['addr_subtype']
+            self.addr = kwargs['addr']
+            self.addr_len = len(self.addr) + 1  # 1 octet subtype
+            assert self._addr_len_valid()
+
+            self.intf_subtype = kwargs['intf_subtype']
+            self.intf_num = kwargs['intf_num']
+
+            self.oid = kwargs['oid']
+            self.oid_len = len(self.oid)
+            assert self._oid_len_valid()
+
+            self.len = self._ADDR_PACK_SIZE + self.addr_len - 1 \
+                + self._INTF_PACK_SIZE + self.oid_len
+            assert self._len_valid()
+            self.typelen = (self.tlv_type << LLDP_TLV_TYPE_SHIFT) | self.len
+
+    def serialize(self):
+        tlv_info = struct.pack(self._ADDR_PACK_STR,
+                               self.addr_len, self.addr_subtype)
+        tlv_info += self.addr
+        tlv_info += struct.pack(self._INTF_PACK_STR,
+                                self.intf_subtype, self.intf_num, self.oid_len)
+        tlv_info += self.oid
+        return struct.pack('!H', self.typelen) + tlv_info
+
+    def _addr_len_valid(self):
+        return (self._ADDR_LEN_MIN <= self.addr_len or
+                self.addr_len <= self._ADDR_LEN_MAX)
+
+    def _oid_len_valid(self):
+        return (self._OID_LEN_MIN <= self.oid_len and
+                self.oid_len <= self._OID_LEN_MAX)
+
+
[email protected]_tlv_type(LLDP_TLV_ORGANIZATIONALLY_SPECIFIC)
+class OrganizationallySpecific(LLDPBasicTLV):
+    _PACK_STR = '!3sB'
+    _PACK_SIZE = struct.calcsize(_PACK_STR)
+    _LEN_MIN = _PACK_SIZE
+    _LEN_MAX = 511
+
+    def __init__(self, buf=None, *args, **kwargs):
+        super(OrganizationallySpecific, self).__init__(buf, *args, **kwargs)
+        if buf:
+            (self.oui, self.subtype) = struct.unpack(
+                self._PACK_STR, self.tlv_info[:self._PACK_SIZE])
+        else:
+            self.oui = kwargs['oui']
+            self.subtype = kwargs['subtype']
+            self.info = kwargs['info']
+            self.len = self._PACK_SIZE + len(self.info)
+            assert self._len_valid()
+            self.typelen = (self.tlv_type << LLDP_TLV_TYPE_SHIFT) | self.len
+
+    def serialize(self):
+        return struct.pack('!H3sB', self.typelen, self.oui, self.subtype)
diff --git a/ryu/lib/packet/vlan.py b/ryu/lib/packet/vlan.py
index dfd0934..fba8394 100644
--- a/ryu/lib/packet/vlan.py
+++ b/ryu/lib/packet/vlan.py
@@ -17,6 +17,7 @@ import struct
 from . import packet_base
 from . import arp
 from . import ipv4
+from . import lldp
 from ryu.ofproto import ether
 from ryu.ofproto.ofproto_parser import msg_pack_into
 
@@ -47,3 +48,4 @@ class vlan(packet_base.PacketBase):
 
 vlan.register_packet_type(arp.arp, ether.ETH_TYPE_ARP)
 vlan.register_packet_type(ipv4.ipv4, ether.ETH_TYPE_IP)
+vlan.register_packet_type(lldp.lldp, ether.ETH_TYPE_LLDP)
diff --git a/ryu/ofproto/ether.py b/ryu/ofproto/ether.py
index 747de7f..a4f5525 100644
--- a/ryu/ofproto/ether.py
+++ b/ryu/ofproto/ether.py
@@ -20,3 +20,4 @@ ETH_TYPE_8021Q = 0x8100
 ETH_TYPE_IPV6 = 0x86dd
 ETH_TYPE_MPLS = 0x8847
 ETH_TYPE_SLOW = 0x8809
+ETH_TYPE_LLDP = 0x88cc
diff --git a/ryu/tests/unit/packet/test_lldp.py 
b/ryu/tests/unit/packet/test_lldp.py
new file mode 100644
index 0000000..23402f8
--- /dev/null
+++ b/ryu/tests/unit/packet/test_lldp.py
@@ -0,0 +1,260 @@
+# 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.
+
+# vim: tabstop=4 shiftwidth=4 softtabstop=4
+
+import unittest
+import logging
+import struct
+from nose.tools import ok_, eq_, nottest
+
+from ryu.ofproto import ether
+from ryu.lib.packet import packet
+from ryu.lib.packet import ethernet
+from ryu.lib.packet import lldp
+
+LOG = logging.getLogger(__name__)
+
+
+class TestLLDPMandatoryTLV(unittest.TestCase):
+    def setUp(self):
+        # sample data is based on:
+        # http://wiki.wireshark.org/LinkLayerDiscoveryProtocol
+        #
+        # mandatory TLV only
+        self.data = '\x01\x80\xc2\x00\x00\x0e\x00\x04' \
+                    + '\x96\x1f\xa7\x26\x88\xcc\x02\x07' \
+                    + '\x04\x00\x04\x96\x1f\xa7\x26\x04' \
+                    + '\x04\x05\x31\x2f\x33\x06\x02\x00' \
+                    + '\x78\x00\x00'
+
+    def tearDown(self):
+        pass
+
+    def test_get_tlv_type(self):
+        buf = str(bytearray('\x02\x07\x04\x00\x04\x96\x1f\xa7\x26'))
+        eq_(lldp.LLDPBasicTLV.get_type(buf), lldp.LLDP_TLV_CHASSIS_ID)
+
+    def test_parse_without_ethernet(self):
+        buf = self.data[ethernet.ethernet._MIN_LEN:]
+        (lldp_pkt, cls) = lldp.lldp.parser(buf)
+        eq_(lldp_pkt.length, len(buf))
+
+        tlvs = lldp_pkt.tlvs
+        eq_(tlvs[0].tlv_type, lldp.LLDP_TLV_CHASSIS_ID)
+        eq_(tlvs[0].len, 7)
+        eq_(tlvs[0].subtype, lldp.ChassisID.SUB_MAC_ADDRESS)
+        eq_(tlvs[0].chassis_id, '\x00\x04\x96\x1f\xa7\x26')
+        eq_(tlvs[1].tlv_type, lldp.LLDP_TLV_PORT_ID)
+        eq_(tlvs[1].len, 4)
+        eq_(tlvs[1].subtype, lldp.PortID.SUB_INTERFACE_NAME)
+        eq_(tlvs[1].port_id, '1/3')
+        eq_(tlvs[2].tlv_type, lldp.LLDP_TLV_TTL)
+        eq_(tlvs[2].len, 2)
+        eq_(tlvs[2].ttl, 120)
+        eq_(tlvs[3].tlv_type, lldp.LLDP_TLV_END)
+
+    def test_parse(self):
+        buf = self.data
+        pkt = packet.Packet(buf)
+
+        eq_(type(pkt.next()), ethernet.ethernet)
+        eq_(type(pkt.next()), lldp.lldp)
+
+    def test_tlv(self):
+        tlv = lldp.ChassisID(subtype=lldp.ChassisID.SUB_MAC_ADDRESS,
+                             chassis_id='\x00\x04\x96\x1f\xa7\x26')
+        eq_(tlv.tlv_type, lldp.LLDP_TLV_CHASSIS_ID)
+        eq_(tlv.len, 7)
+        (typelen, ) = struct.unpack('!H', '\x02\x07')
+        eq_(tlv.typelen, typelen)
+
+    def test_serialize_without_ethernet(self):
+        tlv_chassis_id = lldp.ChassisID(subtype=lldp.ChassisID.SUB_MAC_ADDRESS,
+                                        chassis_id='\x00\x04\x96\x1f\xa7\x26')
+        tlv_port_id = lldp.PortID(subtype=lldp.PortID.SUB_INTERFACE_NAME,
+                                  port_id='1/3')
+        tlv_ttl = lldp.TTL(ttl=120)
+        tlv_end = lldp.End()
+        tlvs = (tlv_chassis_id, tlv_port_id, tlv_ttl, tlv_end)
+        lldp_pkt = lldp.lldp(tlvs)
+
+        eq_(lldp_pkt.serialize(None, None),
+            self.data[ethernet.ethernet._MIN_LEN:])
+
+    def test_serialize(self):
+        pkt = packet.Packet()
+
+        dst = lldp.LLDP_MAC_NEAREST_BRIDGE
+        src = '\x00\x04\x96\x1f\xa7\x26'
+        ethertype = ether.ETH_TYPE_LLDP
+        eth_pkt = ethernet.ethernet(dst, src, ethertype)
+        pkt.add_protocol(eth_pkt)
+
+        tlv_chassis_id = lldp.ChassisID(subtype=lldp.ChassisID.SUB_MAC_ADDRESS,
+                                        chassis_id=src)
+        tlv_port_id = lldp.PortID(subtype=lldp.PortID.SUB_INTERFACE_NAME,
+                                  port_id='1/3')
+        tlv_ttl = lldp.TTL(ttl=120)
+        tlv_end = lldp.End()
+        tlvs = (tlv_chassis_id, tlv_port_id, tlv_ttl, tlv_end)
+        lldp_pkt = lldp.lldp(tlvs)
+        pkt.add_protocol(lldp_pkt)
+
+        eq_(len(pkt.protocols), 2)
+
+        pkt.serialize()
+        eq_(pkt.data, self.data)
+
+
+class TestLLDPOptionalTLV(unittest.TestCase):
+    def setUp(self):
+        # sample data is based on:
+        # http://wiki.wireshark.org/LinkLayerDiscoveryProtocol
+        #
+        # include optional TLV
+        self.data = '\x01\x80\xc2\x00\x00\x0e\x00\x01' \
+                    + '\x30\xf9\xad\xa0\x88\xcc\x02\x07' \
+                    + '\x04\x00\x01\x30\xf9\xad\xa0\x04' \
+                    + '\x04\x05\x31\x2f\x31\x06\x02\x00' \
+                    + '\x78\x08\x17\x53\x75\x6d\x6d\x69' \
+                    + '\x74\x33\x30\x30\x2d\x34\x38\x2d' \
+                    + '\x50\x6f\x72\x74\x20\x31\x30\x30' \
+                    + '\x31\x00\x0a\x0d\x53\x75\x6d\x6d' \
+                    + '\x69\x74\x33\x30\x30\x2d\x34\x38' \
+                    + '\x00\x0c\x4c\x53\x75\x6d\x6d\x69' \
+                    + '\x74\x33\x30\x30\x2d\x34\x38\x20' \
+                    + '\x2d\x20\x56\x65\x72\x73\x69\x6f' \
+                    + '\x6e\x20\x37\x2e\x34\x65\x2e\x31' \
+                    + '\x20\x28\x42\x75\x69\x6c\x64\x20' \
+                    + '\x35\x29\x20\x62\x79\x20\x52\x65' \
+                    + '\x6c\x65\x61\x73\x65\x5f\x4d\x61' \
+                    + '\x73\x74\x65\x72\x20\x30\x35\x2f' \
+                    + '\x32\x37\x2f\x30\x35\x20\x30\x34' \
+                    + '\x3a\x35\x33\x3a\x31\x31\x00\x0e' \
+                    + '\x05\x01\x00\x14\x00\x14\x10\x0e' \
+                    + '\x07' \
+                    + '\x06\x00\x01\x30\xf9\xad\xa0\x02' \
+                    + '\x00\x00\x03\xe9\x00\xfe\x07\x00' \
+                    + '\x12\x0f\x02\x07\x01\x00\xfe\x09' \
+                    + '\x00\x12\x0f\x01\x03\x6c\x00\x00' \
+                    + '\x10\xfe\x09\x00\x12\x0f\x03\x01' \
+                    + '\x00\x00\x00\x00\xfe\x06\x00\x12' \
+                    + '\x0f\x04\x05\xf2\xfe\x06\x00\x80' \
+                    + '\xc2\x01\x01\xe8\xfe\x07\x00\x80' \
+                    + '\xc2\x02\x01\x00\x00\xfe\x17\x00' \
+                    + '\x80\xc2\x03\x01\xe8\x10\x76\x32' \
+                    + '\x2d\x30\x34\x38\x38\x2d\x30\x33' \
+                    + '\x2d\x30\x35\x30\x35\x00\xfe\x05' \
+                    + '\x00\x80\xc2\x04\x00\x00\x00'
+
+    def tearDown(self):
+        pass
+
+    def test_parse(self):
+        buf = self.data
+        pkt = packet.Packet(buf)
+
+        eq_(type(pkt.next()), ethernet.ethernet)
+        lldp_pkt = pkt.next()
+        eq_(type(lldp_pkt), lldp.lldp)
+        eq_(lldp_pkt.length, len(buf) - ethernet.ethernet._MIN_LEN)
+
+        tlvs = lldp_pkt.tlvs
+
+        # Port Description
+        eq_(tlvs[3].tlv_type, lldp.LLDP_TLV_PORT_DESCRIPTION)
+        eq_(tlvs[3].port_description, 'Summit300-48-Port 1001\x00')
+
+        # System Name
+        eq_(tlvs[4].tlv_type, lldp.LLDP_TLV_SYSTEM_NAME)
+        eq_(tlvs[4].system_name, 'Summit300-48\x00')
+
+        # System Description
+
+        eq_(tlvs[5].tlv_type, lldp.LLDP_TLV_SYSTEM_DESCRIPTION)
+        eq_(tlvs[5].system_description,
+            'Summit300-48 - Version 7.4e.1 (Build 5) '
+            + 'by Release_Master 05/27/05 04:53:11\x00')
+
+        # SystemCapabilities
+        eq_(tlvs[6].tlv_type, lldp.LLDP_TLV_SYSTEM_CAPABILITIES)
+        eq_(tlvs[6].subtype, lldp.ChassisID.SUB_CHASSIS_COMPONENT)
+        eq_(tlvs[6].system_cap & lldp.SystemCapabilities.CAP_MAC_BRIDGE,
+            lldp.SystemCapabilities.CAP_MAC_BRIDGE)
+        eq_(tlvs[6].enabled_cap & lldp.SystemCapabilities.CAP_MAC_BRIDGE,
+            lldp.SystemCapabilities.CAP_MAC_BRIDGE)
+        eq_(tlvs[6].system_cap & lldp.SystemCapabilities.CAP_TELEPHONE, 0)
+        eq_(tlvs[6].enabled_cap & lldp.SystemCapabilities.CAP_TELEPHONE, 0)
+
+        # Management Address
+        eq_(tlvs[7].tlv_type, lldp.LLDP_TLV_MANAGEMENT_ADDRESS)
+        eq_(tlvs[7].addr_len, 7)
+        eq_(tlvs[7].addr, '\x00\x01\x30\xf9\xad\xa0')
+        eq_(tlvs[7].intf_num, 1001)
+
+        # Organizationally Specific
+        eq_(tlvs[8].tlv_type, lldp.LLDP_TLV_ORGANIZATIONALLY_SPECIFIC)
+        eq_(tlvs[8].oui, '\x00\x12\x0f')  # IEEE 802.3
+        eq_(tlvs[8].subtype, 0x02)  # Power Via MDI
+
+        # End
+        eq_(tlvs[16].tlv_type, lldp.LLDP_TLV_END)
+
+    def test_serialize(self):
+        pkt = packet.Packet()
+
+        dst = lldp.LLDP_MAC_NEAREST_BRIDGE
+        src = '\x00\x01\x30\xf9\xad\xa0'
+        ethertype = ether.ETH_TYPE_LLDP
+        eth_pkt = ethernet.ethernet(dst, src, ethertype)
+        pkt.add_protocol(eth_pkt)
+
+        tlv_chassis_id = lldp.ChassisID(subtype=lldp.ChassisID.SUB_MAC_ADDRESS,
+                                        chassis_id=src)
+        tlv_port_id = lldp.PortID(subtype=lldp.PortID.SUB_INTERFACE_NAME,
+                                  port_id='1/1')
+        tlv_ttl = lldp.TTL(ttl=120)
+        tlv_port_description = lldp.PortDescription(
+            port_description='Summit300-48-Port 1001\x00')
+        tlv_system_name = lldp.SystemName(system_name='Summit300-48\x00')
+        tlv_system_description = lldp.SystemDescription(
+            system_description='Summit300-48 - Version 7.4e.1 (Build 5) '
+                               + 'by Release_Master 05/27/05 04:53:11\x00')
+        tlv_system_capabilities = lldp.SystemCapabilities(
+            subtype=lldp.ChassisID.SUB_CHASSIS_COMPONENT,
+            system_cap=0x14,
+            enabled_cap=0x14)
+        tlv_management_address = lldp.ManagementAddress(
+            addr_subtype=0x06, addr='\x00\x01\x30\xf9\xad\xa0',
+            intf_subtype=0x02, intf_num=1001,
+            oid='')
+        tlv_organizationally_specific = lldp.OrganizationallySpecific(
+            oui='\x00\x12\x0f', subtype=0x02, info='\x07\x01\x00')
+        tlv_end = lldp.End()
+        tlvs = (tlv_chassis_id, tlv_port_id, tlv_ttl, tlv_port_description,
+                tlv_system_name, tlv_system_description,
+                tlv_system_capabilities, tlv_management_address,
+                tlv_organizationally_specific, tlv_end)
+        lldp_pkt = lldp.lldp(tlvs)
+        pkt.add_protocol(lldp_pkt)
+
+        eq_(len(pkt.protocols), 2)
+
+        pkt.serialize()
+
+        # self.data has many organizationally specific TLVs
+        data = str(pkt.data[:-2])
+        eq_(data, self.data[:len(data)])
-- 
1.7.1



------------------------------------------------------------------------------
Everyone hates slow websites. So do we.
Make your web apps faster with AppDynamics
Download AppDynamics Lite for free today:
http://p.sf.net/sfu/appdyn_d2d_jan
_______________________________________________
Ryu-devel mailing list
[email protected]
https://lists.sourceforge.net/lists/listinfo/ryu-devel

Reply via email to