Giant patch of changes to support OpenFlow 1.3
The following people have contributed to this patch:
- Ali Al-Shabibi <alshabibi.ali@gmail.com>
- Ayaka Koshibe <ayaka@onlab.us>
- Brian O'Connor <bocon@onlab.us>
- Jonathan Hart <jono@onlab.us>
- Matteo Gerola <mgerola@create-net.org>
- Michele Santuari <michele.santuari@create-net.org>
- Pavlin Radoslavov <pavlin@onlab.us>
- Saurav Das <sauravdas@alumni.stanford.edu>
- Toshio Koide <t-koide@onlab.us>
- Yuta HIGUCHI <y-higuchi@onlab.us>
The patch includes the following changes:
- New Floodlight I/O loop / state machine
- New switch/port handling
- New role management (incl. Role.EQUAL)
- Added Floodlight debug framework
- Updates to Controller.java
- Move to Loxigen's OpenflowJ library
- Added OF1.3 support
- Added support for different switches (via DriverManager)
- Updated ONOS modules to use new APIs
- Added and updated unit tests
Change-Id: Ic70a8d50f7136946193d2ba2e4dc0b4bfac5f599
diff --git a/src/main/java/net/onrc/onos/apps/sdnip/SdnIp.java b/src/main/java/net/onrc/onos/apps/sdnip/SdnIp.java
index f4bf28d..135d0a7 100644
--- a/src/main/java/net/onrc/onos/apps/sdnip/SdnIp.java
+++ b/src/main/java/net/onrc/onos/apps/sdnip/SdnIp.java
@@ -17,6 +17,7 @@
import net.floodlightcontroller.core.IFloodlightProviderService;
import net.floodlightcontroller.core.IOFSwitch;
+import net.floodlightcontroller.core.IOFSwitch.PortChangeType;
import net.floodlightcontroller.core.IOFSwitchListener;
import net.floodlightcontroller.core.module.FloodlightModuleContext;
import net.floodlightcontroller.core.module.FloodlightModuleException;
@@ -68,6 +69,7 @@
import org.openflow.protocol.action.OFAction;
import org.openflow.protocol.action.OFActionOutput;
import org.openflow.util.HexString;
+import org.projectfloodlight.openflow.protocol.OFPortDesc;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -83,8 +85,7 @@
import com.googlecode.concurrenttrees.radixinverted.InvertedRadixTree;
public class SdnIp implements IFloodlightModule, ISdnIpService,
- IArpRequester,
- IOFSwitchListener, IConfigInfoService {
+ IArpRequester, IOFSwitchListener, IConfigInfoService {
private static final Logger log = LoggerFactory.getLogger(SdnIp.class);
private final CallerId callerId = new CallerId("SDNIP");
@@ -146,7 +147,7 @@
// True when we have a full mesh of shortest paths between gateways
private volatile boolean topologyReady = false;
- //private SingletonTask topologyChangeDetectorTask;
+ // private SingletonTask topologyChangeDetectorTask;
private SetMultimap<InetAddress, RibUpdate> prefixesWaitingOnArp;
@@ -156,7 +157,7 @@
private Map<InetAddress, Path> pushedPaths;
private Map<Prefix, Path> prefixToPath;
- // private Multimap<Prefix, PushedFlowMod> pushedFlows;
+ // private Multimap<Prefix, PushedFlowMod> pushedFlows;
private Multimap<Prefix, FlowId> pushedFlowIds;
private FlowCache flowCache;
@@ -245,8 +246,7 @@
@Override
public Collection<Class<? extends IFloodlightService>> getModuleServices() {
- Collection<Class<? extends IFloodlightService>> l
- = new ArrayList<>();
+ Collection<Class<? extends IFloodlightService>> l = new ArrayList<>();
l.add(ISdnIpService.class);
l.add(IConfigInfoService.class);
return l;
@@ -254,8 +254,7 @@
@Override
public Map<Class<? extends IFloodlightService>, IFloodlightService> getServiceImpls() {
- Map<Class<? extends IFloodlightService>, IFloodlightService> m
- = new HashMap<>();
+ Map<Class<? extends IFloodlightService>, IFloodlightService> m = new HashMap<>();
m.put(ISdnIpService.class, this);
m.put(IConfigInfoService.class, this);
return m;
@@ -263,8 +262,7 @@
@Override
public Collection<Class<? extends IFloodlightService>> getModuleDependencies() {
- Collection<Class<? extends IFloodlightService>> l
- = new ArrayList<>();
+ Collection<Class<? extends IFloodlightService>> l = new ArrayList<>();
l.add(IFloodlightProviderService.class);
l.add(IRestApiService.class);
l.add(IControllerRegistryService.class);
@@ -289,11 +287,14 @@
restApi = context.getServiceImpl(IRestApiService.class);
proxyArp = context.getServiceImpl(IProxyArpService.class);
- controllerRegistryService = context.getServiceImpl(IControllerRegistryService.class);
+ controllerRegistryService = context
+ .getServiceImpl(IControllerRegistryService.class);
pathRuntime = context.getServiceImpl(IPathCalcRuntimeService.class);
- //ScheduledExecutorService executor = Executors.newScheduledThreadPool(1);
- //topologyChangeDetectorTask = new SingletonTask(executor, new TopologyChangeDetector());
+ // ScheduledExecutorService executor =
+ // Executors.newScheduledThreadPool(1);
+ // topologyChangeDetectorTask = new SingletonTask(executor, new
+ // TopologyChangeDetector());
pathsWaitingOnArp = new HashMap<>();
prefixesWaitingOnArp = Multimaps.synchronizedSetMultimap(
@@ -301,7 +302,7 @@
pushedPaths = new HashMap<>();
prefixToPath = new HashMap<>();
-// pushedFlows = HashMultimap.<Prefix, PushedFlowMod>create();
+ // pushedFlows = HashMultimap.<Prefix, PushedFlowMod>create();
pushedFlowIds = HashMultimap.create();
flowCache = new FlowCache(floodlightProvider);
@@ -439,8 +440,10 @@
RibEntry rib = bgpRoutes.put(prefix.toBinaryString(), update.getRibEntry());
if (rib != null && !rib.equals(update.getRibEntry())) {
- // There was an existing nexthop for this prefix. This update supersedes that,
- // so we need to remove the old flows for this prefix from the switches
+ // There was an existing nexthop for this prefix. This update
+ // supersedes that,
+ // so we need to remove the old flows for this prefix from the
+ // switches
executeDeletePrefix(prefix, rib);
}
@@ -527,11 +530,11 @@
}
/**
- * Add a flow to match dst-IP prefix and rewrite MAC for one IP prefix
- * to all other border switches.
+ * Add a flow to match dst-IP prefix and rewrite MAC for one IP prefix to
+ * all other border switches.
*/
private void addPrefixFlows(Prefix prefix, Interface egressInterface,
- MACAddress nextHopMacAddress) {
+ MACAddress nextHopMacAddress) {
log.debug("Adding flows for prefix {}, next hop mac {}",
prefix, nextHopMacAddress);
@@ -550,10 +553,11 @@
// Create the DataPath object: dstSwitchPort
SwitchPort dstPort =
- new SwitchPort(new Dpid(egressInterface.getDpid()),
- new PortNumber(egressInterface.getPort()));
+ new SwitchPort(new Dpid(egressInterface.getDpid()),
+ new PortNumber(egressInterface.getPort()));
- // We only need one flow mod per switch, so pick one interface on each switch
+ // We only need one flow mod per switch, so pick one interface on each
+ // switch
Map<Long, Interface> srcInterfaces = new HashMap<>();
for (Interface intf : interfaces.values()) {
if (!srcInterfaces.containsKey(intf.getDpid())
@@ -572,8 +576,8 @@
// Create DataPath object: srcSwitchPort
SwitchPort srcPort =
- new SwitchPort(new Dpid(srcInterface.getDpid()),
- new PortNumber(srcInterface.getPort()));
+ new SwitchPort(new Dpid(srcInterface.getDpid()),
+ new PortNumber(srcInterface.getPort()));
DataPath dataPath = new DataPath();
dataPath.setSrcPort(srcPort);
@@ -620,9 +624,11 @@
synchronized (this) {
Prefix prefix = update.getPrefix();
- //if (ptree.remove(prefix, update.getRibEntry())) {
- // TODO check the change of logic here - remove doesn't check that the
- // rib entry was what we expected (and we can't do this concurrently)
+ // if (ptree.remove(prefix, update.getRibEntry())) {
+ // TODO check the change of logic here - remove doesn't check that
+ // the
+ // rib entry was what we expected (and we can't do this
+ // concurrently)
if (bgpRoutes.remove(prefix.toBinaryString())) {
/*
* Only delete flows if an entry was actually removed from the tree.
@@ -644,8 +650,8 @@
Path path = prefixToPath.remove(prefix);
if (path != null) {
- //path could be null if we added to the Ptree but didn't push
- //flows yet because we were waiting to resolve ARP
+ // path could be null if we added to the Ptree but didn't push
+ // flows yet because we were waiting to resolve ARP
path.decrementUsers();
if (path.getUsers() <= 0 && !path.isPermanent()) {
@@ -707,9 +713,8 @@
}*/
}
-
- //TODO test next-hop changes
- //TODO check delete/add synchronization
+ // TODO test next-hop changes
+ // TODO check delete/add synchronization
/**
* On startup, we need to calculate a full mesh of paths between all gateway
@@ -778,15 +783,16 @@
flowPath.setFlowPathType(FlowPathType.FP_TYPE_SHORTEST_PATH);
flowPath.setFlowPathUserState(FlowPathUserState.FP_USER_ADD);
- // Insert the dest-mac based forwarding flow entry to the non-first-hop switches
+ // Insert the dest-mac based forwarding flow entry to the non-first-hop
+ // switches
FlowPathFlags flowPathFlags = new FlowPathFlags();
flowPathFlags.setFlags(FlowPathFlags.DISCARD_FIRST_HOP_ENTRY);
flowPath.setFlowPathFlags(flowPathFlags);
// Create the DataPath object: dstSwitchPort
SwitchPort dstPort =
- new SwitchPort(new Dpid(dstInterface.getDpid()),
- new PortNumber(dstInterface.getPort()));
+ new SwitchPort(new Dpid(dstInterface.getDpid()),
+ new PortNumber(dstInterface.getPort()));
for (Interface srcInterface : interfaces.values()) {
@@ -799,8 +805,8 @@
// Create the DataPath object: srcSwitchPort
SwitchPort srcPort =
- new SwitchPort(new Dpid(srcInterface.getDpid()),
- new PortNumber(srcInterface.getPort()));
+ new SwitchPort(new Dpid(srcInterface.getDpid()),
+ new PortNumber(srcInterface.getPort()));
DataPath dataPath = new DataPath();
dataPath.setSrcPort(srcPort);
@@ -814,7 +820,8 @@
flowPath.setFlowEntryMatch(flowEntryMatch);
// NOTE: No need to add ACTION_OUTPUT. It is implied when creating
- // Shortest Path Flow, and is always the last action for the Flow Entries
+ // Shortest Path Flow, and is always the last action for the Flow
+ // Entries
log.debug("FlowPath of MAC based forwarding: {}", flowPath.toString());
// TODO: Add the flow by using the new Path Intent framework
/*
@@ -832,11 +839,11 @@
@Override
public void beginRoutingNew() {
- setupBgpPathsNew();
+ setupBgpPathsNew();
- //setupFullMesh();
+ // setupFullMesh();
- //Suppress link discovery on external-facing router ports
+ // Suppress link discovery on external-facing router ports
for (Interface intf : interfaces.values()) {
linkDiscoveryService.disableDiscoveryOnPort(intf.getDpid(), intf.getPort());
@@ -851,37 +858,39 @@
}
/**
- * Setup the Paths to the BGP Daemon.
- *
- * Run a loop for all of the bgpPeers
- * Push flow from BGPd to the peer
- * Push flow from peer to BGPd
- * Parameters to pass to the intent are as follows:
- * String id,
- * long srcSwitch, long srcPort, long srcMac, int srcIP,
- * long dstSwitch, long dstPort, long dstMac, int dstIP
+ * Setup the Paths to the BGP Daemon. Run a loop for all of the bgpPeers
+ * Push flow from BGPd to the peer Push flow from peer to BGPd Parameters to
+ * pass to the intent are as follows: String id, long srcSwitch, long
+ * srcPort, long srcMac, int srcIP, long dstSwitch, long dstPort, long
+ * dstMac, int dstIP
*/
private void setupBgpPathsNew() {
IntentOperationList operations = new IntentOperationList();
for (BgpPeer bgpPeer : bgpPeers.values()) {
Interface peerInterface = interfaces.get(bgpPeer.getInterfaceName());
- //Inet4Address.
+ // Inet4Address.
int srcIP = InetAddresses.coerceToInteger(peerInterface.getIpAddress());
int dstIP = InetAddresses.coerceToInteger(bgpPeer.getIpAddress());
- String fwdIntentId = caller + ":" + controllerRegistryService.getNextUniqueId();
- String bwdIntentId = caller + ":" + controllerRegistryService.getNextUniqueId();
+ String fwdIntentId = caller + ":"
+ + controllerRegistryService.getNextUniqueId();
+ String bwdIntentId = caller + ":"
+ + controllerRegistryService.getNextUniqueId();
SwitchPort srcPort =
- new SwitchPort(bgpdAttachmentPoint.dpid(),
- bgpdAttachmentPoint.port());
+ new SwitchPort(bgpdAttachmentPoint.dpid(),
+ bgpdAttachmentPoint.port());
SwitchPort dstPort =
- new SwitchPort(new Dpid(peerInterface.getDpid()),
- new PortNumber(peerInterface.getSwitchPort().port()));
+ new SwitchPort(new Dpid(peerInterface.getDpid()),
+ new PortNumber(peerInterface.getSwitchPort().port()));
ShortestPathIntent fwdIntent = new ShortestPathIntent(fwdIntentId,
- srcPort.dpid().value(), srcPort.port().value(), ShortestPathIntent.EMPTYMACADDRESS, srcIP,
- dstPort.dpid().value(), dstPort.port().value(), ShortestPathIntent.EMPTYMACADDRESS, dstIP);
+ srcPort.dpid().value(), srcPort.port().value(),
+ ShortestPathIntent.EMPTYMACADDRESS, srcIP,
+ dstPort.dpid().value(), dstPort.port().value(),
+ ShortestPathIntent.EMPTYMACADDRESS, dstIP);
ShortestPathIntent bwdIntent = new ShortestPathIntent(bwdIntentId,
- dstPort.dpid().value(), dstPort.port().value(), ShortestPathIntent.EMPTYMACADDRESS, dstIP,
- srcPort.dpid().value(), srcPort.port().value(), ShortestPathIntent.EMPTYMACADDRESS, srcIP);
+ dstPort.dpid().value(), dstPort.port().value(),
+ ShortestPathIntent.EMPTYMACADDRESS, dstIP,
+ srcPort.dpid().value(), srcPort.port().value(),
+ ShortestPathIntent.EMPTYMACADDRESS, srcIP);
IntentOperation.Operator operator = IntentOperation.Operator.ADD;
operations.add(operator, fwdIntent);
operations.add(operator, bwdIntent);
@@ -914,11 +923,13 @@
flowEntryMatch.enableEthernetFrameType(Ethernet.TYPE_IPV4);
// Match both source address and dest address
- IPv4Net dstIPv4Net = new IPv4Net(bgpPeer.getIpAddress().getHostAddress() + "/32");
+ IPv4Net dstIPv4Net = new IPv4Net(bgpPeer.getIpAddress().getHostAddress()
+ + "/32");
flowEntryMatch.enableDstIPv4Net(dstIPv4Net);
- IPv4Net srcIPv4Net = new IPv4Net(peerInterface.getIpAddress().getHostAddress() + "/32");
+ IPv4Net srcIPv4Net = new IPv4Net(peerInterface.getIpAddress()
+ .getHostAddress() + "/32");
flowEntryMatch.enableSrcIPv4Net(srcIPv4Net);
// Match TCP protocol
@@ -935,13 +946,13 @@
DataPath dataPath = new DataPath();
SwitchPort srcPort =
- new SwitchPort(bgpdAttachmentPoint.dpid(),
- bgpdAttachmentPoint.port());
+ new SwitchPort(bgpdAttachmentPoint.dpid(),
+ bgpdAttachmentPoint.port());
dataPath.setSrcPort(srcPort);
SwitchPort dstPort =
- new SwitchPort(new Dpid(peerInterface.getDpid()),
- new PortNumber(peerInterface.getSwitchPort().port()));
+ new SwitchPort(new Dpid(peerInterface.getDpid()),
+ new PortNumber(peerInterface.getSwitchPort().port()));
dataPath.setDstPort(dstPort);
flowPath.setDataPath(dataPath);
@@ -1070,13 +1081,10 @@
// match ICMP protocol BGP -> Peer
flowPath.setFlowId(new FlowId());
-
flowEntryMatch.enableDstIPv4Net(dstIPv4Net);
flowEntryMatch.enableSrcIPv4Net(srcIPv4Net);
flowPath.setFlowEntryMatch(flowEntryMatch);
-
flowPath.setDataPath(dataPath);
-
log.debug("ICMP flowPath: {}", flowPath.toString());
// TODO: Add the flow by using the new Path Intent framework
@@ -1111,11 +1119,14 @@
log.debug("Pushing path to {} at {} on {}",
path.getDstIpAddress().getHostAddress(), macAddress,
path.getDstInterface().getSwitchPort());
- // These paths should always be to BGP peers. Paths to non-peers are
+ // These paths should always be to BGP peers. Paths to non-peers
+ // are
// handled once the first prefix is ready to push
if (pushedPaths.containsKey(path.getDstIpAddress())) {
- // A path already got pushed to this endpoint while we were waiting
- // for ARP. We'll copy over the permanent attribute if it is set on this path.
+ // A path already got pushed to this endpoint while we were
+ // waiting
+ // for ARP. We'll copy over the permanent attribute if it is
+ // set on this path.
if (path.isPermanent()) {
pushedPaths.get(path.getDstIpAddress()).setPermanent();
}
@@ -1135,9 +1146,12 @@
if (rib != null && rib.equals(update.getRibEntry())) {
log.debug("Pushing prefix {} next hop {}", update.getPrefix(),
rib.getNextHop().getHostAddress());
- // We only push prefix flows if the prefix is still in the ptree
- // and the next hop is the same as our update. The prefix could
- // have been removed while we were waiting for the ARP, or the
+ // We only push prefix flows if the prefix is still in the
+ // ptree
+ // and the next hop is the same as our update. The prefix
+ // could
+ // have been removed while we were waiting for the ARP, or
+ // the
// next hop could have changed.
executeRibAdd(update);
} else {
@@ -1247,7 +1261,7 @@
setupBgpPaths();
setupFullMesh();
- //Suppress link discovery on external-facing router ports
+ // Suppress link discovery on external-facing router ports
for (Interface intf : interfaces.values()) {
linkDiscoveryService.disableDiscoveryOnPort(intf.getDpid(), intf.getPort());
}
@@ -1285,7 +1299,8 @@
*/
}
- // Actually we only need to go half way round to verify full mesh connectivity
+ // Actually we only need to go half way round to verify full mesh
+ // connectivity
private void checkTopologyReady() {
for (Interface dstInterface : interfaces.values()) {
for (Interface srcInterface : interfaces.values()) {
@@ -1329,25 +1344,25 @@
try {
RibUpdate update = ribUpdates.take();
switch (update.getOperation()) {
- case UPDATE:
- if (validateUpdate(update)) {
- processRibAdd(update);
- } else {
- log.debug("Rib UPDATE out of order: {} via {}",
- update.getPrefix(), update.getRibEntry().getNextHop());
- }
- break;
- case DELETE:
- if (validateUpdate(update)) {
- processRibDelete(update);
- } else {
- log.debug("Rib DELETE out of order: {} via {}",
- update.getPrefix(), update.getRibEntry().getNextHop());
- }
- break;
- default:
- log.error("Unknown operation {}", update.getOperation());
- break;
+ case UPDATE:
+ if (validateUpdate(update)) {
+ processRibAdd(update);
+ } else {
+ log.debug("Rib UPDATE out of order: {} via {}",
+ update.getPrefix(), update.getRibEntry().getNextHop());
+ }
+ break;
+ case DELETE:
+ if (validateUpdate(update)) {
+ processRibDelete(update);
+ } else {
+ log.debug("Rib DELETE out of order: {} via {}",
+ update.getPrefix(), update.getRibEntry().getNextHop());
+ }
+ break;
+ default:
+ log.error("Unknown operation {}", update.getOperation());
+ break;
}
} catch (InterruptedException e) {
log.debug("Interrupted while taking from updates queue", e);
@@ -1368,10 +1383,11 @@
RibEntry oldEntry = bgpRoutes.getValueForExactKey(
update.getPrefix().toBinaryString());
- //If there is no existing entry we must assume this is the most recent
- //update. However this might not always be the case as we might have a
- //POST then DELETE reordering.
- //if (oldEntry == null || !newEntry.getNextHop().equals(oldEntry.getNextHop())) {
+ // If there is no existing entry we must assume this is the most recent
+ // update. However this might not always be the case as we might have a
+ // POST then DELETE reordering.
+ // if (oldEntry == null ||
+ // !newEntry.getNextHop().equals(oldEntry.getNextHop())) {
if (oldEntry == null) {
return true;
}
@@ -1387,7 +1403,7 @@
}
return newEntry.getSysUpTime() == oldEntry.getSysUpTime() &&
- newEntry.getSequenceNum() > oldEntry.getSequenceNum();
+ newEntry.getSequenceNum() > oldEntry.getSequenceNum();
}
private Interface longestInterfacePrefixMatch(InetAddress address) {
@@ -1438,8 +1454,17 @@
}
*/
+ // ******************
+ // IOFSwitchListener
+ // ******************
+
@Override
- public void addedSwitch(IOFSwitch sw) {
+ public void switchActivatedMaster(long swId) {
+ IOFSwitch sw = floodlightProvider.getSwitch(swId);
+ if (sw == null) {
+ log.warn("Added switch not available {} ", swId);
+ return;
+ }
if (!topologyReady) {
sw.clearAllFlowMods();
}
@@ -1448,12 +1473,30 @@
}
@Override
- public void removedSwitch(IOFSwitch sw) {
+ public void switchActivatedEqual(long swId) {
+ // TODO Auto-generated method stub
+
+ }
+
+ @Override
+ public void switchMasterToEqual(long swId) {
+ // TODO Auto-generated method stub
+
+ }
+
+ @Override
+ public void switchEqualToMaster(long swId) {
+ // for now treat as switchActivatedMaster
+ switchActivatedMaster(swId);
+ }
+
+ @Override
+ public void switchDisconnected(long swId) {
// Not used
}
@Override
- public void switchPortChanged(Long switchId) {
+ public void switchPortChanged(long swId, OFPortDesc port, PortChangeType pct) {
// Not used
}