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