Merge "Add menu link for Java apidocs"
[genius.git] / alivenessmonitor / alivenessmonitor-impl / src / main / java / org / opendaylight / genius / alivenessmonitor / 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.internal;
9
10 import com.google.common.base.Strings;
11 import org.apache.commons.lang3.StringUtils;
12 import org.opendaylight.controller.liblldp.EtherTypes;
13 import org.opendaylight.controller.liblldp.LLDP;
14 import org.opendaylight.controller.liblldp.LLDPTLV;
15 import org.opendaylight.controller.liblldp.LLDPTLV.TLVType;
16 import org.opendaylight.controller.liblldp.Packet;
17 import org.opendaylight.genius.interfacemanager.globals.IfmConstants;
18 import org.opendaylight.genius.mdsalutil.ActionInfo;
19 import org.opendaylight.genius.mdsalutil.ActionType;
20 import org.opendaylight.genius.mdsalutil.MDSALUtil;
21 import org.opendaylight.genius.mdsalutil.packet.Ethernet;
22 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.iana._if.type.rev140508.Tunnel;
23 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.interfaces.rev140508.InterfaceType;
24 import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodeConnectorId;
25 import org.opendaylight.yang.gen.v1.urn.opendaylight.packet.service.rev130709.PacketReceived;
26 import org.opendaylight.yang.gen.v1.urn.opendaylight.packet.service.rev130709.TransmitPacketInput;
27 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.alivenessmonitor.rev160411.endpoint.EndpointType;
28 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.alivenessmonitor.rev160411.endpoint.endpoint.type.Interface;
29 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.alivenessmonitor.rev160411.monitor.configs.MonitoringInfo;
30 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.interfacemanager.rpcs.rev160406.OdlInterfaceRpcService;
31 import org.slf4j.Logger;
32 import org.slf4j.LoggerFactory;
33
34 import java.math.BigInteger;
35 import java.nio.charset.Charset;
36 import java.util.ArrayList;
37 import java.util.Collections;
38 import java.util.List;
39 import java.util.concurrent.ExecutionException;
40 import java.util.concurrent.atomic.AtomicInteger;
41
42 public class AlivenessProtocolHandlerLLDP extends AbstractAlivenessProtocolHandler {
43     private static final Logger LOG = LoggerFactory.getLogger(AlivenessProtocolHandlerLLDP.class);
44     private AtomicInteger packetId = new AtomicInteger(0);
45
46     public AlivenessProtocolHandlerLLDP(ServiceProvider serviceProvider) {
47         super(serviceProvider);
48     }
49
50     @Override
51     public Class<?> getPacketClass() {
52         return LLDP.class;
53     }
54
55     @Override
56     public String handlePacketIn(Packet protocolPacket, PacketReceived packetReceived) {
57         String sSourceDpnId = null;
58         String sPortNumber = null;
59         int nServiceId = -1;
60         int packetId = 0;
61
62         String sTmp = null;
63
64         byte lldpTlvTypeCur;
65
66         LLDP lldpPacket = (LLDP)protocolPacket;
67
68         LLDPTLV lldpTlvCur = lldpPacket.getSystemNameId();
69         if (lldpTlvCur != null) {
70             sSourceDpnId = new String(lldpTlvCur.getValue(), Charset.defaultCharset());
71         }
72
73         lldpTlvCur = lldpPacket.getPortId();
74         if (lldpTlvCur != null) {
75             sPortNumber = new String(lldpTlvCur.getValue(), Charset.defaultCharset());
76         }
77
78         for (LLDPTLV lldpTlv : lldpPacket.getOptionalTLVList()) {
79             lldpTlvTypeCur = lldpTlv.getType();
80
81             if (lldpTlvTypeCur == LLDPTLV.TLVType.SystemName.getValue()) {
82                 sSourceDpnId = new String(lldpTlvCur.getValue(), Charset.defaultCharset());
83             }
84         }
85
86         for (LLDPTLV lldpTlv : lldpPacket.getCustomTlvList()) {
87             lldpTlvTypeCur = lldpTlv.getType();
88
89             if (lldpTlvTypeCur == LLDPTLV.TLVType.Custom.getValue()) {
90                 sTmp = new String(lldpTlv.getValue());
91                 nServiceId = 0;
92             }
93         }
94
95         String interfaceName = null;
96
97         //TODO: Check if the below fields are required
98         if (!Strings.isNullOrEmpty(sTmp) && sTmp.contains("#")) {
99             String[] asTmp = sTmp.split("#");
100             interfaceName = asTmp[0];
101             packetId = Integer.parseInt(asTmp[1]);
102             LOG.debug("Custom LLDP Value on received packet: " + sTmp);
103         }
104
105         if(!Strings.isNullOrEmpty(interfaceName)) {
106             String monitorKey = new StringBuilder().append(interfaceName).append(EtherTypes.LLDP).toString();
107             return monitorKey;
108         } else {
109             LOG.debug("No associated interface found to handle received LLDP Packet");
110         }
111         return null;
112     }
113
114     @Override
115     public void startMonitoringTask(MonitoringInfo monitorInfo) {
116         String sourceInterface;
117
118         EndpointType source = monitorInfo.getSource().getEndpointType();
119         if( source instanceof Interface) {
120             Interface intf = (Interface)source;
121             sourceInterface = intf.getInterfaceName();
122         } else {
123             LOG.warn("Invalid source endpoint. Could not retrieve source interface to send LLDP Packet");
124             return;
125         }
126
127         //Get Mac Address for the source interface
128         org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.interfaces.rev140508.interfaces.state.Interface interfaceState = getInterfaceFromOperDS(sourceInterface);
129         byte[] sourceMac = getMacAddress(interfaceState, sourceInterface);
130         if(sourceMac == null) {
131             LOG.error("Could not read mac address for the source interface {} from the Inventory. "
132                     + "LLDP packet cannot be send.", sourceInterface);
133             return;
134         }
135
136         OdlInterfaceRpcService interfaceService = serviceProvider.getInterfaceManager();
137
138         long nodeId = -1, portNum = -1;
139         try {
140             String lowerLayerIf = interfaceState.getLowerLayerIf().get(0);
141             NodeConnectorId nodeConnectorId = new NodeConnectorId(lowerLayerIf);
142             nodeId = Long.valueOf(getDpnFromNodeConnectorId(nodeConnectorId));
143             portNum = Long.valueOf(getPortNoFromNodeConnectorId(nodeConnectorId));
144         }catch(Exception e) {
145             LOG.error("Failed to retrieve node id and port number ", e);
146             return;
147         }
148
149         Ethernet ethenetLLDPPacket = makeLLDPPacket(Long.toString(nodeId), portNum, 0, sourceMac, sourceInterface);
150
151         try {
152             List<ActionInfo> actions = getInterfaceActions(interfaceState, portNum);
153             if(actions.isEmpty()) {
154                 LOG.error("No interface actions to send packet out over interface {}", sourceInterface);
155                 return;
156             }
157             TransmitPacketInput transmitPacketInput = MDSALUtil.getPacketOut(actions,
158                     ethenetLLDPPacket.serialize(), nodeId, MDSALUtil.getNodeConnRef(BigInteger.valueOf(nodeId), "0xfffffffd"));
159             serviceProvider.getPacketProcessingService().transmitPacket(transmitPacketInput);
160         } catch (Exception 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     private List<ActionInfo> getInterfaceActions(org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.interfaces.rev140508.interfaces.state.Interface interfaceState, long portNum) throws InterruptedException, ExecutionException {
181         Class<? extends InterfaceType> intfType;
182         if(interfaceState != null) {
183             intfType = interfaceState.getType();
184         } else {
185             LOG.error("Could not retrieve port type for interface {} to construct actions", interfaceState.getName());
186             return Collections.emptyList();
187         }
188
189         List<ActionInfo> actionInfos  = new ArrayList<ActionInfo>();
190         // Set the LLDP service Id which is 0
191         if(Tunnel.class.equals(intfType)) {
192             actionInfos.add(new ActionInfo(ActionType.set_field_tunnel_id, new BigInteger[] {
193                     BigInteger.valueOf(0)}));
194         }
195         actionInfos.add(new ActionInfo(ActionType.output, new String[] { Long.toString(portNum) }));
196         return actionInfos;
197     }
198
199     private static LLDPTLV buildLLDTLV(LLDPTLV.TLVType tlvType, byte[] abyTLV) {
200         return new LLDPTLV().setType(tlvType.getValue()).setLength((short) abyTLV.length).setValue(abyTLV);
201     }
202
203     private int getPacketId() {
204         int id = packetId.incrementAndGet();
205         if(id > 16000) {
206             LOG.debug("Resetting the LLDP Packet Id counter");
207             packetId.set(0);
208         }
209
210         return id;
211     }
212
213     public Ethernet makeLLDPPacket(String nodeId,
214             long portNum, int serviceId, byte[] srcMac, String sourceInterface) {
215
216         // Create LLDP TTL TLV
217         LLDPTLV lldpTlvTTL = buildLLDTLV(LLDPTLV.TLVType.TTL, new byte[] { (byte) 0, (byte) 120 });
218
219         LLDPTLV lldpTlvChassisId = buildLLDTLV(LLDPTLV.TLVType.ChassisID, LLDPTLV.createChassisIDTLVValue(colonize(StringUtils
220                 .leftPad(Long.toHexString(MDSALUtil.getDpnIdFromNodeName(nodeId).longValue()), 16,
221                         "0"))));
222         LLDPTLV lldpTlvSystemName = buildLLDTLV(TLVType.SystemName, LLDPTLV.createSystemNameTLVValue(nodeId));
223
224         LLDPTLV lldpTlvPortId = buildLLDTLV(TLVType.PortID, LLDPTLV.createPortIDTLVValue(
225                 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());
232
233         List<LLDPTLV> lstLLDPTLVCustom = new ArrayList<>();
234         lstLLDPTLVCustom.add(lldpTlvCustom);
235
236         LLDP lldpDiscoveryPacket = new LLDP();
237         lldpDiscoveryPacket.setChassisId(lldpTlvChassisId)
238                            .setPortId(lldpTlvPortId)
239                            .setTtl(lldpTlvTTL)
240                            .setSystemNameId(lldpTlvSystemName)
241                            .setOptionalTLVList(lstLLDPTLVCustom);
242
243         byte[] destMac = LLDP.LLDPMulticastMac;
244
245         Ethernet ethernetPacket = new Ethernet();
246         ethernetPacket.setSourceMACAddress(srcMac)
247                       .setDestinationMACAddress(destMac)
248                       .setEtherType(EtherTypes.LLDP.shortValue())
249                       .setPayload(lldpDiscoveryPacket);
250
251         return ethernetPacket;
252     }
253
254     private String colonize(String orig) {
255         return orig.replaceAll("(?<=..)(..)", ":$1");
256     }
257
258     @Override
259     public String getUniqueMonitoringKey(MonitoringInfo monitorInfo) {
260         String interfaceName = getInterfaceName(monitorInfo.getSource().getEndpointType());
261         return new StringBuilder().append(interfaceName).append(EtherTypes.LLDP).toString();
262     }
263
264     private String getInterfaceName(EndpointType endpoint) {
265         String interfaceName = null;
266         if(endpoint instanceof Interface) {
267             interfaceName = ((Interface)endpoint).getInterfaceName();
268         }
269         return interfaceName;
270     }
271
272 }