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