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