LLDP数据包中添加自定义LLDPDU

在LLDP数据包中添加自定义LLDPDU

如何在LLDP数据包中携带发送时间戳
LLDP描述文件:ryu/lib/packet/lldp.py

一、lldp.py

  1. 增添 TLV 类型
  2. 增添 TimeStamp 类
  3. lldp 类的修改

1.1 lldp.py :增添TLV类型:TimeStamp

  # 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
  LLDP_TLV_SEND_TIME = 11  # Time stamp for sending LLDP packet, using for delay measurement.

增添发送时间戳:LLDP_TLV_SEND_TIME = 11 ,用于解析

1.2 lldp.py :增添 TimeStamp 类

参考lldp.py中的 TTL 类

@lldp.set_tlv_type(LLDP_TLV_SEND_TIME)
class TimeStamp(LLDPBasicTLV):
    _PACK_STR = '!d'
    _PACK_SIZE = struct.calcsize(_PACK_STR)
    _LEN_MIN = _PACK_SIZE
    _LEN_MAX = _PACK_SIZE
  
    def __init__(self, buf=None, *args, **kwargs):
        super(TimeStamp, self).__init__(buf, *args, **kwargs)
        if buf:
        (self.timestamp, ) = struct.unpack(
                self._PACK_STR, self.tlv_info[:self._PACK_SIZE])
        else:
            self.timestamp = kwargs['timestamp']
            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('!Hd', self.typelen, self.timestamp)

1.3 lldp.py : lldp 类的修改

去掉最后一项,即不判断最后一个为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)

修改 break 条件

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:]
        if tlv.tlv_type == LLDP_TLV_SEND_TIME: # END 改为LLDP_TLV_SEND_TIME
            break
        assert len(buf) > 0
    lldp_pkt = cls(tlvs)
    assert lldp_pkt._tlvs_len_valid()
    assert lldp_pkt._tlvs_valid()
    return lldp_pkt, None, buf

二、switches.py:

  1. switches.py文件中的LLDPPacket类完成了LLDP数据包的初始化和序列化实现
  2. lldp_packet方法可以构造LLDP数据包,并返回序列化之后的数据。在此函数中,我们需要添加timestamp的TLV
  3. lldp_parse:将获取到的字节流的数据解析为对应的LLDP数据包,由于在发送之前,我们加入了一个timestamp的TLV,所以解析时需要完成这个TLV的解析,并将TimeStamp作为返回值返回

2.1、switches.py:@staticmethod lldp_packet

1. 增添方法的参数 timestamp

def lldp_packet(dpid, port_no, dl_addr, ttl, timestamp):

2. 增添 Timestamp 属性

tlv_ttl = lldp.TTL(ttl=ttl)
tlv_timestamp = lldp.TimeStamp(timestamp=timestamp)
tlv_end = lldp.End()

3. 修改tlvs:增添 timestamp 参数

 tlvs = (tlv_chassis_id, tlv_port_id, tlv_ttl, tlv_timestamp, tlv_end)

2.2、switches.py:@staticmethod lldp_parse

tlv_timestamp = lldp_pkt.tlvs[11]
timestamp = tlv_timestamp
return  src_dpid, src_port_no, timestamp,

解析取出timestamp,要与之前LLDP声明的一致,之前声明的为11

2.3、switches.py: def _port_added 修改:增加时间戳

def _port_added(self, port):
    _time = time.time()
    lldp_data = LLDPPacket.lldp_packet(port.dpid, port.port_no,
                       port.hw_addr, self.DEFAULT_TTL, _time)

第二处:

LLDP_PACKET_LEN = len(LLDPPacket.lldp_packet(0, 0, DONTCARE_STR, 0, 0))

2.4 switches.py:增添返回的变量

调用了lldp_parse方法的地方,返回的值都要增添一个timestamp

src_dpid, src_port_no, timestamp = LLDPPacket.lldp_parse(msg.data)

2.5 发送LLDP报文出修改,每次重新插入时间戳

  def send_lldp_packet(self, port):
    try:
      port_data = self.ports.lldp_sent(port)
    except KeyError:
      # ports can be modified during our sleep in self.lldp_loop()
      # LOG.debug('send_lld error', exc_info=True)
      return
    if port_data.is_down:
      return

    dp = self.dps.get(port.dpid, None)
    if dp is None:
      # datapath was already deleted
      return
    timestamp = time.time()
    lldp_data = LLDPPacket.lldp_packet(
      port.dpid, port.port_no, port.hw_addr, self.DEFAULT_TTL, timestamp)
    # LOG.debug('lldp sent dpid=%s, port_no=%d', dp.id, port.port_no)
    # TODO:XXX
    if dp.ofproto.OFP_VERSION == ofproto_v1_0.OFP_VERSION:
      actions = [dp.ofproto_parser.OFPActionOutput(port.port_no)]
      dp.send_packet_out(actions=actions, data=lldp_data)
    elif dp.ofproto.OFP_VERSION >= ofproto_v1_2.OFP_VERSION:
      actions = [dp.ofproto_parser.OFPActionOutput(port.port_no)]
      out = dp.ofproto_parser.OFPPacketOut(
        datapath=dp, in_port=dp.ofproto.OFPP_CONTROLLER,
        buffer_id=dp.ofproto.OFP_NO_BUFFER, actions=actions,
        data=lldp_data)
      dp.send_msg(out)
    #print port_data #cat
    else:
      LOG.error('cannot send lldp packet. unsupported version. %x',
            dp.ofproto.OFP_VERSION)

注意

在Ryu的Switches模块中,被发送的LLDP都是一次构造之后保存起来,发送时直接发送的,所以添加的时间戳会固定在第一次构造时的时间。所以如果希望正确地插入发送时间戳,还需要进行额外的逻辑修改。但是这也许就破坏了Ryu设计的完整性,所以如何操作还需要读者自行斟酌。

参考:李呈: Ryu:如何在LLDP中添加自定义LLDPDU

你可能感兴趣的:(LLDP数据包中添加自定义LLDPDU)