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