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