Implemented a mechanism to remember ARP requests and answer them when the response is received
diff --git a/src/main/java/net/onrc/onos/ofcontroller/bgproute/BgpRoute.java b/src/main/java/net/onrc/onos/ofcontroller/bgproute/BgpRoute.java
index 2034118..cb361af 100644
--- a/src/main/java/net/onrc/onos/ofcontroller/bgproute/BgpRoute.java
+++ b/src/main/java/net/onrc/onos/ofcontroller/bgproute/BgpRoute.java
@@ -120,7 +120,7 @@
List<Link> activeLinks = topoLinkService.getActiveLinks();
for (Link l : activeLinks){
- log.debug("active link: {}", l);
+ //log.debug("active link: {}", l);
}
Iterator<LDUpdate> it = linkUpdates.iterator();
@@ -224,7 +224,7 @@
//TODO We'll initialise this here for now, but it should really be done as
//part of the controller core
- proxyArp = new ProxyArpManager(floodlightProvider, topology);
+ proxyArp = new ProxyArpManager(floodlightProvider, topology, devices);
linkUpdates = new ArrayList<LDUpdate>();
ScheduledExecutorService executor = Executors.newScheduledThreadPool(1);
@@ -435,7 +435,7 @@
node.rib.routerId.getHostAddress()});
//TODO this is wrong, we shouldn't be dealing with BGP peers here.
- //We need to figure out where the device is attached and what it's
+ //We need to figure out where the device is attached and what its
//mac address is by learning.
//The next hop is not necessarily the peer, and the peer's attachment
//point is not necessarily the next hop's attachment point.
diff --git a/src/main/java/net/onrc/onos/ofcontroller/proxyarp/HostArpRequester.java b/src/main/java/net/onrc/onos/ofcontroller/proxyarp/HostArpRequester.java
new file mode 100644
index 0000000..20c6a28
--- /dev/null
+++ b/src/main/java/net/onrc/onos/ofcontroller/proxyarp/HostArpRequester.java
@@ -0,0 +1,28 @@
+package net.onrc.onos.ofcontroller.proxyarp;
+
+import net.floodlightcontroller.packet.ARP;
+
+public class HostArpRequester implements IArpRequester {
+
+ private IProxyArpService arpService;
+ private ARP arpRequest;
+ private long dpid;
+ private short port;
+ //private long requestTime; //in ms
+
+ public HostArpRequester(IProxyArpService arpService, ARP arpRequest,
+ long dpid, short port) {
+
+ this.arpService = arpService;
+ this.arpRequest = arpRequest;
+ this.dpid = dpid;
+ this.port = port;
+ //this.requestTime = System.currentTimeMillis();
+ }
+
+ @Override
+ public void arpResponse(byte[] mac) {
+ arpService.sendArpReply(arpRequest, dpid, port, mac);
+ }
+
+}
diff --git a/src/main/java/net/onrc/onos/ofcontroller/proxyarp/IArpRequester.java b/src/main/java/net/onrc/onos/ofcontroller/proxyarp/IArpRequester.java
new file mode 100644
index 0000000..2a74944
--- /dev/null
+++ b/src/main/java/net/onrc/onos/ofcontroller/proxyarp/IArpRequester.java
@@ -0,0 +1,5 @@
+package net.onrc.onos.ofcontroller.proxyarp;
+
+public interface IArpRequester {
+ public void arpResponse(byte[] mac);
+}
diff --git a/src/main/java/net/onrc/onos/ofcontroller/proxyarp/IProxyArpService.java b/src/main/java/net/onrc/onos/ofcontroller/proxyarp/IProxyArpService.java
new file mode 100644
index 0000000..1ff91e1
--- /dev/null
+++ b/src/main/java/net/onrc/onos/ofcontroller/proxyarp/IProxyArpService.java
@@ -0,0 +1,41 @@
+package net.onrc.onos.ofcontroller.proxyarp;
+
+import java.net.InetAddress;
+
+import net.floodlightcontroller.packet.ARP;
+
+public interface IProxyArpService {
+
+ public final int ARP_REQUEST_TIMEOUT = 2000; //ms
+
+ /**
+ * Tell the IProxyArpService to send an ARP reply with the targetMac to
+ * the host on the specified switchport.
+ * @param arpRequest
+ * @param dpid
+ * @param port
+ * @param targetMac
+ */
+ public void sendArpReply(ARP arpRequest, long dpid, short port, byte[] targetMac);
+
+ /**
+ * Returns the mac address if there is a valid entry in the cache.
+ * Otherwise returns null.
+ * @param ipAddress
+ * @return
+ */
+ public byte[] lookupMac(InetAddress ipAddress);
+
+ /**
+ * Tell the IProxyArpService to send an ARP request for the IP address.
+ * The request will be broadcast out all edge ports in the network.
+ * As an optimization, the IProxyArpService will first check its cache and
+ * return the MAC address if it is already known. If not, the request will be
+ * sent and the callback will be called when the MAC address is known
+ * (or if the request times out).
+ * @param ipAddress
+ * @param requester
+ * @return
+ */
+ public byte[] sendArpRequest(InetAddress ipAddress, IArpRequester requester);
+}
diff --git a/src/main/java/net/onrc/onos/ofcontroller/proxyarp/ProxyArpManager.java b/src/main/java/net/onrc/onos/ofcontroller/proxyarp/ProxyArpManager.java
index a5246c0..538bb41 100644
--- a/src/main/java/net/onrc/onos/ofcontroller/proxyarp/ProxyArpManager.java
+++ b/src/main/java/net/onrc/onos/ofcontroller/proxyarp/ProxyArpManager.java
@@ -6,18 +6,23 @@
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
+import java.util.Timer;
+import java.util.TimerTask;
+import java.util.concurrent.ConcurrentHashMap;
import net.floodlightcontroller.core.FloodlightContext;
import net.floodlightcontroller.core.IFloodlightProviderService;
import net.floodlightcontroller.core.IOFMessageListener;
import net.floodlightcontroller.core.IOFSwitch;
+import net.floodlightcontroller.devicemanager.IDeviceService;
import net.floodlightcontroller.packet.ARP;
import net.floodlightcontroller.packet.Ethernet;
import net.floodlightcontroller.topology.ITopologyService;
-import net.floodlightcontroller.topology.NodePortTuple;
import net.floodlightcontroller.util.MACAddress;
import org.openflow.protocol.OFMessage;
@@ -31,27 +36,87 @@
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
-public class ProxyArpManager implements IOFMessageListener {
+public class ProxyArpManager implements IProxyArpService, IOFMessageListener {
private static Logger log = LoggerFactory.getLogger(ProxyArpManager.class);
- private final long ARP_ENTRY_TIMEOUT = 600000; //ms (== 5 mins)
+ private final long ARP_ENTRY_TIMEOUT = 600000; //ms (== 10 mins)
+ private final long ARP_REQUEST_TIMEOUT_THREAD_PERIOD = 60000; //ms (== 1 min)
+
protected IFloodlightProviderService floodlightProvider;
protected ITopologyService topology;
+ protected IDeviceService devices;
protected Map<InetAddress, ArpTableEntry> arpTable;
+ //protected ConcurrentHashMap<InetAddress, Set<ArpRequest>> arpRequests;
+ protected ConcurrentHashMap<InetAddress, ArpRequest> arpRequests;
+
+ private class ArpRequest {
+ private Set<IArpRequester> requesters;
+ private long requestTime;
+
+ public ArpRequest(){
+ this.requesters = new HashSet<IArpRequester>();
+ this.requestTime = System.currentTimeMillis();
+ }
+
+ public synchronized void addRequester(IArpRequester requester){
+ requestTime = System.currentTimeMillis();
+ requesters.add(requester);
+ }
+
+ public boolean isExpired(){
+ return (System.currentTimeMillis() - requestTime)
+ > IProxyArpService.ARP_REQUEST_TIMEOUT;
+ }
+
+ public synchronized void dispatchReply(byte[] replyMacAddress){
+ for (IArpRequester requester : requesters){
+ requester.arpResponse(replyMacAddress);
+ }
+ }
+ }
+
public ProxyArpManager(IFloodlightProviderService floodlightProvider,
- ITopologyService topology){
+ ITopologyService topology, IDeviceService devices){
this.floodlightProvider = floodlightProvider;
this.topology = topology;
+ this.devices = devices;
arpTable = new HashMap<InetAddress, ArpTableEntry>();
+ //arpRequests = new ConcurrentHashMap<InetAddress, Set<ArpRequest>>();
+ arpRequests = new ConcurrentHashMap<InetAddress, ArpRequest>();
+
+ Timer arpRequestTimeoutTimer = new Timer();
+ arpRequestTimeoutTimer.scheduleAtFixedRate(new TimerTask() {
+ @Override
+ public void run() {
+ synchronized (arpRequests) {
+ log.debug("Current have {} outstanding requests",
+ arpRequests.size());
+
+ Iterator<Map.Entry<InetAddress, ArpRequest>> it
+ = arpRequests.entrySet().iterator();
+
+ while (it.hasNext()){
+ Map.Entry<InetAddress, ArpRequest> entry
+ = it.next();
+
+ if (entry.getValue().isExpired()){
+ log.debug("Cleaning expired ARP request for {}",
+ entry.getKey().getHostAddress());
+ it.remove();
+ }
+ }
+ }
+ }
+ }, 0, ARP_REQUEST_TIMEOUT_THREAD_PERIOD);
}
@Override
public String getName() {
- return "ProxyArpManager";
+ return "ProxyArpManager";
}
@Override
@@ -78,42 +143,107 @@
IFloodlightProviderService.CONTEXT_PI_PAYLOAD);
if (eth.getEtherType() == Ethernet.TYPE_ARP){
-
-
ARP arp = (ARP) eth.getPayload();
if (arp.getOpCode() == ARP.OP_REQUEST) {
- log.debug("ARP request received for {}", bytesToStringAddr(arp.getTargetProtocolAddress()));
-
- byte[] mac = lookupArpTable(arp.getTargetProtocolAddress());
-
- if (mac == null){
- //Mac address is not in our arp table.
- //We need to flood the request out edge ports
- Set<NodePortTuple> broadcastPorts = topology.getBroadcastDomainPorts();
- log.debug("size {}", broadcastPorts.size());
- for (NodePortTuple nodePort : broadcastPorts){
- log.debug("Port {}", nodePort);
- }
- broadcastArpRequestOutEdge(pi, sw.getId(), pi.getInPort());
- }
- else {
- //We know the address, so send a reply
- log.debug("Sending reply of {}", MACAddress.valueOf(mac).toString());
- sendArpReply(arp, pi, mac, sw);
- }
+ handleArpRequest(sw, pi, arp);
}
else if (arp.getOpCode() == ARP.OP_REPLY) {
- log.debug("ARP reply recieved for {}", bytesToStringAddr(arp.getSenderProtocolAddress()));
-
- log.debug("arp table {}", arpTable.keySet());
-
- updateArpTable(arp);
+ handleArpReply(sw, pi, arp);
}
}
+ //TODO should we propagate ARP or swallow it?
+ //Always propagate for now so DeviceManager can learn the host location
return Command.CONTINUE;
}
+
+ protected void handleArpRequest(IOFSwitch sw, OFPacketIn pi, ARP arp) {
+ log.debug("ARP request received for {}",
+ bytesToStringAddr(arp.getTargetProtocolAddress()));
+
+ byte[] mac = lookupArpTable(arp.getTargetProtocolAddress());
+
+ if (mac == null){
+ //Mac address is not in our arp table.
+
+ //TODO check what the DeviceManager thinks
+
+ //Record where the request came from so we know where to send the reply
+ InetAddress target;
+ try {
+ target = InetAddress.getByAddress(arp.getTargetProtocolAddress());
+ } catch (UnknownHostException e) {
+ log.debug("Invalid address in ARP request", e);
+ //return Command.CONTINUE; //Continue or stop?
+ return;
+ }
+
+ synchronized (arpRequests) {
+ //arpRequests.putIfAbsent(target,
+ //Collections.synchronizedSet(new HashSet<ArpRequest>()));
+ // new ArpRequest());
+ //Set<ArpRequest> requesters = arpRequests.get(target);
+ if (arpRequests.get(target) == null) {
+ arpRequests.put(target, new ArpRequest());
+ }
+ ArpRequest request = arpRequests.get(target);
+
+ request.addRequester(new HostArpRequester(this, arp,
+ sw.getId(), pi.getInPort()));
+ }
+
+
+ //Flood the request out edge ports
+ broadcastArpRequestOutEdge(pi, sw.getId(), pi.getInPort());
+ }
+ else {
+ //We know the address, so send a reply
+ log.debug("Sending reply of {}", MACAddress.valueOf(mac).toString());
+ //sendArpReply(arp, pi, mac, sw);
+ sendArpReply(arp, sw.getId(), pi.getInPort(), mac);
+ }
+ }
+
+ protected void handleArpReply(IOFSwitch sw, OFPacketIn pi, ARP arp){
+ log.debug("ARP reply recieved for {}",
+ bytesToStringAddr(arp.getSenderProtocolAddress()));
+
+ updateArpTable(arp);
+
+ //See if anyone's waiting for this ARP reply
+ InetAddress addr;
+ try {
+ addr = InetAddress.getByAddress(arp.getSenderProtocolAddress());
+ } catch (UnknownHostException e) {
+ return;
+ }
+
+ ArpRequest request = null;
+ synchronized (arpRequests) {
+ request = arpRequests.get(addr);
+ if (request != null) {
+ arpRequests.remove(addr);
+ }
+ }
+ if (request != null && !request.isExpired()) {
+ request.dispatchReply(arp.getSenderHardwareAddress());
+ }
+
+ /*
+ Set<ArpRequest> requests = arpRequests.get(addr);
+ if (requests != null){
+
+ synchronized (requests) {
+ for (ArpRequest request : requests) {
+ if (!request.isExpired()){
+ request.getRequester().arpResponse(
+ arp.getSenderHardwareAddress());
+ }
+ }
+ }
+ }*/
+ }
private synchronized byte[] lookupArpTable(byte[] ipAddress){
InetAddress addr;
@@ -210,26 +340,30 @@
}
}
- private void sendArpReply(ARP arpRequest, OFPacketIn pi, byte[] macRequested, IOFSwitch sw){
+ public void sendArpReply(ARP arpRequest, long dpid, short port, byte[] targetMac) {
+ //private void sendArpReply(ARP arpRequest, OFPacketIn pi, byte[] macRequested, IOFSwitch sw){
ARP arpReply = new ARP();
arpReply.setHardwareType(ARP.HW_TYPE_ETHERNET)
.setProtocolType(ARP.PROTO_TYPE_IP)
.setHardwareAddressLength((byte)Ethernet.DATALAYER_ADDRESS_LENGTH)
.setProtocolAddressLength((byte)4) //can't find the constant anywhere
.setOpCode(ARP.OP_REPLY)
- .setSenderHardwareAddress(macRequested)
+ //.setSenderHardwareAddress(macRequested)
+ .setSenderHardwareAddress(targetMac)
.setSenderProtocolAddress(arpRequest.getTargetProtocolAddress())
.setTargetHardwareAddress(arpRequest.getSenderHardwareAddress())
.setTargetProtocolAddress(arpRequest.getSenderProtocolAddress());
Ethernet eth = new Ethernet();
eth.setDestinationMACAddress(arpRequest.getSenderHardwareAddress())
- .setSourceMACAddress(macRequested)
+ //.setSourceMACAddress(macRequested)
+ .setSourceMACAddress(targetMac)
.setEtherType(Ethernet.TYPE_ARP)
.setPayload(arpReply);
List<OFAction> actions = new ArrayList<OFAction>();
- actions.add(new OFActionOutput(pi.getInPort()));
+ //actions.add(new OFActionOutput(pi.getInPort()));
+ actions.add(new OFActionOutput(port));
OFPacketOut po = new OFPacketOut();
po.setInPort(OFPort.OFPP_NONE)
@@ -242,9 +376,15 @@
List<OFMessage> msgList = new ArrayList<OFMessage>();
msgList.add(po);
+
+ IOFSwitch sw = floodlightProvider.getSwitches().get(dpid);
+
+ if (sw == null) {
+ return;
+ }
try {
- log.debug("Sending ARP reply to {}/{}", HexString.toHexString(sw.getId()), pi.getInPort());
+ log.debug("Sending ARP reply to {}/{}", HexString.toHexString(sw.getId()), port);
sw.write(msgList, null);
sw.flush();
} catch (IOException e) {
@@ -265,4 +405,14 @@
if (addr == null) return "";
else return addr.getHostAddress();
}
+
+
+ public byte[] lookupMac(InetAddress ipAddress){
+ //TODO implement
+ return null;
+ }
+ public byte[] sendArpRequest(InetAddress ipAddress, IArpRequester requester){
+ //TODO implement
+ return null;
+ }
}