2 * Copyright (c) 2013 Cisco Systems, Inc. and others. All rights reserved.
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
12 package org.opendaylight.controller.arphandler.internal;
14 import java.net.InetAddress;
15 import java.net.UnknownHostException;
16 import java.util.Arrays;
17 import java.util.Collections;
18 import java.util.EnumSet;
19 import java.util.HashSet;
21 import java.util.Timer;
22 import java.util.TimerTask;
23 import java.util.concurrent.BlockingQueue;
24 import java.util.concurrent.ConcurrentHashMap;
25 import java.util.concurrent.ConcurrentMap;
26 import java.util.concurrent.CopyOnWriteArraySet;
27 import java.util.concurrent.LinkedBlockingQueue;
29 import org.opendaylight.controller.arphandler.ARPCacheEvent;
30 import org.opendaylight.controller.arphandler.ARPEvent;
31 import org.opendaylight.controller.arphandler.ARPReply;
32 import org.opendaylight.controller.arphandler.ARPRequest;
33 import org.opendaylight.controller.clustering.services.CacheConfigException;
34 import org.opendaylight.controller.clustering.services.CacheExistException;
35 import org.opendaylight.controller.clustering.services.ICacheUpdateAware;
36 import org.opendaylight.controller.clustering.services.IClusterContainerServices;
37 import org.opendaylight.controller.clustering.services.IClusterServices;
38 import org.opendaylight.controller.connectionmanager.IConnectionManager;
39 import org.opendaylight.controller.hosttracker.HostIdFactory;
40 import org.opendaylight.controller.hosttracker.IHostId;
41 import org.opendaylight.controller.hosttracker.IfHostListener;
42 import org.opendaylight.controller.hosttracker.IfIptoHost;
43 import org.opendaylight.controller.hosttracker.hostAware.HostNodeConnector;
44 import org.opendaylight.controller.hosttracker.hostAware.IHostFinder;
45 import org.opendaylight.controller.sal.connection.ConnectionLocality;
46 import org.opendaylight.controller.sal.core.ConstructionException;
47 import org.opendaylight.controller.sal.core.Node;
48 import org.opendaylight.controller.sal.core.NodeConnector;
49 import org.opendaylight.controller.sal.packet.ARP;
50 import org.opendaylight.controller.sal.packet.Ethernet;
51 import org.opendaylight.controller.sal.packet.IDataPacketService;
52 import org.opendaylight.controller.sal.packet.IListenDataPacket;
53 import org.opendaylight.controller.sal.packet.IPv4;
54 import org.opendaylight.controller.sal.packet.Packet;
55 import org.opendaylight.controller.sal.packet.PacketResult;
56 import org.opendaylight.controller.sal.packet.RawPacket;
57 import org.opendaylight.controller.sal.routing.IRouting;
58 import org.opendaylight.controller.sal.utils.EtherTypes;
59 import org.opendaylight.controller.sal.utils.HexEncode;
60 import org.opendaylight.controller.sal.utils.NetUtils;
61 import org.opendaylight.controller.switchmanager.ISwitchManager;
62 import org.opendaylight.controller.switchmanager.Subnet;
63 import org.opendaylight.controller.topologymanager.ITopologyManager;
64 import org.slf4j.Logger;
65 import org.slf4j.LoggerFactory;
67 public class ArpHandler implements IHostFinder, IListenDataPacket, ICacheUpdateAware<ARPEvent, Boolean> {
68 private static final Logger log = LoggerFactory.getLogger(ArpHandler.class);
69 static final String ARP_EVENT_CACHE_NAME = "arphandler.arpRequestReplyEvent";
70 private IfIptoHost hostTracker;
71 private ISwitchManager switchManager;
72 private ITopologyManager topologyManager;
73 private IDataPacketService dataPacketService;
74 private IRouting routing;
75 private IClusterContainerServices clusterContainerService;
76 private IConnectionManager connectionManager;
77 private Set<IfHostListener> hostListeners = new CopyOnWriteArraySet<IfHostListener>();
78 private ConcurrentMap<InetAddress, Set<HostNodeConnector>> arpRequestors;
79 private ConcurrentMap<InetAddress, Short> countDownTimers;
80 private Timer periodicTimer;
81 private BlockingQueue<ARPCacheEvent> ARPCacheEvents = new LinkedBlockingQueue<ARPCacheEvent>();
82 private Thread cacheEventHandler;
83 private boolean stopping = false;
86 * A cluster allocated cache. Used for synchronizing ARP request/reply
87 * events across all cluster controllers. To raise an event, we put() a
88 * specific event object (as key) and all nodes handle it in the
89 * entryUpdated callback.
91 * In case of ARPReply, we put true value to send replies to any requestors
92 * by calling generateAndSendReply
94 private ConcurrentMap<ARPEvent, Boolean> arpRequestReplyEvent;
96 void setConnectionManager(IConnectionManager cm) {
97 this.connectionManager = cm;
100 void unsetConnectionManager(IConnectionManager cm) {
101 if (this.connectionManager == cm) {
102 connectionManager = null;
106 void setClusterContainerService(IClusterContainerServices s) {
107 this.clusterContainerService = s;
110 void unsetClusterContainerService(IClusterContainerServices s) {
111 if (this.clusterContainerService == s) {
112 this.clusterContainerService = null;
116 void setRouting(IRouting r) {
120 void unsetRouting(IRouting r) {
121 if (this.routing == r) {
126 void setHostListener(IfHostListener s) {
127 if (this.hostListeners != null) {
128 this.hostListeners.add(s);
132 void unsetHostListener(IfHostListener s) {
133 if (this.hostListeners != null) {
134 this.hostListeners.remove(s);
138 void setDataPacketService(IDataPacketService s) {
139 this.dataPacketService = s;
142 void unsetDataPacketService(IDataPacketService s) {
143 if (this.dataPacketService == s) {
144 this.dataPacketService = null;
148 public void setHostTracker(IfIptoHost hostTracker) {
149 log.debug("Setting HostTracker");
150 this.hostTracker = hostTracker;
153 public void unsetHostTracker(IfIptoHost s) {
154 log.debug("UNSetting HostTracker");
155 if (this.hostTracker == s) {
156 this.hostTracker = null;
160 public void setTopologyManager(ITopologyManager tm) {
161 this.topologyManager = tm;
164 public void unsetTopologyManager(ITopologyManager tm) {
165 if (this.topologyManager == tm) {
166 this.topologyManager = null;
170 protected void sendARPReply(NodeConnector p, byte[] sMAC, InetAddress sIP, byte[] tMAC, InetAddress tIP) {
171 byte[] senderIP = sIP.getAddress();
172 byte[] targetIP = tIP.getAddress();
173 ARP arp = createARP(ARP.REPLY, sMAC, senderIP, tMAC, targetIP);
175 Ethernet ethernet = createEthernet(sMAC, tMAC, arp);
177 RawPacket destPkt = this.dataPacketService.encodeDataPacket(ethernet);
178 destPkt.setOutgoingNodeConnector(p);
180 this.dataPacketService.transmitDataPacket(destPkt);
183 protected void handleARPPacket(Ethernet eHeader, ARP pkt, NodeConnector p) {
185 byte[] sourceMAC = eHeader.getSourceMACAddress();
186 byte[] targetMAC = eHeader.getDestinationMACAddress();
188 * Sanity Check; drop ARP packets originated by the controller itself.
189 * This is to avoid continuous flooding
191 if (Arrays.equals(sourceMAC, getControllerMAC())) {
192 if (log.isDebugEnabled()) {
193 log.debug("Receive a self originated ARP pkt (srcMAC {}) --> DROP",
194 HexEncode.bytesToHexString(sourceMAC));
199 InetAddress targetIP, sourceIP;
201 targetIP = InetAddress.getByAddress(pkt.getTargetProtocolAddress());
202 sourceIP = InetAddress.getByAddress(pkt.getSenderProtocolAddress());
203 } catch (UnknownHostException e1) {
204 log.debug("Invalid host in ARP packet: {}", e1.getMessage());
208 Subnet subnet = null;
209 if (switchManager != null) {
210 subnet = switchManager.getSubnetByNetworkAddress(sourceIP);
212 if (subnet == null) {
213 log.debug("ARPHandler: can't find subnet matching {}, drop packet", sourceIP);
217 // Make sure that the host is a legitimate member of this subnet
218 if (!subnet.hasNodeConnector(p)) {
219 log.debug("{} showing up on {} does not belong to {}", new Object[] { sourceIP, p, subnet });
223 HostNodeConnector requestor = null;
224 if (NetUtils.isUnicastMACAddr(sourceMAC) && p.getNode() != null) {
226 requestor = new HostNodeConnector(sourceMAC, sourceIP, p, subnet.getVlan());
227 } catch (ConstructionException e) {
228 log.debug("Received ARP packet with invalid MAC: {}", HexEncode.bytesToHexString(sourceMAC));
232 * Learn host from the received ARP REQ/REPLY, inform Host Tracker
234 log.trace("Inform Host tracker of new host {}", requestor.getNetworkAddress());
235 for (IfHostListener listener : this.hostListeners) {
236 listener.hostListener(requestor);
241 * OpCode != request -> ARP Reply. If there are hosts (in arpRequestors)
242 * waiting for the ARP reply for this sourceIP, it's time to generate
243 * the reply and send it to these hosts.
245 * If sourceIP==targetIP, it is a Gratuitous ARP. If there are hosts (in
246 * arpRequestors) waiting for the ARP reply for this sourceIP, it's time
247 * to generate the reply and send it to these hosts
250 if (pkt.getOpCode() != ARP.REQUEST || sourceIP.equals(targetIP)) {
251 // Raise a reply event so that any waiting requestors will be sent a
253 // the true value indicates we should generate replies to requestors
254 // across the cluster
255 log.trace("Received ARP reply packet from {}, reply to all requestors.", sourceIP);
256 arpRequestReplyEvent.put(new ARPReply(sourceIP, sourceMAC), true);
261 * ARP Request Handling: If targetIP is the IP of the subnet, reply with
262 * ARP REPLY If targetIP is a known host, PROXY ARP (by sending ARP
263 * REPLY) on behalf of known target hosts. For unknown target hosts,
264 * generate and send an ARP request to ALL switches/ports using the IP
265 * address defined in the subnet as source address
268 * If target IP is gateway IP, Send ARP reply
270 if ((targetIP.equals(subnet.getNetworkAddress()))
271 && (NetUtils.isBroadcastMACAddr(targetMAC) || Arrays.equals(targetMAC, getControllerMAC()))) {
272 if (connectionManager.getLocalityStatus(p.getNode()) == ConnectionLocality.LOCAL) {
273 if (log.isTraceEnabled()) {
274 log.trace("Received local ARP req. for default gateway. Replying with controller MAC: {}",
275 HexEncode.bytesToHexString(getControllerMAC()));
277 sendARPReply(p, getControllerMAC(), targetIP, pkt.getSenderHardwareAddress(), sourceIP);
279 log.trace("Received non-local ARP req. for default gateway. Raising reply event");
280 arpRequestReplyEvent.put(
281 new ARPReply(p, targetIP, getControllerMAC(), sourceIP, pkt.getSenderHardwareAddress()), false);
286 // Hosttracker hosts db key implementation
287 IHostId id = HostIdFactory.create(targetIP, null);
288 HostNodeConnector host = hostTracker.hostQuery(id);
289 // unknown host, initiate ARP request
291 // add the requestor to the list so that we can replay the reply
292 // when the host responds
293 if (requestor != null) {
294 Set<HostNodeConnector> requestorSet = arpRequestors.get(targetIP);
295 if (requestorSet == null) {
296 requestorSet = Collections.newSetFromMap(new ConcurrentHashMap<HostNodeConnector, Boolean>());
297 arpRequestors.put(targetIP, requestorSet);
299 requestorSet.add(requestor);
300 countDownTimers.put(targetIP, (short) 2); // reset timeout to
303 // Raise a bcast request event, all controllers need to send one
304 log.trace("Sending a bcast ARP request for {}", targetIP);
305 arpRequestReplyEvent.put(new ARPRequest(targetIP, subnet), false);
308 * Target host known (across the cluster), send ARP REPLY make sure
309 * that targetMAC matches the host's MAC if it is not broadcastMAC
311 if (NetUtils.isBroadcastMACAddr(targetMAC) || Arrays.equals(host.getDataLayerAddressBytes(), targetMAC)) {
312 log.trace("Received ARP req. for known host {}, sending reply...", targetIP);
313 if (connectionManager.getLocalityStatus(p.getNode()) == ConnectionLocality.LOCAL) {
314 sendARPReply(p, host.getDataLayerAddressBytes(), host.getNetworkAddress(),
315 pkt.getSenderHardwareAddress(), sourceIP);
317 arpRequestReplyEvent.put(new ARPReply(p, host.getNetworkAddress(), host.getDataLayerAddressBytes(),
318 sourceIP, pkt.getSenderHardwareAddress()), false);
322 * Target MAC has been changed. For now, discard it. TODO: We
323 * may need to send unicast ARP REQUEST on behalf of the target
324 * back to the sender to trigger the sender to update its table
331 * Send a broadcast ARP Request to the switch/ ports using the
332 * networkAddress of the subnet as sender IP the controller's MAC as sender
333 * MAC the targetIP as the target Network Address
335 protected void sendBcastARPRequest(InetAddress targetIP, Subnet subnet) {
336 log.trace("sendBcatARPRequest targetIP:{} subnet:{}", targetIP, subnet);
337 Set<NodeConnector> nodeConnectors;
338 if (subnet.isFlatLayer2()) {
339 nodeConnectors = new HashSet<NodeConnector>();
340 for (Node n : this.switchManager.getNodes()) {
341 nodeConnectors.addAll(this.switchManager.getUpNodeConnectors(n));
344 nodeConnectors = subnet.getNodeConnectors();
346 byte[] targetHardwareAddress = new byte[] { (byte) 0, (byte) 0, (byte) 0, (byte) 0, (byte) 0, (byte) 0 };
348 // TODO: should use IBroadcastHandler instead
349 for (NodeConnector p : nodeConnectors) {
350 // filter out any non-local or internal ports
351 if (!(connectionManager.getLocalityStatus(p.getNode()) == ConnectionLocality.LOCAL)
352 || topologyManager.isInternal(p)) {
355 log.trace("Sending toward nodeConnector:{}", p);
356 byte[] senderIP = subnet.getNetworkAddress().getAddress();
357 byte[] targetIPByte = targetIP.getAddress();
358 ARP arp = createARP(ARP.REQUEST, getControllerMAC(), senderIP, targetHardwareAddress, targetIPByte);
360 byte[] destMACAddress = NetUtils.getBroadcastMACAddr();
361 Ethernet ethernet = createEthernet(getControllerMAC(), destMACAddress, arp);
363 // TODO For now send port-by-port, see how to optimize to
364 // send to multiple ports at once
365 RawPacket destPkt = this.dataPacketService.encodeDataPacket(ethernet);
366 destPkt.setOutgoingNodeConnector(p);
368 this.dataPacketService.transmitDataPacket(destPkt);
373 * Send a unicast ARP Request to the known host on a specific switch/port as
374 * defined in the host. The sender IP is the networkAddress of the subnet
375 * The sender MAC is the controller's MAC
377 protected void sendUcastARPRequest(HostNodeConnector host, Subnet subnet) {
378 log.trace("sendUcastARPRequest host:{} subnet:{}", host, subnet);
379 NodeConnector outPort = host.getnodeConnector();
380 if (outPort == null) {
381 log.error("Failed sending UcastARP because cannot extract output port from Host: {}", host);
385 byte[] senderIP = subnet.getNetworkAddress().getAddress();
386 byte[] targetIP = host.getNetworkAddress().getAddress();
387 byte[] targetMAC = host.getDataLayerAddressBytes();
388 ARP arp = createARP(ARP.REQUEST, getControllerMAC(), senderIP, targetMAC, targetIP);
390 Ethernet ethernet = createEthernet(getControllerMAC(), targetMAC, arp);
392 RawPacket destPkt = this.dataPacketService.encodeDataPacket(ethernet);
393 destPkt.setOutgoingNodeConnector(outPort);
395 this.dataPacketService.transmitDataPacket(destPkt);
399 public void find(InetAddress networkAddress) {
400 log.trace("Received find IP {}", networkAddress);
402 Subnet subnet = null;
403 if (switchManager != null) {
404 subnet = switchManager.getSubnetByNetworkAddress(networkAddress);
406 if (subnet == null) {
407 log.debug("Can't find subnet matching IP {}", networkAddress);
411 // send a broadcast ARP Request to this IP
412 arpRequestReplyEvent.put(new ARPRequest(networkAddress, subnet), false);
416 * Probe the host by sending a unicast ARP Request to the host
419 public void probe(HostNodeConnector host) {
420 log.trace("Received probe host {}", host);
422 Subnet subnet = null;
423 if (switchManager != null) {
424 subnet = switchManager.getSubnetByNetworkAddress(host.getNetworkAddress());
426 if (subnet == null) {
427 log.debug("can't find subnet matching {}", host.getNetworkAddress());
431 if (connectionManager.getLocalityStatus(host.getnodeconnectorNode()) == ConnectionLocality.LOCAL) {
432 log.trace("Send a ucast ARP req. to: {}", host);
433 sendUcastARPRequest(host, subnet);
435 log.trace("Raise a ucast ARP req. event to: {}", host);
436 arpRequestReplyEvent.put(new ARPRequest(host, subnet), false);
441 * An IP packet is punted to the controller, this means that the destination
442 * host is not known to the controller. Need to discover it by sending a
443 * Broadcast ARP Request
448 protected void handlePuntedIPPacket(IPv4 pkt, NodeConnector p) {
450 InetAddress dIP = NetUtils.getInetAddress(pkt.getDestinationAddress());
455 // try to find a matching subnet
456 Subnet subnet = null;
457 if (switchManager != null) {
458 subnet = switchManager.getSubnetByNetworkAddress(dIP);
460 if (subnet == null) {
461 log.debug("Can't find subnet matching {}, drop packet", dIP);
465 // see if we know about the host
466 // Hosttracker hosts db key implementation
467 IHostId id = HostIdFactory.create(dIP, null);
468 HostNodeConnector host = hostTracker.hostFind(id);
471 // if we don't, know about the host, try to find it
472 log.trace("Punted IP pkt to {}, sending bcast ARP event...", dIP);
474 * unknown destination host, initiate bcast ARP request
476 arpRequestReplyEvent.put(new ARPRequest(dIP, subnet), false);
478 } else if (routing == null || routing.getRoute(p.getNode(), host.getnodeconnectorNode()) != null) {
480 * if IRouting is available, make sure that this packet can get it's
481 * destination normally before teleporting it there. If it's not
482 * available, then assume it's reachable.
484 * TODO: come up with a way to do this in the absence of IRouting
487 log.trace("forwarding punted IP pkt to {} received at {}", dIP, p);
490 * if we know where the host is and there's a path from where this
491 * packet was punted to where the host is, then deliver it to the
494 NodeConnector nc = host.getnodeConnector();
496 // re-encode the Ethernet packet (the parent of the IPv4 packet)
497 RawPacket rp = this.dataPacketService.encodeDataPacket(pkt.getParent());
498 rp.setOutgoingNodeConnector(nc);
499 this.dataPacketService.transmitDataPacket(rp);
501 log.trace("ignoring punted IP pkt to {} because there is no route from {}", dIP, p);
505 public byte[] getControllerMAC() {
506 if (switchManager == null) {
509 return switchManager.getControllerMAC();
513 * Function called by the dependency manager when all the required
514 * dependencies are satisfied
518 arpRequestors = new ConcurrentHashMap<InetAddress, Set<HostNodeConnector>>();
519 countDownTimers = new ConcurrentHashMap<InetAddress, Short>();
520 cacheEventHandler = new Thread(new ARPCacheEventHandler(), "ARPCacheEventHandler Thread");
527 @SuppressWarnings({ "unchecked" })
528 private void retrieveCaches() {
529 ConcurrentMap<?, ?> map;
531 if (this.clusterContainerService == null) {
532 log.error("Cluster service unavailable, can't retieve ARPHandler caches!");
536 map = clusterContainerService.getCache(ARP_EVENT_CACHE_NAME);
538 this.arpRequestReplyEvent = (ConcurrentMap<ARPEvent, Boolean>) map;
540 log.error("Cache allocation failed for {}", ARP_EVENT_CACHE_NAME);
544 private void allocateCaches() {
545 if (clusterContainerService == null) {
546 nonClusterObjectCreate();
547 log.error("Clustering service unavailable. Allocated non-cluster caches for ARPHandler.");
552 clusterContainerService.createCache(ARP_EVENT_CACHE_NAME,
553 EnumSet.of(IClusterServices.cacheMode.TRANSACTIONAL));
554 } catch (CacheConfigException e) {
555 log.error("ARPHandler cache configuration invalid!");
556 } catch (CacheExistException e) {
557 log.debug("ARPHandler cache exists, skipped allocation.");
562 private void nonClusterObjectCreate() {
563 arpRequestReplyEvent = new ConcurrentHashMap<ARPEvent, Boolean>();
567 * Function called by the dependency manager when at least one dependency
568 * become unsatisfied or when the component is shutting down because for
569 * example bundle is being stopped.
573 cacheEventHandler.interrupt();
577 * Function called by dependency manager after "init ()" is called and after
578 * the services provided by the class are registered in the service registry
583 startPeriodicTimer();
584 cacheEventHandler.start();
588 * Function called by the dependency manager before the services exported by
589 * the component are unregistered, this will be followed by a "destroy ()"
598 cancelPeriodicTimer();
601 void setSwitchManager(ISwitchManager s) {
602 log.debug("SwitchManager service set.");
603 this.switchManager = s;
606 void unsetSwitchManager(ISwitchManager s) {
607 if (this.switchManager == s) {
608 log.debug("SwitchManager service UNset.");
609 this.switchManager = null;
614 public PacketResult receiveDataPacket(RawPacket inPkt) {
616 return PacketResult.IGNORED;
618 log.trace("Received a frame of size: {}", inPkt.getPacketData().length);
619 Packet formattedPak = this.dataPacketService.decodeDataPacket(inPkt);
620 if (formattedPak instanceof Ethernet) {
621 Object nextPak = formattedPak.getPayload();
622 if (nextPak instanceof IPv4) {
623 log.trace("Handle IP packet: {}", formattedPak);
624 handlePuntedIPPacket((IPv4) nextPak, inPkt.getIncomingNodeConnector());
625 } else if (nextPak instanceof ARP) {
626 log.trace("Handle ARP packet: {}", formattedPak);
627 handleARPPacket((Ethernet) formattedPak, (ARP) nextPak, inPkt.getIncomingNodeConnector());
630 return PacketResult.IGNORED;
633 private ARP createARP(short opCode, byte[] senderMacAddress, byte[] senderIP, byte[] targetMacAddress,
636 arp.setHardwareType(ARP.HW_TYPE_ETHERNET);
637 arp.setProtocolType(EtherTypes.IPv4.shortValue());
638 arp.setHardwareAddressLength((byte) 6);
639 arp.setProtocolAddressLength((byte) 4);
640 arp.setOpCode(opCode);
641 arp.setSenderHardwareAddress(senderMacAddress);
642 arp.setSenderProtocolAddress(senderIP);
643 arp.setTargetHardwareAddress(targetMacAddress);
644 arp.setTargetProtocolAddress(targetIP);
648 private Ethernet createEthernet(byte[] sourceMAC, byte[] targetMAC, ARP arp) {
649 Ethernet ethernet = new Ethernet();
650 ethernet.setSourceMACAddress(sourceMAC);
651 ethernet.setDestinationMACAddress(targetMAC);
652 ethernet.setEtherType(EtherTypes.ARP.shortValue());
653 ethernet.setPayload(arp);
657 private void startPeriodicTimer() {
658 this.periodicTimer = new Timer("ArpHandler Periodic Timer");
659 this.periodicTimer.scheduleAtFixedRate(new TimerTask() {
662 Set<InetAddress> targetIPs = countDownTimers.keySet();
663 Set<InetAddress> expiredTargets = new HashSet<InetAddress>();
664 for (InetAddress t : targetIPs) {
665 short tick = countDownTimers.get(t);
668 expiredTargets.add(t);
670 countDownTimers.replace(t, tick);
673 for (InetAddress tIP : expiredTargets) {
674 countDownTimers.remove(tIP);
675 // Remove the requestor(s) who have been waiting for the ARP
676 // reply from this target for more than 1sec
677 arpRequestors.remove(tIP);
678 log.debug("ARP reply was not received from {}", tIP);
681 // Clean up ARP event cache
683 if (clusterContainerService.amICoordinator() && !arpRequestReplyEvent.isEmpty()) {
684 arpRequestReplyEvent.clear();
686 } catch (Exception e) {
687 log.warn("ARPHandler: A cluster member failed to clear event cache.");
693 private void cancelPeriodicTimer() {
694 if (this.periodicTimer != null) {
695 this.periodicTimer.cancel();
699 private void generateAndSendReply(InetAddress sourceIP, byte[] sourceMAC) {
700 if (log.isTraceEnabled()) {
701 log.trace("generateAndSendReply called with params sourceIP:{} sourceMAC:{}", sourceIP,
702 HexEncode.bytesToHexString(sourceMAC));
704 Set<HostNodeConnector> hosts = arpRequestors.remove(sourceIP);
705 if ((hosts == null) || hosts.isEmpty()) {
706 log.trace("Bailing out no requestors Hosts");
709 countDownTimers.remove(sourceIP);
710 for (HostNodeConnector host : hosts) {
711 if (log.isTraceEnabled()) {
713 "Sending ARP Reply with src {}/{}, target {}/{}",
714 new Object[] { HexEncode.bytesToHexString(sourceMAC), sourceIP,
715 HexEncode.bytesToHexString(host.getDataLayerAddressBytes()), host.getNetworkAddress() });
717 if (connectionManager.getLocalityStatus(host.getnodeconnectorNode()) == ConnectionLocality.LOCAL) {
718 sendARPReply(host.getnodeConnector(), sourceMAC, sourceIP, host.getDataLayerAddressBytes(),
719 host.getNetworkAddress());
722 * In the remote event a requestor moved to another controller
723 * it may turn out it now we need to send the ARP reply from a
724 * different controller, this cover the case
726 arpRequestReplyEvent.put(
727 new ARPReply(host.getnodeConnector(), sourceIP, sourceMAC, host.getNetworkAddress(), host
728 .getDataLayerAddressBytes()), false);
734 public void entryUpdated(ARPEvent key, Boolean new_value, String cacheName, boolean originLocal) {
735 log.trace("Got and entryUpdated for cacheName {} key {} isNew {}", cacheName, key, new_value);
736 enqueueARPCacheEvent(key, new_value);
740 public void entryCreated(ARPEvent key, String cacheName, boolean originLocal) {
745 public void entryDeleted(ARPEvent key, String cacheName, boolean originLocal) {
749 private void enqueueARPCacheEvent(ARPEvent event, boolean new_value) {
751 ARPCacheEvent cacheEvent = new ARPCacheEvent(event, new_value);
752 if (!ARPCacheEvents.contains(cacheEvent)) {
753 this.ARPCacheEvents.add(cacheEvent);
754 log.trace("Enqueued {}", event);
756 } catch (Exception e) {
757 log.debug("enqueueARPCacheEvent caught Interrupt Exception for event {}", event);
762 * this thread monitors the connectionEvent queue for new incoming events
765 private class ARPCacheEventHandler implements Runnable {
770 ARPCacheEvent ev = ARPCacheEvents.take();
771 ARPEvent event = ev.getEvent();
772 if (event instanceof ARPRequest) {
773 ARPRequest req = (ARPRequest) event;
774 // If broadcast request
775 if (req.getHost() == null) {
776 log.trace("Trigger and ARP Broadcast Request upon receipt of {}", req);
777 sendBcastARPRequest(req.getTargetIP(), req.getSubnet());
779 // If unicast and local, send reply
780 } else if (connectionManager.getLocalityStatus(req.getHost().getnodeconnectorNode()) == ConnectionLocality.LOCAL) {
781 log.trace("ARPCacheEventHandler - sendUcatARPRequest upon receipt of {}", req);
782 sendUcastARPRequest(req.getHost(), req.getSubnet());
784 } else if (event instanceof ARPReply) {
785 ARPReply rep = (ARPReply) event;
786 // New reply received by controller, notify all awaiting
787 // requestors across the cluster
788 if (ev.isNewReply()) {
789 log.trace("Trigger a generateAndSendReply in response to {}", rep);
790 generateAndSendReply(rep.getTargetIP(), rep.getTargetMac());
791 // Otherwise, a specific reply. If local, send out.
792 } else if (connectionManager.getLocalityStatus(rep.getPort().getNode()) == ConnectionLocality.LOCAL) {
793 log.trace("ARPCacheEventHandler - sendUcatARPReply locally in response to {}", rep);
794 sendARPReply(rep.getPort(), rep.getSourceMac(), rep.getSourceIP(), rep.getTargetMac(),
798 } catch (InterruptedException e) {
799 ARPCacheEvents.clear();