在LLDP数据包中添加自定义LLDPDU
如何在LLDP数据包中携带发送时间戳
LLDP描述文件:ryu/lib/packet/lldp.py
一、lldp.py
- 增添 TLV 类型
- 增添 TimeStamp 类
- 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:
- switches.py文件中的LLDPPacket类完成了LLDP数据包的初始化和序列化实现
- lldp_packet方法可以构造LLDP数据包,并返回序列化之后的数据。在此函数中,我们需要添加timestamp的TLV
- 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设计的完整性,所以如何操作还需要读者自行斟酌。