BUG-2218: Keep existing link augmentations during discovery process
[controller.git] / opendaylight / adsal / arphandler / src / main / java / org / opendaylight / controller / arphandler / internal / ArpHandler.java
1 /*
2  * Copyright (c) 2013 Cisco 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
9 /*
10  *
11  */
12 package org.opendaylight.controller.arphandler.internal;
13
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;
20 import java.util.Set;
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;
28
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.IEEE8021Q;
53 import org.opendaylight.controller.sal.packet.IListenDataPacket;
54 import org.opendaylight.controller.sal.packet.IPv4;
55 import org.opendaylight.controller.sal.packet.Packet;
56 import org.opendaylight.controller.sal.packet.PacketResult;
57 import org.opendaylight.controller.sal.packet.RawPacket;
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;
66
67 /**
68  * The ArpHandler offers services to react on ARP requests and replies
69  * sent by network hosts. Moreover it allows for creating ARP messages
70  * by the controller itself.
71  *
72  * The ARP Handler on ODL doesn't use the requester MAC address in
73  * order to avoid to have to build a spanning tree where to forward
74  * ARP Requests. The ARP requests are broadcast packets so in order to
75  * reach everywhere need to be flooded, when you flood in a network
76  * that is not a tree (all the networks has some level of redundancy)
77  * that would create forwarding loops without a spanning tree. Given
78  * the need is only to send out the ARP requests toward all the hosts
79  * we actually don't need to implement a flooding mechanism in software
80  * (which would be expensive) we just send out the ARP request toward
81  * all the ports that are suspected to be host ports on all the
82  * switches (from the controller). Now the condition for which a port
83  * is marked as host port could potentially be incorrect so when the
84  * controller sends out the ARP Request that could come back to the
85  * controller and could cause another request not needed. So changing
86  * the source MAC address of the request to be the one of the controller,
87  * controller can protect itself from honoring twice the same request.
88  * This enables an ARP handler resolution, without the need of spanning
89  * tree and limiting software flooding to the minimum required.
90  */
91
92 public class ArpHandler implements IHostFinder, IListenDataPacket, ICacheUpdateAware<ARPEvent, Boolean> {
93     private static final Logger log = LoggerFactory.getLogger(ArpHandler.class);
94     static final String ARP_EVENT_CACHE_NAME = "arphandler.arpRequestReplyEvent";
95     private IfIptoHost hostTracker;
96     private ISwitchManager switchManager;
97     private ITopologyManager topologyManager;
98     private IDataPacketService dataPacketService;
99     private IClusterContainerServices clusterContainerService;
100     private IConnectionManager connectionManager;
101     private Set<IfHostListener> hostListeners = new CopyOnWriteArraySet<IfHostListener>();
102     private ConcurrentMap<InetAddress, Set<HostNodeConnector>> arpRequestors;
103     private ConcurrentMap<InetAddress, Short> countDownTimers;
104     private Timer periodicTimer;
105     private BlockingQueue<ARPCacheEvent> ARPCacheEvents = new LinkedBlockingQueue<ARPCacheEvent>();
106     private Thread cacheEventHandler;
107     private boolean stopping = false;
108
109     /*
110      * A cluster allocated cache. Used for synchronizing ARP request/reply
111      * events across all cluster controllers. To raise an event, we put() a
112      * specific event object (as key) and all nodes handle it in the
113      * entryUpdated callback.
114      *
115      * In case of ARPReply, we put true value to send replies to any requestors
116      * by calling generateAndSendReply
117      */
118     private ConcurrentMap<ARPEvent, Boolean> arpRequestReplyEvent;
119
120     void setConnectionManager(IConnectionManager cm) {
121         this.connectionManager = cm;
122     }
123
124     void unsetConnectionManager(IConnectionManager cm) {
125         if (this.connectionManager == cm) {
126             connectionManager = null;
127         }
128     }
129
130     void setClusterContainerService(IClusterContainerServices s) {
131         this.clusterContainerService = s;
132     }
133
134     void unsetClusterContainerService(IClusterContainerServices s) {
135         if (this.clusterContainerService == s) {
136             this.clusterContainerService = null;
137         }
138     }
139
140     void setHostListener(IfHostListener s) {
141         if (this.hostListeners != null) {
142             this.hostListeners.add(s);
143         }
144     }
145
146     void unsetHostListener(IfHostListener s) {
147         if (this.hostListeners != null) {
148             this.hostListeners.remove(s);
149         }
150     }
151
152     void setDataPacketService(IDataPacketService s) {
153         this.dataPacketService = s;
154     }
155
156     void unsetDataPacketService(IDataPacketService s) {
157         if (this.dataPacketService == s) {
158             this.dataPacketService = null;
159         }
160     }
161
162     public void setHostTracker(IfIptoHost hostTracker) {
163         log.debug("Setting HostTracker");
164         this.hostTracker = hostTracker;
165     }
166
167     public void unsetHostTracker(IfIptoHost s) {
168         log.debug("UNSetting HostTracker");
169         if (this.hostTracker == s) {
170             this.hostTracker = null;
171         }
172     }
173
174     public void setTopologyManager(ITopologyManager tm) {
175         this.topologyManager = tm;
176     }
177
178     public void unsetTopologyManager(ITopologyManager tm) {
179         if (this.topologyManager == tm) {
180             this.topologyManager = null;
181         }
182     }
183
184     protected void sendARPReply(NodeConnector p, byte[] sMAC, InetAddress sIP, byte[] tMAC, InetAddress tIP, short vlan) {
185         byte[] senderIP = sIP.getAddress();
186         byte[] targetIP = tIP.getAddress();
187         ARP arp = createARP(ARP.REPLY, sMAC, senderIP, tMAC, targetIP);
188
189         if(log.isTraceEnabled()) {
190             log.trace("Sending Arp Reply with srcMac {} - srcIp {} - dstMac {} - dstIp {} - outport {}",
191                     HexEncode.bytesToHexString(sMAC),
192                     sIP, HexEncode.bytesToHexString(tMAC), tIP, p);
193         }
194
195         Ethernet ethernet = createEthernet(sMAC, tMAC, arp, vlan);
196
197         RawPacket destPkt = this.dataPacketService.encodeDataPacket(ethernet);
198         destPkt.setOutgoingNodeConnector(p);
199
200         this.dataPacketService.transmitDataPacket(destPkt);
201     }
202
203     private void logArpPacket(ARP pkt, NodeConnector p, short vlan) {
204         try {
205             log.trace("Received Arp {} with srcMac {} - srcIp {} - dstMac {} - dstIp {} - inport {} {}",
206                     ((pkt.getOpCode() == ARP.REQUEST) ? "Request" : "Reply"),
207                     HexEncode.bytesToHexString(pkt.getSenderHardwareAddress()),
208                     InetAddress.getByAddress(pkt.getSenderProtocolAddress()),
209                     HexEncode.bytesToHexString(pkt.getTargetHardwareAddress()),
210                     InetAddress.getByAddress(pkt.getTargetProtocolAddress()), p, (vlan != 0 ? "on vlan " + vlan : ""));
211
212         } catch (UnknownHostException e) {
213             log.warn("Illegal Ip Address in the ARP packet", e);
214         }
215     }
216
217     protected void handleARPPacket(Ethernet eHeader, ARP pkt, NodeConnector p, short vlan) {
218
219         if(log.isTraceEnabled()) {
220             logArpPacket(pkt, p, vlan);
221         }
222
223         byte[] sourceMAC = eHeader.getSourceMACAddress();
224         byte[] targetMAC = eHeader.getDestinationMACAddress();
225         /*
226          * Sanity Check; drop ARP packets originated by the controller itself.
227          * This is to avoid continuous flooding
228          */
229         if (Arrays.equals(sourceMAC, getControllerMAC())) {
230             if (log.isDebugEnabled()) {
231                 log.debug("Receive a self originated ARP pkt (srcMAC {}) --> DROP",
232                         HexEncode.bytesToHexString(sourceMAC));
233             }
234             return;
235         }
236
237         InetAddress targetIP, sourceIP;
238         try {
239             targetIP = InetAddress.getByAddress(pkt.getTargetProtocolAddress());
240             sourceIP = InetAddress.getByAddress(pkt.getSenderProtocolAddress());
241         } catch (UnknownHostException e1) {
242             log.debug("Invalid host in ARP packet: {}", e1.getMessage());
243             return;
244         }
245
246         Subnet subnet = null;
247         if (switchManager != null) {
248             subnet = switchManager.getSubnetByNetworkAddress(sourceIP);
249         }
250         if (subnet == null) {
251             log.debug("ARPHandler: can't find subnet matching {}, drop packet", sourceIP);
252             return;
253         }
254
255         // Make sure that the host is a legitimate member of this subnet
256         if (!subnet.hasNodeConnector(p)) {
257             log.debug("{} showing up on {} does not belong to {}", new Object[] { sourceIP, p, subnet });
258             return;
259         }
260
261         HostNodeConnector requestor = null;
262         if (NetUtils.isUnicastMACAddr(sourceMAC) && p.getNode() != null) {
263             try {
264                 requestor = new HostNodeConnector(sourceMAC, sourceIP, p, vlan);
265             } catch (ConstructionException e) {
266                 log.debug("Received ARP packet with invalid MAC: {}", HexEncode.bytesToHexString(sourceMAC));
267                 return;
268             }
269             /*
270              * Learn host from the received ARP REQ/REPLY, inform Host Tracker
271              */
272             log.trace("Inform Host tracker of new host {}", requestor.getNetworkAddress());
273             for (IfHostListener listener : this.hostListeners) {
274                 listener.hostListener(requestor);
275             }
276         }
277
278         /*
279          * OpCode != request -> ARP Reply. If there are hosts (in arpRequestors)
280          * waiting for the ARP reply for this sourceIP, it's time to generate
281          * the reply and send it to these hosts.
282          *
283          * If sourceIP==targetIP, it is a Gratuitous ARP. If there are hosts (in
284          * arpRequestors) waiting for the ARP reply for this sourceIP, it's time
285          * to generate the reply and send it to these hosts
286          */
287
288         if (pkt.getOpCode() != ARP.REQUEST || sourceIP.equals(targetIP)) {
289             // Raise a reply event so that any waiting requestors will be sent a
290             // reply
291             // the true value indicates we should generate replies to requestors
292             // across the cluster
293             log.trace("Received ARP reply packet from {}, reply to all requestors.", sourceIP);
294             arpRequestReplyEvent.put(new ARPReply(sourceIP, sourceMAC, vlan), true);
295             return;
296         }
297
298         /*
299          * ARP Request Handling: If targetIP is the IP of the subnet, reply with
300          * ARP REPLY If targetIP is a known host, PROXY ARP (by sending ARP
301          * REPLY) on behalf of known target hosts. For unknown target hosts,
302          * generate and send an ARP request to ALL switches/ports using the IP
303          * address defined in the subnet as source address
304          */
305         /*
306          * If target IP is gateway IP, Send ARP reply
307          */
308         if ((targetIP.equals(subnet.getNetworkAddress()))
309                 && (NetUtils.isBroadcastMACAddr(targetMAC) || Arrays.equals(targetMAC, getControllerMAC()))) {
310             if (connectionManager.getLocalityStatus(p.getNode()) == ConnectionLocality.LOCAL) {
311                 if (log.isTraceEnabled()) {
312                     log.trace("Received local ARP req. for default gateway. Replying with controller MAC: {}",
313                             HexEncode.bytesToHexString(getControllerMAC()));
314                 }
315                 sendARPReply(p, getControllerMAC(), targetIP, pkt.getSenderHardwareAddress(), sourceIP, vlan);
316             } else {
317                 log.trace("Received non-local ARP req. for default gateway. Raising reply event");
318                 arpRequestReplyEvent.put(
319                         new ARPReply(p, targetIP, getControllerMAC(), sourceIP, pkt.getSenderHardwareAddress(), vlan), false);
320             }
321             return;
322         }
323
324         // Hosttracker hosts db key implementation
325         IHostId id = HostIdFactory.create(targetIP, null);
326         HostNodeConnector host = hostTracker.hostQuery(id);
327         // unknown host, initiate ARP request
328         if (host == null) {
329             // add the requestor to the list so that we can replay the reply
330             // when the host responds
331             if (requestor != null) {
332                 Set<HostNodeConnector> requestorSet = arpRequestors.get(targetIP);
333                 if (requestorSet == null) {
334                     requestorSet = Collections.newSetFromMap(new ConcurrentHashMap<HostNodeConnector, Boolean>());
335                     arpRequestors.put(targetIP, requestorSet);
336                 }
337                 requestorSet.add(requestor);
338                 countDownTimers.put(targetIP, (short) 2); // reset timeout to
339                                                           // 2sec
340             }
341             // Raise a bcast request event, all controllers need to send one
342             log.trace("Sending a bcast ARP request for {}", targetIP);
343             arpRequestReplyEvent.put(new ARPRequest(targetIP, subnet), false);
344         } else {
345             /*
346              * Target host known (across the cluster), send ARP REPLY make sure
347              * that targetMAC matches the host's MAC if it is not broadcastMAC
348              */
349             if (NetUtils.isBroadcastMACAddr(targetMAC) || Arrays.equals(host.getDataLayerAddressBytes(), targetMAC)) {
350                 log.trace("Received ARP req. for known host {}, sending reply...", targetIP);
351                 if (connectionManager.getLocalityStatus(p.getNode()) == ConnectionLocality.LOCAL) {
352                     sendARPReply(p, host.getDataLayerAddressBytes(), host.getNetworkAddress(),
353                             pkt.getSenderHardwareAddress(), sourceIP, vlan);
354                 } else {
355                     arpRequestReplyEvent.put(new ARPReply(p, host.getNetworkAddress(), host.getDataLayerAddressBytes(),
356                             sourceIP, pkt.getSenderHardwareAddress(), vlan), false);
357                 }
358             } else {
359                 /*
360                  * Target MAC has been changed. For now, discard it. TODO: We
361                  * may need to send unicast ARP REQUEST on behalf of the target
362                  * back to the sender to trigger the sender to update its table
363                  */
364             }
365         }
366     }
367
368     /**
369      * Send a broadcast ARP Request to the switch/ ports using the
370      * networkAddress of the subnet as sender IP the controller's MAC as sender
371      * MAC the targetIP as the target Network Address
372      */
373     protected void sendBcastARPRequest(InetAddress targetIP, Subnet subnet) {
374         log.trace("sendBcatARPRequest targetIP:{} subnet:{}", targetIP, subnet);
375         Set<NodeConnector> nodeConnectors;
376         if (subnet.isFlatLayer2()) {
377             nodeConnectors = new HashSet<NodeConnector>();
378             for (Node n : this.switchManager.getNodes()) {
379                 nodeConnectors.addAll(this.switchManager.getUpNodeConnectors(n));
380             }
381         } else {
382             nodeConnectors = subnet.getNodeConnectors();
383         }
384         byte[] targetHardwareAddress = new byte[] { (byte) 0, (byte) 0, (byte) 0, (byte) 0, (byte) 0, (byte) 0 };
385
386         // TODO: should use IBroadcastHandler instead
387         for (NodeConnector p : nodeConnectors) {
388             // filter out any non-local or internal ports
389             if (!(connectionManager.getLocalityStatus(p.getNode()) == ConnectionLocality.LOCAL)
390                     || topologyManager.isInternal(p)) {
391                 continue;
392             }
393             log.trace("Sending toward nodeConnector:{}", p);
394             byte[] senderIP = subnet.getNetworkAddress().getAddress();
395             byte[] targetIPByte = targetIP.getAddress();
396             ARP arp = createARP(ARP.REQUEST, getControllerMAC(), senderIP, targetHardwareAddress, targetIPByte);
397
398             if(log.isTraceEnabled()) {
399                 log.trace("Sending Broadcast Arp Request with srcMac {} - srcIp {} - dstMac {} - dstIp {} - outport {}", HexEncode.bytesToHexString(getControllerMAC()),
400                         subnet.getNetworkAddress(), HexEncode.bytesToHexString(targetHardwareAddress), targetIP, p);
401             }
402
403             byte[] destMACAddress = NetUtils.getBroadcastMACAddr();
404             Ethernet ethernet = createEthernet(getControllerMAC(), destMACAddress, arp, (short)0);
405
406             // TODO For now send port-by-port, see how to optimize to
407             // send to multiple ports at once
408             RawPacket destPkt = this.dataPacketService.encodeDataPacket(ethernet);
409             destPkt.setOutgoingNodeConnector(p);
410
411             this.dataPacketService.transmitDataPacket(destPkt);
412         }
413     }
414
415     /**
416      * Send a unicast ARP Request to the known host on specific (switch/port,
417      * vlan) as defined in the host. The sender IP is the networkAddress of the
418      * subnet The sender MAC is the controller's MAC
419      */
420     protected void sendUcastARPRequest(HostNodeConnector host, Subnet subnet) {
421         log.trace("sendUcastARPRequest host:{} subnet:{}", host, subnet);
422         NodeConnector outPort = host.getnodeConnector();
423         if (outPort == null) {
424             log.error("Failed sending UcastARP because cannot extract output port from Host: {}", host);
425             return;
426         }
427
428         byte[] senderIP = subnet.getNetworkAddress().getAddress();
429         byte[] targetIP = host.getNetworkAddress().getAddress();
430         byte[] targetMAC = host.getDataLayerAddressBytes();
431         ARP arp = createARP(ARP.REQUEST, getControllerMAC(), senderIP, targetMAC, targetIP);
432
433         if(log.isTraceEnabled()) {
434             log.trace("Sending Unicast Arp Request with srcMac {} - srcIp {} - dstMac {} - dstIp {} - outport {}",
435                     HexEncode.bytesToHexString(getControllerMAC()),
436                     subnet.getNetworkAddress(), HexEncode.bytesToHexString(targetMAC), host.getNetworkAddress(),
437                     outPort);
438         }
439
440         Ethernet ethernet = createEthernet(getControllerMAC(), targetMAC, arp, host.getVlan());
441
442         RawPacket destPkt = this.dataPacketService.encodeDataPacket(ethernet);
443         destPkt.setOutgoingNodeConnector(outPort);
444
445         this.dataPacketService.transmitDataPacket(destPkt);
446     }
447
448     @Override
449     public void find(InetAddress networkAddress) {
450         log.trace("Received find IP {}", networkAddress);
451
452         Subnet subnet = null;
453         if (switchManager != null) {
454             subnet = switchManager.getSubnetByNetworkAddress(networkAddress);
455         }
456         if (subnet == null) {
457             log.debug("Can't find subnet matching IP {}", networkAddress);
458             return;
459         }
460
461         // send a broadcast ARP Request to this IP
462         arpRequestReplyEvent.put(new ARPRequest(networkAddress, subnet), false);
463     }
464
465     /*
466      * Probe the host by sending a unicast ARP Request to the host
467      */
468     @Override
469     public void probe(HostNodeConnector host) {
470         log.trace("Received probe host {}", host);
471
472         Subnet subnet = null;
473         if (switchManager != null) {
474             subnet = switchManager.getSubnetByNetworkAddress(host.getNetworkAddress());
475         }
476         if (subnet == null) {
477             log.debug("can't find subnet matching {}", host.getNetworkAddress());
478             return;
479         }
480
481         if (connectionManager.getLocalityStatus(host.getnodeconnectorNode()) == ConnectionLocality.LOCAL) {
482             log.trace("Send a ucast ARP req. to: {}", host);
483             sendUcastARPRequest(host, subnet);
484         } else {
485             log.trace("Raise a ucast ARP req. event to: {}", host);
486             arpRequestReplyEvent.put(new ARPRequest(host, subnet), false);
487         }
488     }
489
490     /**
491      * An IP packet is punted to the controller, this means that the destination
492      * host is not known to the controller. Need to discover it by sending a
493      * Broadcast ARP Request
494      *
495      * @param pkt
496      * @param p
497      */
498     protected void handlePuntedIPPacket(IPv4 pkt, NodeConnector p, short vlan) {
499
500         InetAddress dIP = NetUtils.getInetAddress(pkt.getDestinationAddress());
501         if (dIP == null) {
502             return;
503         }
504
505         // try to find a matching subnet
506         Subnet subnet = null;
507         if (switchManager != null) {
508             subnet = switchManager.getSubnetByNetworkAddress(dIP);
509         }
510         if (subnet == null) {
511             log.debug("Can't find subnet matching {}, drop packet", dIP);
512             return;
513         }
514         // If packet is sent to the default gw (us), ignore it for now
515         if (subnet.getNetworkAddress().equals(dIP)) {
516             log.trace("Ignore IP packet destined to default gw");
517             return;
518         }
519
520         // see if we know about the host
521         // Hosttracker hosts db key implementation
522         HostNodeConnector host = hostTracker.hostFind(dIP);
523
524         if (host == null) {
525             // if we don't know about the host, try to find it
526             log.trace("Punted IP pkt to {}, sending bcast ARP event...", dIP);
527             /*
528              * unknown destination host, initiate bcast ARP request
529              */
530             arpRequestReplyEvent.put(new ARPRequest(dIP, subnet), false);
531
532         } else {
533             log.trace("Ignoring punted IP pkt to known host: {} (received on: {})", dIP, p);
534         }
535     }
536
537     public byte[] getControllerMAC() {
538         if (switchManager == null) {
539             return null;
540         }
541         return switchManager.getControllerMAC();
542     }
543
544     /**
545      * Function called by the dependency manager when all the required
546      * dependencies are satisfied
547      *
548      */
549     void init() {
550         arpRequestors = new ConcurrentHashMap<InetAddress, Set<HostNodeConnector>>();
551         countDownTimers = new ConcurrentHashMap<InetAddress, Short>();
552         cacheEventHandler = new Thread(new ARPCacheEventHandler(), "ARPCacheEventHandler Thread");
553
554         allocateCaches();
555         retrieveCaches();
556
557     }
558
559     @SuppressWarnings({ "unchecked" })
560     private void retrieveCaches() {
561         ConcurrentMap<?, ?> map;
562
563         if (this.clusterContainerService == null) {
564             log.error("Cluster service unavailable, can't retieve ARPHandler caches!");
565             return;
566         }
567
568         map = clusterContainerService.getCache(ARP_EVENT_CACHE_NAME);
569         if (map != null) {
570             this.arpRequestReplyEvent = (ConcurrentMap<ARPEvent, Boolean>) map;
571         } else {
572             log.error("Cache allocation failed for {}", ARP_EVENT_CACHE_NAME);
573         }
574     }
575
576     private void allocateCaches() {
577         if (clusterContainerService == null) {
578             nonClusterObjectCreate();
579             log.error("Clustering service unavailable. Allocated non-cluster caches for ARPHandler.");
580             return;
581         }
582
583         try {
584             clusterContainerService.createCache(ARP_EVENT_CACHE_NAME,
585                     EnumSet.of(IClusterServices.cacheMode.TRANSACTIONAL));
586         } catch (CacheConfigException e) {
587             log.error("ARPHandler cache configuration invalid!");
588         } catch (CacheExistException e) {
589             log.debug("ARPHandler cache exists, skipped allocation.");
590         }
591
592     }
593
594     private void nonClusterObjectCreate() {
595         arpRequestReplyEvent = new ConcurrentHashMap<ARPEvent, Boolean>();
596     }
597
598     /**
599      * Function called by the dependency manager when at least one dependency
600      * become unsatisfied or when the component is shutting down because for
601      * example bundle is being stopped.
602      *
603      */
604     void destroy() {
605         cacheEventHandler.interrupt();
606     }
607
608     /**
609      * Function called by dependency manager after "init ()" is called and after
610      * the services provided by the class are registered in the service registry
611      *
612      */
613     void start() {
614         stopping = false;
615         startPeriodicTimer();
616         cacheEventHandler.start();
617     }
618
619     /**
620      * Function called by the dependency manager before the services exported by
621      * the component are unregistered, this will be followed by a "destroy ()"
622      * calls
623      *
624      */
625     void stop() {
626     }
627
628     void stopping() {
629         stopping = true;
630         cancelPeriodicTimer();
631     }
632
633     void setSwitchManager(ISwitchManager s) {
634         log.debug("SwitchManager service set.");
635         this.switchManager = s;
636     }
637
638     void unsetSwitchManager(ISwitchManager s) {
639         if (this.switchManager == s) {
640             log.debug("SwitchManager service UNset.");
641             this.switchManager = null;
642         }
643     }
644
645     @Override
646     public PacketResult receiveDataPacket(RawPacket inPkt) {
647         if (inPkt == null) {
648             return PacketResult.IGNORED;
649         }
650         log.trace("Received a frame of size: {}", inPkt.getPacketData().length);
651         Packet formattedPak = this.dataPacketService.decodeDataPacket(inPkt);
652         if (formattedPak instanceof Ethernet) {
653             Packet nextPak = formattedPak.getPayload();
654             short vlan = 0;
655             if (nextPak instanceof IEEE8021Q) {
656                 vlan = ((IEEE8021Q) nextPak).getVid();
657                 log.trace("Moved after the dot1Q header");
658                 nextPak = ((IEEE8021Q) nextPak).getPayload();
659             }
660             if (nextPak instanceof IPv4) {
661                 log.trace("Handle IP packet: {}", formattedPak);
662                 handlePuntedIPPacket((IPv4) nextPak, inPkt.getIncomingNodeConnector(), vlan);
663             } else if (nextPak instanceof ARP) {
664                 log.trace("Handle ARP packet: {}", formattedPak);
665                 handleARPPacket((Ethernet) formattedPak, (ARP) nextPak, inPkt.getIncomingNodeConnector(), vlan);
666             }
667         }
668         return PacketResult.IGNORED;
669     }
670
671     private ARP createARP(short opCode, byte[] senderMacAddress, byte[] senderIP, byte[] targetMacAddress,
672             byte[] targetIP) {
673         ARP arp = new ARP();
674         arp.setHardwareType(ARP.HW_TYPE_ETHERNET);
675         arp.setProtocolType(EtherTypes.IPv4.shortValue());
676         arp.setHardwareAddressLength((byte) 6);
677         arp.setProtocolAddressLength((byte) 4);
678         arp.setOpCode(opCode);
679         arp.setSenderHardwareAddress(senderMacAddress);
680         arp.setSenderProtocolAddress(senderIP);
681         arp.setTargetHardwareAddress(targetMacAddress);
682         arp.setTargetProtocolAddress(targetIP);
683         return arp;
684     }
685
686     private Ethernet createEthernet(byte[] sourceMAC, byte[] targetMAC, ARP arp, short vlan) {
687         Ethernet ethernet = new Ethernet();
688         ethernet.setSourceMACAddress(sourceMAC);
689         ethernet.setDestinationMACAddress(targetMAC);
690         if (vlan == 0) {
691             ethernet.setEtherType(EtherTypes.ARP.shortValue());
692             ethernet.setPayload(arp);
693         } else {
694             IEEE8021Q dot1q = new IEEE8021Q();
695             dot1q.setVid(vlan);
696             dot1q.setEtherType(EtherTypes.ARP.shortValue());
697             dot1q.setPayload(arp);
698             dot1q.setCfi((byte)0);
699             dot1q.setPcp((byte)0);
700             ethernet.setEtherType(EtherTypes.VLANTAGGED.shortValue());
701             ethernet.setPayload(dot1q);
702         }
703         return ethernet;
704     }
705
706     private void startPeriodicTimer() {
707         this.periodicTimer = new Timer("ArpHandler Periodic Timer");
708         this.periodicTimer.scheduleAtFixedRate(new TimerTask() {
709             @Override
710             public void run() {
711                 Set<InetAddress> targetIPs = countDownTimers.keySet();
712                 Set<InetAddress> expiredTargets = new HashSet<InetAddress>();
713                 for (InetAddress t : targetIPs) {
714                     short tick = countDownTimers.get(t);
715                     tick--;
716                     if (tick <= 0) {
717                         expiredTargets.add(t);
718                     } else {
719                         countDownTimers.replace(t, tick);
720                     }
721                 }
722                 for (InetAddress tIP : expiredTargets) {
723                     countDownTimers.remove(tIP);
724                     // Remove the requestor(s) who have been waiting for the ARP
725                     // reply from this target for more than 1sec
726                     arpRequestors.remove(tIP);
727                     log.debug("ARP reply was not received from {}", tIP);
728                 }
729
730                 // Clean up ARP event cache
731                 try {
732                     if (clusterContainerService.amICoordinator() && !arpRequestReplyEvent.isEmpty()) {
733                         arpRequestReplyEvent.clear();
734                     }
735                 } catch (Exception e) {
736                     log.warn("ARPHandler: A cluster member failed to clear event cache.");
737                 }
738             }
739         }, 0, 1000);
740     }
741
742     private void cancelPeriodicTimer() {
743         if (this.periodicTimer != null) {
744             this.periodicTimer.cancel();
745         }
746     }
747
748     private void generateAndSendReply(InetAddress sourceIP, byte[] sourceMAC, short vlan) {
749         if (log.isTraceEnabled()) {
750             log.trace("generateAndSendReply called with params sourceIP:{} sourceMAC:{}", sourceIP,
751                     HexEncode.bytesToHexString(sourceMAC));
752         }
753         Set<HostNodeConnector> hosts = arpRequestors.remove(sourceIP);
754         if ((hosts == null) || hosts.isEmpty()) {
755             log.trace("Bailing out no requestors Hosts");
756             return;
757         }
758         countDownTimers.remove(sourceIP);
759         for (HostNodeConnector host : hosts) {
760             if (log.isTraceEnabled()) {
761                 log.trace(
762                         "Sending ARP Reply with src {}/{}, target {}/{} {}",
763                         new Object[] { HexEncode.bytesToHexString(sourceMAC), sourceIP,
764                                 HexEncode.bytesToHexString(host.getDataLayerAddressBytes()), host.getNetworkAddress(),
765                                 (vlan != 0 ? "on vlan " + vlan : "") });
766             }
767             if (connectionManager.getLocalityStatus(host.getnodeconnectorNode()) == ConnectionLocality.LOCAL) {
768                 sendARPReply(host.getnodeConnector(), sourceMAC, sourceIP, host.getDataLayerAddressBytes(),
769                         host.getNetworkAddress(), vlan);
770             } else {
771                 /*
772                  * In the remote event a requestor moved to another controller
773                  * it may turn out it now we need to send the ARP reply from a
774                  * different controller, this cover the case
775                  */
776                 arpRequestReplyEvent.put(
777                         new ARPReply(host.getnodeConnector(), sourceIP, sourceMAC, host.getNetworkAddress(), host
778                                 .getDataLayerAddressBytes(), vlan), false);
779             }
780         }
781     }
782
783     @Override
784     public void entryUpdated(ARPEvent key, Boolean new_value, String cacheName, boolean originLocal) {
785         log.trace("Got and entryUpdated for cacheName {} key {} isNew {}", cacheName, key, new_value);
786         enqueueARPCacheEvent(key, new_value);
787     }
788
789     @Override
790     public void entryCreated(ARPEvent key, String cacheName, boolean originLocal) {
791         // nothing to do
792     }
793
794     @Override
795     public void entryDeleted(ARPEvent key, String cacheName, boolean originLocal) {
796         // nothing to do
797     }
798
799     private void enqueueARPCacheEvent(ARPEvent event, boolean new_value) {
800         try {
801             ARPCacheEvent cacheEvent = new ARPCacheEvent(event, new_value);
802             if (!ARPCacheEvents.contains(cacheEvent)) {
803                 this.ARPCacheEvents.add(cacheEvent);
804                 log.trace("Enqueued {}", event);
805             }
806         } catch (Exception e) {
807             log.debug("enqueueARPCacheEvent caught Interrupt Exception for event {}", event);
808         }
809     }
810
811     /*
812      * this thread monitors the connectionEvent queue for new incoming events
813      * from
814      */
815     private class ARPCacheEventHandler implements Runnable {
816         @Override
817         public void run() {
818             while (!stopping) {
819                 try {
820                     ARPCacheEvent ev = ARPCacheEvents.take();
821                     ARPEvent event = ev.getEvent();
822                     if (event instanceof ARPRequest) {
823                         ARPRequest req = (ARPRequest) event;
824                         // If broadcast request
825                         if (req.getHost() == null) {
826                             log.trace("Trigger and ARP Broadcast Request upon receipt of {}", req);
827                             sendBcastARPRequest(req.getTargetIP(), req.getSubnet());
828
829                             // If unicast and local, send reply
830                         } else if (connectionManager.getLocalityStatus(req.getHost().getnodeconnectorNode()) == ConnectionLocality.LOCAL) {
831                             log.trace("ARPCacheEventHandler - sendUcatARPRequest upon receipt of {}", req);
832                             sendUcastARPRequest(req.getHost(), req.getSubnet());
833                         }
834                     } else if (event instanceof ARPReply) {
835                         ARPReply rep = (ARPReply) event;
836                         // New reply received by controller, notify all awaiting
837                         // requestors across the cluster
838                         if (ev.isNewReply()) {
839                             log.trace("Trigger a generateAndSendReply in response to {}", rep);
840                             generateAndSendReply(rep.getTargetIP(), rep.getTargetMac(), rep.getVlan());
841                             // Otherwise, a specific reply. If local, send out.
842                         } else if (connectionManager.getLocalityStatus(rep.getPort().getNode()) == ConnectionLocality.LOCAL) {
843                             log.trace("ARPCacheEventHandler - sendUcatARPReply locally in response to {}", rep);
844                             sendARPReply(rep.getPort(), rep.getSourceMac(), rep.getSourceIP(), rep.getTargetMac(),
845                                     rep.getTargetIP(), rep.getVlan());
846                         }
847                     }
848                 } catch (InterruptedException e) {
849                     ARPCacheEvents.clear();
850                     return;
851                 }
852             }
853         }
854     }
855 }