X-Git-Url: https://git.opendaylight.org/gerrit/gitweb?p=controller.git;a=blobdiff_plain;f=opendaylight%2Farphandler%2Fsrc%2Fmain%2Fjava%2Forg%2Fopendaylight%2Fcontroller%2Farphandler%2Finternal%2FArpHandler.java;h=811c7aca8370525d13bcabd2ef46d898bcc4a88f;hp=26a86c53a4694ff45ca4fbdf2ff7dbc1a2721dcb;hb=5d061e05111778938c9d8e0e05f09a8c99835594;hpb=1757a30c3631e2a5ef97c6b5e79ad2c87fc7d855 diff --git a/opendaylight/arphandler/src/main/java/org/opendaylight/controller/arphandler/internal/ArpHandler.java b/opendaylight/arphandler/src/main/java/org/opendaylight/controller/arphandler/internal/ArpHandler.java index 26a86c53a4..811c7aca83 100644 --- a/opendaylight/arphandler/src/main/java/org/opendaylight/controller/arphandler/internal/ArpHandler.java +++ b/opendaylight/arphandler/src/main/java/org/opendaylight/controller/arphandler/internal/ArpHandler.java @@ -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 hostListener = Collections .synchronizedSet(new HashSet()); + private ConcurrentMap> arpRequestors; + private ConcurrentMap 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 requestorSet = arpRequestors + .get(targetIP); + if ((requestorSet == null) || requestorSet.isEmpty()) { + requestorSet = new HashSet(); + 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>(); + countDownTimers = new ConcurrentHashMap(); } /** @@ -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 targetIPs = countDownTimers.keySet(); + Set expiredTargets = new HashSet(); + 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 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()); + } + } }