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