Fix FindBugs issues with odlparent 3.1.0
[genius.git] / alivenessmonitor / alivenessmonitor-impl-protocols / src / main / java / org / opendaylight / genius / alivenessmonitor / protocols / internal / AlivenessProtocolHandlerLLDP.java
1 /*
2  * Copyright (c) 2016 Ericsson India Global Services Pvt Ltd. and others.  All rights reserved.
3  *
4  * This program and the accompanying materials are made available under the
5  * terms of the Eclipse Public License v1.0 which accompanies this distribution,
6  * and is available at http://www.eclipse.org/legal/epl-v10.html
7  */
8 package org.opendaylight.genius.alivenessmonitor.protocols.internal;
9
10 import static org.opendaylight.infrautils.utils.concurrent.JdkFutures.addErrorLogging;
11
12 import com.google.common.base.Optional;
13 import com.google.common.base.Strings;
14 import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
15 import java.math.BigInteger;
16 import java.nio.charset.Charset;
17 import java.nio.charset.StandardCharsets;
18 import java.util.ArrayList;
19 import java.util.Collections;
20 import java.util.List;
21 import java.util.concurrent.ExecutionException;
22 import java.util.concurrent.atomic.AtomicInteger;
23 import org.apache.commons.lang3.StringUtils;
24 import org.opendaylight.controller.md.sal.binding.api.DataBroker;
25 import org.opendaylight.controller.md.sal.common.api.data.ReadFailedException;
26 import org.opendaylight.genius.alivenessmonitor.protocols.AlivenessProtocolHandlerRegistry;
27 import org.opendaylight.genius.interfacemanager.globals.IfmConstants;
28 import org.opendaylight.genius.mdsalutil.ActionInfo;
29 import org.opendaylight.genius.mdsalutil.MDSALUtil;
30 import org.opendaylight.genius.mdsalutil.actions.ActionOutput;
31 import org.opendaylight.genius.mdsalutil.actions.ActionSetFieldTunnelId;
32 import org.opendaylight.genius.mdsalutil.packet.Ethernet;
33 import org.opendaylight.openflowplugin.libraries.liblldp.EtherTypes;
34 import org.opendaylight.openflowplugin.libraries.liblldp.LLDP;
35 import org.opendaylight.openflowplugin.libraries.liblldp.LLDPTLV;
36 import org.opendaylight.openflowplugin.libraries.liblldp.LLDPTLV.TLVType;
37 import org.opendaylight.openflowplugin.libraries.liblldp.PacketException;
38 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.iana._if.type.rev140508.Tunnel;
39 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev130715.Uri;
40 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.interfaces.rev140508.InterfaceType;
41 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.alivenessmonitor.rev160411.endpoint.EndpointType;
42 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.alivenessmonitor.rev160411.endpoint.endpoint.type.Interface;
43 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.alivenessmonitor.rev160411.monitor.configs.MonitoringInfo;
44 import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodeConnectorId;
45 import org.opendaylight.yang.gen.v1.urn.opendaylight.packet.service.rev130709.PacketProcessingService;
46 import org.opendaylight.yang.gen.v1.urn.opendaylight.packet.service.rev130709.PacketReceived;
47 import org.opendaylight.yang.gen.v1.urn.opendaylight.packet.service.rev130709.TransmitPacketInput;
48 import org.slf4j.Logger;
49 import org.slf4j.LoggerFactory;
50
51 public class AlivenessProtocolHandlerLLDP extends AbstractAlivenessProtocolHandler<LLDP> {
52
53     private static final Logger LOG = LoggerFactory.getLogger(AlivenessProtocolHandlerLLDP.class);
54
55     // TODO org.opendaylight.openflowplugin.libraries.liblldp.LLDPTLV uses Charset.defaultCharset() .. bug there?
56     private static final Charset LLDPTLV_CHARSET = StandardCharsets.US_ASCII;
57
58     private final PacketProcessingService packetProcessingService;
59     private final AtomicInteger packetId = new AtomicInteger(0);
60
61     public AlivenessProtocolHandlerLLDP(
62             final DataBroker dataBroker,
63             final AlivenessProtocolHandlerRegistry alivenessProtocolHandlerRegistry,
64             final PacketProcessingService packetProcessingService) {
65         super(dataBroker, alivenessProtocolHandlerRegistry,
66                 org.opendaylight.yang.gen.v1.urn.opendaylight.genius.alivenessmonitor.rev160411.EtherTypes.Lldp);
67         this.packetProcessingService = packetProcessingService;
68     }
69
70     @Override
71     public Class<LLDP> getPacketClass() {
72         return LLDP.class;
73     }
74
75     @Override
76     // TODO remove DLS_DEAD_LOCAL_STORE when 1st  below lldpTlvTypeCur below is removed
77     @SuppressFBWarnings({"DLS_DEAD_LOCAL_STORE", "NP_NULL_PARAM_DEREF"})
78     public String handlePacketIn(LLDP lldpPacket, PacketReceived packetReceived) {
79         String tempString = null;
80         byte lldpTlvTypeCur;
81
82         // TODO Remove? this seems completely pointless - lldpTlvTypeCur will get overwritten below..
83         for (LLDPTLV lldpTlv : lldpPacket.getOptionalTLVList()) {
84             lldpTlvTypeCur = lldpTlv.getType();
85         }
86
87         for (LLDPTLV lldpTlv : lldpPacket.getCustomTlvList()) {
88             lldpTlvTypeCur = lldpTlv.getType();
89
90             if (lldpTlvTypeCur == LLDPTLV.TLVType.Custom.getValue()) {
91                 tempString = new String(lldpTlv.getValue(), LLDPTLV_CHARSET);
92             }
93         }
94
95         String interfaceName = null;
96
97         // TODO: Check if the below fields are required
98         if (!Strings.isNullOrEmpty(tempString) && tempString.contains("#")) {
99             String[] asTmp = tempString.split("#");
100             interfaceName = asTmp[0];
101             LOG.debug("Custom LLDP Value on received packet: {}", tempString);
102         }
103
104         if (!Strings.isNullOrEmpty(interfaceName)) {
105             String monitorKey = interfaceName + EtherTypes.LLDP;
106             return monitorKey;
107         } else {
108             LOG.debug("No associated interface found to handle received LLDP Packet");
109             return null;
110         }
111     }
112
113     @Override
114     public void startMonitoringTask(MonitoringInfo monitorInfo) {
115         String sourceInterface;
116
117         EndpointType source = monitorInfo.getSource().getEndpointType();
118         if (source instanceof Interface) {
119             Interface intf = (Interface) source;
120             sourceInterface = intf.getInterfaceName();
121         } else {
122             LOG.warn("Invalid source endpoint. Could not retrieve source interface to send LLDP Packet");
123             return;
124         }
125
126         // Get Mac Address for the source interface
127         org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.interfaces.rev140508
128             .interfaces.state.Interface interfaceState;
129         try {
130             interfaceState = getInterfaceFromOperDS(sourceInterface);
131         } catch (ReadFailedException e) {
132             LOG.error("getInterfaceFromOperDS failed for sourceInterface: {}", sourceInterface, e);
133             return;
134         }
135
136         Optional<byte[]> optSourceMac = getMacAddress(interfaceState);
137         if (!optSourceMac.isPresent()) {
138             LOG.error("Could not read mac address for the source interface {} from the Inventory. "
139                     + "LLDP packet cannot be send.", sourceInterface);
140             return;
141         }
142         byte[] sourceMac = optSourceMac.get();
143
144         String lowerLayerIf = interfaceState.getLowerLayerIf().get(0);
145         NodeConnectorId nodeConnectorId = new NodeConnectorId(lowerLayerIf);
146         long nodeId = Long.parseLong(getDpnFromNodeConnectorId(nodeConnectorId));
147         long portNum = Long.parseLong(getPortNoFromNodeConnectorId(nodeConnectorId));
148         Ethernet ethenetLLDPPacket = makeLLDPPacket(Long.toString(nodeId), portNum, sourceMac, sourceInterface);
149
150         try {
151             List<ActionInfo> actions = getInterfaceActions(interfaceState, portNum);
152             if (actions.isEmpty()) {
153                 LOG.error("No interface actions to send packet out over interface {}", sourceInterface);
154                 return;
155             }
156             TransmitPacketInput transmitPacketInput = MDSALUtil.getPacketOut(actions, ethenetLLDPPacket.serialize(),
157                     nodeId, MDSALUtil.getNodeConnRef(BigInteger.valueOf(nodeId), "0xfffffffd"));
158             addErrorLogging(packetProcessingService.transmitPacket(transmitPacketInput),
159                     LOG, "transmitPacket() failed: {}", transmitPacketInput);
160         } catch (InterruptedException | ExecutionException | PacketException e) {
161             LOG.error("Error while sending LLDP Packet", e);
162         }
163     }
164
165     public static String getDpnFromNodeConnectorId(NodeConnectorId portId) {
166         /*
167          * NodeConnectorId is of form 'openflow:dpnid:portnum'
168          */
169         String[] split = portId.getValue().split(IfmConstants.OF_URI_SEPARATOR);
170         return split[1];
171     }
172
173     public static String getPortNoFromNodeConnectorId(NodeConnectorId portId) {
174         /*
175          * NodeConnectorId is of form 'openflow:dpnid:portnum'
176          */
177         String[] split = portId.getValue().split(IfmConstants.OF_URI_SEPARATOR);
178         return split[2];
179     }
180
181     private List<ActionInfo> getInterfaceActions(org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf
182             .interfaces.rev140508.interfaces.state.Interface interfaceState, long portNum)
183                     throws InterruptedException, ExecutionException {
184         Class<? extends InterfaceType> intfType;
185         if (interfaceState != null) {
186             intfType = interfaceState.getType();
187         } else {
188             LOG.error("Could not retrieve port type for interface to construct actions");
189             return Collections.emptyList();
190         }
191
192         List<ActionInfo> actionInfos = new ArrayList<>();
193         // Set the LLDP service Id which is 0
194         if (Tunnel.class.equals(intfType)) {
195             actionInfos.add(new ActionSetFieldTunnelId(BigInteger.ZERO));
196         }
197         actionInfos.add(new ActionOutput(new Uri(Long.toString(portNum))));
198         return actionInfos;
199     }
200
201     @SuppressWarnings("AbbreviationAsWordInName")
202     private static LLDPTLV buildLLDTLV(LLDPTLV.TLVType tlvType, byte[] abyTLV) {
203         return new LLDPTLV().setType(tlvType.getValue()).setLength((short) abyTLV.length).setValue(abyTLV);
204     }
205
206     private int getPacketId() {
207         int id = packetId.incrementAndGet();
208         if (id > 16000) {
209             LOG.debug("Resetting the LLDP Packet Id counter");
210             packetId.set(0);
211         }
212
213         return id;
214     }
215
216     public Ethernet makeLLDPPacket(String nodeId, long portNum, byte[] srcMac, String sourceInterface) {
217
218         // Create LLDP TTL TLV
219         LLDPTLV lldpTlvTTL = buildLLDTLV(LLDPTLV.TLVType.TTL, new byte[] { (byte) 0, (byte) 120 });
220
221         LLDPTLV lldpTlvChassisId = buildLLDTLV(LLDPTLV.TLVType.ChassisID, LLDPTLV.createChassisIDTLVValue(colonize(
222                 StringUtils.leftPad(Long.toHexString(MDSALUtil.getDpnIdFromNodeName(nodeId).longValue()), 16, "0"))));
223         LLDPTLV lldpTlvSystemName = buildLLDTLV(TLVType.SystemName, LLDPTLV.createSystemNameTLVValue(nodeId));
224
225         LLDPTLV lldpTlvPortId = buildLLDTLV(TLVType.PortID, LLDPTLV.createPortIDTLVValue(Long.toHexString(portNum)));
226
227         String customValue = sourceInterface + "#" + getPacketId();
228
229         LOG.debug("Sending LLDP packet, custome value {}", customValue);
230
231         LLDPTLV lldpTlvCustom = buildLLDTLV(TLVType.Custom, customValue.getBytes(LLDPTLV_CHARSET));
232
233         @SuppressWarnings("AbbreviationAsWordInName")
234         List<LLDPTLV> lstLLDPTLVCustom = new ArrayList<>();
235         lstLLDPTLVCustom.add(lldpTlvCustom);
236
237         LLDP lldpDiscoveryPacket = new LLDP();
238         lldpDiscoveryPacket.setChassisId(lldpTlvChassisId).setPortId(lldpTlvPortId).setTtl(lldpTlvTTL)
239                 .setSystemNameId(lldpTlvSystemName).setOptionalTLVList(lstLLDPTLVCustom);
240
241         byte[] destMac = LLDP.LLDP_MULTICAST_MAC;
242
243         Ethernet ethernetPacket = new Ethernet();
244         ethernetPacket.setSourceMACAddress(srcMac).setDestinationMACAddress(destMac)
245                 .setEtherType(EtherTypes.LLDP.shortValue()).setPayload(lldpDiscoveryPacket);
246
247         return ethernetPacket;
248     }
249
250     private String colonize(String orig) {
251         return orig.replaceAll("(?<=..)(..)", ":$1");
252     }
253
254     @Override
255     public String getUniqueMonitoringKey(MonitoringInfo monitorInfo) {
256         String interfaceName = getInterfaceName(monitorInfo.getSource().getEndpointType());
257         return interfaceName + EtherTypes.LLDP;
258     }
259
260     private String getInterfaceName(EndpointType endpoint) {
261         String interfaceName = null;
262         if (endpoint instanceof Interface) {
263             interfaceName = ((Interface) endpoint).getInterfaceName();
264         }
265         return interfaceName;
266     }
267 }