blob: a0bf4973279301a16162eff60d43e8f239d29694 [file] [log] [blame]
amit.ghosh6104cf52021-01-07 16:53:28 +01001/*
2 * Copyright 2018-present Open Networking Foundation
3 *
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
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
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 implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16package org.opencord.olttopology.impl;
17
18import static java.util.concurrent.Executors.newSingleThreadExecutor;
19import static org.onlab.util.Tools.groupedThreads;
20
21import org.apache.commons.lang.ArrayUtils;
22import org.onlab.packet.EthType;
23import org.onlab.packet.Ethernet;
24import org.onlab.packet.Ip4Address;
25import org.onlab.packet.LLDP;
26import org.onlab.packet.LLDPTLV;
27import org.onlab.packet.MacAddress;
28import org.onlab.util.KryoNamespace;
29import org.onosproject.cfg.ComponentConfigService;
30import org.onosproject.codec.CodecService;
31import org.onosproject.core.ApplicationId;
32import org.onosproject.core.CoreService;
33import org.onosproject.mastership.MastershipService;
34import org.onosproject.net.ConnectPoint;
35import org.onosproject.net.Device;
36import org.onosproject.net.DeviceId;
37import org.onosproject.net.Port;
38import org.onosproject.net.device.DeviceEvent;
39import org.onosproject.net.device.DeviceListener;
40import org.onosproject.net.device.DeviceService;
41import org.onosproject.net.flow.DefaultTrafficTreatment;
42import org.onosproject.net.flow.TrafficTreatment;
43import org.onosproject.net.flowobjective.FlowObjectiveService;
44import org.onosproject.net.packet.DefaultOutboundPacket;
45import org.onosproject.net.packet.InboundPacket;
46import org.onosproject.net.packet.OutboundPacket;
47import org.onosproject.net.packet.PacketContext;
48import org.onosproject.net.packet.PacketProcessor;
49import org.onosproject.net.packet.PacketService;
50import org.onosproject.store.serializers.KryoNamespaces;
51import org.onosproject.store.service.EventuallyConsistentMap;
52import org.onosproject.store.service.StorageService;
53import org.onosproject.store.service.WallClockTimestamp;
54import org.opencord.olttopology.OltNeighborInfo;
55import org.opencord.olttopology.OltTopologyInformationService;
56import org.opencord.sadis.BaseInformationService;
57import org.opencord.sadis.SadisService;
58import org.opencord.sadis.SubscriberAndDeviceInformation;
59import org.osgi.service.component.ComponentContext;
60import org.osgi.service.component.annotations.Activate;
61import org.osgi.service.component.annotations.Component;
62import org.osgi.service.component.annotations.Deactivate;
63import org.osgi.service.component.annotations.Modified;
64import org.osgi.service.component.annotations.Reference;
65import org.osgi.service.component.annotations.ReferenceCardinality;
66import org.slf4j.Logger;
67
68import java.nio.ByteBuffer;
69import java.util.ArrayList;
70import java.util.Arrays;
71import java.util.Dictionary;
72import java.util.List;
73import java.util.Map;
74import java.util.Map.Entry;
75import java.util.Objects;
76import java.util.Optional;
77import java.util.Properties;
78import java.util.concurrent.ConcurrentHashMap;
79import java.util.concurrent.Executors;
80import java.util.concurrent.ScheduledExecutorService;
81import java.util.concurrent.ScheduledFuture;
82import java.util.concurrent.TimeUnit;
83import java.util.concurrent.ExecutorService;
84import java.util.stream.Collectors;
85
86import static org.onlab.util.Tools.get;
87import static org.opencord.olttopology.impl.OsgiPropertyConstants.DEFAULT_CHASSIS_ID;
88import static org.opencord.olttopology.impl.OsgiPropertyConstants.DEFAULT_CHASSIS_ID_DEFAULT;
89import static org.opencord.olttopology.impl.OsgiPropertyConstants.DEFAULT_DEST_MAC_ADDRESS;
90import static org.opencord.olttopology.impl.OsgiPropertyConstants.DEFAULT_DEST_MAC_ADDRESS_DEFAULT;
91import static org.opencord.olttopology.impl.OsgiPropertyConstants.DEFAULT_TTL_IN_SECS;
92import static org.opencord.olttopology.impl.OsgiPropertyConstants.DEFAULT_TTL_IN_SECS_DEFAULT;
93import static org.opencord.olttopology.impl.OsgiPropertyConstants.DEFAULT_LLDP_SEND_PERIODICITY;
94import static org.opencord.olttopology.impl.OsgiPropertyConstants.LLDP_SEND_PERIODICITY_STR;
95
96import static org.slf4j.LoggerFactory.getLogger;
97
98/**
99 * Application to keep track of the topology of OLT devices.
100 */
101@Component(immediate = true,
102 property = {
103 DEFAULT_DEST_MAC_ADDRESS + ":String=" + DEFAULT_DEST_MAC_ADDRESS_DEFAULT,
104 DEFAULT_TTL_IN_SECS + ":Integer=" + DEFAULT_TTL_IN_SECS_DEFAULT,
105 DEFAULT_CHASSIS_ID + ":Integer=" + DEFAULT_CHASSIS_ID_DEFAULT,
106 LLDP_SEND_PERIODICITY_STR + ":Integer=" + DEFAULT_LLDP_SEND_PERIODICITY
107 }
108)
109public class OltTopology implements OltTopologyInformationService {
110 // Subtype value for IPv4 as per the LLDP specs
111 public static final byte IP_ADDR_SUB_TYPE = 0x1;
112 // 5 below is address subtype + IP4 address len
113 public static final byte IP_ADDR_STRING_LEN = 0x5;
114 // Value of interface sub type as per the LLDP specs
115 public static final byte INTERFACE_SUB_TYPE = 0x1;
116 // Value of interface number set in the management address
117 // field of LLDP packets being sent out
118 public static final int INTERFACE_NUM = 0;
119 // Value of the OID set in the management address field of
120 // LLDP packets being sent out
121 public static final byte OID_STRING = 0x0;
122 // Value of SystemName TLV as per the LLDP specs
123 public static final byte SYSTEMNAME_TLV_TYPE = 0x5;
124 // Value of Management Address TLV as per the LLDP specs
125 public static final byte MANAGEMENT_ADDR_TLV_TYPE = 0x8;
126 // Value of Port TLV sub type as per the LLDP specs
127 public static final byte PORT_TLV_SUB_TYPE = 5;
128 private static final String APP_NAME = "org.opencord.olttopology";
129 // Name for the consistent map where the neighbor information is stored
130 private static final String NEIGHBORS = "olt-neighbors";
131 private final Logger log = getLogger(getClass());
132 // deviceListener to be able to receive events about the OLT devices
133 private final DeviceListener deviceListener = new InternalDeviceListener();
134 // Service to execute periodic sending of LLDP packets
135 private final ScheduledExecutorService scheduledExecutorService =
136 Executors.newSingleThreadScheduledExecutor();
137 // our application-specific event handler for processing LLDP messages
138 // received from the OLT devices
139 private final ReactivePacketProcessor processor = new ReactivePacketProcessor();
140 // Map for storing information about the OLTs, is map of OLT deviceId to
141 // uplink port of the OLT
142 private final Map<DeviceId, Port> oltPortMap = new ConcurrentHashMap<>();
143 // cfg variable to set the parameters dynamically.
144 protected String destMacAddress = DEFAULT_DEST_MAC_ADDRESS_DEFAULT;
145 // References to the various services that this app uses
146 @Reference(cardinality = ReferenceCardinality.MANDATORY)
147 protected MastershipService mastershipService;
148 @Reference(cardinality = ReferenceCardinality.MANDATORY)
149 protected DeviceService deviceService;
150 @Reference(cardinality = ReferenceCardinality.MANDATORY)
151 protected CoreService coreService;
152 @Reference(cardinality = ReferenceCardinality.MANDATORY)
153 protected ComponentConfigService componentConfigService;
154 @Reference(cardinality = ReferenceCardinality.MANDATORY)
155 protected PacketService packetService;
156 @Reference(cardinality = ReferenceCardinality.MANDATORY)
157 protected StorageService storageService;
158 @Reference(cardinality = ReferenceCardinality.MANDATORY)
159 protected CodecService codecService;
160 @Reference(cardinality = ReferenceCardinality.MANDATORY)
161 protected SadisService sadisService;
162 @Reference(cardinality = ReferenceCardinality.MANDATORY)
163 protected FlowObjectiveService flowObjectiveService;
164 protected BaseInformationService<SubscriberAndDeviceInformation> subsService;
165 private short ttlInSecs = DEFAULT_TTL_IN_SECS_DEFAULT;
166 private int chassisId = DEFAULT_CHASSIS_ID_DEFAULT;
167 private int lldpSendPeriodicity = DEFAULT_LLDP_SEND_PERIODICITY;
168 private ApplicationId appId;
169 // Map for storing information about the neighbor connected to an OLT port
170 private EventuallyConsistentMap<ConnectPoint, OltNeighborInfo> neighbors;
171 private ScheduledFuture<?> futureTask;
172
173 protected ExecutorService packetProcessorExecutor;
174 protected ExecutorService eventExecutor;
175
176 private static boolean isNniPort(Port port) {
177 if (port.annotations().keys().contains("portName")) {
178 return port.annotations().value("portName").contains("nni-");
179 }
180 return false;
181 }
182
183 @Activate
184 public void activate(ComponentContext context) {
185 modified(context);
186 appId = coreService.registerApplication(APP_NAME);
187 componentConfigService.registerProperties(getClass());
188 codecService.registerCodec(OltNeighborInfo.class, new OltTopologyInformationCodec());
189
190 subsService = sadisService.getSubscriberInfoService();
191
192 // look for all provisioned devices in Sadis and put them in oltData
193 deviceService.getDevices().forEach(this::createAndProcessDevice);
194
195 // The NEIGHBORS map should be available across ONOS instance failures,
196 // create it using the storage service.
197 KryoNamespace serializer = KryoNamespace.newBuilder()
198 .register(KryoNamespaces.API)
199 .register(OltNeighborInfo.class)
200 .register(ConnectPoint.class)
201 .register(java.util.Date.class)
202 .register(org.onosproject.net.Port.class)
203 .register(org.onlab.packet.LLDPOrganizationalTLV.class)
204 .build();
205
206 neighbors = storageService.<ConnectPoint, OltNeighborInfo>eventuallyConsistentMapBuilder()
207 .withName(NEIGHBORS)
208 .withSerializer(serializer)
209 .withTimestampProvider((k, v) -> new WallClockTimestamp())
210 .build();
211
212 deviceService.addListener(deviceListener);
213
214 // register our event handler
215 packetService.addProcessor(processor, PacketProcessor.director(2));
216 futureTask = scheduledExecutorService.scheduleAtFixedRate(this::oltTopologyTimerTask, 0, lldpSendPeriodicity,
217 TimeUnit.MINUTES);
218 packetProcessorExecutor = newSingleThreadExecutor(groupedThreads("onos/olttopology", "packet-%d", log));
219 eventExecutor = newSingleThreadExecutor(groupedThreads("onos/olttopology", "events-%d", log));
220
221 log.info("Started with Application ID {}", appId.id());
222 }
223
224 @Deactivate
225 public void deactivate() {
226 futureTask.cancel(true);
227 scheduledExecutorService.shutdownNow();
228 packetService.removeProcessor(processor);
229 deviceService.removeListener(deviceListener);
230 codecService.unregisterCodec(OltNeighborInfo.class);
231 componentConfigService.unregisterProperties(getClass(), false);
232 packetProcessorExecutor.shutdown();
233 eventExecutor.shutdown();
234 log.info("Stopped");
235 }
236
237 @Modified
238 public void modified(ComponentContext context) {
239 Dictionary<?, ?> properties = context != null ? context.getProperties() : new Properties();
240 try {
241 String destMac = get(properties, DEFAULT_DEST_MAC_ADDRESS);
242 destMacAddress = Objects.isNull(destMac) ? DEFAULT_DEST_MAC_ADDRESS_DEFAULT : destMac;
243 String ttlInsecsStr = get(properties, DEFAULT_TTL_IN_SECS);
244 ttlInSecs = Short.parseShort(ttlInsecsStr.trim());
245
246 String chassisIdStr = get(properties, DEFAULT_CHASSIS_ID);
247 chassisId = Integer.parseInt(chassisIdStr.trim());
248
249 String lldpPeriodicity = get(properties, LLDP_SEND_PERIODICITY_STR);
250 int newLldpSendPeriodicity = Integer.parseInt(lldpPeriodicity);
251
252 if (newLldpSendPeriodicity <= 0) {
253 log.error("lldpSendPeriodicity should be a positive integer");
254 } else if (newLldpSendPeriodicity != lldpSendPeriodicity) {
255 lldpSendPeriodicity = newLldpSendPeriodicity;
256 lldpPeriodicity(newLldpSendPeriodicity);
257 }
258
259 log.debug("OLT properties: destMacAddress: {}, ttlInSecs: {}, chassisId: {}, lldpSendPeriodicity{}",
260 destMacAddress, ttlInSecs, chassisId, lldpSendPeriodicity);
261 } catch (Exception e) {
262 log.error("Error while modifying the properties", e);
263 }
264 }
265
266 @Override
267 public Map<ConnectPoint, OltNeighborInfo> getNeighbours() {
268 return neighbors.entrySet().stream().collect(Collectors.toMap(Entry::getKey, Entry::getValue));
269 }
270
271 /**
272 * Sets periodicity in minutes for sending out LLDP packet to OLT NNI Ports.
273 *
274 * @param timer Value in minutes.
275 */
276 @Override
277 public void lldpPeriodicity(int timer) {
278 if (timer > 0) {
279 if (futureTask != null) {
280 futureTask.cancel(true);
281 }
282
283 futureTask = scheduledExecutorService.scheduleAtFixedRate(this::oltTopologyTimerTask, 0,
284 timer,
285 TimeUnit.MINUTES);
286 log.info("LLDP Packet out Periodicity updated to {} minutes", timer);
287 }
288 }
289
290 /**
291 * Creates entry in the oltData map.
292 * provision LLDP flow on enabled NNI ports if device is present in Sadis config
293 *
294 * @param dev Device to look for
295 */
296 private void createAndProcessDevice(Device dev) {
297 SubscriberAndDeviceInformation deviceInfo = subsService.get(dev.serialNumber());
298 log.debug("CreateAndProcessDevice: deviceInfo {}", deviceInfo);
299
300 if (deviceInfo != null) {
301 // TODO FIXME, this works only with one NNI
302 Optional<Port> optPort = deviceService.getPorts(dev.id())
303 .stream().filter(OltTopology::isNniPort).findFirst();
304 if (optPort.isPresent()) {
305 Port port = optPort.get();
306 oltPortMap.put(dev.id(), port);
307 return;
308 }
309 }
310 log.warn("CreateAndProcessDevice: failed to update the oltdata for device {}", dev);
311 }
312
313 /**
314 * Updates OltData Map with new AccessDeviceData if it is already present in OltMap,
315 * Provisions LLDP flow on enabled NNI port if it is present in Sadis config
316 * Only one NNI port is supported as of now.
317 *
318 * @param dev Device to look for
319 * @param uplink Uplink port number
320 * @return true if updated else false
321 */
322 private boolean updateOltData(Device dev, Port uplink) {
323 // check if this device is provisioned in Sadis
324 SubscriberAndDeviceInformation deviceInfo = subsService.get(dev.serialNumber());
325 log.debug("updateAccessDevice: deviceInfo {}", deviceInfo);
326
327 if (deviceInfo != null) {
328 oltPortMap.replace(dev.id(), uplink);
329 log.debug("updateAccessDevice: Stored did {} uplink {}", dev.id(), uplink);
330 return true;
331 }
332 return false;
333 }
334
335 /**
336 * Sends LLDP Packet to OLT NNI Port.
337 *
338 * @param devId Access Device data for OLT
339 * @param nniPort NNI port info of OLT
340 */
341 private void sendLldpPackets(DeviceId devId, Port nniPort) {
342 if (!mastershipService.isLocalMaster(devId)) {
343 return;
344 }
345
346 // Get NNI Port name to be filled in LLDP packet port TLV.
347 String portName = (nniPort.annotations().value("portName").isEmpty()) ? "" :
348 nniPort.annotations().value("portName");
349
350 // Get System Name value from device name.
351 Device d = deviceService.getDevice(devId);
352 String[] systemName = d.id().uri().toString().split(":", 2);
353
354 MacAddress destMac = MacAddress.valueOf(destMacAddress);
355 MacAddress srcMac = createMacFromDevId(d.id());
356
357 SubscriberAndDeviceInformation deviceInfo = subsService.get(d.serialNumber());
358
359 // Initialized deviceIP address, to be sent in Management Address TLV.
360 Ip4Address devIpAddr = Ip4Address.valueOf("0.0.0.0");
361
362 // Get OLT device IP address from deviceInfo, to be filled in management address TLV.
363 if (deviceInfo != null) {
364 devIpAddr = deviceInfo.ipAddress();
365 log.debug("sendLldpPackets: did {} nniPort {} devIP {}", d.id(), nniPort, devIpAddr);
366 } else {
367 log.warn("device {} not found in Sadis NOT sending LLDP packet", d.id());
368 return;
369 }
370
371 //Created LLDP packet.
372 Ethernet packet = createLldpPacket(destMac, srcMac, chassisId,
373 portName, ttlInSecs, systemName[1], devIpAddr
374 .toString());
375
376 // Create the connect point to send the packet to and emit
377 ConnectPoint toSendTo = new ConnectPoint(d.id(), nniPort.number());
378
379 //Sends LLDP packet to connect point(NNI port).
380 TrafficTreatment t = DefaultTrafficTreatment.builder()
381 .setOutput(toSendTo.port()).build();
382 OutboundPacket o = new DefaultOutboundPacket(
383 toSendTo.deviceId(), t,
384 ByteBuffer.wrap(packet.serialize()));
385 if (log.isTraceEnabled()) {
386 log.trace("Sending LLDP packet {} at {}",
387 packet, toSendTo);
388 }
389
390 packetService.emit(o);
391 }
392
393 /**
394 * Creates MAC address for LLDP packet from OLT device ID string.
395 *
396 * @param id Device ID of OLT
397 * @return Mac address
398 */
399 private MacAddress createMacFromDevId(DeviceId id) {
400 String strId = id.toString();
401 String macStr = strId.substring(7);
402 String formattedMac = macStr.replaceAll("(.{2})", "$1" + ":");
403 formattedMac = formattedMac.substring(0, formattedMac.length() - 1);
404
405 return MacAddress.valueOf(formattedMac);
406 }
407
408 /**
409 * Creates LLDP packet to be sent out of OLT.
410 *
411 * @param destMac Destination LLDP Mac address
412 * @param srcMac Source Mac of OLT device
413 * @param chassisId Chassis ID TLV value
414 * @param port NNI port information
415 * @param ttl TTL value in sec
416 * @param systemName System name TLV value
417 * @param mgmtAddr Management Address TLV value
418 * @return LLDP ethernet packet
419 */
420 private Ethernet createLldpPacket(MacAddress destMac, MacAddress srcMac,
421 int chassisId, String port, short ttl,
422 String systemName, String mgmtAddr) {
423
424 Ethernet ethPkt = new Ethernet();
425 ethPkt.setEtherType(Ethernet.TYPE_LLDP);
426 ethPkt.setDestinationMACAddress(destMac);
427 ethPkt.setSourceMACAddress(srcMac);
428
429 LLDP lldpPkt = new LLDP();
430
431 setChassisId(lldpPkt, chassisId);
432 setPortId(lldpPkt, port);
433 setTtl(lldpPkt, ttl);
434
435 List<LLDPTLV> optionalTlv = new ArrayList<>();
436 optionalTlv.add(createSystemNameTlv(systemName));
437 optionalTlv.add(createMgmtAddressTlv(mgmtAddr));
438
439 lldpPkt.setOptionalTLVList(optionalTlv);
440
441 ethPkt.setPayload(lldpPkt);
442 return ethPkt;
443 }
444
445 /**
446 * Sets Chassis ID TLV for LLDP packet.
447 *
448 * @param lldpPkt LLDP packet reference
449 * @param chassisId Chassid ID tlv value
450 */
451 private void setChassisId(LLDP lldpPkt, final int chassisId) {
452 final byte chassisTlvSubtype = 1;
453
454 byte[] chassis = ArrayUtils.addAll(new byte[]{chassisTlvSubtype},
455 ByteBuffer.allocate(String.valueOf(chassisId).length())
456 .put(String.valueOf(chassisId).getBytes()).array());
457
458 LLDPTLV chassisTlv = new LLDPTLV();
459 lldpPkt.setChassisId(chassisTlv.setLength((byte) chassis.length)
460 .setType(LLDP.CHASSIS_TLV_TYPE)
461 .setValue(chassis));
462 }
463
464 /**
465 * Sets Port ID tlv for LLDP packet.
466 *
467 * @param lldpPkt LLDP packet reference
468 * @param ifaceName Port Name TLV value
469 */
470 private void setPortId(LLDP lldpPkt, final String ifaceName) {
471
472 byte[] port = ArrayUtils.addAll(new byte[]{PORT_TLV_SUB_TYPE},
473 ifaceName.getBytes());
474
475 LLDPTLV portTlv = new LLDPTLV();
476 lldpPkt.setPortId(portTlv.setLength((byte) port.length)
477 .setType(LLDP.PORT_TLV_TYPE)
478 .setValue(port));
479 }
480
481 /**
482 * Sets TTL tlv for LLDP packet.
483 *
484 * @param lldpPkt LLDP Packet reference
485 * @param timeInSecs TTL tlv value in sec
486 */
487 private void setTtl(LLDP lldpPkt, final short timeInSecs) {
488 byte[] time = ByteBuffer.allocate(2).putShort(timeInSecs).array();
489
490 LLDPTLV ttlTlv = new LLDPTLV();
491
492 lldpPkt.setTtl(ttlTlv.setType(LLDP.TTL_TLV_TYPE)
493 .setLength((short) time.length)
494 .setValue(time));
495 }
496
497 /**
498 * Creates System name TLV for LLDP packet.
499 *
500 * @param systemName System name tlv value
501 * @return systemName TLV
502 */
503 private LLDPTLV createSystemNameTlv(String systemName) {
504 byte[] bytes = systemName.getBytes();
505
506 LLDPTLV sysNameTlv = new LLDPTLV();
507
508 return sysNameTlv.setType(SYSTEMNAME_TLV_TYPE)
509 .setLength((byte) bytes.length)
510 .setValue(bytes);
511 }
512
513 /**
514 * Sets Management address TLV for LLDP packet.
515 *
516 * @param mgmtAddress Management address tlv value
517 * @return Management address TLV
518 */
519 private LLDPTLV createMgmtAddressTlv(String mgmtAddress) {
520
521 Ip4Address ipAddr = Ip4Address.valueOf(mgmtAddress);
522
523 byte[] addrStr = ArrayUtils.addAll(new byte[]{IP_ADDR_SUB_TYPE},
524 ipAddr.toOctets());
525
526 byte[] ipAddrBytes = ArrayUtils.addAll(new byte[]{IP_ADDR_STRING_LEN},
527 addrStr);
528
529 byte[] bytesInterfacetype = ArrayUtils.addAll(ipAddrBytes,
530 ByteBuffer.allocate(1).put(INTERFACE_SUB_TYPE).array());
531 byte[] bytesInterfaceNumber = ArrayUtils.addAll(bytesInterfacetype,
532 ByteBuffer.allocate(4).putInt(INTERFACE_NUM).array());
533 byte[] finalMgmtAddrBytes = ArrayUtils.addAll(bytesInterfaceNumber,
534 ByteBuffer.allocate(1).put(OID_STRING).array());
535
536 LLDPTLV mgmtAddrTlv = new LLDPTLV();
537 return mgmtAddrTlv.setType(MANAGEMENT_ADDR_TLV_TYPE)
538 .setLength((byte) finalMgmtAddrBytes.length)
539 .setValue(finalMgmtAddrBytes);
540 }
541
542 /**
543 * Processes incoming LLDP packets from NNI ports.
544 *
545 * @param pkt Inbound packet received at ONOS from OLT NNI port
546 */
547 private void handleLldpPacket(InboundPacket pkt) {
548
549 Ethernet packet = pkt.parsed();
550
551 if (packet == null) {
552 log.warn("Packet is null");
553 return;
554 }
555
556 log.debug("Got a packet {}", packet);
557
558 // Check if Ethernet type is LLDP.
559 if (packet.getEtherType() == Ethernet.TYPE_LLDP) {
560 // Get payload of Packet.
561 LLDP lldpPacket = (LLDP) packet.getPayload();
562
563 // Fetch all optional TLVs from Packet.
564 List<LLDPTLV> optionalTLVs = lldpPacket.getOptionalTLVList();
565
566 // Look for the system name and neighbor
567 // management IP address TLVs.
568 String systemName = null;
569 String neighMgmtAddr = "";
570 if (optionalTLVs != null) {
571 for (LLDPTLV tlv : optionalTLVs) {
572 // Fetching system name TLV.
573 if (tlv.getType() == SYSTEMNAME_TLV_TYPE) {
574 systemName = new String(tlv.getValue());
575 } else if (tlv.getType() == MANAGEMENT_ADDR_TLV_TYPE) {
576 /* Fetching 4 Octets from MANAGEMENT Address TLV to get IP address. */
577 byte[] neighMgmtIpBytes = Arrays.copyOfRange(tlv.getValue(), 2, 6);
578 Ip4Address neighMgmtIpaddress = Ip4Address.valueOf(neighMgmtIpBytes);
579 neighMgmtAddr = neighMgmtIpaddress.toString();
580 }
581 }
582 }
583
584 if (systemName == null) {
585 // We expect the system name to be sent by the neighbor, in absence
586 // we don't store the information
587 return;
588 }
589
590 int portIdTlvLen = lldpPacket.getPortId().getLength();
591
592 String portName = new String(lldpPacket.getPortId().getValue())
593 .substring(1, portIdTlvLen);
594
595 // The OLT name stored in the topology information is the device uri
596 // excluding the "of:" part
597 DeviceId devId = pkt.receivedFrom().deviceId();
598 String deviceUri = devId.uri().toString();
599 String[] oltName = deviceUri.split(":", 2);
600 Port oltNni = deviceService.getPort(pkt.receivedFrom());
601
602 String devSerial = deviceService.getDevice(devId).serialNumber();
603
604 // Creating object of OltNeighborInfo with all info required.
605 OltNeighborInfo newNeighbor = new OltNeighborInfo(systemName,
606 portName,
607 oltName[1],
608 oltNni,
609 devSerial);
610 newNeighbor.setMgmtAddress(neighMgmtAddr);
611
612 // Store all the other optional in the neighbour information
613 // this is for future use
614 for (LLDPTLV tlv : optionalTLVs) {
615 if (tlv.getType() != SYSTEMNAME_TLV_TYPE && tlv.getType() != MANAGEMENT_ADDR_TLV_TYPE) {
616 newNeighbor.addOtherOptionalLldpTlvs(tlv);
617 }
618 }
619
620 /*
621 Checking if Neighbor information is already present.
622 If Yes, then update current information, else
623 adding new information.
624 */
625 OltNeighborInfo curNeighbor = neighbors.get(pkt.receivedFrom());
626 if (newNeighbor.equals(curNeighbor)) {
627 curNeighbor.updateTimeStamp();
628 neighbors.put(pkt.receivedFrom(), curNeighbor);
629 } else {
630 // received first time on this connect point or old was purged
631 neighbors.put(pkt.receivedFrom(), newNeighbor);
632 }
633 }
634 }
635
636 /**
637 * Removes Entry for device from Olt topology table.
638 *
639 * @param devIdToRm Device ID to be removed
640 */
641 void removeNeighborsOfDevice(DeviceId devIdToRm) {
642 for (Map.Entry<ConnectPoint, OltNeighborInfo> neighEntry : neighbors.entrySet()) {
643 if (neighEntry.getKey().deviceId().toString().contains(devIdToRm.toString())) {
644 neighbors.remove(neighEntry.getKey());
645 }
646 }
647 }
648
649 /**
650 * oltTopologyTimerTask method to send LLDP packets periodically to the neighbours.
651 */
652 public void oltTopologyTimerTask() {
653 oltPortMap.forEach((key, p) -> {
654 //Port p = deviceService.getPort(key, value);
655 if (p != null && p.isEnabled()) {
656 sendLldpPackets(key, p);
657 }
658 });
659 }
660
661 private class InternalDeviceListener implements DeviceListener {
662 /**
663 * Device Listener Event, will be called if Device is added or state is changed or Updated.
664 *
665 * @param event Device event
666 */
667 @Override
668 public void event(DeviceEvent event) {
669 eventExecutor.execute(() -> {
670 DeviceId devId = event.subject().id();
671 /* Checking DEVICE_REMOVED and DEVICE_AVAILABILITY_CHANGED events before
672 Mastership check as with these events mastership check will always give false.
673 */
674 if (event.type().equals(DeviceEvent.Type.DEVICE_REMOVED)) {
675 removeNeighborsOfDevice(devId);
676 return;
677 } else if (event.type().equals(DeviceEvent.Type.DEVICE_AVAILABILITY_CHANGED)) {
678 if (!deviceService.isAvailable(devId)) {
679 removeNeighborsOfDevice(devId);
680 return;
681 }
682 }
683
684 if (!mastershipService.isLocalMaster(devId)) {
685 return;
686 }
687
688 switch (event.type()) {
689 case DEVICE_ADDED:
690 if (!oltPortMap.containsKey(devId)) {
691 createAndProcessDevice(deviceService.getDevice(devId));
692 }
693 break;
694 case PORT_ADDED:
695 /*
696 If NNI port is detected, update it in the map.
697 */
698 /* TODO: put null check for the annotations return*/
699 if (isNniPort(event.port())) {
700 if (oltPortMap.containsKey(devId)) {
701 if (!updateOltData(deviceService.getDevice(devId), event.port())) {
702 return;
703 }
704 } else {
705 return;
706 }
707 }
708
709 if (Objects.nonNull(oltPortMap.get(devId)) &&
710 oltPortMap.get(devId).equals(event.port()) &&
711 event.port().isEnabled()) {
712 sendLldpPackets(devId, event.port());
713 }
714 break;
715 case PORT_REMOVED:
716 // Remove from neighbor map and LLDP flow if NNI port is removed.
717 if (Objects.nonNull(oltPortMap.get(devId)) &&
718 oltPortMap.get(devId).equals(event.port())) {
719 // the uplink port has been removed; remove the connect
720 // point from the neighbors
721 neighbors.remove(new ConnectPoint(devId, event.port().number()));
722 }
723 break;
724 case PORT_UPDATED:
725 // if Port is enabled, provision LLDP flow and send LLDP packet to NNI Port.
726 if (!oltPortMap.get(devId).equals(event.port())) {
727 break;
728 }
729 if (event.port().isEnabled()) {
730 sendLldpPackets(devId, event.port());
731 } else {
732 neighbors.remove(new ConnectPoint(devId, event.port().number()));
733 }
734 break;
735
736 default:
737 log.debug("event {} not handle for the device {}", event.type(), devId);
738 break;
739 }
740 });
741 }
742 }
743
744 /*
745 * Class to do the processing of the LLDP packets received from the Packet Service.
746 */
747 private class ReactivePacketProcessor implements PacketProcessor {
748 @Override
749 public void process(PacketContext context) {
750 packetProcessorExecutor.execute(() -> {
751 DeviceId devId = context.inPacket().receivedFrom().deviceId();
752 if (!mastershipService.isLocalMaster(devId)) {
753 return;
754 }
755 // Extract the original Ethernet frame from the packet information
756 InboundPacket pkt = context.inPacket();
757 Ethernet ethPkt = pkt.parsed();
758
759 if (ethPkt == null) {
760 log.warn("ethPkt null while processing context: {}", context);
761 return;
762 }
763
764 if (EthType.EtherType.lookup(ethPkt.getEtherType()) == EthType.EtherType.LLDP) {
765 handleLldpPacket(context.inPacket());
766 }
767 });
768 }
769 }
770}