| /* |
| * Copyright 2018-present Open Networking Foundation |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| package org.opencord.olttopology.impl; |
| |
| import static java.util.concurrent.Executors.newSingleThreadExecutor; |
| import static org.onlab.util.Tools.groupedThreads; |
| |
| import org.apache.commons.lang.ArrayUtils; |
| import org.onlab.packet.EthType; |
| import org.onlab.packet.Ethernet; |
| import org.onlab.packet.Ip4Address; |
| import org.onlab.packet.LLDP; |
| import org.onlab.packet.LLDPTLV; |
| import org.onlab.packet.MacAddress; |
| import org.onlab.util.KryoNamespace; |
| import org.onosproject.cfg.ComponentConfigService; |
| import org.onosproject.codec.CodecService; |
| import org.onosproject.core.ApplicationId; |
| import org.onosproject.core.CoreService; |
| import org.onosproject.mastership.MastershipService; |
| import org.onosproject.net.ConnectPoint; |
| import org.onosproject.net.Device; |
| import org.onosproject.net.DeviceId; |
| import org.onosproject.net.Port; |
| import org.onosproject.net.device.DeviceEvent; |
| import org.onosproject.net.device.DeviceListener; |
| import org.onosproject.net.device.DeviceService; |
| import org.onosproject.net.flow.DefaultTrafficTreatment; |
| import org.onosproject.net.flow.TrafficTreatment; |
| import org.onosproject.net.flowobjective.FlowObjectiveService; |
| import org.onosproject.net.packet.DefaultOutboundPacket; |
| import org.onosproject.net.packet.InboundPacket; |
| import org.onosproject.net.packet.OutboundPacket; |
| import org.onosproject.net.packet.PacketContext; |
| import org.onosproject.net.packet.PacketProcessor; |
| import org.onosproject.net.packet.PacketService; |
| import org.onosproject.store.serializers.KryoNamespaces; |
| import org.onosproject.store.service.EventuallyConsistentMap; |
| import org.onosproject.store.service.StorageService; |
| import org.onosproject.store.service.WallClockTimestamp; |
| import org.opencord.olttopology.OltNeighborInfo; |
| import org.opencord.olttopology.OltTopologyInformationService; |
| import org.opencord.sadis.BaseInformationService; |
| import org.opencord.sadis.SadisService; |
| import org.opencord.sadis.SubscriberAndDeviceInformation; |
| import org.osgi.service.component.ComponentContext; |
| import org.osgi.service.component.annotations.Activate; |
| import org.osgi.service.component.annotations.Component; |
| import org.osgi.service.component.annotations.Deactivate; |
| import org.osgi.service.component.annotations.Modified; |
| import org.osgi.service.component.annotations.Reference; |
| import org.osgi.service.component.annotations.ReferenceCardinality; |
| import org.slf4j.Logger; |
| |
| import java.nio.ByteBuffer; |
| import java.util.ArrayList; |
| import java.util.Arrays; |
| import java.util.Dictionary; |
| import java.util.List; |
| import java.util.Map; |
| import java.util.Map.Entry; |
| import java.util.Objects; |
| import java.util.Optional; |
| import java.util.Properties; |
| import java.util.concurrent.ConcurrentHashMap; |
| import java.util.concurrent.Executors; |
| import java.util.concurrent.ScheduledExecutorService; |
| import java.util.concurrent.ScheduledFuture; |
| import java.util.concurrent.TimeUnit; |
| import java.util.concurrent.ExecutorService; |
| import java.util.stream.Collectors; |
| |
| import static org.onlab.util.Tools.get; |
| import static org.opencord.olttopology.impl.OsgiPropertyConstants.DEFAULT_CHASSIS_ID; |
| import static org.opencord.olttopology.impl.OsgiPropertyConstants.DEFAULT_CHASSIS_ID_DEFAULT; |
| import static org.opencord.olttopology.impl.OsgiPropertyConstants.DEFAULT_DEST_MAC_ADDRESS; |
| import static org.opencord.olttopology.impl.OsgiPropertyConstants.DEFAULT_DEST_MAC_ADDRESS_DEFAULT; |
| import static org.opencord.olttopology.impl.OsgiPropertyConstants.DEFAULT_TTL_IN_SECS; |
| import static org.opencord.olttopology.impl.OsgiPropertyConstants.DEFAULT_TTL_IN_SECS_DEFAULT; |
| import static org.opencord.olttopology.impl.OsgiPropertyConstants.DEFAULT_LLDP_SEND_PERIODICITY; |
| import static org.opencord.olttopology.impl.OsgiPropertyConstants.LLDP_SEND_PERIODICITY_STR; |
| |
| import static org.slf4j.LoggerFactory.getLogger; |
| |
| /** |
| * Application to keep track of the topology of OLT devices. |
| */ |
| @Component(immediate = true, |
| property = { |
| DEFAULT_DEST_MAC_ADDRESS + ":String=" + DEFAULT_DEST_MAC_ADDRESS_DEFAULT, |
| DEFAULT_TTL_IN_SECS + ":Integer=" + DEFAULT_TTL_IN_SECS_DEFAULT, |
| DEFAULT_CHASSIS_ID + ":Integer=" + DEFAULT_CHASSIS_ID_DEFAULT, |
| LLDP_SEND_PERIODICITY_STR + ":Integer=" + DEFAULT_LLDP_SEND_PERIODICITY |
| } |
| ) |
| public class OltTopology implements OltTopologyInformationService { |
| // Subtype value for IPv4 as per the LLDP specs |
| public static final byte IP_ADDR_SUB_TYPE = 0x1; |
| // 5 below is address subtype + IP4 address len |
| public static final byte IP_ADDR_STRING_LEN = 0x5; |
| // Value of interface sub type as per the LLDP specs |
| public static final byte INTERFACE_SUB_TYPE = 0x1; |
| // Value of interface number set in the management address |
| // field of LLDP packets being sent out |
| public static final int INTERFACE_NUM = 0; |
| // Value of the OID set in the management address field of |
| // LLDP packets being sent out |
| public static final byte OID_STRING = 0x0; |
| // Value of SystemName TLV as per the LLDP specs |
| public static final byte SYSTEMNAME_TLV_TYPE = 0x5; |
| // Value of Management Address TLV as per the LLDP specs |
| public static final byte MANAGEMENT_ADDR_TLV_TYPE = 0x8; |
| // Value of Port TLV sub type as per the LLDP specs |
| public static final byte PORT_TLV_SUB_TYPE = 5; |
| private static final String APP_NAME = "org.opencord.olttopology"; |
| // Name for the consistent map where the neighbor information is stored |
| private static final String NEIGHBORS = "olt-neighbors"; |
| private final Logger log = getLogger(getClass()); |
| // deviceListener to be able to receive events about the OLT devices |
| private final DeviceListener deviceListener = new InternalDeviceListener(); |
| // Service to execute periodic sending of LLDP packets |
| private final ScheduledExecutorService scheduledExecutorService = |
| Executors.newSingleThreadScheduledExecutor(); |
| // our application-specific event handler for processing LLDP messages |
| // received from the OLT devices |
| private final ReactivePacketProcessor processor = new ReactivePacketProcessor(); |
| // Map for storing information about the OLTs, is map of OLT deviceId to |
| // uplink port of the OLT |
| private final Map<DeviceId, Port> oltPortMap = new ConcurrentHashMap<>(); |
| // cfg variable to set the parameters dynamically. |
| protected String destMacAddress = DEFAULT_DEST_MAC_ADDRESS_DEFAULT; |
| // References to the various services that this app uses |
| @Reference(cardinality = ReferenceCardinality.MANDATORY) |
| protected MastershipService mastershipService; |
| @Reference(cardinality = ReferenceCardinality.MANDATORY) |
| protected DeviceService deviceService; |
| @Reference(cardinality = ReferenceCardinality.MANDATORY) |
| protected CoreService coreService; |
| @Reference(cardinality = ReferenceCardinality.MANDATORY) |
| protected ComponentConfigService componentConfigService; |
| @Reference(cardinality = ReferenceCardinality.MANDATORY) |
| protected PacketService packetService; |
| @Reference(cardinality = ReferenceCardinality.MANDATORY) |
| protected StorageService storageService; |
| @Reference(cardinality = ReferenceCardinality.MANDATORY) |
| protected CodecService codecService; |
| @Reference(cardinality = ReferenceCardinality.MANDATORY) |
| protected SadisService sadisService; |
| @Reference(cardinality = ReferenceCardinality.MANDATORY) |
| protected FlowObjectiveService flowObjectiveService; |
| protected BaseInformationService<SubscriberAndDeviceInformation> subsService; |
| private short ttlInSecs = DEFAULT_TTL_IN_SECS_DEFAULT; |
| private int chassisId = DEFAULT_CHASSIS_ID_DEFAULT; |
| private int lldpSendPeriodicity = DEFAULT_LLDP_SEND_PERIODICITY; |
| private ApplicationId appId; |
| // Map for storing information about the neighbor connected to an OLT port |
| private EventuallyConsistentMap<ConnectPoint, OltNeighborInfo> neighbors; |
| private ScheduledFuture<?> futureTask; |
| |
| protected ExecutorService packetProcessorExecutor; |
| protected ExecutorService eventExecutor; |
| |
| private static boolean isNniPort(Port port) { |
| if (port.annotations().keys().contains("portName")) { |
| return port.annotations().value("portName").contains("nni-"); |
| } |
| return false; |
| } |
| |
| @Activate |
| public void activate(ComponentContext context) { |
| modified(context); |
| appId = coreService.registerApplication(APP_NAME); |
| componentConfigService.registerProperties(getClass()); |
| codecService.registerCodec(OltNeighborInfo.class, new OltTopologyInformationCodec()); |
| |
| subsService = sadisService.getSubscriberInfoService(); |
| |
| // look for all provisioned devices in Sadis and put them in oltData |
| deviceService.getDevices().forEach(this::createAndProcessDevice); |
| |
| // The NEIGHBORS map should be available across ONOS instance failures, |
| // create it using the storage service. |
| KryoNamespace serializer = KryoNamespace.newBuilder() |
| .register(KryoNamespaces.API) |
| .register(OltNeighborInfo.class) |
| .register(ConnectPoint.class) |
| .register(java.util.Date.class) |
| .register(org.onosproject.net.Port.class) |
| .register(org.onlab.packet.LLDPOrganizationalTLV.class) |
| .build(); |
| |
| neighbors = storageService.<ConnectPoint, OltNeighborInfo>eventuallyConsistentMapBuilder() |
| .withName(NEIGHBORS) |
| .withSerializer(serializer) |
| .withTimestampProvider((k, v) -> new WallClockTimestamp()) |
| .build(); |
| |
| deviceService.addListener(deviceListener); |
| |
| // register our event handler |
| packetService.addProcessor(processor, PacketProcessor.director(2)); |
| futureTask = scheduledExecutorService.scheduleAtFixedRate(this::oltTopologyTimerTask, 0, lldpSendPeriodicity, |
| TimeUnit.MINUTES); |
| packetProcessorExecutor = newSingleThreadExecutor(groupedThreads("onos/olttopology", "packet-%d", log)); |
| eventExecutor = newSingleThreadExecutor(groupedThreads("onos/olttopology", "events-%d", log)); |
| |
| log.info("Started with Application ID {}", appId.id()); |
| } |
| |
| @Deactivate |
| public void deactivate() { |
| futureTask.cancel(true); |
| scheduledExecutorService.shutdownNow(); |
| packetService.removeProcessor(processor); |
| deviceService.removeListener(deviceListener); |
| codecService.unregisterCodec(OltNeighborInfo.class); |
| componentConfigService.unregisterProperties(getClass(), false); |
| packetProcessorExecutor.shutdown(); |
| eventExecutor.shutdown(); |
| log.info("Stopped"); |
| } |
| |
| @Modified |
| public void modified(ComponentContext context) { |
| Dictionary<?, ?> properties = context != null ? context.getProperties() : new Properties(); |
| try { |
| String destMac = get(properties, DEFAULT_DEST_MAC_ADDRESS); |
| destMacAddress = Objects.isNull(destMac) ? DEFAULT_DEST_MAC_ADDRESS_DEFAULT : destMac; |
| String ttlInsecsStr = get(properties, DEFAULT_TTL_IN_SECS); |
| ttlInSecs = Short.parseShort(ttlInsecsStr.trim()); |
| |
| String chassisIdStr = get(properties, DEFAULT_CHASSIS_ID); |
| chassisId = Integer.parseInt(chassisIdStr.trim()); |
| |
| String lldpPeriodicity = get(properties, LLDP_SEND_PERIODICITY_STR); |
| int newLldpSendPeriodicity = Integer.parseInt(lldpPeriodicity); |
| |
| if (newLldpSendPeriodicity <= 0) { |
| log.error("lldpSendPeriodicity should be a positive integer"); |
| } else if (newLldpSendPeriodicity != lldpSendPeriodicity) { |
| lldpSendPeriodicity = newLldpSendPeriodicity; |
| lldpPeriodicity(newLldpSendPeriodicity); |
| } |
| |
| log.debug("OLT properties: destMacAddress: {}, ttlInSecs: {}, chassisId: {}, lldpSendPeriodicity{}", |
| destMacAddress, ttlInSecs, chassisId, lldpSendPeriodicity); |
| } catch (Exception e) { |
| log.error("Error while modifying the properties", e); |
| } |
| } |
| |
| @Override |
| public Map<ConnectPoint, OltNeighborInfo> getNeighbours() { |
| return neighbors.entrySet().stream().collect(Collectors.toMap(Entry::getKey, Entry::getValue)); |
| } |
| |
| /** |
| * Sets periodicity in minutes for sending out LLDP packet to OLT NNI Ports. |
| * |
| * @param timer Value in minutes. |
| */ |
| @Override |
| public void lldpPeriodicity(int timer) { |
| if (timer > 0) { |
| if (futureTask != null) { |
| futureTask.cancel(true); |
| } |
| |
| futureTask = scheduledExecutorService.scheduleAtFixedRate(this::oltTopologyTimerTask, 0, |
| timer, |
| TimeUnit.MINUTES); |
| log.info("LLDP Packet out Periodicity updated to {} minutes", timer); |
| } |
| } |
| |
| /** |
| * Creates entry in the oltData map. |
| * provision LLDP flow on enabled NNI ports if device is present in Sadis config |
| * |
| * @param dev Device to look for |
| */ |
| private void createAndProcessDevice(Device dev) { |
| SubscriberAndDeviceInformation deviceInfo = subsService.get(dev.serialNumber()); |
| log.debug("CreateAndProcessDevice: deviceInfo {}", deviceInfo); |
| |
| if (deviceInfo != null) { |
| // TODO FIXME, this works only with one NNI |
| Optional<Port> optPort = deviceService.getPorts(dev.id()) |
| .stream().filter(OltTopology::isNniPort).findFirst(); |
| if (optPort.isPresent()) { |
| Port port = optPort.get(); |
| oltPortMap.put(dev.id(), port); |
| return; |
| } |
| } |
| log.warn("CreateAndProcessDevice: failed to update the oltdata for device {}", dev); |
| } |
| |
| /** |
| * Updates OltData Map with new AccessDeviceData if it is already present in OltMap, |
| * Provisions LLDP flow on enabled NNI port if it is present in Sadis config |
| * Only one NNI port is supported as of now. |
| * |
| * @param dev Device to look for |
| * @param uplink Uplink port number |
| * @return true if updated else false |
| */ |
| private boolean updateOltData(Device dev, Port uplink) { |
| // check if this device is provisioned in Sadis |
| SubscriberAndDeviceInformation deviceInfo = subsService.get(dev.serialNumber()); |
| log.debug("updateAccessDevice: deviceInfo {}", deviceInfo); |
| |
| if (deviceInfo != null) { |
| oltPortMap.replace(dev.id(), uplink); |
| log.debug("updateAccessDevice: Stored did {} uplink {}", dev.id(), uplink); |
| return true; |
| } |
| return false; |
| } |
| |
| /** |
| * Sends LLDP Packet to OLT NNI Port. |
| * |
| * @param devId Access Device data for OLT |
| * @param nniPort NNI port info of OLT |
| */ |
| private void sendLldpPackets(DeviceId devId, Port nniPort) { |
| if (!mastershipService.isLocalMaster(devId)) { |
| return; |
| } |
| |
| // Get NNI Port name to be filled in LLDP packet port TLV. |
| String portName = (nniPort.annotations().value("portName").isEmpty()) ? "" : |
| nniPort.annotations().value("portName"); |
| |
| // Get System Name value from device name. |
| Device d = deviceService.getDevice(devId); |
| String[] systemName = d.id().uri().toString().split(":", 2); |
| |
| MacAddress destMac = MacAddress.valueOf(destMacAddress); |
| MacAddress srcMac = createMacFromDevId(d.id()); |
| |
| SubscriberAndDeviceInformation deviceInfo = subsService.get(d.serialNumber()); |
| |
| // Initialized deviceIP address, to be sent in Management Address TLV. |
| Ip4Address devIpAddr = Ip4Address.valueOf("0.0.0.0"); |
| |
| // Get OLT device IP address from deviceInfo, to be filled in management address TLV. |
| if (deviceInfo != null) { |
| devIpAddr = deviceInfo.ipAddress(); |
| log.debug("sendLldpPackets: did {} nniPort {} devIP {}", d.id(), nniPort, devIpAddr); |
| } else { |
| log.warn("device {} not found in Sadis NOT sending LLDP packet", d.id()); |
| return; |
| } |
| |
| //Created LLDP packet. |
| Ethernet packet = createLldpPacket(destMac, srcMac, chassisId, |
| portName, ttlInSecs, systemName[1], devIpAddr |
| .toString()); |
| |
| // Create the connect point to send the packet to and emit |
| ConnectPoint toSendTo = new ConnectPoint(d.id(), nniPort.number()); |
| |
| //Sends LLDP packet to connect point(NNI port). |
| TrafficTreatment t = DefaultTrafficTreatment.builder() |
| .setOutput(toSendTo.port()).build(); |
| OutboundPacket o = new DefaultOutboundPacket( |
| toSendTo.deviceId(), t, |
| ByteBuffer.wrap(packet.serialize())); |
| if (log.isTraceEnabled()) { |
| log.trace("Sending LLDP packet {} at {}", |
| packet, toSendTo); |
| } |
| |
| packetService.emit(o); |
| } |
| |
| /** |
| * Creates MAC address for LLDP packet from OLT device ID string. |
| * |
| * @param id Device ID of OLT |
| * @return Mac address |
| */ |
| private MacAddress createMacFromDevId(DeviceId id) { |
| String strId = id.toString(); |
| String macStr = strId.substring(7); |
| String formattedMac = macStr.replaceAll("(.{2})", "$1" + ":"); |
| formattedMac = formattedMac.substring(0, formattedMac.length() - 1); |
| |
| return MacAddress.valueOf(formattedMac); |
| } |
| |
| /** |
| * Creates LLDP packet to be sent out of OLT. |
| * |
| * @param destMac Destination LLDP Mac address |
| * @param srcMac Source Mac of OLT device |
| * @param chassisId Chassis ID TLV value |
| * @param port NNI port information |
| * @param ttl TTL value in sec |
| * @param systemName System name TLV value |
| * @param mgmtAddr Management Address TLV value |
| * @return LLDP ethernet packet |
| */ |
| private Ethernet createLldpPacket(MacAddress destMac, MacAddress srcMac, |
| int chassisId, String port, short ttl, |
| String systemName, String mgmtAddr) { |
| |
| Ethernet ethPkt = new Ethernet(); |
| ethPkt.setEtherType(Ethernet.TYPE_LLDP); |
| ethPkt.setDestinationMACAddress(destMac); |
| ethPkt.setSourceMACAddress(srcMac); |
| |
| LLDP lldpPkt = new LLDP(); |
| |
| setChassisId(lldpPkt, chassisId); |
| setPortId(lldpPkt, port); |
| setTtl(lldpPkt, ttl); |
| |
| List<LLDPTLV> optionalTlv = new ArrayList<>(); |
| optionalTlv.add(createSystemNameTlv(systemName)); |
| optionalTlv.add(createMgmtAddressTlv(mgmtAddr)); |
| |
| lldpPkt.setOptionalTLVList(optionalTlv); |
| |
| ethPkt.setPayload(lldpPkt); |
| return ethPkt; |
| } |
| |
| /** |
| * Sets Chassis ID TLV for LLDP packet. |
| * |
| * @param lldpPkt LLDP packet reference |
| * @param chassisId Chassid ID tlv value |
| */ |
| private void setChassisId(LLDP lldpPkt, final int chassisId) { |
| final byte chassisTlvSubtype = 1; |
| |
| byte[] chassis = ArrayUtils.addAll(new byte[]{chassisTlvSubtype}, |
| ByteBuffer.allocate(String.valueOf(chassisId).length()) |
| .put(String.valueOf(chassisId).getBytes()).array()); |
| |
| LLDPTLV chassisTlv = new LLDPTLV(); |
| lldpPkt.setChassisId(chassisTlv.setLength((byte) chassis.length) |
| .setType(LLDP.CHASSIS_TLV_TYPE) |
| .setValue(chassis)); |
| } |
| |
| /** |
| * Sets Port ID tlv for LLDP packet. |
| * |
| * @param lldpPkt LLDP packet reference |
| * @param ifaceName Port Name TLV value |
| */ |
| private void setPortId(LLDP lldpPkt, final String ifaceName) { |
| |
| byte[] port = ArrayUtils.addAll(new byte[]{PORT_TLV_SUB_TYPE}, |
| ifaceName.getBytes()); |
| |
| LLDPTLV portTlv = new LLDPTLV(); |
| lldpPkt.setPortId(portTlv.setLength((byte) port.length) |
| .setType(LLDP.PORT_TLV_TYPE) |
| .setValue(port)); |
| } |
| |
| /** |
| * Sets TTL tlv for LLDP packet. |
| * |
| * @param lldpPkt LLDP Packet reference |
| * @param timeInSecs TTL tlv value in sec |
| */ |
| private void setTtl(LLDP lldpPkt, final short timeInSecs) { |
| byte[] time = ByteBuffer.allocate(2).putShort(timeInSecs).array(); |
| |
| LLDPTLV ttlTlv = new LLDPTLV(); |
| |
| lldpPkt.setTtl(ttlTlv.setType(LLDP.TTL_TLV_TYPE) |
| .setLength((short) time.length) |
| .setValue(time)); |
| } |
| |
| /** |
| * Creates System name TLV for LLDP packet. |
| * |
| * @param systemName System name tlv value |
| * @return systemName TLV |
| */ |
| private LLDPTLV createSystemNameTlv(String systemName) { |
| byte[] bytes = systemName.getBytes(); |
| |
| LLDPTLV sysNameTlv = new LLDPTLV(); |
| |
| return sysNameTlv.setType(SYSTEMNAME_TLV_TYPE) |
| .setLength((byte) bytes.length) |
| .setValue(bytes); |
| } |
| |
| /** |
| * Sets Management address TLV for LLDP packet. |
| * |
| * @param mgmtAddress Management address tlv value |
| * @return Management address TLV |
| */ |
| private LLDPTLV createMgmtAddressTlv(String mgmtAddress) { |
| |
| Ip4Address ipAddr = Ip4Address.valueOf(mgmtAddress); |
| |
| byte[] addrStr = ArrayUtils.addAll(new byte[]{IP_ADDR_SUB_TYPE}, |
| ipAddr.toOctets()); |
| |
| byte[] ipAddrBytes = ArrayUtils.addAll(new byte[]{IP_ADDR_STRING_LEN}, |
| addrStr); |
| |
| byte[] bytesInterfacetype = ArrayUtils.addAll(ipAddrBytes, |
| ByteBuffer.allocate(1).put(INTERFACE_SUB_TYPE).array()); |
| byte[] bytesInterfaceNumber = ArrayUtils.addAll(bytesInterfacetype, |
| ByteBuffer.allocate(4).putInt(INTERFACE_NUM).array()); |
| byte[] finalMgmtAddrBytes = ArrayUtils.addAll(bytesInterfaceNumber, |
| ByteBuffer.allocate(1).put(OID_STRING).array()); |
| |
| LLDPTLV mgmtAddrTlv = new LLDPTLV(); |
| return mgmtAddrTlv.setType(MANAGEMENT_ADDR_TLV_TYPE) |
| .setLength((byte) finalMgmtAddrBytes.length) |
| .setValue(finalMgmtAddrBytes); |
| } |
| |
| /** |
| * Processes incoming LLDP packets from NNI ports. |
| * |
| * @param pkt Inbound packet received at ONOS from OLT NNI port |
| */ |
| private void handleLldpPacket(InboundPacket pkt) { |
| |
| Ethernet packet = pkt.parsed(); |
| |
| if (packet == null) { |
| log.warn("Packet is null"); |
| return; |
| } |
| |
| log.debug("Got a packet {}", packet); |
| |
| // Check if Ethernet type is LLDP. |
| if (packet.getEtherType() == Ethernet.TYPE_LLDP) { |
| // Get payload of Packet. |
| LLDP lldpPacket = (LLDP) packet.getPayload(); |
| |
| // Fetch all optional TLVs from Packet. |
| List<LLDPTLV> optionalTLVs = lldpPacket.getOptionalTLVList(); |
| |
| // Look for the system name and neighbor |
| // management IP address TLVs. |
| String systemName = null; |
| String neighMgmtAddr = ""; |
| if (optionalTLVs != null) { |
| for (LLDPTLV tlv : optionalTLVs) { |
| // Fetching system name TLV. |
| if (tlv.getType() == SYSTEMNAME_TLV_TYPE) { |
| systemName = new String(tlv.getValue()); |
| } else if (tlv.getType() == MANAGEMENT_ADDR_TLV_TYPE) { |
| /* Fetching 4 Octets from MANAGEMENT Address TLV to get IP address. */ |
| byte[] neighMgmtIpBytes = Arrays.copyOfRange(tlv.getValue(), 2, 6); |
| Ip4Address neighMgmtIpaddress = Ip4Address.valueOf(neighMgmtIpBytes); |
| neighMgmtAddr = neighMgmtIpaddress.toString(); |
| } |
| } |
| } |
| |
| if (systemName == null) { |
| // We expect the system name to be sent by the neighbor, in absence |
| // we don't store the information |
| return; |
| } |
| |
| int portIdTlvLen = lldpPacket.getPortId().getLength(); |
| |
| String portName = new String(lldpPacket.getPortId().getValue()) |
| .substring(1, portIdTlvLen); |
| |
| // The OLT name stored in the topology information is the device uri |
| // excluding the "of:" part |
| DeviceId devId = pkt.receivedFrom().deviceId(); |
| String deviceUri = devId.uri().toString(); |
| String[] oltName = deviceUri.split(":", 2); |
| Port oltNni = deviceService.getPort(pkt.receivedFrom()); |
| |
| String devSerial = deviceService.getDevice(devId).serialNumber(); |
| |
| // Creating object of OltNeighborInfo with all info required. |
| OltNeighborInfo newNeighbor = new OltNeighborInfo(systemName, |
| portName, |
| oltName[1], |
| oltNni, |
| devSerial); |
| newNeighbor.setMgmtAddress(neighMgmtAddr); |
| |
| // Store all the other optional in the neighbour information |
| // this is for future use |
| for (LLDPTLV tlv : optionalTLVs) { |
| if (tlv.getType() != SYSTEMNAME_TLV_TYPE && tlv.getType() != MANAGEMENT_ADDR_TLV_TYPE) { |
| newNeighbor.addOtherOptionalLldpTlvs(tlv); |
| } |
| } |
| |
| /* |
| Checking if Neighbor information is already present. |
| If Yes, then update current information, else |
| adding new information. |
| */ |
| OltNeighborInfo curNeighbor = neighbors.get(pkt.receivedFrom()); |
| if (newNeighbor.equals(curNeighbor)) { |
| curNeighbor.updateTimeStamp(); |
| neighbors.put(pkt.receivedFrom(), curNeighbor); |
| } else { |
| // received first time on this connect point or old was purged |
| neighbors.put(pkt.receivedFrom(), newNeighbor); |
| } |
| } |
| } |
| |
| /** |
| * Removes Entry for device from Olt topology table. |
| * |
| * @param devIdToRm Device ID to be removed |
| */ |
| void removeNeighborsOfDevice(DeviceId devIdToRm) { |
| for (Map.Entry<ConnectPoint, OltNeighborInfo> neighEntry : neighbors.entrySet()) { |
| if (neighEntry.getKey().deviceId().toString().contains(devIdToRm.toString())) { |
| neighbors.remove(neighEntry.getKey()); |
| } |
| } |
| } |
| |
| /** |
| * oltTopologyTimerTask method to send LLDP packets periodically to the neighbours. |
| */ |
| public void oltTopologyTimerTask() { |
| oltPortMap.forEach((key, p) -> { |
| //Port p = deviceService.getPort(key, value); |
| if (p != null && p.isEnabled()) { |
| sendLldpPackets(key, p); |
| } |
| }); |
| } |
| |
| private class InternalDeviceListener implements DeviceListener { |
| /** |
| * Device Listener Event, will be called if Device is added or state is changed or Updated. |
| * |
| * @param event Device event |
| */ |
| @Override |
| public void event(DeviceEvent event) { |
| eventExecutor.execute(() -> { |
| DeviceId devId = event.subject().id(); |
| /* Checking DEVICE_REMOVED and DEVICE_AVAILABILITY_CHANGED events before |
| Mastership check as with these events mastership check will always give false. |
| */ |
| if (event.type().equals(DeviceEvent.Type.DEVICE_REMOVED)) { |
| removeNeighborsOfDevice(devId); |
| return; |
| } else if (event.type().equals(DeviceEvent.Type.DEVICE_AVAILABILITY_CHANGED)) { |
| if (!deviceService.isAvailable(devId)) { |
| removeNeighborsOfDevice(devId); |
| return; |
| } |
| } |
| |
| if (!mastershipService.isLocalMaster(devId)) { |
| return; |
| } |
| |
| switch (event.type()) { |
| case DEVICE_ADDED: |
| if (!oltPortMap.containsKey(devId)) { |
| createAndProcessDevice(deviceService.getDevice(devId)); |
| } |
| break; |
| case PORT_ADDED: |
| /* |
| If NNI port is detected, update it in the map. |
| */ |
| /* TODO: put null check for the annotations return*/ |
| if (isNniPort(event.port())) { |
| if (oltPortMap.containsKey(devId)) { |
| if (!updateOltData(deviceService.getDevice(devId), event.port())) { |
| return; |
| } |
| } else { |
| return; |
| } |
| } |
| |
| if (Objects.nonNull(oltPortMap.get(devId)) && |
| oltPortMap.get(devId).equals(event.port()) && |
| event.port().isEnabled()) { |
| sendLldpPackets(devId, event.port()); |
| } |
| break; |
| case PORT_REMOVED: |
| // Remove from neighbor map and LLDP flow if NNI port is removed. |
| if (Objects.nonNull(oltPortMap.get(devId)) && |
| oltPortMap.get(devId).equals(event.port())) { |
| // the uplink port has been removed; remove the connect |
| // point from the neighbors |
| neighbors.remove(new ConnectPoint(devId, event.port().number())); |
| } |
| break; |
| case PORT_UPDATED: |
| // if Port is enabled, provision LLDP flow and send LLDP packet to NNI Port. |
| if (Objects.nonNull(oltPortMap.get(devId)) && |
| !oltPortMap.get(devId).equals(event.port())) { |
| break; |
| } |
| if (event.port().isEnabled()) { |
| sendLldpPackets(devId, event.port()); |
| } else { |
| neighbors.remove(new ConnectPoint(devId, event.port().number())); |
| } |
| break; |
| |
| default: |
| log.debug("event {} not handle for the device {}", event.type(), devId); |
| break; |
| } |
| }); |
| } |
| } |
| |
| /* |
| * Class to do the processing of the LLDP packets received from the Packet Service. |
| */ |
| private class ReactivePacketProcessor implements PacketProcessor { |
| @Override |
| public void process(PacketContext context) { |
| packetProcessorExecutor.execute(() -> { |
| DeviceId devId = context.inPacket().receivedFrom().deviceId(); |
| if (!mastershipService.isLocalMaster(devId)) { |
| return; |
| } |
| // Extract the original Ethernet frame from the packet information |
| InboundPacket pkt = context.inPacket(); |
| Ethernet ethPkt = pkt.parsed(); |
| |
| if (ethPkt == null) { |
| log.warn("ethPkt null while processing context: {}", context); |
| return; |
| } |
| |
| if (EthType.EtherType.lookup(ethPkt.getEtherType()) == EthType.EtherType.LLDP) { |
| handleLldpPacket(context.inPacket()); |
| } |
| }); |
| } |
| } |
| } |