HA - Cache synch for Topology Manager
[controller.git] / opendaylight / arphandler / src / main / java / org / opendaylight / controller / arphandler / internal / ArpHandler.java
index 26a86c53a4694ff45ca4fbdf2ff7dbc1a2721dcb..811c7aca8370525d13bcabd2ef46d898bcc4a88f 100644 (file)
@@ -18,9 +18,10 @@ import java.util.Arrays;
 import java.util.Collections;
 import java.util.HashSet;
 import java.util.Set;
-
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
+import java.util.Timer;
+import java.util.TimerTask;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ConcurrentMap;
 
 import org.opendaylight.controller.hosttracker.IfHostListener;
 import org.opendaylight.controller.hosttracker.IfIptoHost;
@@ -43,15 +44,22 @@ import org.opendaylight.controller.sal.utils.HexEncode;
 import org.opendaylight.controller.sal.utils.NetUtils;
 import org.opendaylight.controller.switchmanager.ISwitchManager;
 import org.opendaylight.controller.switchmanager.Subnet;
+import org.opendaylight.controller.topologymanager.ITopologyManager;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
 
 public class ArpHandler implements IHostFinder, IListenDataPacket {
     private static final Logger logger = LoggerFactory
             .getLogger(ArpHandler.class);
     private IfIptoHost hostTracker = null;
     private ISwitchManager switchManager = null;
+    private ITopologyManager topologyManager;
     private IDataPacketService dataPacketService = null;
     private Set<IfHostListener> hostListener = Collections
             .synchronizedSet(new HashSet<IfHostListener>());
+    private ConcurrentMap<InetAddress, Set<HostNodeConnector>> arpRequestors;
+    private ConcurrentMap<InetAddress, Short> countDownTimers;
+    private Timer periodicTimer;
 
     void setHostListener(IfHostListener s) {
         if (this.hostListener != null) {
@@ -91,6 +99,16 @@ public class ArpHandler implements IHostFinder, IListenDataPacket {
         }
     }
 
+    public void setTopologyManager(ITopologyManager tm) {
+        this.topologyManager = tm;
+    }
+
+    public void unsetTopologyManager(ITopologyManager tm) {
+        if (this.topologyManager == tm) {
+            this.topologyManager = null;
+        }
+    }
+
     protected void sendARPReply(NodeConnector p, byte[] sMAC, InetAddress sIP,
             byte[] tMAC, InetAddress tIP) {
         byte[] senderIP = sIP.getAddress();
@@ -182,13 +200,13 @@ public class ArpHandler implements IHostFinder, IListenDataPacket {
             return;
         }
 
+        HostNodeConnector requestor = null;
         if (isUnicastMAC(sourceMAC)) {
             // TODO For not this is only OPENFLOW but we need to fix this
             if (p.getType().equals(
                     NodeConnector.NodeConnectorIDType.OPENFLOW)) {
-                HostNodeConnector host = null;
                 try {
-                    host = new HostNodeConnector(sourceMAC, sourceIP, p, subnet
+                    requestor = new HostNodeConnector(sourceMAC, sourceIP, p, subnet
                             .getVlan());
                 } catch (ConstructionException e) {
                     return;
@@ -197,27 +215,34 @@ public class ArpHandler implements IHostFinder, IListenDataPacket {
                  * Learn host from the received ARP REQ/REPLY, inform
                  * Host Tracker
                  */
-                logger.debug("Inform Host tracker of new host {}", host);
+                logger.debug("Inform Host tracker of new host {}", requestor.getNetworkAddress());
                 synchronized (this.hostListener) {
                     for (IfHostListener listener : this.hostListener) {
-                        listener.hostListener(host);
+                        listener.hostListener(requestor);
                     }
                 }
             }
         }
         /*
-         * No further action is needed if this is a gratuitous ARP
+         * Gratuitous ARP. If there are hosts (in arpRequestors) waiting for the
+         * ARP reply for this sourceIP, it's time to generate the reply and it
+         * to these hosts
          */
         if (sourceIP.equals(targetIP)) {
+            generateAndSendReply(sourceIP, sourceMAC);
             return;
         }
 
         /*
-         * No further action is needed if this is a ARP Reply
+         * ARP Reply. If there are hosts (in arpRequesttors) waiting for the ARP
+         * reply for this sourceIP, it's time to generate the reply and it to
+         * these hosts
          */
         if (pkt.getOpCode() != ARP.REQUEST) {
+            generateAndSendReply(sourceIP, sourceMAC);
             return;
         }
+
         /*
          * ARP Request Handling:
          * If targetIP is the IP of the subnet, reply with ARP REPLY
@@ -241,6 +266,19 @@ public class ArpHandler implements IHostFinder, IListenDataPacket {
          */
         HostNodeConnector host = hostTracker.hostQuery(targetIP);
         if (host == null) {
+            // add the requestor to the list so that we can replay the reply
+            // when the host responds
+            if (requestor != null) {
+                Set<HostNodeConnector> requestorSet = arpRequestors
+                        .get(targetIP);
+                if ((requestorSet == null) || requestorSet.isEmpty()) {
+                    requestorSet = new HashSet<HostNodeConnector>();
+                    countDownTimers.put(targetIP, (short) 2); // set max timeout
+                                                              // to 2sec
+                }
+                requestorSet.add(requestor);
+                arpRequestors.put(targetIP, requestorSet);
+            }
             sendBcastARPRequest(targetIP, subnet);
             return;
         }
@@ -283,6 +321,9 @@ public class ArpHandler implements IHostFinder, IListenDataPacket {
             nodeConnectors = subnet.getNodeConnectors();
         }
         for (NodeConnector p : nodeConnectors) {
+            if (topologyManager.isInternal(p)) {
+                continue;
+            }
             ARP arp = new ARP();
             byte[] senderIP = subnet.getNetworkAddress().getAddress();
             byte[] targetIPB = targetIP.getAddress();
@@ -309,7 +350,7 @@ public class ArpHandler implements IHostFinder, IListenDataPacket {
                     .setEtherType(EtherTypes.ARP.shortValue()).setPayload(arp);
 
             // TODO For now send port-by-port, see how to optimize to
-            // send to a bunch of port on the same node in a shoot
+            // send to multiple ports at once
             RawPacket destPkt = this.dataPacketService.encodeDataPacket(ethernet);
             destPkt.setOutgoingNodeConnector(p);
 
@@ -442,6 +483,8 @@ public class ArpHandler implements IHostFinder, IListenDataPacket {
      *
      */
     void init() {
+        arpRequestors = new ConcurrentHashMap<InetAddress, Set<HostNodeConnector>>();
+        countDownTimers = new ConcurrentHashMap<InetAddress, Short>();
     }
 
     /**
@@ -460,6 +503,7 @@ public class ArpHandler implements IHostFinder, IListenDataPacket {
      *
      */
     void start() {
+        startPeriodicTimer();
     }
 
     /**
@@ -469,6 +513,7 @@ public class ArpHandler implements IHostFinder, IListenDataPacket {
      *
      */
     void stop() {
+        cancelPeriodicTimer();
     }
 
     void setSwitchManager(ISwitchManager s) {
@@ -507,4 +552,54 @@ public class ArpHandler implements IHostFinder, IListenDataPacket {
         }
         return PacketResult.IGNORED;
     }
+
+    private void startPeriodicTimer() {
+        this.periodicTimer = new Timer("ArpHandler Periodic Timer");
+        this.periodicTimer.scheduleAtFixedRate(new TimerTask() {
+            @Override
+            public void run() {
+                Set<InetAddress> targetIPs = countDownTimers.keySet();
+                Set<InetAddress> expiredTargets = new HashSet<InetAddress>();
+                for (InetAddress t : targetIPs) {
+                    short tick = countDownTimers.get(t);
+                    tick--;
+                    if (tick <= 0) {
+                        expiredTargets.add(t);
+                    } else {
+                        countDownTimers.replace(t, tick);
+                    }
+                }
+                for (InetAddress t : expiredTargets) {
+                    countDownTimers.remove(t);
+                    // remove the requestor(s) who have been waited for the ARP
+                    // reply from this target for more than 1sec
+                    arpRequestors.remove(t);
+                    logger.debug("{} didn't respond to ARP request", t);
+                }
+            }
+        }, 0, 1000);
+    }
+
+    private void cancelPeriodicTimer() {
+        if (this.periodicTimer != null) {
+            this.periodicTimer.cancel();
+        }
+    }
+
+    private void generateAndSendReply(InetAddress sourceIP, byte[] sourceMAC) {
+        Set<HostNodeConnector> hosts = arpRequestors.remove(sourceIP);
+        if ((hosts == null) || hosts.isEmpty()) {
+            return;
+        }
+        countDownTimers.remove(sourceIP);
+        for (HostNodeConnector host : hosts) {
+            logger.debug(
+                    "Sending ARP Reply with src {}/{}, target {}/{}",
+                    new Object[] { sourceMAC, sourceIP,
+                            host.getDataLayerAddressBytes(),
+                            host.getNetworkAddress() });
+            sendARPReply(host.getnodeConnector(), sourceMAC, sourceIP,
+                    host.getDataLayerAddressBytes(), host.getNetworkAddress());
+        }
+    }
 }