Bump version odlparent->6.0.0,mdsal->5.0.3
[genius.git] / arputil / arputil-impl / src / main / java / org / opendaylight / genius / arputil / internal / ArpUtilImpl.java
1 /*
2  * Copyright (c) 2016, 2017 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.arputil.internal;
9
10 import static com.google.common.base.Preconditions.checkArgument;
11 import static com.google.common.base.Preconditions.checkNotNull;
12 import static org.opendaylight.mdsal.binding.util.Datastore.OPERATIONAL;
13
14 import com.google.common.util.concurrent.FutureCallback;
15 import com.google.common.util.concurrent.Futures;
16 import com.google.common.util.concurrent.ListenableFuture;
17 import com.google.common.util.concurrent.MoreExecutors;
18 import com.google.common.util.concurrent.SettableFuture;
19 import java.net.InetAddress;
20 import java.net.UnknownHostException;
21 import java.nio.charset.StandardCharsets;
22 import java.util.ArrayList;
23 import java.util.List;
24 import java.util.concurrent.ConcurrentHashMap;
25 import java.util.concurrent.ConcurrentMap;
26 import java.util.concurrent.ExecutionException;
27 import java.util.concurrent.ExecutorService;
28 import java.util.concurrent.Future;
29 import javax.inject.Inject;
30 import javax.inject.Singleton;
31 import org.apache.aries.blueprint.annotation.service.Reference;
32 import org.opendaylight.genius.arputil.api.ArpConstants;
33 import org.opendaylight.genius.mdsalutil.MDSALUtil;
34 import org.opendaylight.genius.mdsalutil.MetaDataUtil;
35 import org.opendaylight.genius.mdsalutil.NWUtil;
36 import org.opendaylight.genius.mdsalutil.packet.ARP;
37 import org.opendaylight.genius.mdsalutil.packet.Ethernet;
38 import org.opendaylight.infrautils.inject.AbstractLifecycle;
39 import org.opendaylight.infrautils.metrics.Meter;
40 import org.opendaylight.infrautils.metrics.MetricProvider;
41 import org.opendaylight.infrautils.utils.concurrent.Executors;
42 import org.opendaylight.mdsal.binding.api.DataBroker;
43 import org.opendaylight.mdsal.binding.api.NotificationPublishService;
44 import org.opendaylight.mdsal.binding.api.NotificationService;
45 import org.opendaylight.mdsal.binding.util.Datastore.Operational;
46 import org.opendaylight.mdsal.binding.util.ManagedNewTransactionRunner;
47 import org.opendaylight.mdsal.binding.util.ManagedNewTransactionRunnerImpl;
48 import org.opendaylight.openflowplugin.libraries.liblldp.HexEncode;
49 import org.opendaylight.openflowplugin.libraries.liblldp.Packet;
50 import org.opendaylight.openflowplugin.libraries.liblldp.PacketException;
51 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev130715.IetfInetUtil;
52 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev130715.IpAddress;
53 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.yang.types.rev130715.MacAddress;
54 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.yang.types.rev130715.PhysAddress;
55 import org.opendaylight.yang.gen.v1.urn.opendaylight.action.types.rev131112.action.list.Action;
56 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.FlowCapableNodeConnector;
57 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.arputil.rev160406.ArpRequestReceivedBuilder;
58 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.arputil.rev160406.ArpResponseReceivedBuilder;
59 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.arputil.rev160406.GetMacInput;
60 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.arputil.rev160406.GetMacOutput;
61 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.arputil.rev160406.GetMacOutputBuilder;
62 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.arputil.rev160406.MacChangedBuilder;
63 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.arputil.rev160406.OdlArputilService;
64 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.arputil.rev160406.SendArpRequestInput;
65 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.arputil.rev160406.SendArpRequestInputBuilder;
66 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.arputil.rev160406.SendArpRequestOutput;
67 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.arputil.rev160406.SendArpResponseInput;
68 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.arputil.rev160406.SendArpResponseOutput;
69 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.arputil.rev160406.interfaces.InterfaceAddress;
70 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.interfacemanager.rpcs.rev160406.GetEgressActionsForInterfaceInputBuilder;
71 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.interfacemanager.rpcs.rev160406.GetEgressActionsForInterfaceOutput;
72 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.interfacemanager.rpcs.rev160406.GetInterfaceFromIfIndexInput;
73 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.interfacemanager.rpcs.rev160406.GetInterfaceFromIfIndexInputBuilder;
74 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.interfacemanager.rpcs.rev160406.GetInterfaceFromIfIndexOutput;
75 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.interfacemanager.rpcs.rev160406.GetPortFromInterfaceInputBuilder;
76 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.interfacemanager.rpcs.rev160406.GetPortFromInterfaceOutput;
77 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.interfacemanager.rpcs.rev160406.OdlInterfaceRpcService;
78 import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodeConnectorRef;
79 import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodeId;
80 import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodeRef;
81 import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.Nodes;
82 import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.node.NodeConnector;
83 import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.Node;
84 import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.NodeKey;
85 import org.opendaylight.yang.gen.v1.urn.opendaylight.model.match.types.rev131026.match.Metadata;
86 import org.opendaylight.yang.gen.v1.urn.opendaylight.packet.service.rev130709.PacketInReason;
87 import org.opendaylight.yang.gen.v1.urn.opendaylight.packet.service.rev130709.PacketProcessingListener;
88 import org.opendaylight.yang.gen.v1.urn.opendaylight.packet.service.rev130709.PacketProcessingService;
89 import org.opendaylight.yang.gen.v1.urn.opendaylight.packet.service.rev130709.PacketReceived;
90 import org.opendaylight.yang.gen.v1.urn.opendaylight.packet.service.rev130709.SendToController;
91 import org.opendaylight.yang.gen.v1.urn.opendaylight.packet.service.rev130709.TransmitPacketInput;
92 import org.opendaylight.yang.gen.v1.urn.opendaylight.packet.service.rev130709.TransmitPacketInputBuilder;
93 import org.opendaylight.yang.gen.v1.urn.opendaylight.packet.service.rev130709.TransmitPacketOutput;
94 import org.opendaylight.yangtools.concepts.ListenerRegistration;
95 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
96 import org.opendaylight.yangtools.yang.common.RpcError.ErrorType;
97 import org.opendaylight.yangtools.yang.common.RpcResult;
98 import org.opendaylight.yangtools.yang.common.RpcResultBuilder;
99 import org.opendaylight.yangtools.yang.common.Uint64;
100 import org.slf4j.Logger;
101 import org.slf4j.LoggerFactory;
102
103 @Singleton
104 public class ArpUtilImpl extends AbstractLifecycle implements OdlArputilService, PacketProcessingListener {
105     private static final Logger LOG = LoggerFactory.getLogger(ArpUtilImpl.class);
106     private static final String MODULENAME = "odl.genius.arputil.";
107     private static final String OPENFLOW_PFX = "openflow:";
108
109     private final ManagedNewTransactionRunner txRunner;
110     private final PacketProcessingService packetProcessingService;
111     private final NotificationPublishService notificationPublishService;
112     private final NotificationService notificationService;
113     private final OdlInterfaceRpcService odlInterfaceRpcService;
114     private ListenerRegistration<ArpUtilImpl> listenerRegistration;
115     private final ExecutorService threadPool = Executors.newFixedThreadPool(1, "ArpUtil", LOG);
116     private final ConcurrentMap<String, String> macsDB = new ConcurrentHashMap<>();
117     private final ConcurrentMap<String, SettableFuture<RpcResult<GetMacOutput>>> macAddrs = new ConcurrentHashMap<>();
118
119     private final Meter arpRespRecvd;
120     private final Meter arpRespRecvdNotification;
121     private final Meter arpRespRecvdNotificationRejected;
122     private final Meter arpReqRecvd;
123     private final Meter arpReqRecvdNotification;
124     private final Meter arpReqRecvdNotificationRejected;
125
126
127     @Inject
128     public ArpUtilImpl(@Reference final DataBroker dataBroker,
129                        final PacketProcessingService packetProcessingService,
130                        @Reference final NotificationPublishService notificationPublishService,
131                        @Reference final NotificationService notificationService,
132                        final OdlInterfaceRpcService odlInterfaceRpcService,
133                        @Reference  final MetricProvider metricProvider) {
134         this.txRunner = new ManagedNewTransactionRunnerImpl(dataBroker);
135         this.packetProcessingService = packetProcessingService;
136         this.notificationPublishService = notificationPublishService;
137         this.notificationService = notificationService;
138         this.odlInterfaceRpcService = odlInterfaceRpcService;
139
140         arpRespRecvd = metricProvider.newMeter(this,MODULENAME + "arpResponseReceived");
141         arpRespRecvdNotification = metricProvider.newMeter(this,MODULENAME + "arpResponseReceivedNotification");
142         arpRespRecvdNotificationRejected = metricProvider.newMeter(this,
143                 MODULENAME + "arpResponseReceivedNotificationRejected");
144         arpReqRecvd = metricProvider.newMeter(this,MODULENAME + "arpRequestReceived");
145         arpReqRecvdNotification = metricProvider.newMeter(this,MODULENAME + "arpRequestReceivedNotification");
146         arpReqRecvdNotificationRejected = metricProvider.newMeter(this,
147                 MODULENAME + "arpRequestReceivedNotificationRejected");
148     }
149
150     @Override
151     public void start() {
152         LOG.info("{} start", getClass().getSimpleName());
153         listenerRegistration = notificationService.registerNotificationListener(this);
154     }
155
156     @Override
157     public void stop() {
158         LOG.info("{} stop", getClass().getSimpleName());
159
160         if (listenerRegistration != null) {
161             listenerRegistration.close();
162             listenerRegistration = null;
163         }
164     }
165
166     private String getIpAddressInString(IpAddress ipAddress) throws UnknownHostException {
167         return InetAddress.getByName(ipAddress.getIpv4Address().getValue()).getHostAddress();
168     }
169
170     @Override
171     public ListenableFuture<RpcResult<GetMacOutput>> getMac(GetMacInput input) {
172         try {
173             final String dstIpAddress = getIpAddressInString(input.getIpaddress());
174             LOG.trace("getMac rpc invoked for ip {}", dstIpAddress);
175             if (macAddrs.get(dstIpAddress) != null) {
176                 if (LOG.isInfoEnabled()) {
177                     LOG.info("get mac already in progress for the ip {}", dstIpAddress);
178                 }
179                 return macAddrs.get(dstIpAddress);
180             }
181             SendArpRequestInputBuilder builder = new SendArpRequestInputBuilder()
182                     .setInterfaceAddress(input.getInterfaceAddress()).setIpaddress(input.getIpaddress());
183             ListenableFuture<RpcResult<SendArpRequestOutput>> arpReqFt = sendArpRequest(builder.build());
184             final SettableFuture<RpcResult<GetMacOutput>> ft = SettableFuture.create();
185
186             Futures.addCallback(arpReqFt, new FutureCallback<RpcResult<SendArpRequestOutput>>() {
187                 @Override
188                 public void onFailure(Throwable ex) {
189                     RpcResultBuilder<GetMacOutput> resultBuilder = RpcResultBuilder.<GetMacOutput>failed()
190                             .withError(ErrorType.APPLICATION, ex.getMessage(), ex);
191                     ft.set(resultBuilder.build());
192                 }
193
194                 @Override
195                 public void onSuccess(RpcResult<SendArpRequestOutput> result) {
196                     LOG.trace("Successfully sent the arp pkt out for ip {}", dstIpAddress);
197                 }
198             }, MoreExecutors.directExecutor());
199
200             macAddrs.put(dstIpAddress, ft);
201             return ft;
202         } catch (UnknownHostException e) {
203             LOG.error("Failed to handle getMac request for {}", input.getIpaddress(), e);
204             RpcResultBuilder<GetMacOutput> resultBuilder = RpcResultBuilder.<GetMacOutput>failed()
205                     .withError(ErrorType.APPLICATION, e.getMessage(), e);
206             return Futures.immediateFuture(resultBuilder.build());
207         }
208     }
209
210     private byte[] getIpAddressBytes(IpAddress ip) throws UnknownHostException {
211         return InetAddress.getByName(ip.getIpv4Address().getValue()).getAddress();
212     }
213
214     @Override
215     public ListenableFuture<RpcResult<SendArpRequestOutput>> sendArpRequest(SendArpRequestInput arpReqInput) {
216         LOG.trace("rpc sendArpRequest invoked for ip {}", arpReqInput.getIpaddress());
217         Uint64 dpnId;
218         byte[] payload;
219         String interfaceName = null;
220         byte[] srcIpBytes;
221         byte[] dstIpBytes;
222         byte[] srcMac;
223
224         RpcResultBuilder<SendArpRequestOutput> failureBuilder = RpcResultBuilder.failed();
225         RpcResultBuilder<SendArpRequestOutput> successBuilder = RpcResultBuilder.success();
226
227         try {
228             dstIpBytes = getIpAddressBytes(arpReqInput.getIpaddress());
229         } catch (UnknownHostException e) {
230             LOG.error("Cannot get IP address", e);
231             failureBuilder.withError(ErrorType.APPLICATION, ArpConstants.UNKNOWN_IP_ADDRESS_SUPPLIED);
232             return Futures.immediateFuture(failureBuilder.build());
233         }
234
235         int localErrorCount = 0;
236         for (InterfaceAddress interfaceAddress : arpReqInput.nonnullInterfaceAddress()) {
237             try {
238                 interfaceName = interfaceAddress.getInterface();
239                 srcIpBytes = getIpAddressBytes(interfaceAddress.getIpAddress());
240
241                 GetPortFromInterfaceOutput portResult = getPortFromInterface(interfaceName);
242                 checkNotNull(portResult);
243                 dpnId = portResult.getDpid();
244                 Long portid = portResult.getPortno().toJava();
245                 checkArgument(null != dpnId && !Uint64.ZERO.equals(dpnId),
246                     ArpConstants.DPN_NOT_FOUND_ERROR, interfaceName);
247
248                 NodeConnectorRef ref = MDSALUtil.getNodeConnRef(dpnId, portid.toString());
249                 checkNotNull(ref, ArpConstants.NODE_CONNECTOR_NOT_FOUND_ERROR, interfaceName);
250
251                 LOG.trace("sendArpRequest received dpnId {} out interface {}", dpnId, interfaceName);
252                 if (interfaceAddress.getMacaddress() == null) {
253                     srcMac = txRunner.<Operational, ExecutionException, byte[]>
254                         applyInterruptiblyWithNewReadOnlyTransactionAndClose(OPERATIONAL,
255                             tx -> tx.read((InstanceIdentifier<NodeConnector>) ref.getValue()).get()
256                                 .map(nc -> nc.augmentation(FlowCapableNodeConnector.class))
257                                 .map(FlowCapableNodeConnector::getHardwareAddress)
258                                 .map(MacAddress::getValue)
259                                 .map(HexEncode::bytesFromHexString)
260                                 .orElse(null));
261                 } else {
262                     String macAddr = interfaceAddress.getMacaddress().getValue();
263                     srcMac = HexEncode.bytesFromHexString(macAddr);
264                 }
265                 checkNotNull(srcMac, ArpConstants.FAILED_TO_GET_SRC_MAC_FOR_INTERFACE, interfaceName, ref.getValue());
266                 checkNotNull(srcIpBytes, ArpConstants.FAILED_TO_GET_SRC_IP_FOR_INTERFACE, interfaceName);
267
268                 payload = ArpPacketUtil.getPayload(ArpConstants.ARP_REQUEST_OP, srcMac, srcIpBytes,
269                         ArpPacketUtil.ETHERNET_BROADCAST_DESTINATION, dstIpBytes);
270
271                 List<Action> actions = getEgressAction(interfaceName);
272                 sendPacketOutWithActions(dpnId, payload, ref, actions);
273
274                 LOG.trace("sent arp request for {}", arpReqInput.getIpaddress());
275             } catch (UnknownHostException | PacketException | InterruptedException | ExecutionException e) {
276                 LOG.trace("failed to send arp req for {} on interface {}", arpReqInput.getIpaddress(), interfaceName);
277
278                 failureBuilder.withError(ErrorType.APPLICATION,
279                     ArpConstants.FAILED_TO_SEND_ARP_REQ_FOR_INTERFACE + interfaceName, e);
280                 successBuilder.withError(ErrorType.APPLICATION,
281                     ArpConstants.FAILED_TO_SEND_ARP_REQ_FOR_INTERFACE + interfaceName, e);
282                 localErrorCount++;
283             }
284         }
285         if (localErrorCount == arpReqInput.getInterfaceAddress().size()) {
286             // All the requests failed
287             return Futures.immediateFuture(failureBuilder.build());
288         }
289         return Futures.immediateFuture(successBuilder.build());
290     }
291
292     public ListenableFuture<RpcResult<TransmitPacketOutput>> sendPacketOut(
293             Uint64 dpnId, byte[] payload, NodeConnectorRef ref) {
294         NodeConnectorRef nodeConnectorRef = MDSALUtil.getNodeConnRef(dpnId, "0xfffffffd");
295         return packetProcessingService.transmitPacket(new TransmitPacketInputBuilder().setPayload(payload)
296                 .setNode(new NodeRef(InstanceIdentifier.builder(Nodes.class)
297                         .child(Node.class, new NodeKey(new NodeId(OPENFLOW_PFX + dpnId))).build()))
298                 .setIngress(nodeConnectorRef).setEgress(ref).build());
299     }
300
301     private Future<RpcResult<TransmitPacketOutput>> sendPacketOutWithActions(
302             Uint64 dpnId, byte[] payload, NodeConnectorRef ref, List<Action> actions) {
303         NodeConnectorRef nodeConnectorRef = MDSALUtil.getNodeConnRef(dpnId, "0xfffffffd");
304         TransmitPacketInput transmitPacketInput = new TransmitPacketInputBuilder().setPayload(payload)
305                 .setNode(new NodeRef(InstanceIdentifier.builder(Nodes.class)
306                         .child(Node.class, new NodeKey(new NodeId(OPENFLOW_PFX + dpnId))).build()))
307                 .setIngress(nodeConnectorRef).setEgress(ref).setAction(actions).build();
308         LOG.trace("PacketOut message framed for transmitting {}", transmitPacketInput);
309         return packetProcessingService.transmitPacket(transmitPacketInput);
310     }
311
312     private List<Action> getEgressAction(String interfaceName) {
313         List<Action> actions = new ArrayList<>();
314         try {
315             GetEgressActionsForInterfaceInputBuilder egressAction = new GetEgressActionsForInterfaceInputBuilder()
316                     .setIntfName(interfaceName);
317             OdlInterfaceRpcService intfRpc = odlInterfaceRpcService;
318             if (intfRpc == null) {
319                 LOG.error("Unable to obtain interfaceMgrRpc service, ignoring egress actions for interfaceName {}",
320                         interfaceName);
321                 return actions;
322             }
323             Future<RpcResult<GetEgressActionsForInterfaceOutput>> result = intfRpc
324                     .getEgressActionsForInterface(egressAction.build());
325             RpcResult<GetEgressActionsForInterfaceOutput> rpcResult = result.get();
326             if (!rpcResult.isSuccessful()) {
327                 LOG.warn("RPC Call to Get egress actions for interface {} returned with Errors {}", interfaceName,
328                         rpcResult.getErrors());
329             } else {
330                 actions = rpcResult.getResult().getAction();
331             }
332         } catch (InterruptedException | ExecutionException e) {
333             LOG.error("Exception when egress actions for interface {}", interfaceName, e);
334         }
335         return actions;
336     }
337
338     @Override
339     public ListenableFuture<RpcResult<SendArpResponseOutput>> sendArpResponse(SendArpResponseInput input) {
340         LOG.trace("sendArpResponse rpc invoked");
341         Uint64 dpnId;
342         byte[] payload;
343         byte[] srcMac;
344
345         try {
346             String interfaceName = input.getInterface();
347             GetPortFromInterfaceOutput portResult = getPortFromInterface(interfaceName);
348             checkNotNull(portResult);
349             dpnId = portResult.getDpid();
350             Long portid = portResult.getPortno().toJava();
351             NodeConnectorRef ref = MDSALUtil.getNodeConnRef(dpnId, portid.toString());
352             checkArgument(null != dpnId && !Uint64.ZERO.equals(dpnId),
353                 ArpConstants.DPN_NOT_FOUND_ERROR, interfaceName);
354             checkNotNull(ref, ArpConstants.NODE_CONNECTOR_NOT_FOUND_ERROR, interfaceName);
355
356             LOG.trace("sendArpRequest received dpnId {} out interface {}", dpnId, interfaceName);
357
358             byte[] srcIpBytes = getIpAddressBytes(input.getSrcIpaddress());
359             byte[] dstIpBytes = getIpAddressBytes(input.getDstIpaddress());
360             if (input.getSrcMacaddress() == null) {
361                 srcMac = portResult.getPhyAddress().getBytes(StandardCharsets.UTF_8);
362             } else {
363                 String macAddr = input.getSrcMacaddress().getValue();
364                 srcMac = HexEncode.bytesFromHexString(macAddr);
365             }
366             byte[] dstMac = NWUtil.parseMacAddress(input.getDstMacaddress().getValue());
367             checkNotNull(srcIpBytes, ArpConstants.FAILED_TO_GET_SRC_IP_FOR_INTERFACE, interfaceName);
368             payload = ArpPacketUtil.getPayload(ArpConstants.ARP_RESPONSE_OP, srcMac, srcIpBytes, dstMac, dstIpBytes);
369
370             List<Action> actions = getEgressAction(interfaceName);
371             sendPacketOutWithActions(dpnId, payload, ref, actions);
372             LOG.debug("Sent ARP response for IP {}, from source MAC {} to target MAC {} and target IP {} via dpnId {}",
373                     input.getSrcIpaddress().getIpv4Address().getValue(), HexEncode.bytesToHexStringFormat(srcMac),
374                     HexEncode.bytesToHexStringFormat(dstMac), input.getDstIpaddress().getIpv4Address().getValue(),
375                     dpnId);
376         } catch (UnknownHostException | PacketException | InterruptedException | ExecutionException e) {
377             LOG.error("failed to send arp response for {}: ", input.getSrcIpaddress(), e);
378             return RpcResultBuilder.<SendArpResponseOutput>failed()
379                     .withError(ErrorType.APPLICATION, e.getMessage(), e).buildFuture();
380         }
381         RpcResultBuilder<SendArpResponseOutput> rpcResultBuilder = RpcResultBuilder.success();
382         return Futures.immediateFuture(rpcResultBuilder.build());
383     }
384
385     @Override
386     public void onPacketReceived(PacketReceived packetReceived) {
387         Class<? extends PacketInReason> pktInReason = packetReceived.getPacketInReason();
388         LOG.trace("Packet Received {}", packetReceived);
389
390         if (pktInReason == SendToController.class) {
391             try {
392                 Uint64 dpnId = extractDpnId(packetReceived);
393                 int tableId = packetReceived.getTableId().getValue().toJava();
394
395                 byte[] data = packetReceived.getPayload();
396                 Ethernet ethernet = new Ethernet();
397
398                 ethernet.deserialize(data, 0, data.length * Byte.SIZE);
399                 if (ethernet.getEtherType() != ArpConstants.ETH_TYPE_ARP) {
400                     return;
401                 }
402
403                 Packet pkt = ethernet.getPayload();
404                 ARP arp = (ARP) pkt;
405                 InetAddress srcInetAddr = InetAddress.getByAddress(arp.getSenderProtocolAddress());
406                 InetAddress dstInetAddr = InetAddress.getByAddress(arp.getTargetProtocolAddress());
407                 byte[] srcMac = ethernet.getSourceMACAddress();
408                 byte[] dstMac = ethernet.getDestinationMACAddress();
409
410                 Metadata metadata = packetReceived.getMatch().getMetadata();
411
412                 String interfaceName = getInterfaceName(metadata);
413
414                 checkAndFireMacChangedNotification(interfaceName, srcInetAddr, srcMac);
415                 macsDB.put(interfaceName + "-" + srcInetAddr.getHostAddress(), NWUtil.toStringMacAddress(srcMac));
416                 if (arp.getOpCode() == ArpConstants.ARP_REQUEST_OP) {
417                     fireArpReqRecvdNotification(interfaceName, srcInetAddr, srcMac, dstInetAddr, dpnId, tableId,
418                             metadata.getMetadata());
419                 } else {
420                     fireArpRespRecvdNotification(interfaceName, srcInetAddr, srcMac, dpnId, tableId,
421                                                  metadata.getMetadata(), dstInetAddr, dstMac);
422                 }
423                 if (macAddrs.get(srcInetAddr.getHostAddress()) != null) {
424                     threadPool.execute(new MacResponderTask(arp));
425                 }
426             } catch (PacketException | UnknownHostException | InterruptedException | ExecutionException e) {
427                 LOG.trace("Failed to decode packet", e);
428             }
429         }
430     }
431
432     private GetPortFromInterfaceOutput getPortFromInterface(String interfaceName)
433             throws InterruptedException, ExecutionException {
434         GetPortFromInterfaceInputBuilder getPortFromInterfaceInputBuilder = new GetPortFromInterfaceInputBuilder();
435         getPortFromInterfaceInputBuilder.setIntfName(interfaceName);
436
437         Future<RpcResult<GetPortFromInterfaceOutput>> portFromInterface = odlInterfaceRpcService
438                 .getPortFromInterface(getPortFromInterfaceInputBuilder.build());
439         GetPortFromInterfaceOutput result = portFromInterface.get().getResult();
440         LOG.trace("getPortFromInterface rpc result is {} ", result);
441         if (result != null) {
442             LOG.trace("getPortFromInterface rpc result is {} {} ", result.getDpid(), result.getPortno());
443         }
444         return result;
445     }
446
447     private String getInterfaceName(Metadata metadata)
448             throws InterruptedException, ExecutionException {
449         LOG.debug("metadata received is {} ", metadata);
450
451         GetInterfaceFromIfIndexInputBuilder ifIndexInputBuilder = new GetInterfaceFromIfIndexInputBuilder();
452         Uint64 lportTag = MetaDataUtil.getLportFromMetadata(metadata.getMetadata());
453
454         ifIndexInputBuilder.setIfIndex(lportTag.intValue());
455         GetInterfaceFromIfIndexInput input = ifIndexInputBuilder.build();
456
457         Future<RpcResult<GetInterfaceFromIfIndexOutput>> interfaceFromIfIndex = odlInterfaceRpcService
458                 .getInterfaceFromIfIndex(input);
459         if (interfaceFromIfIndex.get().isSuccessful()) {
460             GetInterfaceFromIfIndexOutput interfaceFromIfIndexOutput = interfaceFromIfIndex.get().getResult();
461             return interfaceFromIfIndexOutput.getInterfaceName();
462         } else {
463             LOG.error("RPC call to get interface name for if index {} failed with errors {}", lportTag,
464                 interfaceFromIfIndex.get().getErrors());
465             return null;
466         }
467     }
468
469     private class MacResponderTask implements Runnable {
470         final ARP arp;
471
472         MacResponderTask(ARP arp) {
473             this.arp = arp;
474         }
475
476         @Override
477         public void run() {
478             InetAddress srcAddr;
479             GetMacOutputBuilder outputBuilder;
480             String srcMac;
481             try {
482                 srcAddr = InetAddress.getByAddress(arp.getSenderProtocolAddress());
483                 srcMac = NWUtil.toStringMacAddress(arp.getSenderHardwareAddress());
484                 SettableFuture<RpcResult<GetMacOutput>> future = macAddrs.remove(srcAddr.getHostAddress());
485                 if (future == null) {
486                     LOG.trace("There are no pending mac requests.");
487                     return;
488                 }
489                 outputBuilder = new GetMacOutputBuilder().setMacaddress(new PhysAddress(srcMac));
490                 future.set(RpcResultBuilder.success(outputBuilder.build()).build());
491                 if (LOG.isTraceEnabled()) {
492                     LOG.trace("sent the mac response for ip {}", srcAddr.getHostAddress());
493                 }
494             } catch (UnknownHostException e) {
495                 LOG.error("failed to send mac response", e);
496             }
497         }
498     }
499
500     private void fireArpRespRecvdNotification(String interfaceName, InetAddress srcInetAddr, byte[] srcMacAddressBytes,
501             Uint64 dpnId, int tableId, Uint64 metadata, InetAddress dstInetAddr, byte[] dstMacAddressBytes)
502                     throws InterruptedException {
503         arpRespRecvd.mark();
504
505         IpAddress srcIp = IetfInetUtil.INSTANCE.ipAddressFor(srcInetAddr);
506         IpAddress dstIp = IetfInetUtil.INSTANCE.ipAddressFor(dstInetAddr);
507         String srcMacAddress = NWUtil.toStringMacAddress(srcMacAddressBytes);
508         PhysAddress srcMac = new PhysAddress(srcMacAddress);
509         String dstMacAddress = NWUtil.toStringMacAddress(dstMacAddressBytes);
510         PhysAddress dstMac = new PhysAddress(dstMacAddress);
511         ArpResponseReceivedBuilder builder = new ArpResponseReceivedBuilder();
512         builder.setInterface(interfaceName);
513         builder.setSrcIpaddress(srcIp);
514         builder.setDpnId(dpnId);
515         builder.setOfTableId((long) tableId);
516         builder.setSrcMac(srcMac);
517         builder.setMetadata(metadata);
518         builder.setDstIpaddress(dstIp);
519         builder.setDstMac(dstMac);
520         ListenableFuture<?> offerNotification = notificationPublishService.offerNotification(builder.build());
521         if (offerNotification != null && offerNotification.equals(NotificationPublishService.REJECTED)) {
522             arpRespRecvdNotificationRejected.mark();
523
524         } else {
525             arpRespRecvdNotification.mark();
526         }
527     }
528
529     private void fireArpReqRecvdNotification(String interfaceName, InetAddress srcInetAddr, byte[] srcMac,
530             InetAddress dstInetAddr, Uint64 dpnId, int tableId, Uint64 metadata) throws InterruptedException {
531         arpReqRecvd.mark();
532         String macAddress = NWUtil.toStringMacAddress(srcMac);
533         ArpRequestReceivedBuilder builder = new ArpRequestReceivedBuilder();
534         builder.setInterface(interfaceName);
535         builder.setDpnId(dpnId);
536         builder.setOfTableId((long) tableId);
537         builder.setSrcIpaddress(IetfInetUtil.INSTANCE.ipAddressFor(srcInetAddr));
538         builder.setDstIpaddress(IetfInetUtil.INSTANCE.ipAddressFor(dstInetAddr));
539         builder.setSrcMac(new PhysAddress(macAddress));
540         builder.setMetadata(metadata);
541         ListenableFuture<?> offerNotification = notificationPublishService.offerNotification(builder.build());
542         if (offerNotification != null && offerNotification.equals(NotificationPublishService.REJECTED)) {
543             arpReqRecvdNotificationRejected.mark();
544         } else {
545             arpReqRecvdNotification.mark();
546         }
547     }
548
549     private void checkAndFireMacChangedNotification(String interfaceName, InetAddress inetAddr, byte[] macAddressBytes)
550             throws InterruptedException {
551
552         IpAddress ip = IetfInetUtil.INSTANCE.ipAddressFor(inetAddr);
553         String macAddress = NWUtil.toStringMacAddress(macAddressBytes);
554         PhysAddress mac = new PhysAddress(macAddress);
555
556         if (!macAddress.equals(macsDB.get(interfaceName + "-" + inetAddr.getHostAddress()))) {
557             if (LOG.isTraceEnabled()) {
558                 LOG.trace("mac address changed for {}", inetAddr);
559             }
560             MacChangedBuilder builder = new MacChangedBuilder();
561             builder.setInterface(interfaceName);
562             builder.setIpaddress(ip);
563             builder.setMacaddress(mac);
564             notificationPublishService.putNotification(builder.build());
565         }
566     }
567
568     private Uint64 extractDpnId(PacketReceived packetReceived) {
569         NodeKey nodeKey = packetReceived.getIngress().getValue().firstKeyOf(Node.class);
570         String nodeKeyString = nodeKey.getId().getValue();
571
572         if (!nodeKeyString.startsWith(OPENFLOW_PFX)) {
573             LOG.warn("Could not extract DPN for packet-in, doesn't start with 'openflow:' {}", packetReceived);
574             return null;
575         }
576
577         return Uint64.valueOf(nodeKeyString.substring(OPENFLOW_PFX.length()));
578     }
579 }