af75afb5da777d40349a261cf96cfdf94d9314a1
[netvirt.git] / openstack / net-virt-providers / src / main / java / org / opendaylight / ovsdb / openstack / netvirt / providers / openflow13 / services / arp / GatewayMacResolverService.java
1 /*
2  * Copyright (c) 2015 Brocade Communications Systems, Inc. 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.ovsdb.openstack.netvirt.providers.openflow13.services.arp;
9
10 import com.google.common.base.Preconditions;
11 import com.google.common.collect.ImmutableList;
12 import com.google.common.util.concurrent.FutureCallback;
13 import com.google.common.util.concurrent.Futures;
14 import com.google.common.util.concurrent.JdkFutureAdapters;
15 import com.google.common.util.concurrent.ListenableFuture;
16 import com.google.common.util.concurrent.ListeningExecutorService;
17 import com.google.common.util.concurrent.MoreExecutors;
18 import org.opendaylight.controller.sal.binding.api.BindingAwareBroker.ProviderContext;
19 import org.opendaylight.openflowplugin.api.OFConstants;
20 import org.opendaylight.ovsdb.openstack.netvirt.api.ConfigurationService;
21 import org.opendaylight.ovsdb.openstack.netvirt.api.GatewayMacResolver;
22 import org.opendaylight.ovsdb.openstack.netvirt.api.GatewayMacResolverListener;
23 import org.opendaylight.ovsdb.openstack.netvirt.api.NodeCacheManager;
24 import org.opendaylight.ovsdb.openstack.netvirt.providers.ConfigInterface;
25 import org.opendaylight.ovsdb.openstack.netvirt.providers.NetvirtProvidersProvider;
26 import org.opendaylight.ovsdb.openstack.netvirt.providers.openflow13.AbstractServiceInstance;
27 import org.opendaylight.ovsdb.openstack.netvirt.providers.openflow13.Service;
28 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev100924.Ipv4Address;
29 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.yang.types.rev100924.MacAddress;
30 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.FlowCapableNode;
31 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.FlowId;
32 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.tables.Table;
33 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.tables.TableKey;
34 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.tables.table.Flow;
35 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.tables.table.FlowBuilder;
36 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.tables.table.FlowKey;
37 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.service.rev130819.AddFlowInputBuilder;
38 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.service.rev130819.AddFlowOutput;
39 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.service.rev130819.RemoveFlowInputBuilder;
40 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.service.rev130819.SalFlowService;
41 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.rev131026.FlowCookie;
42 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.rev131026.FlowModFlags;
43 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.rev131026.FlowRef;
44 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.rev131026.flow.InstructionsBuilder;
45 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.rev131026.flow.Match;
46 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.rev131026.flow.MatchBuilder;
47 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.rev131026.instruction.instruction.ApplyActionsCaseBuilder;
48 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.rev131026.instruction.instruction.apply.actions._case.ApplyActions;
49 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.rev131026.instruction.instruction.apply.actions._case.ApplyActionsBuilder;
50 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.rev131026.instruction.list.Instruction;
51 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.rev131026.instruction.list.InstructionBuilder;
52 import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodeRef;
53 import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.Nodes;
54 import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.node.NodeConnector;
55 import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.node.NodeConnectorKey;
56 import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.Node;
57 import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.NodeKey;
58 import org.opendaylight.yang.gen.v1.urn.opendaylight.model.match.types.rev131026.match.EthernetMatch;
59 import org.opendaylight.yang.gen.v1.urn.opendaylight.model.match.types.rev131026.match.layer._3.match.ArpMatch;
60 import org.opendaylight.yang.gen.v1.urn.opendaylight.packet.service.rev130709.PacketProcessingListener;
61 import org.opendaylight.yang.gen.v1.urn.opendaylight.packet.service.rev130709.PacketProcessingService;
62 import org.opendaylight.yang.gen.v1.urn.opendaylight.packet.service.rev130709.PacketReceived;
63 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
64 import org.opendaylight.yangtools.yang.binding.KeyedInstanceIdentifier;
65 import org.opendaylight.yangtools.yang.common.RpcResult;
66 import org.osgi.framework.BundleContext;
67 import org.osgi.framework.ServiceReference;
68 import org.slf4j.Logger;
69 import org.slf4j.LoggerFactory;
70
71 import java.math.BigInteger;
72 import java.util.Collections;
73 import java.util.List;
74 import java.util.Map.Entry;
75 import java.util.concurrent.Callable;
76 import java.util.concurrent.ConcurrentHashMap;
77 import java.util.concurrent.ConcurrentMap;
78 import java.util.concurrent.Executors;
79 import java.util.concurrent.Future;
80 import java.util.concurrent.ScheduledExecutorService;
81 import java.util.concurrent.TimeUnit;
82 import java.util.concurrent.atomic.AtomicBoolean;
83 import java.util.concurrent.atomic.AtomicLong;
84
85 import static com.google.common.base.Preconditions.checkNotNull;
86
87 /**
88  *
89  * @author Anil Vishnoi (avishnoi@Brocade.com)
90  *
91  */
92 public class GatewayMacResolverService extends AbstractServiceInstance
93                                         implements ConfigInterface, GatewayMacResolver,PacketProcessingListener {
94
95     private static final Logger LOG = LoggerFactory.getLogger(GatewayMacResolverService.class);
96     private static final String ARP_REPLY_TO_CONTROLLER_FLOW_NAME = "GatewayArpReplyRouter";
97     private static final int ARP_REPLY_TO_CONTROLLER_FLOW_PRIORITY = 10000;
98     private static final Instruction SEND_TO_CONTROLLER_INSTRUCTION;
99     private ArpSender arpSender;
100     private SalFlowService flowService;
101     private final AtomicLong flowCookie = new AtomicLong();
102     private final ConcurrentMap<Ipv4Address, ArpResolverMetadata> gatewayToArpMetadataMap =
103             new ConcurrentHashMap<>();
104     private static final int ARP_WATCH_BROTHERS = 10;
105     private static final int WAIT_CYCLES = 3;
106     private static final int PER_CYCLE_WAIT_DURATION = 1000;
107     private static final int REFRESH_INTERVAL = 10;
108     private final ListeningExecutorService arpWatcherWall = MoreExecutors.listeningDecorator(Executors.newFixedThreadPool(ARP_WATCH_BROTHERS));
109     private final ScheduledExecutorService gatewayMacRefresherPool = Executors.newScheduledThreadPool(1);
110     private final ScheduledExecutorService refreshRequester = Executors.newSingleThreadScheduledExecutor();
111     private AtomicBoolean initializationDone = new AtomicBoolean(false);
112     private volatile ConfigurationService configurationService;
113     private volatile NodeCacheManager nodeCacheManager;
114
115     static {
116         ApplyActions applyActions = new ApplyActionsBuilder().setAction(
117                 ImmutableList.of(ArpFlowFactory.createSendToControllerAction(0))).build();
118         SEND_TO_CONTROLLER_INSTRUCTION = new InstructionBuilder().setOrder(0)
119             .setInstruction(new ApplyActionsCaseBuilder().setApplyActions(applyActions).build())
120             .build();
121     }
122
123     public GatewayMacResolverService(){
124         super(Service.GATEWAY_RESOLVER);
125     }
126
127     public GatewayMacResolverService(Service service){
128         super(service);
129     }
130
131     private void init(){
132         if(!initializationDone.get()){
133             initializationDone.set(true);
134             ProviderContext providerContext = NetvirtProvidersProvider.getProviderContext();
135             checkNotNull(providerContext);
136             PacketProcessingService packetProcessingService = providerContext.getRpcService(PacketProcessingService.class);
137             if (packetProcessingService != null) {
138                 LOG.debug("{} was found.", PacketProcessingService.class.getSimpleName());
139                 this.arpSender = new ArpSender(packetProcessingService);
140             } else {
141                 LOG.error("Missing service {}", PacketProcessingService.class.getSimpleName());
142                 this.arpSender = null;
143             }
144             flowService = providerContext.getRpcService(SalFlowService.class);
145             refreshRequester.scheduleWithFixedDelay(new Runnable(){
146
147                 @Override
148                 public void run() {
149                     if (!gatewayToArpMetadataMap.isEmpty()){
150                         for(final Entry<Ipv4Address, ArpResolverMetadata> gatewayToArpMetadataEntry : gatewayToArpMetadataMap.entrySet()){
151                             final Ipv4Address gatewayIp = gatewayToArpMetadataEntry.getKey();
152                             final ArpResolverMetadata gatewayMetaData =
153                                     checkAndGetExternalBridgeDpid(
154                                             resetFlowToRemove(gatewayIp, gatewayToArpMetadataEntry.getValue()));
155                             gatewayMacRefresherPool.schedule(new Runnable(){
156
157                                 @Override
158                                 public void run() {
159
160                                     final Node externalNetworkBridge = getExternalBridge(gatewayMetaData.getExternalNetworkBridgeDpid());
161                                     if(externalNetworkBridge == null){
162                                         LOG.error("MAC address for gateway {} can not be resolved, because external bridge {} "
163                                                 + "is not connected to controller.",
164                                                 gatewayIp.getValue(),
165                                                 gatewayMetaData.getExternalNetworkBridgeDpid() );
166                                     } else {
167                                         LOG.debug("Refresh Gateway Mac for gateway {} using source ip {} and mac {} for ARP request",
168                                                 gatewayIp.getValue(),gatewayMetaData.getArpRequestSourceIp().getValue(),gatewayMetaData.getArpRequestSourceMacAddress().getValue());
169
170                                         sendGatewayArpRequest(externalNetworkBridge,gatewayIp,gatewayMetaData.getArpRequestSourceIp(), gatewayMetaData.getArpRequestSourceMacAddress());
171                                     }
172                                 }
173                             }, 1, TimeUnit.SECONDS);
174                         }
175                     }
176                 }
177             }, REFRESH_INTERVAL, REFRESH_INTERVAL, TimeUnit.SECONDS);
178         }
179     }
180     /**
181      * Method do following actions:
182      * 1. Install flow to direct ARP response packet to controller
183      * 2. Send ARP request packet out on all port of the given External network bridge.
184      * 3. Cache the flow that need to be removed once ARP resolution is done.
185      * 4. Return listenable future so that user can add callback to get the MacAddress
186      * @param externalNetworkBridgeDpid Broadcast ARP request packet on this bridge
187      * @param gatewayIp IP address for which MAC need to be resolved
188      * @param sourceIpAddress Source Ip address for the ARP request packet
189      * @param sourceMacAddress Source Mac address for the ARP request packet
190      * @param periodicRefresh Enable/Disable periodic refresh of the Gateway Mac address
191      * @return Future object
192      */
193     @Override
194     public ListenableFuture<MacAddress> resolveMacAddress(
195             final GatewayMacResolverListener gatewayMacResolverListener, final Long externalNetworkBridgeDpid,
196             final Boolean refreshExternalNetworkBridgeDpidIfNeeded,
197             final Ipv4Address gatewayIp, final Ipv4Address sourceIpAddress, final MacAddress sourceMacAddress,
198             final Boolean periodicRefresh){
199         Preconditions.checkNotNull(refreshExternalNetworkBridgeDpidIfNeeded);
200         Preconditions.checkNotNull(sourceIpAddress);
201         Preconditions.checkNotNull(sourceMacAddress);
202         Preconditions.checkNotNull(gatewayIp);
203
204         LOG.info("Trigger Mac resolution for gateway {}, using source ip {} and mac {}",
205                 gatewayIp.getValue(),sourceIpAddress.getValue(),sourceMacAddress.getValue());
206
207         init();
208         if(gatewayToArpMetadataMap.containsKey(gatewayIp)){
209             if(gatewayToArpMetadataMap.get(gatewayIp).getGatewayMacAddress() != null){
210                 return arpWatcherWall.submit(new Callable<MacAddress>(){
211
212                     @Override
213                     public MacAddress call() throws Exception {
214                         return gatewayToArpMetadataMap.get(gatewayIp).getGatewayMacAddress();
215                     }
216                 });
217             }
218         }else{
219             gatewayToArpMetadataMap.put(gatewayIp,new ArpResolverMetadata(gatewayMacResolverListener,
220                     externalNetworkBridgeDpid, refreshExternalNetworkBridgeDpidIfNeeded,
221                     gatewayIp, sourceIpAddress, sourceMacAddress, periodicRefresh));
222         }
223
224
225         final Node externalNetworkBridge = getExternalBridge(externalNetworkBridgeDpid);
226         if (externalNetworkBridge == null) {
227             if (!refreshExternalNetworkBridgeDpidIfNeeded) {
228                 LOG.error("MAC address for gateway {} can not be resolved, because external bridge {} "
229                         + "is not connected to controller.", gatewayIp.getValue(), externalNetworkBridgeDpid);
230             } else {
231                 LOG.debug("MAC address for gateway {} can not be resolved, since dpid was not refreshed yet",
232                         gatewayIp.getValue());
233             }
234             return null;
235         }
236
237         sendGatewayArpRequest(externalNetworkBridge,gatewayIp,sourceIpAddress, sourceMacAddress);
238
239         //Wait for MacAddress population in cache
240         return waitForMacAddress(gatewayIp);
241     }
242
243     private Node getExternalBridge(final Long externalNetworkBridgeDpid){
244         if (externalNetworkBridgeDpid != null) {
245             final String nodeName = OPENFLOW + externalNetworkBridgeDpid;
246             return getOpenFlowNode(nodeName);
247         }
248         return null;
249     }
250
251     private ArpResolverMetadata checkAndGetExternalBridgeDpid(ArpResolverMetadata gatewayMetaData) {
252         final Long origDpid = gatewayMetaData.getExternalNetworkBridgeDpid();
253
254         // If we are not allowing dpid to change, there is nothing further to do here
255         if (!gatewayMetaData.isRefreshExternalNetworkBridgeDpidIfNeeded()) {
256             return gatewayMetaData;
257         }
258
259         // If current dpid is null, or if mac is not getting resolved, make an attempt to
260         // grab a different dpid, so a different (or updated) external bridge gets used
261         if (origDpid == null || !gatewayMetaData.isGatewayMacAddressResolved()) {
262             Long newDpid = getAnotherExternalBridgeDpid(origDpid);
263             gatewayMetaData.setExternalNetworkBridgeDpid(newDpid);
264         }
265
266         return gatewayMetaData;
267     }
268
269     private Long getAnotherExternalBridgeDpid(final Long unwantedDpid) {
270         LOG.trace("Being asked to find a new dpid. unwantedDpid:{} cachemanager:{} configurationService:{}",
271                 unwantedDpid, nodeCacheManager, configurationService);
272
273         if (nodeCacheManager == null) {
274             LOG.error("Unable to find external dpid to use for resolver: no nodeCacheManager");
275             return unwantedDpid;
276         }
277         if (configurationService == null) {
278             LOG.error("Unable to find external dpid to use for resolver: no configurationService");
279             return unwantedDpid;
280         }
281
282         // Pickup another dpid in list that is different than the unwanted one provided and is in the
283         // operational tree. If none can be found, return the provided dpid as a last resort.
284         // NOTE: We are assuming that all the br-ex are serving one external network and gateway ip of
285         // the external network is reachable from every br-ex
286         // TODO: Consider other deployment scenario, and think of another solution.
287         List<Long> dpids =  nodeCacheManager.getBridgeDpids(configurationService.getExternalBridgeName());
288         Collections.shuffle(dpids);
289         for (Long dpid : dpids) {
290             if (dpid == null || dpid.equals(unwantedDpid) || getExternalBridge(dpid) == null) {
291                 continue;
292             }
293
294             LOG.debug("Gateway Mac Resolver Service will use dpid {}", dpid);
295             return dpid;
296         }
297
298         LOG.warn("Unable to find usable external dpid for resolver. Best choice is still {}", unwantedDpid);
299         return unwantedDpid;
300     }
301
302     private void sendGatewayArpRequest(final Node externalNetworkBridge,final Ipv4Address gatewayIp,
303             final Ipv4Address sourceIpAddress, final MacAddress sourceMacAddress){
304         final ArpMessageAddress senderAddress = new ArpMessageAddress(sourceMacAddress,sourceIpAddress);
305
306         //Build arp reply router flow
307         final Flow arpReplyToControllerFlow = createArpReplyToControllerFlow(senderAddress, gatewayIp);
308
309         final InstanceIdentifier<Node> nodeIid = InstanceIdentifier.builder(Nodes.class)
310                 .child(Node.class, externalNetworkBridge.getKey())
311                 .build();
312         final InstanceIdentifier<Flow> flowIid = createFlowIid(arpReplyToControllerFlow, nodeIid);
313         final NodeRef nodeRef = new NodeRef(nodeIid);
314
315         //Install flow
316         Future<RpcResult<AddFlowOutput>> addFlowResult = flowService.addFlow(new AddFlowInputBuilder(
317                 arpReplyToControllerFlow).setFlowRef(new FlowRef(flowIid)).setNode(nodeRef).build());
318         //wait for flow installation
319         Futures.addCallback(JdkFutureAdapters.listenInPoolThread(addFlowResult),
320                 new FutureCallback<RpcResult<AddFlowOutput>>() {
321
322             @Override
323             public void onSuccess(RpcResult<AddFlowOutput> result) {
324                 if (!result.isSuccessful()) {
325                     LOG.warn("Flow to route ARP Reply to Controller is not installed successfully : {} \nErrors: {}", flowIid,result.getErrors());
326                     return;
327                 }
328                 LOG.debug("Flow to route ARP Reply to Controller installed successfully : {}", flowIid);
329
330                 ArpResolverMetadata gatewayArpMetadata = gatewayToArpMetadataMap.get(gatewayIp);
331                 if (gatewayArpMetadata == null) {
332                     LOG.warn("No metadata found for gatewayIp: {}", gatewayIp);
333                     return;
334                 }
335
336                 //cache metadata
337                 gatewayArpMetadata.setFlowToRemove(new RemoveFlowInputBuilder(arpReplyToControllerFlow).setNode(nodeRef).build());
338
339                 //get MAC DA for ARP packets
340                 MacAddress arpRequestDestMacAddress = gatewayArpMetadata.getArpRequestDestMacAddress();
341
342                 //Send ARP request packets
343                 for (NodeConnector egressNc : externalNetworkBridge.getNodeConnector()) {
344                     KeyedInstanceIdentifier<NodeConnector, NodeConnectorKey> egressNcIid = nodeIid.child(
345                             NodeConnector.class, new NodeConnectorKey(egressNc.getId()));
346                     ListenableFuture<RpcResult<Void>> futureSendArpResult = arpSender.sendArp(
347                             senderAddress, gatewayIp, arpRequestDestMacAddress, egressNcIid);
348                     Futures.addCallback(futureSendArpResult, logResult(gatewayIp, egressNcIid));
349                 }
350             }
351
352             @Override
353             public void onFailure(Throwable t) {
354                 LOG.warn("ARP Reply to Controller flow was not created: {}", flowIid, t);
355             }
356             }
357         );
358     }
359
360     private ListenableFuture<MacAddress> waitForMacAddress(final Ipv4Address gatewayIp){
361
362         return arpWatcherWall.submit(new Callable<MacAddress>(){
363
364             @Override
365             public MacAddress call() throws Exception {
366                 for(int cycle = 0;cycle < WAIT_CYCLES;cycle++){
367                     //Sleep before checking mac address, so meanwhile ARP request packets
368                     // will be broadcasted on the bridge.
369                     Thread.sleep(PER_CYCLE_WAIT_DURATION);
370                     ArpResolverMetadata arpResolverMetadata = gatewayToArpMetadataMap.get(gatewayIp);
371                     if(arpResolverMetadata != null && arpResolverMetadata.getGatewayMacAddress() != null){
372                         if(!arpResolverMetadata.isPeriodicRefresh()){
373                             resetFlowToRemove(gatewayIp, arpResolverMetadata);
374                             return gatewayToArpMetadataMap.remove(gatewayIp).getGatewayMacAddress();
375                         }
376                         return arpResolverMetadata.getGatewayMacAddress();
377                     }
378                 }
379                 return null;
380             }
381         });
382     }
383
384     private Flow createArpReplyToControllerFlow(final ArpMessageAddress senderAddress, final Ipv4Address ipForRequestedMac) {
385         checkNotNull(senderAddress);
386         checkNotNull(ipForRequestedMac);
387         FlowBuilder arpFlow = new FlowBuilder().setTableId(Service.CLASSIFIER.getTable())
388             .setFlowName(ARP_REPLY_TO_CONTROLLER_FLOW_NAME)
389             .setPriority(ARP_REPLY_TO_CONTROLLER_FLOW_PRIORITY)
390             .setBufferId(OFConstants.OFP_NO_BUFFER)
391             .setIdleTimeout(0)
392             .setHardTimeout(0)
393             .setCookie(new FlowCookie(BigInteger.valueOf(flowCookie.incrementAndGet())))
394             .setFlags(new FlowModFlags(false, false, false, false, false));
395
396         EthernetMatch ethernetMatch = ArpFlowFactory.createEthernetMatch(senderAddress.getHardwareAddress());
397         ArpMatch arpMatch = ArpFlowFactory.createArpMatch(senderAddress, ipForRequestedMac);
398         Match match = new MatchBuilder().setEthernetMatch(ethernetMatch).setLayer3Match(arpMatch).build();
399         arpFlow.setMatch(match);
400         arpFlow.setInstructions(new InstructionsBuilder().setInstruction(
401                 ImmutableList.of(SEND_TO_CONTROLLER_INSTRUCTION)).build());
402         arpFlow.setId(createFlowId(ipForRequestedMac));
403         return arpFlow.build();
404     }
405
406     private FlowId createFlowId(Ipv4Address ipForRequestedMac) {
407         String flowId = ARP_REPLY_TO_CONTROLLER_FLOW_NAME + "|" + ipForRequestedMac.getValue();
408         return new FlowId(flowId);
409     }
410
411     private static InstanceIdentifier<Flow> createFlowIid(Flow flow, InstanceIdentifier<Node> nodeIid) {
412         return nodeIid.builder()
413             .augmentation(FlowCapableNode.class)
414             .child(Table.class, new TableKey(flow.getTableId()))
415             .child(Flow.class, new FlowKey(flow.getId()))
416             .build();
417     }
418
419     private FutureCallback<RpcResult<Void>> logResult(final Ipv4Address tpa,
420             final KeyedInstanceIdentifier<NodeConnector, NodeConnectorKey> egressNcIid) {
421         return new FutureCallback<RpcResult<Void>>() {
422
423             @Override
424             public void onSuccess(RpcResult<Void> result) {
425                 LOG.debug("ARP Request for IP {} was sent from {}.", tpa.getValue(), egressNcIid);
426             }
427
428             @Override
429             public void onFailure(Throwable t) {
430                 LOG.warn("ARP Request for IP {} was NOT sent from {}.", tpa.getValue(), egressNcIid);
431             }
432         };
433     }
434
435     @Override
436     public void onPacketReceived(PacketReceived potentialArp) {
437         Arp arp = ArpResolverUtils.getArpFrom(potentialArp);
438         if(arp != null){
439             if (arp.getOperation() != ArpOperation.REPLY.intValue()) {
440                 LOG.trace("Packet is not ARP REPLY packet.");
441                 return;
442             }
443             if (LOG.isTraceEnabled()) {
444                 LOG.trace("ARP REPLY received - {}", ArpUtils.getArpToStringFormat(arp));
445             }
446             NodeKey nodeKey = potentialArp.getIngress().getValue().firstKeyOf(Node.class, NodeKey.class);
447             if (nodeKey == null) {
448                 LOG.info("Unknown source node of ARP packet: {}", potentialArp);
449                 return;
450             }
451             Ipv4Address gatewayIpAddress = ArpUtils.bytesToIp(arp.getSenderProtocolAddress());
452             MacAddress gatewayMacAddress = ArpUtils.bytesToMac(arp.getSenderHardwareAddress());
453             ArpResolverMetadata candidateGatewayIp = gatewayToArpMetadataMap.get(gatewayIpAddress);
454             if(candidateGatewayIp != null){
455                 LOG.debug("Resolved MAC for Gateway Ip {} is {}",gatewayIpAddress.getValue(),gatewayMacAddress.getValue());
456                 candidateGatewayIp.setGatewayMacAddress(gatewayMacAddress);
457                 resetFlowToRemove(gatewayIpAddress, candidateGatewayIp);
458             }
459         }
460     }
461
462     @Override
463     public void setDependencies(BundleContext bundleContext,
464             ServiceReference serviceReference) {
465         super.setDependencies(bundleContext.getServiceReference(GatewayMacResolver.class.getName()), this);
466
467     }
468
469     @Override
470     public void setDependencies(Object impl) {
471         if (impl instanceof NodeCacheManager) {
472             nodeCacheManager = (NodeCacheManager) impl;
473         } else if (impl instanceof ConfigurationService) {
474             configurationService = (ConfigurationService) impl;
475         }
476     }
477
478     @Override
479     public void stopPeriodicRefresh(Ipv4Address gatewayIp) {
480         init();
481         resetFlowToRemove(gatewayIp, null);
482         gatewayToArpMetadataMap.remove(gatewayIp);
483     }
484
485     private ArpResolverMetadata resetFlowToRemove(
486             final Ipv4Address gatewayIp, ArpResolverMetadata gatewayArpMetadata) {
487         checkNotNull(gatewayIp);
488
489         // If gatewayArpMetadata was not provided, look it up
490         if (gatewayArpMetadata == null) {
491             gatewayArpMetadata = gatewayToArpMetadataMap.get(gatewayIp);
492         }
493         if (gatewayArpMetadata != null && gatewayArpMetadata.getFlowToRemove() != null) {
494             LOG.debug("Flow to route ARP Reply to Controller from {} being removed from node {}",
495                     gatewayIp, gatewayArpMetadata.getFlowToRemove().getNode());
496             flowService.removeFlow(gatewayArpMetadata.getFlowToRemove());
497             gatewayArpMetadata.setFlowToRemove(null);
498         }
499         return gatewayArpMetadata;
500     }
501
502 }