Merge branch 'proxyarp'
diff --git a/src/main/java/net/onrc/onos/ofcontroller/bgproute/BgpPeer.java b/src/main/java/net/onrc/onos/ofcontroller/bgproute/BgpPeer.java
new file mode 100644
index 0000000..7425a07
--- /dev/null
+++ b/src/main/java/net/onrc/onos/ofcontroller/bgproute/BgpPeer.java
@@ -0,0 +1,42 @@
+package net.onrc.onos.ofcontroller.bgproute;
+
+import java.net.InetAddress;
+
+import net.floodlightcontroller.util.MACAddress;
+
+import org.codehaus.jackson.annotate.JsonProperty;
+
+import com.google.common.net.InetAddresses;
+
+public class BgpPeer {
+ private String interfaceName;
+ private InetAddress ipAddress;
+ private MACAddress macAddress;
+
+ public String getInterfaceName() {
+ return interfaceName;
+ }
+
+ @JsonProperty("interface")
+ public void setInterfaceName(String interfaceName) {
+ this.interfaceName = interfaceName;
+ }
+
+ public InetAddress getIpAddress() {
+ return ipAddress;
+ }
+
+ @JsonProperty("ipAddress")
+ public void setIpAddress(String ipAddress) {
+ this.ipAddress = InetAddresses.forString(ipAddress);
+ }
+
+ public MACAddress getMacAddress() {
+ return macAddress;
+ }
+
+ @JsonProperty("macAddress")
+ public void setMacAddress(String macAddress) {
+ this.macAddress = MACAddress.valueOf(macAddress);
+ }
+}
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 4b6e328..c57d4d8 100644
--- a/src/main/java/net/onrc/onos/ofcontroller/bgproute/BgpRoute.java
+++ b/src/main/java/net/onrc/onos/ofcontroller/bgproute/BgpRoute.java
@@ -6,11 +6,15 @@
import java.net.UnknownHostException;
import java.util.ArrayList;
import java.util.Collection;
+import java.util.Comparator;
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.concurrent.ConcurrentSkipListSet;
+import java.util.concurrent.Executors;
+import java.util.concurrent.ScheduledExecutorService;
+import java.util.concurrent.TimeUnit;
import net.floodlightcontroller.core.IFloodlightProviderService;
import net.floodlightcontroller.core.IOFSwitch;
@@ -19,15 +23,22 @@
import net.floodlightcontroller.core.module.FloodlightModuleException;
import net.floodlightcontroller.core.module.IFloodlightModule;
import net.floodlightcontroller.core.module.IFloodlightService;
+import net.floodlightcontroller.core.util.SingletonTask;
import net.floodlightcontroller.devicemanager.IDeviceService;
import net.floodlightcontroller.packet.Ethernet;
+import net.floodlightcontroller.packet.IPv4;
import net.floodlightcontroller.restserver.IRestApiService;
+import net.floodlightcontroller.routing.Link;
import net.floodlightcontroller.topology.ITopologyListener;
import net.floodlightcontroller.topology.ITopologyService;
+import net.onrc.onos.ofcontroller.core.INetMapTopologyService.ITopoLinkService;
import net.onrc.onos.ofcontroller.core.INetMapTopologyService.ITopoRouteService;
+import net.onrc.onos.ofcontroller.core.internal.TopoLinkServiceImpl;
import net.onrc.onos.ofcontroller.linkdiscovery.ILinkDiscovery;
import net.onrc.onos.ofcontroller.linkdiscovery.ILinkDiscovery.LDUpdate;
+import net.onrc.onos.ofcontroller.proxyarp.ProxyArpManager;
import net.onrc.onos.ofcontroller.util.DataPath;
+import net.onrc.onos.ofcontroller.util.Dpid;
import net.onrc.onos.ofcontroller.util.FlowEntry;
import net.onrc.onos.ofcontroller.util.Port;
import net.onrc.onos.ofcontroller.util.SwitchPort;
@@ -63,12 +74,12 @@
protected IDeviceService devices;
protected IRestApiService restApi;
+ protected ProxyArpManager proxyArp;
+
protected static Ptree ptree;
protected String bgpdRestIp;
protected String routerId;
- protected String gatewaysFilename = "gateways.json";
-
- protected Set<InetAddress> routerIpAddresses;
+ protected String gatewaysFilename = "config.json";
//We need to identify our flows somehow. But like it says in LearningSwitch.java,
//the controller/OS should hand out cookie IDs to prevent conflicts.
@@ -77,17 +88,73 @@
protected final long L2_FWD_COOKIE = APP_COOKIE + 1;
//Cookie for flows in ingress switches that rewrite the MAC address
protected final long MAC_RW_COOKIE = APP_COOKIE + 2;
+ //Cookie for flows that setup BGP paths
+ protected final long BGP_COOKIE = APP_COOKIE + 3;
//Forwarding uses priority 0, and the mac rewrite entries in ingress switches
//need to be higher priority than this otherwise the rewrite may not get done
protected final short SDNIP_PRIORITY = 10;
+ protected final short BGP_PORT = 179;
+
+ protected final int TOPO_DETECTION_WAIT = 2; //seconds
+
+ //Configuration stuff
protected Map<String, GatewayRouter> gatewayRouters;
protected List<String> switches;
+ protected Map<String, Interface> interfaces;
+ protected List<BgpPeer> bgpPeers;
+ protected SwitchPort bgpdAttachmentPoint;
//True when all switches have connected
protected volatile boolean switchesConnected = false;
//True when we have a full mesh of shortest paths between gateways
protected volatile boolean topologyReady = false;
+
+ //protected ConcurrentSkipListSet<LDUpdate> linkUpdates;
+ protected ArrayList<LDUpdate> linkUpdates;
+ protected SingletonTask topologyChangeDetectorTask;
+
+ //protected ILinkStorage linkStorage;//XXX
+
+ protected class TopologyChangeDetector implements Runnable {
+ @Override
+ public void run() {
+ log.debug("Running topology change detection task");
+ synchronized (linkUpdates) {
+ //This is the model the REST API uses to retrive network graph info
+ ITopoLinkService topoLinkService = new TopoLinkServiceImpl();
+
+ List<Link> activeLinks = topoLinkService.getActiveLinks();
+ for (Link l : activeLinks){
+ log.debug("active link: {}", l);
+ }
+
+ Iterator<LDUpdate> it = linkUpdates.iterator();
+ while (it.hasNext()){
+ LDUpdate ldu = it.next();
+ Link l = new Link(ldu.getSrc(), ldu.getSrcPort(),
+ ldu.getDst(), ldu.getDstPort());
+
+ if (activeLinks.contains(l)){
+ log.debug("Not found: {}", l);
+ it.remove();
+ }
+ }
+ }
+
+ if (linkUpdates.isEmpty()){
+ //All updates have been seen in network map.
+ //We can check if topology is ready
+ log.debug("No know changes outstanding. Checking topology now");
+ checkStatus();
+ }
+ else {
+ //We know of some link updates that haven't propagated to the database yet
+ log.debug("Some changes not found in network map- size {}", linkUpdates.size());
+ topologyChangeDetectorTask.reschedule(TOPO_DETECTION_WAIT, TimeUnit.SECONDS);
+ }
+ }
+ }
private void readGatewaysConfiguration(String gatewaysFilename){
File gatewaysFile = new File(gatewaysFilename);
@@ -98,10 +165,14 @@
gatewayRouters = config.getGateways();
switches = config.getSwitches();
+ interfaces = config.getInterfaces();
+ bgpPeers = config.getPeers();
- for (String sw : switches){
- log.debug("Switchjoin {}", sw);
- }
+ bgpdAttachmentPoint = new SwitchPort(
+ new Dpid(config.getBgpdAttachmentDpid()),
+ new Port(config.getBgpdAttachmentPort()));
+ //bgpdAttachmentDpid = config.getBgpdAttachmentDpid();
+ //bgpdAttachmentPort = config.getBgpdAttachmentPort();
} catch (JsonParseException e) {
log.error("Error in JSON file", e);
@@ -149,14 +220,40 @@
ptree = new Ptree(32);
- routerIpAddresses = new HashSet<InetAddress>();
+ //routerIpAddresses = new HashSet<InetAddress>();
// Register floodlight provider and REST handler.
floodlightProvider = context.getServiceImpl(IFloodlightProviderService.class);
topology = context.getServiceImpl(ITopologyService.class);
topoRouteService = context.getServiceImpl(ITopoRouteService.class);
devices = context.getServiceImpl(IDeviceService.class);
- restApi = context.getServiceImpl(IRestApiService.class);
+ restApi = context.getServiceImpl(IRestApiService.class);
+
+ //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);
+
+ /*
+ linkStorage = new LinkStorageImpl();
+ //XXX Hack to pull out the database location from NetworkGraphPublisher's config
+ String databaseConfig = null;
+ for (IFloodlightModule fm : context.getAllModules()){
+ if (fm instanceof NetworkGraphPublisher){
+ Map<String, String> configMap = context.getConfigParams(fm);
+ databaseConfig = configMap.get("dbconf");
+ break;
+ }
+ }
+ if (databaseConfig == null){
+ log.error("Couldn't find database config string \"dbconf\"");
+ System.exit(1);
+ }
+ linkStorage.init(databaseConfig);
+ */
+ //linkUpdates = new ConcurrentSkipListSet<ILinkDiscovery.LDUpdate>();
+ linkUpdates = new ArrayList<LDUpdate>();
+ ScheduledExecutorService executor = Executors.newScheduledThreadPool(1);
+ topologyChangeDetectorTask = new SingletonTask(executor, new TopologyChangeDetector());
//Read in config values
bgpdRestIp = context.getConfigParams(this).get("BgpdRestIp");
@@ -407,9 +504,6 @@
match.setDataLayerSource(ingressRouter.getRouterMac().toBytes());
match.setWildcards(match.getWildcards() & ~OFMatch.OFPFW_DL_SRC);
-
- //match.setDataLayerDestination(ingressRouter.getSdnRouterMac().toBytes());
- //match.setWildcards(match.getWildcards() & ~OFMatch.OFPFW_DL_DST);
InetAddress address = null;
try {
@@ -429,7 +523,7 @@
//Set up output action
OFActionOutput outputAction = new OFActionOutput();
- outputAction.setMaxLength((short)0xffff); //TODO check what this is (and if needed for mac rewrite)
+ outputAction.setMaxLength((short)0xffff);
Port outputPort = shortestPath.flowEntries().get(0).outPort();
outputAction.setPort(outputPort.value());
@@ -502,9 +596,6 @@
match.setDataLayerSource(ingressRouter.getRouterMac().toBytes());
match.setWildcards(match.getWildcards() & ~OFMatch.OFPFW_DL_SRC);
- //match.setDataLayerDestination(ingressRouter.getSdnRouterMac().toBytes());
- //match.setWildcards(match.getWildcards() & ~OFMatch.OFPFW_DL_DST);
-
InetAddress address = null;
try {
address = InetAddress.getByAddress(node.key);
@@ -651,11 +742,138 @@
}
}
+ private void setupBgpPaths(){
+ for (BgpPeer bgpPeer : bgpPeers){
+ Interface peerInterface = interfaces.get(bgpPeer.getInterfaceName());
+
+ DataPath path = topoRouteService.getShortestPath(
+ peerInterface.getSwitchPort(), bgpdAttachmentPoint);
+
+ if (path == null){
+ log.debug("Unable to compute path for BGP traffic for {}",
+ bgpPeer.getIpAddress());
+ continue;
+ }
+
+ //Set up the flow mod
+ OFFlowMod fm =
+ (OFFlowMod) floodlightProvider.getOFMessageFactory()
+ .getMessage(OFType.FLOW_MOD);
+
+ OFActionOutput action = new OFActionOutput();
+ action.setMaxLength((short)0xffff);
+ List<OFAction> actions = new ArrayList<OFAction>();
+ actions.add(action);
+
+ fm.setIdleTimeout((short)0)
+ .setHardTimeout((short)0)
+ .setBufferId(OFPacketOut.BUFFER_ID_NONE)
+ .setCookie(BGP_COOKIE)
+ .setCommand(OFFlowMod.OFPFC_ADD)
+ .setPriority(SDNIP_PRIORITY)
+ .setActions(actions)
+ .setLengthU(OFFlowMod.MINIMUM_LENGTH+OFActionOutput.MINIMUM_LENGTH);
+
+ //Forward = gateway -> bgpd, reverse = bgpd -> gateway
+ OFMatch forwardMatchSrc = new OFMatch();
+
+
+ String interfaceCidrAddress = peerInterface.getIpAddress().getHostAddress()
+ + "/32";
+ String peerCidrAddress = bgpPeer.getIpAddress().getHostAddress()
+ + "/32";
+
+ //Common match fields
+ forwardMatchSrc.setDataLayerType(Ethernet.TYPE_IPv4);
+ //forwardMatch.setWildcards(forwardMatch.getWildcards() & ~OFMatch.OFPFW_DL_TYPE);
+ forwardMatchSrc.setNetworkProtocol(IPv4.PROTOCOL_TCP);
+ forwardMatchSrc.setTransportDestination(BGP_PORT);
+ forwardMatchSrc.setWildcards(forwardMatchSrc.getWildcards() & ~OFMatch.OFPFW_IN_PORT
+ & ~OFMatch.OFPFW_DL_TYPE & ~OFMatch.OFPFW_NW_PROTO);
+
+
+ OFMatch reverseMatchSrc = forwardMatchSrc.clone();
+
+ forwardMatchSrc.setFromCIDR(peerCidrAddress, OFMatch.STR_NW_SRC);
+ forwardMatchSrc.setFromCIDR(interfaceCidrAddress, OFMatch.STR_NW_DST);
+
+ OFMatch forwardMatchDst = forwardMatchSrc.clone();
+
+ forwardMatchSrc.setTransportSource(BGP_PORT);
+ forwardMatchSrc.setWildcards(forwardMatchSrc.getWildcards() & ~OFMatch.OFPFW_TP_SRC);
+ forwardMatchDst.setTransportDestination(BGP_PORT);
+ forwardMatchDst.setWildcards(forwardMatchDst.getWildcards() & ~OFMatch.OFPFW_TP_DST);
+
+ reverseMatchSrc.setFromCIDR(interfaceCidrAddress, OFMatch.STR_NW_SRC);
+ reverseMatchSrc.setFromCIDR(peerCidrAddress, OFMatch.STR_NW_DST);
+
+ OFMatch reverseMatchDst = reverseMatchSrc.clone();
+
+ reverseMatchSrc.setTransportSource(BGP_PORT);
+ reverseMatchSrc.setWildcards(forwardMatchSrc.getWildcards() & ~OFMatch.OFPFW_TP_SRC);
+ reverseMatchDst.setTransportDestination(BGP_PORT);
+ reverseMatchDst.setWildcards(forwardMatchDst.getWildcards() & ~OFMatch.OFPFW_TP_DST);
+
+ fm.setMatch(forwardMatchSrc);
+
+ for (FlowEntry flowEntry : path.flowEntries()){
+ OFFlowMod forwardFlowModSrc, forwardFlowModDst;
+ OFFlowMod reverseFlowModSrc, reverseFlowModDst;
+ try {
+ forwardFlowModSrc = fm.clone();
+ forwardFlowModDst = fm.clone();
+ reverseFlowModSrc = fm.clone();
+ reverseFlowModDst = fm.clone();
+ } catch (CloneNotSupportedException e) {
+ log.warn("Clone failed", e);
+ continue;
+ }
+
+ forwardMatchSrc.setInputPort(flowEntry.inPort().value());
+ forwardFlowModSrc.setMatch(forwardMatchSrc);
+ ((OFActionOutput)forwardFlowModSrc.getActions().get(0))
+ .setPort(flowEntry.outPort().value());
+
+ forwardMatchDst.setInputPort(flowEntry.inPort().value());
+ forwardFlowModDst.setMatch(forwardMatchDst);
+ ((OFActionOutput)forwardFlowModDst.getActions().get(0))
+ .setPort(flowEntry.outPort().value());
+
+ reverseMatchSrc.setInputPort(flowEntry.outPort().value());
+ reverseFlowModSrc.setMatch(reverseMatchSrc);
+ ((OFActionOutput)reverseFlowModSrc.getActions().get(0))
+ .setPort(flowEntry.inPort().value());
+
+ reverseMatchDst.setInputPort(flowEntry.outPort().value());
+ reverseFlowModDst.setMatch(reverseMatchDst);
+ ((OFActionOutput)reverseFlowModDst.getActions().get(0))
+ .setPort(flowEntry.inPort().value());
+
+ IOFSwitch sw = floodlightProvider.getSwitches().get(flowEntry.dpid().value());
+
+ //Hopefully the switch is there
+ List<OFMessage> msgList = new ArrayList<OFMessage>(2);
+ msgList.add(forwardFlowModSrc);
+ msgList.add(forwardFlowModDst);
+ msgList.add(reverseFlowModSrc);
+ msgList.add(reverseFlowModDst);
+
+ try {
+ sw.write(msgList, null);
+ sw.flush();
+ } catch (IOException e) {
+ log.error("Failure writing flow mod", e);
+ }
+ }
+ }
+ }
private void beginRouting(){
log.debug("Topology is now ready, beginning routing function");
+ setupBgpPaths();
+ setupFullMesh();
- //traverse ptree and create flows for all routes
+ //Traverse ptree and create flows for all routes
for (PtreeNode node = ptree.begin(); node != null; node = ptree.next(node)){
if (node.rib != null){
prefixAdded(node);
@@ -718,6 +936,8 @@
floodlightProvider.addOFSwitchListener(this);
topology.addListener(this);
+ floodlightProvider.addOFMessageListener(OFType.PACKET_IN, proxyArp);
+
//Retrieve the RIB from BGPd during startup
retrieveRib();
}
@@ -734,16 +954,41 @@
//They happen way too frequently (may be a bug in our link discovery)
refreshNeeded = true;
}
+
log.debug("Topo change {}", ldu.getOperation());
+ /*
+ if (ldu.getOperation().equals(ILinkDiscovery.UpdateOperation.LINK_ADDED)){
+ log.debug("Link Added: src={} outPort={} dst={} inPort={}",
+ new Object[] {
+ HexString.toHexString(ldu.getSrc()), ldu.getSrcPort(),
+ HexString.toHexString(ldu.getDst()), ldu.getDstPort()});
+ TopoLinkServiceImpl impl = new TopoLinkServiceImpl();
+
+ List<Link> retval = impl.getActiveLinks();
+
+ log.debug("retval size {}", retval.size());
+
+ for (Link l : retval){
+ log.debug("link {}", l);
+ }
+ }
+ */
+ if (ldu.getOperation().equals(ILinkDiscovery.UpdateOperation.LINK_ADDED)){
+ synchronized (linkUpdates) {
+ linkUpdates.add(ldu);
+ }
+ }
}
if (refreshNeeded){
- if (topologyReady){
+ topologyChangeDetectorTask.reschedule(TOPO_DETECTION_WAIT, TimeUnit.SECONDS);
+ /*if (topologyReady){
setupFullMesh();
}
else{
checkStatus();
- }
+ }*/
+
}
}
diff --git a/src/main/java/net/onrc/onos/ofcontroller/bgproute/Configuration.java b/src/main/java/net/onrc/onos/ofcontroller/bgproute/Configuration.java
index 5194584..65617c8 100644
--- a/src/main/java/net/onrc/onos/ofcontroller/bgproute/Configuration.java
+++ b/src/main/java/net/onrc/onos/ofcontroller/bgproute/Configuration.java
@@ -4,15 +4,38 @@
import java.util.Map;
import org.codehaus.jackson.annotate.JsonProperty;
+import org.openflow.util.HexString;
public class Configuration {
- List<String> switches;
- Map<String, GatewayRouter> gateways;
+ private long bgpdAttachmentDpid;
+ private short bgpdAttachmentPort;
+ private List<String> switches;
+ private Map<String, Interface> interfaces;
+ private List<BgpPeer> peers;
+ private Map<String, GatewayRouter> gateways;
public Configuration() {
// TODO Auto-generated constructor stub
}
+ public long getBgpdAttachmentDpid() {
+ return bgpdAttachmentDpid;
+ }
+
+ @JsonProperty("bgpdAttachmentDpid")
+ public void setBgpdAttachmentDpid(String bgpdAttachmentDpid) {
+ this.bgpdAttachmentDpid = HexString.toLong(bgpdAttachmentDpid);
+ }
+
+ public short getBgpdAttachmentPort() {
+ return bgpdAttachmentPort;
+ }
+
+ @JsonProperty("bgpdAttachmentPort")
+ public void setBgpdAttachmentPort(short bgpdAttachmentPort) {
+ this.bgpdAttachmentPort = bgpdAttachmentPort;
+ }
+
public List<String> getSwitches() {
return switches;
}
@@ -22,6 +45,24 @@
this.switches = switches;
}
+ public Map<String, Interface> getInterfaces() {
+ return interfaces;
+ }
+
+ @JsonProperty("interfaces")
+ public void setInterfaces(Map<String, Interface> interfaces) {
+ this.interfaces = interfaces;
+ }
+
+ public List<BgpPeer> getPeers() {
+ return peers;
+ }
+
+ @JsonProperty("bgpPeers")
+ public void setPeers(List<BgpPeer> peers) {
+ this.peers = peers;
+ }
+
public Map<String, GatewayRouter> getGateways() {
return gateways;
}
diff --git a/src/main/java/net/onrc/onos/ofcontroller/bgproute/GatewayRouter.java b/src/main/java/net/onrc/onos/ofcontroller/bgproute/GatewayRouter.java
index 4016c69..e893acf 100644
--- a/src/main/java/net/onrc/onos/ofcontroller/bgproute/GatewayRouter.java
+++ b/src/main/java/net/onrc/onos/ofcontroller/bgproute/GatewayRouter.java
@@ -15,6 +15,7 @@
private short port;
private MACAddress routerMac;
private IPv4 routerIp;
+ private IPv4 myIpAddress;
public SwitchPort getAttachmentPoint() {
@@ -59,4 +60,13 @@
public void setRouterIp(String routerIp) {
this.routerIp = new IPv4(routerIp);
}
+
+ public IPv4 getMyIpAddress() {
+ return myIpAddress;
+ }
+
+ @JsonProperty("myIpAddress")
+ public void setMyIpAddress(String myIpAddress) {
+ this.myIpAddress = new IPv4(myIpAddress);
+ }
}
diff --git a/src/main/java/net/onrc/onos/ofcontroller/bgproute/Interface.java b/src/main/java/net/onrc/onos/ofcontroller/bgproute/Interface.java
new file mode 100644
index 0000000..15b2125
--- /dev/null
+++ b/src/main/java/net/onrc/onos/ofcontroller/bgproute/Interface.java
@@ -0,0 +1,63 @@
+package net.onrc.onos.ofcontroller.bgproute;
+
+import java.net.InetAddress;
+
+import net.onrc.onos.ofcontroller.util.Dpid;
+import net.onrc.onos.ofcontroller.util.Port;
+import net.onrc.onos.ofcontroller.util.SwitchPort;
+
+import org.codehaus.jackson.annotate.JsonProperty;
+import org.openflow.util.HexString;
+
+import com.google.common.net.InetAddresses;
+
+public class Interface {
+ private SwitchPort switchPort = null;
+ private long dpid;
+ private short port;
+ private InetAddress ipAddress;
+ private int prefixLength;
+
+ public synchronized SwitchPort getSwitchPort() {
+ if (switchPort == null){
+ switchPort = new SwitchPort(new Dpid(dpid), new Port(port));
+ }
+ return switchPort;
+ }
+
+ public long getDpid() {
+ return dpid;
+ }
+
+ @JsonProperty("dpid")
+ public void setDpid(String dpid) {
+ this.dpid = HexString.toLong(dpid);
+ }
+
+ public short getPort() {
+ return port;
+ }
+
+ @JsonProperty("port")
+ public void setPort(short port) {
+ this.port = port;
+ }
+
+ public InetAddress getIpAddress() {
+ return ipAddress;
+ }
+
+ @JsonProperty("ipAddress")
+ public void setIpAddress(String ipAddress) {
+ this.ipAddress = InetAddresses.forString(ipAddress);
+ }
+
+ public int getPrefixLength() {
+ return prefixLength;
+ }
+
+ @JsonProperty("prefixLength")
+ public void setPrefixLength(int prefixLength) {
+ this.prefixLength = prefixLength;
+ }
+}
diff --git a/src/main/java/net/onrc/onos/ofcontroller/proxyarp/ArpTableEntry.java b/src/main/java/net/onrc/onos/ofcontroller/proxyarp/ArpTableEntry.java
new file mode 100644
index 0000000..5830cfd
--- /dev/null
+++ b/src/main/java/net/onrc/onos/ofcontroller/proxyarp/ArpTableEntry.java
@@ -0,0 +1,27 @@
+package net.onrc.onos.ofcontroller.proxyarp;
+
+
+public class ArpTableEntry {
+
+ private byte[] macAddress;
+ private long timeLastSeen;
+
+ public ArpTableEntry(byte[] macAddress, long timeLastSeen) {
+ this.macAddress = macAddress;
+ this.timeLastSeen = timeLastSeen;
+ }
+
+ public byte[] getMacAddress() {
+ return macAddress;
+ }
+
+ public long getTimeLastSeen() {
+ return timeLastSeen;
+ }
+
+ public void setTimeLastSeen(long time){
+ //TODO thread safety issues?
+ timeLastSeen = time;
+ }
+
+}
diff --git a/src/main/java/net/onrc/onos/ofcontroller/proxyarp/ProxyArpManager.java b/src/main/java/net/onrc/onos/ofcontroller/proxyarp/ProxyArpManager.java
new file mode 100644
index 0000000..b7fc896
--- /dev/null
+++ b/src/main/java/net/onrc/onos/ofcontroller/proxyarp/ProxyArpManager.java
@@ -0,0 +1,274 @@
+package net.onrc.onos.ofcontroller.proxyarp;
+
+import java.io.IOException;
+import java.net.InetAddress;
+import java.net.UnknownHostException;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+import net.floodlightcontroller.core.FloodlightContext;
+import net.floodlightcontroller.core.IFloodlightProviderService;
+import net.floodlightcontroller.core.IOFMessageListener;
+import net.floodlightcontroller.core.IOFSwitch;
+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;
+import org.openflow.protocol.OFPacketIn;
+import org.openflow.protocol.OFPacketOut;
+import org.openflow.protocol.OFPort;
+import org.openflow.protocol.OFType;
+import org.openflow.protocol.action.OFAction;
+import org.openflow.protocol.action.OFActionOutput;
+import org.openflow.util.HexString;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class ProxyArpManager implements IOFMessageListener {
+ private static Logger log = LoggerFactory.getLogger(ProxyArpManager.class);
+
+ private final long ARP_ENTRY_TIMEOUT = 600000; //ms (== 5 mins)
+
+ protected IFloodlightProviderService floodlightProvider;
+ protected ITopologyService topology;
+
+
+ protected Map<InetAddress, ArpTableEntry> arpTable;
+
+ public ProxyArpManager(IFloodlightProviderService floodlightProvider,
+ ITopologyService topology){
+ this.floodlightProvider = floodlightProvider;
+ this.topology = topology;
+
+ arpTable = new HashMap<InetAddress, ArpTableEntry>();
+ }
+
+ @Override
+ public String getName() {
+ return "ProxyArpManager";
+ }
+
+ @Override
+ public boolean isCallbackOrderingPrereq(OFType type, String name) {
+ // TODO Auto-generated method stub
+ return false;
+ }
+
+ @Override
+ public boolean isCallbackOrderingPostreq(OFType type, String name) {
+ // TODO Auto-generated method stub
+ return false;
+ }
+
+ @Override
+ public Command receive(
+ IOFSwitch sw, OFMessage msg, FloodlightContext cntx) {
+
+ if (msg.getType() != OFType.PACKET_IN){
+ return Command.CONTINUE;
+ }
+
+ OFPacketIn pi = (OFPacketIn) msg;
+
+ Ethernet eth = IFloodlightProviderService.bcStore.get(cntx,
+ 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);
+ }
+ }
+ else if (arp.getOpCode() == ARP.OP_REPLY) {
+ log.debug("ARP reply recieved for {}", bytesToStringAddr(arp.getSenderProtocolAddress()));
+
+ log.debug("arp table {}", arpTable.keySet());
+
+ updateArpTable(arp);
+ }
+ }
+
+
+ return Command.CONTINUE;
+ }
+
+ private synchronized byte[] lookupArpTable(byte[] ipAddress){
+ InetAddress addr;
+ try {
+ addr = InetAddress.getByAddress(ipAddress);
+ } catch (UnknownHostException e) {
+ log.warn("Unable to create InetAddress", e);
+ return null;
+ }
+
+ ArpTableEntry arpEntry = arpTable.get(addr);
+
+ if (arpEntry == null){
+ return null;
+ }
+
+ if (System.currentTimeMillis() - arpEntry.getTimeLastSeen()
+ > ARP_ENTRY_TIMEOUT){
+ //Entry has timed out so we'll remove it and return null
+ arpTable.remove(addr);
+ return null;
+ }
+
+ return arpEntry.getMacAddress();
+ }
+
+ private synchronized void updateArpTable(ARP arp){
+ InetAddress addr;
+ try {
+ addr = InetAddress.getByAddress(arp.getSenderProtocolAddress());
+ } catch (UnknownHostException e) {
+ log.warn("Unable to create InetAddress", e);
+ return;
+ }
+
+ ArpTableEntry arpEntry = arpTable.get(addr);
+
+ if (arpEntry != null
+ && arpEntry.getMacAddress() == arp.getSenderHardwareAddress()){
+ arpEntry.setTimeLastSeen(System.currentTimeMillis());
+ }
+ else {
+ arpTable.put(addr,
+ new ArpTableEntry(arp.getSenderHardwareAddress(),
+ System.currentTimeMillis()));
+ }
+ }
+
+ private void broadcastArpRequestOutEdge(OFPacketIn pi, long inSwitch, short inPort){
+ for (IOFSwitch sw : floodlightProvider.getSwitches().values()){
+ Collection<Short> enabledPorts = sw.getEnabledPortNumbers();
+ Set<Short> linkPorts = topology.getPortsWithLinks(sw.getId());
+
+ if (linkPorts == null){
+ //I think this means the switch isn't known to topology yet.
+ //Maybe it only just joined.
+ continue;
+ }
+
+ OFPacketOut po = new OFPacketOut();
+ po.setInPort(OFPort.OFPP_NONE)
+ .setBufferId(-1)
+ //.setLengthU(OFActionOutput.MINIMUM_LENGTH);
+ .setPacketData(pi.getPacketData());
+
+ List<OFAction> actions = new ArrayList<OFAction>();
+
+ for (short portNum : enabledPorts){
+ //log.debug("linkPorts {}", linkPorts);
+ //log.debug("portNum {}", portNum);
+ if (linkPorts.contains(portNum) ||
+ (sw.getId() == inSwitch && portNum == inPort)){
+ //If this port isn't an edge port or is the ingress port
+ //for the ARP, don't broadcast out it
+ continue;
+ }
+
+ actions.add(new OFActionOutput(portNum));
+ log.debug("Broadcasting out {}/{}", HexString.toHexString(sw.getId()), portNum);
+ }
+
+ po.setActions(actions);
+ short actionsLength = (short) (actions.size() * OFActionOutput.MINIMUM_LENGTH);
+ po.setActionsLength(actionsLength);
+ po.setLengthU(OFPacketOut.MINIMUM_LENGTH + actionsLength
+ + pi.getPacketData().length);
+
+ List<OFMessage> msgList = new ArrayList<OFMessage>();
+ msgList.add(po);
+
+ try {
+ sw.write(msgList, null);
+ sw.flush();
+ } catch (IOException e) {
+ log.error("Failure writing packet out to switch", e);
+ }
+ }
+ }
+
+ 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)
+ .setSenderProtocolAddress(arpRequest.getTargetProtocolAddress())
+ .setTargetHardwareAddress(arpRequest.getSenderHardwareAddress())
+ .setTargetProtocolAddress(arpRequest.getSenderProtocolAddress());
+
+ Ethernet eth = new Ethernet();
+ eth.setDestinationMACAddress(arpRequest.getSenderHardwareAddress())
+ .setSourceMACAddress(macRequested)
+ .setEtherType(Ethernet.TYPE_ARP)
+ .setPayload(arpReply);
+
+ List<OFAction> actions = new ArrayList<OFAction>();
+ actions.add(new OFActionOutput(pi.getInPort()));
+
+ OFPacketOut po = new OFPacketOut();
+ po.setInPort(OFPort.OFPP_NONE)
+ .setBufferId(-1)
+ .setPacketData(eth.serialize())
+ .setActions(actions)
+ .setActionsLength((short)OFActionOutput.MINIMUM_LENGTH)
+ .setLengthU(OFPacketOut.MINIMUM_LENGTH + OFActionOutput.MINIMUM_LENGTH
+ + po.getPacketData().length);
+
+ List<OFMessage> msgList = new ArrayList<OFMessage>();
+ msgList.add(po);
+
+ try {
+ log.debug("Sending ARP reply to {}/{}", HexString.toHexString(sw.getId()), pi.getInPort());
+ sw.write(msgList, null);
+ sw.flush();
+ } catch (IOException e) {
+ log.warn("Failure writing packet out to switch", e);
+ }
+ }
+
+ private String bytesToStringAddr(byte[] bytes){
+ InetAddress addr;
+ try {
+ addr = InetAddress.getByAddress(bytes);
+ } catch (UnknownHostException e) {
+ // TODO Auto-generated catch block
+ e.printStackTrace();
+ return "";
+ }
+ if (addr == null) return "";
+ else return addr.getHostAddress();
+ }
+}