在LLDP數(shù)據(jù)包中添加自定義LLDPDU
如何在LLDP數(shù)據(jù)包中攜帶發(fā)送時(shí)間戳
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.
增添發(fā)送時(shí)間戳: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 類的修改
去掉最后一項(xiàng),即不判斷最后一個(gè)為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數(shù)據(jù)包的初始化和序列化實(shí)現(xiàn)
- lldp_packet方法可以構(gòu)造LLDP數(shù)據(jù)包,并返回序列化之后的數(shù)據(jù)。在此函數(shù)中,我們需要添加timestamp的TLV
- lldp_parse:將獲取到的字節(jié)流的數(shù)據(jù)解析為對應(yīng)的LLDP數(shù)據(jù)包,由于在發(fā)送之前,我們加入了一個(gè)timestamp的TLV,所以解析時(shí)需要完成這個(gè)TLV的解析,并將TimeStamp作為返回值返回
2.1、switches.py:@staticmethod lldp_packet
1. 增添方法的參數(shù) 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 參數(shù)
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 修改:增加時(shí)間戳
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:增添返回的變量
調(diào)用了lldp_parse方法的地方,返回的值都要增添一個(gè)timestamp
src_dpid, src_port_no, timestamp = LLDPPacket.lldp_parse(msg.data)
2.5 發(fā)送LLDP報(bào)文出修改,每次重新插入時(shí)間戳
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模塊中,被發(fā)送的LLDP都是一次構(gòu)造之后保存起來,發(fā)送時(shí)直接發(fā)送的,所以添加的時(shí)間戳?xí)潭ㄔ诘谝淮螛?gòu)造時(shí)的時(shí)間。所以如果希望正確地插入發(fā)送時(shí)間戳,還需要進(jìn)行額外的邏輯修改。但是這也許就破壞了Ryu設(shè)計(jì)的完整性,所以如何操作還需要讀者自行斟酌。