import java.util.Collections;
import java.util.HashSet;
import java.util.Set;
+import java.util.Timer;
+import java.util.TimerTask;
+import java.util.concurrent.ConcurrentHashMap;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
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;
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 ConcurrentHashMap<InetAddress, Set<HostNodeConnector>> arpRequestors;
+ private ConcurrentHashMap<InetAddress, Short> countDownTimers;
+ private Timer periodicTimer;
void setHostListener(IfHostListener s) {
if (this.hostListener != null) {
this.hostTracker = null;
}
}
+
+ 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) {
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;
* 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
*/
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;
}
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();
.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);
*
*/
void init() {
+ arpRequestors = new ConcurrentHashMap<InetAddress, Set<HostNodeConnector>>();
+ countDownTimers = new ConcurrentHashMap<InetAddress, Short>();
}
/**
*
*/
void start() {
+ startPeriodicTimer();
}
/**
*
*/
void stop() {
+ cancelPeriodicTimer();
}
void setSwitchManager(ISwitchManager s) {
}
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());
+ }
+ }
}