1 # Copyright (C) 2012 Nippon Telegraph and Telephone Corporation.
2 # Copyright (C) 2012 Isaku Yamahata <yamahata at private email ne jp>
4 # Licensed under the Apache License, Version 2.0 (the "License");
5 # you may not use this file except in compliance with the License.
6 # You may obtain a copy of the License at
8 # http://www.apache.org/licenses/LICENSE-2.0
10 # Unless required by applicable law or agreed to in writing, software
11 # distributed under the License is distributed on an "AS IS" BASIS,
12 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
14 # See the License for the specific language governing permissions and
15 # limitations under the License.
18 Link Layer Discovery Protocol(LLDP, IEEE 802.1AB)
19 http://standards.ieee.org/getieee802/download/802.1AB-2009.pdf
24 octets | 1 | 2 | 3 ... n + 2 |
25 --------------------------------------------------------
26 | TLV type | TLV information | TLV information string |
27 | (7bits) | string length | (0-507 octets) |
29 --------------------------------------------------------
33 Organizationally specific TLV format::
35 octets | 1 | 2 | 3 ... 5 | 6 | 7 ... n + 6 |
36 ---------------------------------------------------------------
37 | TLV type | Length | OUI | Subtype | Infomation |
38 | (7bits) | (9bits) | (24bits) | (8bits) | (0-507 octets) |
39 ---------------------------------------------------------------
45 ------------------------------------------------------------------------
46 | Chassis ID | Port ID | TTL | optional TLV | ... | optional TLV | End |
47 ------------------------------------------------------------------------
49 Chasis ID, Port ID, TTL, End are mandatory
50 optional TLV may be inserted in any order
54 from ryu.lib import stringify
55 from ryu.lib.packet import packet_base
58 # LLDP destination MAC address
59 LLDP_MAC_NEAREST_BRIDGE = '01:80:c2:00:00:0e'
60 LLDP_MAC_NEAREST_NON_TPMR_BRIDGE = '01:80:c2:00:00:03'
61 LLDP_MAC_NEAREST_CUSTOMER_BRIDGE = '01:80:c2:00:00:00'
64 LLDP_TLV_TYPELEN_STR = '!H'
66 LLDP_TLV_TYPE_MASK = 0xfe00
67 LLDP_TLV_TYPE_SHIFT = 9
68 LLDP_TLV_LENGTH_MASK = 0x01ff
72 LLDP_TLV_END = 0 # End of LLDPDU
73 LLDP_TLV_CHASSIS_ID = 1 # Chassis ID
74 LLDP_TLV_PORT_ID = 2 # Port ID
75 LLDP_TLV_TTL = 3 # Time To Live
76 LLDP_TLV_PORT_DESCRIPTION = 4 # Port Description
77 LLDP_TLV_SYSTEM_NAME = 5 # System Name
78 LLDP_TLV_SYSTEM_DESCRIPTION = 6 # System Description
79 LLDP_TLV_SYSTEM_CAPABILITIES = 7 # System Capabilities
80 LLDP_TLV_MANAGEMENT_ADDRESS = 8 # Management Address
81 LLDP_TLV_ORGANIZATIONALLY_SPECIFIC = 127 # organizationally Specific TLVs
84 class LLDPBasicTLV(stringify.StringifyMixin):
89 def __init__(self, buf=None, *_args, **_kwargs):
90 super(LLDPBasicTLV, self).__init__()
92 (self.typelen, ) = struct.unpack(
93 LLDP_TLV_TYPELEN_STR, buf[:LLDP_TLV_SIZE])
95 (self.typelen & LLDP_TLV_TYPE_MASK) >> LLDP_TLV_TYPE_SHIFT
96 assert self.tlv_type == tlv_type
98 self.len = self.typelen & LLDP_TLV_LENGTH_MASK
99 assert len(buf) >= self.len + LLDP_TLV_SIZE
101 self.tlv_info = buf[LLDP_TLV_SIZE:]
102 self.tlv_info = self.tlv_info[:self.len]
106 (typelen, ) = struct.unpack(LLDP_TLV_TYPELEN_STR, buf[:LLDP_TLV_SIZE])
107 return (typelen & LLDP_TLV_TYPE_MASK) >> LLDP_TLV_TYPE_SHIFT
110 def set_tlv_type(subcls, tlv_type):
111 assert issubclass(subcls, LLDPBasicTLV)
112 subcls.tlv_type = tlv_type
114 def _len_valid(self):
115 return self._LEN_MIN <= self.len and self.len <= self._LEN_MAX
118 class lldp(packet_base.PacketBase):
119 """LLDPDU encoder/decoder class.
121 An instance has the following attributes at least.
123 ============== =====================================
124 Attribute Description
125 ============== =====================================
126 tlvs List of TLV instance.
127 ============== =====================================
131 def __init__(self, tlvs):
132 super(lldp, self).__init__()
135 # at least it must have chassis id, port id, ttl and end
136 def _tlvs_len_valid(self):
137 return len(self.tlvs) >= 4
139 # chassis id, port id, ttl and end
140 def _tlvs_valid(self):
141 return (self.tlvs[0].tlv_type == LLDP_TLV_CHASSIS_ID and
142 self.tlvs[1].tlv_type == LLDP_TLV_PORT_ID and
143 self.tlvs[2].tlv_type == LLDP_TLV_TTL and
144 self.tlvs[-1].tlv_type == LLDP_TLV_END)
147 def _parser(cls, buf):
151 tlv_type = LLDPBasicTLV.get_type(buf)
152 tlv = cls._tlv_parsers[tlv_type](buf)
154 offset = LLDP_TLV_SIZE + tlv.len
156 if tlv.tlv_type == LLDP_TLV_END:
162 assert lldp_pkt._tlvs_len_valid()
163 assert lldp_pkt._tlvs_valid()
165 return lldp_pkt, None, buf
168 def parser(cls, buf):
170 return cls._parser(buf)
172 return None, None, buf
174 def serialize(self, payload, prev):
176 for tlv in self.tlvs:
177 data += tlv.serialize()
182 def set_type(cls, tlv_cls):
183 cls._tlv_parsers[tlv_cls.tlv_type] = tlv_cls
186 def get_type(cls, tlv_type):
187 return cls._tlv_parsers[tlv_type]
190 def set_tlv_type(cls, tlv_type):
191 def _set_type(tlv_cls):
192 tlv_cls.set_tlv_type(tlv_cls, tlv_type)
193 cls.set_type(tlv_cls)
198 return sum(LLDP_TLV_SIZE + tlv.len for tlv in self.tlvs)
201 @lldp.set_tlv_type(LLDP_TLV_END)
202 class End(LLDPBasicTLV):
203 """End TLV encoder/decoder class
205 ============== =====================================
206 Attribute Description
207 ============== =====================================
208 buf Binary data to parse.
209 ============== =====================================
212 def __init__(self, buf=None, *args, **kwargs):
213 super(End, self).__init__(buf, *args, **kwargs)
221 return struct.pack('!H', self.typelen)
224 @lldp.set_tlv_type(LLDP_TLV_CHASSIS_ID)
225 class ChassisID(LLDPBasicTLV):
226 """Chassis ID TLV encoder/decoder class
228 ============== =====================================
229 Attribute Description
230 ============== =====================================
231 buf Binary data to parse.
233 chassis_id Chassis id corresponding to subtype.
234 ============== =====================================
238 _PACK_SIZE = struct.calcsize(_PACK_STR)
239 # subtype id(1 octet) + chassis id length(1 - 255 octet)
244 SUB_CHASSIS_COMPONENT = 1 # EntPhysicalAlias (IETF RFC 4133)
245 SUB_INTERFACE_ALIAS = 2 # IfAlias (IETF RFC 2863)
246 SUB_PORT_COMPONENT = 3 # EntPhysicalAlias (IETF RFC 4133)
247 SUB_MAC_ADDRESS = 4 # MAC address (IEEE std 802)
248 SUB_NETWORK_ADDRESS = 5 # networkAddress
249 SUB_INTERFACE_NAME = 6 # IfName (IETF RFC 2863)
250 SUB_LOCALLY_ASSIGNED = 7 # local
252 def __init__(self, buf=None, *args, **kwargs):
253 super(ChassisID, self).__init__(buf, *args, **kwargs)
255 (self.subtype, ) = struct.unpack(
256 self._PACK_STR, self.tlv_info[:self._PACK_SIZE])
257 self.chassis_id = self.tlv_info[self._PACK_SIZE:]
259 self.subtype = kwargs['subtype']
260 self.chassis_id = kwargs['chassis_id']
261 self.len = self._PACK_SIZE + len(self.chassis_id)
262 assert self._len_valid()
263 self.typelen = (self.tlv_type << LLDP_TLV_TYPE_SHIFT) | self.len
266 return struct.pack('!HB', self.typelen, self.subtype) + self.chassis_id
269 @lldp.set_tlv_type(LLDP_TLV_PORT_ID)
270 class PortID(LLDPBasicTLV):
271 """Port ID TLV encoder/decoder class
273 ============== =====================================
274 Attribute Description
275 ============== =====================================
276 buf Binary data to parse.
278 port_id Port ID corresponding to subtype.
279 ============== =====================================
282 _PACK_SIZE = struct.calcsize(_PACK_STR)
284 # subtype id(1 octet) + port id length(1 - 255 octet)
289 SUB_INTERFACE_ALIAS = 1 # ifAlias (IETF RFC 2863)
290 SUB_PORT_COMPONENT = 2 # entPhysicalAlias (IETF RFC 4133)
291 SUB_MAC_ADDRESS = 3 # MAC address (IEEE Std 802)
292 SUB_NETWORK_ADDRESS = 4 # networkAddress
293 SUB_INTERFACE_NAME = 5 # ifName (IETF RFC 2863)
294 SUB_AGENT_CIRCUIT_ID = 6 # agent circuit ID(IETF RFC 3046)
295 SUB_LOCALLY_ASSIGNED = 7 # local
297 def __init__(self, buf=None, *args, **kwargs):
298 super(PortID, self).__init__(buf, *args, **kwargs)
300 (self.subtype, ) = struct.unpack(
301 self._PACK_STR, self.tlv_info[:self._PACK_SIZE])
302 self.port_id = self.tlv_info[self._PACK_SIZE:]
304 self.subtype = kwargs['subtype']
305 self.port_id = kwargs['port_id']
306 self.len = self._PACK_SIZE + len(self.port_id)
307 assert self._len_valid()
308 self.typelen = (self.tlv_type << LLDP_TLV_TYPE_SHIFT) | self.len
311 return struct.pack('!HB', self.typelen, self.subtype) + self.port_id
314 @lldp.set_tlv_type(LLDP_TLV_TTL)
315 class TTL(LLDPBasicTLV):
316 """Time To Live TLV encoder/decoder class
318 ============== =====================================
319 Attribute Description
320 ============== =====================================
321 buf Binary data to parse.
323 ============== =====================================
326 _PACK_SIZE = struct.calcsize(_PACK_STR)
327 _LEN_MIN = _PACK_SIZE
328 _LEN_MAX = _PACK_SIZE
330 def __init__(self, buf=None, *args, **kwargs):
331 super(TTL, self).__init__(buf, *args, **kwargs)
333 (self.ttl, ) = struct.unpack(
334 self._PACK_STR, self.tlv_info[:self._PACK_SIZE])
336 self.ttl = kwargs['ttl']
337 self.len = self._PACK_SIZE
338 assert self._len_valid()
339 self.typelen = (self.tlv_type << LLDP_TLV_TYPE_SHIFT) | self.len
342 return struct.pack('!HH', self.typelen, self.ttl)
345 @lldp.set_tlv_type(LLDP_TLV_PORT_DESCRIPTION)
346 class PortDescription(LLDPBasicTLV):
347 """Port description TLV encoder/decoder class
349 ================= =====================================
350 Attribute Description
351 ================= =====================================
352 buf Binary data to parse.
353 port_description Port description.
354 ================= =====================================
358 def __init__(self, buf=None, *args, **kwargs):
359 super(PortDescription, self).__init__(buf, *args, **kwargs)
363 self.port_description = kwargs['port_description']
364 self.len = len(self.port_description)
365 assert self._len_valid()
366 self.typelen = (self.tlv_type << LLDP_TLV_TYPE_SHIFT) | self.len
369 return struct.pack('!H', self.typelen) + self.port_description
372 def port_description(self):
375 @port_description.setter
376 def port_description(self, value):
377 self.tlv_info = value
380 @lldp.set_tlv_type(LLDP_TLV_SYSTEM_NAME)
381 class SystemName(LLDPBasicTLV):
382 """System name TLV encoder/decoder class
384 ================= =====================================
385 Attribute Description
386 ================= =====================================
387 buf Binary data to parse.
388 system_name System name.
389 ================= =====================================
393 def __init__(self, buf=None, *args, **kwargs):
394 super(SystemName, self).__init__(buf, *args, **kwargs)
398 self.system_name = kwargs['system_name']
399 self.len = len(self.system_name)
400 assert self._len_valid()
401 self.typelen = (self.tlv_type << LLDP_TLV_TYPE_SHIFT) | self.len
404 return struct.pack('!H', self.typelen) + self.tlv_info
407 def system_name(self):
411 def system_name(self, value):
412 self.tlv_info = value
415 @lldp.set_tlv_type(LLDP_TLV_SYSTEM_DESCRIPTION)
416 class SystemDescription(LLDPBasicTLV):
417 """System description TLV encoder/decoder class
419 =================== =====================================
420 Attribute Description
421 =================== =====================================
422 buf Binary data to parse.
423 system_description System description.
424 =================== =====================================
428 def __init__(self, buf=None, *args, **kwargs):
429 super(SystemDescription, self).__init__(buf, *args, **kwargs)
433 self.system_description = kwargs['system_description']
434 self.len = len(self.system_description)
435 assert self._len_valid()
436 self.typelen = (self.tlv_type << LLDP_TLV_TYPE_SHIFT) | self.len
439 return struct.pack('!H', self.typelen) + self.tlv_info
442 def system_description(self):
445 @system_description.setter
446 def system_description(self, value):
447 self.tlv_info = value
450 @lldp.set_tlv_type(LLDP_TLV_SYSTEM_CAPABILITIES)
451 class SystemCapabilities(LLDPBasicTLV):
452 """System Capabilities TLV encoder/decoder class
454 ================= =====================================
455 Attribute Description
456 ================= =====================================
457 buf Binary data to parse.
458 system_cap System Capabilities.
459 enabled_cap Enabled Capabilities.
460 ================= =====================================
462 # system cap(2) + enabled cap(2)
464 _PACK_SIZE = struct.calcsize(_PACK_STR)
465 _LEN_MIN = _PACK_SIZE
466 _LEN_MAX = _PACK_SIZE
468 # System Capabilities
469 CAP_REPEATER = (1 << 1) # IETF RFC 2108
470 CAP_MAC_BRIDGE = (1 << 2) # IEEE Std 802.1D
471 CAP_WLAN_ACCESS_POINT = (1 << 3) # IEEE Std 802.11 MIB
472 CAP_ROUTER = (1 << 4) # IETF RFC 1812
473 CAP_TELEPHONE = (1 << 5) # IETF RFC 4293
474 CAP_DOCSIS = (1 << 6) # IETF RFC 4639 and IETF RFC 4546
475 CAP_STATION_ONLY = (1 << 7) # IETF RFC 4293
476 CAP_CVLAN = (1 << 8) # IEEE Std 802.1Q
477 CAP_SVLAN = (1 << 9) # IEEE Std 802.1Q
478 CAP_TPMR = (1 << 10) # IEEE Std 802.1Q
480 def __init__(self, buf=None, *args, **kwargs):
481 super(SystemCapabilities, self).__init__(buf, *args, **kwargs)
483 (self.system_cap, self.enabled_cap) = struct.unpack(
484 self._PACK_STR, self.tlv_info[:self._PACK_SIZE])
486 self.system_cap = kwargs['system_cap']
487 self.enabled_cap = kwargs['enabled_cap']
488 self.len = self._PACK_SIZE
489 assert self._len_valid()
490 self.typelen = (self.tlv_type << LLDP_TLV_TYPE_SHIFT) | self.len
493 return struct.pack('!HHH',
494 self.typelen, self.system_cap, self.enabled_cap)
497 @lldp.set_tlv_type(LLDP_TLV_MANAGEMENT_ADDRESS)
498 class ManagementAddress(LLDPBasicTLV):
499 """Management Address TLV encoder/decoder class
501 ================= =====================================
502 Attribute Description
503 ================= =====================================
504 buf Binary data to parse.
505 addr_subtype Address type.
507 intf_subtype Interface type.
508 intf_num Interface number.
510 ================= =====================================
515 _ADDR_PACK_STR = '!BB' # address string length, address subtype
516 _ADDR_PACK_SIZE = struct.calcsize(_ADDR_PACK_STR)
520 _INTF_PACK_STR = '!BIB' # interface subtype, interface number, oid length
521 _INTF_PACK_SIZE = struct.calcsize(_INTF_PACK_STR)
525 def __init__(self, buf=None, *args, **kwargs):
526 super(ManagementAddress, self).__init__(buf, *args, **kwargs)
528 (self.addr_len, self.addr_subtype) = struct.unpack(
529 self._ADDR_PACK_STR, self.tlv_info[:self._ADDR_PACK_SIZE])
530 assert self._addr_len_valid()
531 offset = self._ADDR_PACK_SIZE + self.addr_len - 1
532 self.addr = self.tlv_info[self._ADDR_PACK_SIZE:offset]
534 (self.intf_subtype, self.intf_num, self.oid_len) = struct.unpack(
536 self.tlv_info[offset:offset + self._INTF_PACK_SIZE])
537 assert self._oid_len_valid()
539 offset = offset + self._INTF_PACK_SIZE
540 self.oid = self.tlv_info[offset:]
542 self.addr_subtype = kwargs['addr_subtype']
543 self.addr = kwargs['addr']
544 self.addr_len = len(self.addr) + 1 # 1 octet subtype
545 assert self._addr_len_valid()
547 self.intf_subtype = kwargs['intf_subtype']
548 self.intf_num = kwargs['intf_num']
550 self.oid = kwargs['oid']
551 self.oid_len = len(self.oid)
552 assert self._oid_len_valid()
554 self.len = self._ADDR_PACK_SIZE + self.addr_len - 1 \
555 + self._INTF_PACK_SIZE + self.oid_len
556 assert self._len_valid()
557 self.typelen = (self.tlv_type << LLDP_TLV_TYPE_SHIFT) | self.len
560 tlv_info = struct.pack(self._ADDR_PACK_STR,
561 self.addr_len, self.addr_subtype)
562 tlv_info += self.addr
563 tlv_info += struct.pack(self._INTF_PACK_STR,
564 self.intf_subtype, self.intf_num, self.oid_len)
566 return struct.pack('!H', self.typelen) + tlv_info
568 def _addr_len_valid(self):
569 return (self._ADDR_LEN_MIN <= self.addr_len or
570 self.addr_len <= self._ADDR_LEN_MAX)
572 def _oid_len_valid(self):
573 return self._OID_LEN_MIN <= self.oid_len <= self._OID_LEN_MAX
576 @lldp.set_tlv_type(LLDP_TLV_ORGANIZATIONALLY_SPECIFIC)
577 class OrganizationallySpecific(LLDPBasicTLV):
578 """Organizationally Specific TLV encoder/decoder class
580 ================= =============================================
581 Attribute Description
582 ================= =============================================
583 buf Binary data to parse.
584 oui Organizationally unique ID.
585 subtype Organizationally defined subtype.
586 info Organizationally defined information string.
587 ================= =============================================
590 _PACK_SIZE = struct.calcsize(_PACK_STR)
591 _LEN_MIN = _PACK_SIZE
594 def __init__(self, buf=None, *args, **kwargs):
595 super(OrganizationallySpecific, self).__init__(buf, *args, **kwargs)
597 (self.oui, self.subtype) = struct.unpack(
598 self._PACK_STR, self.tlv_info[:self._PACK_SIZE])
599 self.info = self.tlv_info[self._PACK_SIZE:]
601 self.oui = kwargs['oui']
602 self.subtype = kwargs['subtype']
603 self.info = kwargs['info']
604 self.len = self._PACK_SIZE + len(self.info)
605 assert self._len_valid()
606 self.typelen = (self.tlv_type << LLDP_TLV_TYPE_SHIFT) | self.len
609 return struct.pack('!H3sB', self.typelen, self.oui,
610 self.subtype) + self.info
613 lldp.set_classes(lldp._tlv_parsers)