using MultiPointToSinglePointIntent
1. adapt SDN-IP existing code to the new intent framework
2. add comments to methods
3. change to PacketMatchBuilder and IdBlockAllocatorBasedIntentIdGenerator
4. clean up the code in SdnIp.java, which includes:
TopologyChangeDetector, checkTopologyReady,
setupFullMesh for layer2 forwarding, flow path relative code,
device storage relative code, flow cache relative code,
flowMod and flowId relative code, and IOFSwitchListener.
This change is for ONOS-1858 (not including the code testing).
Change-Id: I19c1dcca3a2e2a67b307edf566279b7e8554bb84
diff --git a/src/main/java/net/onrc/onos/apps/sdnip/ISdnIpService.java b/src/main/java/net/onrc/onos/apps/sdnip/ISdnIpService.java
index 6189aa7..a2abaa1 100644
--- a/src/main/java/net/onrc/onos/apps/sdnip/ISdnIpService.java
+++ b/src/main/java/net/onrc/onos/apps/sdnip/ISdnIpService.java
@@ -52,5 +52,6 @@
/**
* Start SDN-IP Routing.
*/
- public void beginRoutingNew();
+ public void beginRouting();
+
}
diff --git a/src/main/java/net/onrc/onos/apps/sdnip/Prefix.java b/src/main/java/net/onrc/onos/apps/sdnip/Prefix.java
index d4e730d..81010d6 100644
--- a/src/main/java/net/onrc/onos/apps/sdnip/Prefix.java
+++ b/src/main/java/net/onrc/onos/apps/sdnip/Prefix.java
@@ -192,6 +192,16 @@
return Arrays.copyOf(address, address.length);
}
+ /**
+ * Gets the InetAddress.
+ *
+ * @return the inetAddress
+ */
+ public InetAddress getInetAddress() {
+ return inetAddress;
+ }
+
+
@Override
public boolean equals(Object other) {
if (!(other instanceof Prefix)) {
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 d6713d5..35c3eaa 100644
--- a/src/main/java/net/onrc/onos/apps/sdnip/SdnIp.java
+++ b/src/main/java/net/onrc/onos/apps/sdnip/SdnIp.java
@@ -6,25 +6,25 @@
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.concurrent.BlockingQueue;
+import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.LinkedBlockingQueue;
-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;
import net.floodlightcontroller.core.module.IFloodlightModule;
import net.floodlightcontroller.core.module.IFloodlightService;
import net.floodlightcontroller.restserver.IRestApiService;
import net.floodlightcontroller.util.MACAddress;
+import net.onrc.onos.api.newintent.IntentService;
+import net.onrc.onos.api.newintent.MultiPointToSinglePointIntent;
import net.onrc.onos.apps.proxyarp.IArpRequester;
import net.onrc.onos.apps.proxyarp.IProxyArpService;
import net.onrc.onos.apps.sdnip.RibUpdate.Operation;
@@ -36,8 +36,13 @@
import net.onrc.onos.core.intent.runtime.IPathCalcRuntimeService;
import net.onrc.onos.core.linkdiscovery.ILinkDiscoveryService;
import net.onrc.onos.core.main.config.IConfigInfoService;
+import net.onrc.onos.core.matchaction.action.ModifyDstMacAction;
+import net.onrc.onos.core.matchaction.match.PacketMatch;
+import net.onrc.onos.core.matchaction.match.PacketMatchBuilder;
+import net.onrc.onos.core.newintent.IdBlockAllocatorBasedIntentIdGenerator;
import net.onrc.onos.core.registry.IControllerRegistryService;
import net.onrc.onos.core.util.Dpid;
+import net.onrc.onos.core.util.IPv4;
import net.onrc.onos.core.util.PortNumber;
import net.onrc.onos.core.util.SwitchPort;
import net.sf.json.JSONArray;
@@ -49,7 +54,6 @@
import org.codehaus.jackson.JsonParseException;
import org.codehaus.jackson.map.JsonMappingException;
import org.codehaus.jackson.map.ObjectMapper;
-import org.projectfloodlight.openflow.protocol.OFPortDesc;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -63,12 +67,15 @@
import com.googlecode.concurrenttrees.radixinverted.ConcurrentInvertedRadixTree;
import com.googlecode.concurrenttrees.radixinverted.InvertedRadixTree;
+/**
+ * This class sets up BGP paths, handles RIB updates and relative intents.
+ * TODO: Thread-safe.
+ */
public class SdnIp implements IFloodlightModule, ISdnIpService,
- IArpRequester, IOFSwitchListener, IConfigInfoService {
+ IArpRequester, IConfigInfoService {
private static final Logger log = LoggerFactory.getLogger(SdnIp.class);
- private IFloodlightProviderService floodlightProvider;
private ILinkDiscoveryService linkDiscoveryService;
private IRestApiService restApi;
private IProxyArpService proxyArp;
@@ -86,31 +93,13 @@
/* ShortestPath Intent Variables */
private final String caller = "SdnIp";
private IControllerRegistryService controllerRegistryService;
+ private IntentService intentService;
private IPathCalcRuntimeService pathRuntime;
/* Shortest Intent Path Variables */
+ private IdBlockAllocatorBasedIntentIdGenerator intentIdGenerator;
+ //private static final short ARP_PRIORITY = 20;
- private static final short ARP_PRIORITY = 20;
-
- // The fields below are unused after the move to FlowManager.
- // Remove them if no longer needed.
- /*
- // We need to identify our flows somehow, in lieu of an OS-wide mechanism
- // to hand out cookie IDs to prevent conflicts.
- private static final long APP_COOKIE = 0xa0000000000000L;
- // Cookie for flows that do L2 forwarding within SDN domain to egress routers
- private static final long L2_FWD_COOKIE = APP_COOKIE + 1;
- // Cookie for flows in ingress switches that rewrite the MAC address
- private static final long MAC_RW_COOKIE = APP_COOKIE + 2;
- // Cookie for flows that setup BGP paths
- private static 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
- private static final short SDNIP_PRIORITY = 10;
- */
-
- private static final short BGP_PORT = 179;
-
- private static final int TOPO_DETECTION_WAIT = 2; // seconds
+ //private static final short BGP_PORT = 179;
// Configuration stuff
private List<String> switches;
@@ -120,70 +109,18 @@
private MACAddress bgpdMacAddress;
private short vlan;
- // True when all switches have connected
- private volatile boolean switchesConnected = false;
- // True when we have a full mesh of shortest paths between gateways
- private volatile boolean topologyReady = false;
-
- // private SingletonTask topologyChangeDetectorTask;
-
private SetMultimap<InetAddress, RibUpdate> prefixesWaitingOnArp;
- private Map<InetAddress, Path> pathsWaitingOnArp;
-
private ExecutorService bgpUpdatesExecutor;
- private Map<InetAddress, Path> pushedPaths;
- private Map<Prefix, Path> prefixToPath;
- // private Multimap<Prefix, PushedFlowMod> pushedFlows;
- //private Multimap<Prefix, FlowId> pushedFlowIds;
+ private ConcurrentHashMap<Prefix, MultiPointToSinglePointIntent> pushedRouteIntents;
- // XXX FlowCache has been removed
- //private FlowCache flowCache;
-
- // TODO: Fix for the new Topology Network Graph
- // private volatile Topology topology = null;
-
- /*
- private class TopologyChangeDetector implements Runnable {
- @Override
- public void run() {
- log.debug("Running topology change detection task");
- // TODO: Fix the code below after topoLinkService was removed
- synchronized (linkUpdates) {
-
- ITopoLinkService topoLinkService = new TopoLinkServiceImpl();
-
- List<Link> activeLinks = topoLinkService.getActiveLinks();
-
- 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)){
- it.remove();
- }
- }
- }
-
- if (!topologyReady) {
- if (linkUpdates.isEmpty()) {
- // All updates have been seen in network map.
- // We can check if topology is ready
- log.debug("No known 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 - {} links missing", linkUpdates.size());
- topologyChangeDetectorTask.reschedule(TOPO_DETECTION_WAIT, TimeUnit.SECONDS);
- }
- }
- }
- }
- */
-
+ /**
+ * SDN-IP application has a configuration file. This method is to read all
+ * the info from this file.
+ *
+ * @param configFilename the name of configuration file for SDN-IP application
+ */
private void readConfiguration(String configFilename) {
File gatewaysFile = new File(configFilename);
ObjectMapper mapper = new ObjectMapper();
@@ -215,7 +152,7 @@
throw new ConfigurationRuntimeException("Error in JSON file", e);
}
- // Populate the interface Patricia Tree
+ // Populate the interface InvertedRadixTree
for (Interface intf : interfaces.values()) {
Prefix prefix = new Prefix(intf.getIpAddress().getAddress(),
intf.getPrefixLength());
@@ -242,7 +179,6 @@
@Override
public Collection<Class<? extends IFloodlightService>> getModuleDependencies() {
Collection<Class<? extends IFloodlightService>> l = new ArrayList<>();
- l.add(IFloodlightProviderService.class);
l.add(IRestApiService.class);
l.add(IControllerRegistryService.class);
l.add(IPathCalcRuntimeService.class);
@@ -260,30 +196,23 @@
ribUpdates = new LinkedBlockingQueue<>();
- // Register floodlight provider and REST handler.
- floodlightProvider = context.getServiceImpl(IFloodlightProviderService.class);
- linkDiscoveryService = context.getServiceImpl(ILinkDiscoveryService.class);
+ // Register REST handler.
restApi = context.getServiceImpl(IRestApiService.class);
proxyArp = context.getServiceImpl(IProxyArpService.class);
controllerRegistryService = context
.getServiceImpl(IControllerRegistryService.class);
- pathRuntime = context.getServiceImpl(IPathCalcRuntimeService.class);
- // ScheduledExecutorService executor =
- // Executors.newScheduledThreadPool(1);
- // topologyChangeDetectorTask = new SingletonTask(executor, new
- // TopologyChangeDetector());
+ intentIdGenerator = new IdBlockAllocatorBasedIntentIdGenerator(
+ controllerRegistryService);
- pathsWaitingOnArp = new HashMap<>();
+ // TODO: initialize intentService
+
+ pushedRouteIntents = new ConcurrentHashMap<>();
+
prefixesWaitingOnArp = Multimaps.synchronizedSetMultimap(
HashMultimap.<InetAddress, RibUpdate>create());
- pushedPaths = new HashMap<>();
- prefixToPath = new HashMap<>();
- // pushedFlows = HashMultimap.<Prefix, PushedFlowMod>create();
- // pushedFlowIds = HashMultimap.create();
-
//flowCache = new FlowCache(floodlightProvider);
bgpUpdatesExecutor = Executors.newSingleThreadExecutor(
@@ -321,7 +250,6 @@
public void startUp(FloodlightModuleContext context) {
restApi.addRestletRoutable(new SdnIpWebRoutable());
restApi.addRestletRoutable(new SdnIpWebRoutableNew());
- floodlightProvider.addOFSwitchListener(this);
// Retrieve the RIB from BGPd during startup
retrieveRib();
@@ -347,6 +275,12 @@
return routerId;
}
+ /**
+ * SDN-IP application will fetch all rib entries from BGPd when it starts.
+ * Especially when we restart SDN-IP application while the BGPd has been
+ * running all the time. Then before SDN-IP application re-connects to BGPd,
+ * there are already lots of rib entries in BGPd.
+ */
private void retrieveRib() {
String url = "http://" + bgpdRestIp + "/wm/bgp/" + routerId;
String response = RestClient.get(url);
@@ -400,6 +334,11 @@
}
}
+ /**
+ * Put RIB update to RIB update queue.
+ *
+ * @param update RIB update
+ */
@Override
public void newRibUpdate(RibUpdate update) {
try {
@@ -410,6 +349,15 @@
}
}
+ /**
+ * Process adding RIB update.
+ * Put new RIB update into InvertedRadixTree. If there was an existing nexthop
+ * for this prefix, but the next hop was different, then execute deleting old
+ * RIB update. If the next hop is the SDN domain, we do not handle it at the
+ * moment. Otherwise, execute adding RIB.
+ *
+ * @param update RIB update
+ */
private void processRibAdd(RibUpdate update) {
synchronized (this) {
Prefix prefix = update.getPrefix();
@@ -420,10 +368,9 @@
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
- executeDeletePrefix(prefix, rib);
+ // supersedes that, so we need to remove the old flows for this
+ // prefix from the switches
+ executeDeleteRoute(prefix, rib);
}
if (update.getRibEntry().getNextHop().equals(
@@ -439,405 +386,157 @@
}
}
+ /**
+ * Execute adding RIB update.
+ * Find out the egress Interface and MAC address of next hop router for this
+ * RIB update. If the MAC address can not be found in ARP cache, then this
+ * prefix will be put in prefixesWaitingOnArp queue. Otherwise, new flow
+ * intent will be created and installed.
+ *
+ * @param update RIB update
+ */
private void executeRibAdd(RibUpdate update) {
- // TODO: Fix the code below. Note that "deviceStorage" was removed.
- /*
Prefix prefix = update.getPrefix();
RibEntry rib = update.getRibEntry();
- InetAddress dstIpAddress = rib.getNextHop();
- MACAddress nextHopMacAddress = null;
-
- // See if we know the MAC address of the next hop
- // TODO if we do not treat the next hop as a device in the future, we need to update this
- IDeviceObject nextHopDevice =
- deviceStorage.getDeviceByIP(InetAddresses.coerceToInteger(dstIpAddress));
-
- if (nextHopDevice == null){
- log.debug("NextHopDevice for IP: {} is null", dstIpAddress);
- prefixesWaitingOnArp.put(dstIpAddress,
- new RibUpdate(Operation.UPDATE, prefix, rib));
- proxyArp.sendArpRequest(dstIpAddress, this, true);
- return;
-
- }
- nextHopMacAddress = MACAddress.valueOf(nextHopDevice.getMACAddress());
+ InetAddress nextHopIpAddress = rib.getNextHop();
// Find the attachment point (egress interface) of the next hop
Interface egressInterface = null;
- if (bgpPeers.containsKey(dstIpAddress)) {
- //Route to a peer
- log.debug("Route to peer {}", dstIpAddress);
- BgpPeer peer = bgpPeers.get(dstIpAddress);
+
+ if (bgpPeers.containsKey(nextHopIpAddress)) {
+ // Route to a peer
+ log.debug("Route to peer {}", nextHopIpAddress);
+ BgpPeer peer = bgpPeers.get(nextHopIpAddress);
egressInterface = interfaces.get(peer.getInterfaceName());
} else {
- //Route to non-peer
- log.debug("Route to non-peer {}", dstIpAddress);
- egressInterface = interfacePtree.match(
- new Prefix(dstIpAddress.getAddress(), 32));
+ // Route to non-peer
+ log.debug("Route to non-peer {}", nextHopIpAddress);
+ egressInterface = getOutgoingInterface(nextHopIpAddress);
if (egressInterface == null) {
- log.warn("No outgoing interface found for {}", dstIpAddress.getHostAddress());
+ log.warn("No outgoing interface found for {}", nextHopIpAddress
+ .getHostAddress());
return;
}
}
+ // See if we know the MAC address of the next hop
+ MACAddress nextHopMacAddress = proxyArp.getMacAddress(nextHopIpAddress);
+
if (nextHopMacAddress == null) {
- prefixesWaitingOnArp.put(dstIpAddress,
+ prefixesWaitingOnArp.put(nextHopIpAddress,
new RibUpdate(Operation.UPDATE, prefix, rib));
- proxyArp.sendArpRequest(dstIpAddress, this, true);
+ proxyArp.sendArpRequest(nextHopIpAddress, this, true);
return;
} else {
- if (!bgpPeers.containsKey(dstIpAddress)) {
- //If the prefix is for a non-peer we need to ensure there's a path,
- //and push one if there isn't.
- Path path = pushedPaths.get(dstIpAddress);
- if (path == null) {
- path = new Path(egressInterface, dstIpAddress);
- calculateAndPushPath(path, nextHopMacAddress);
- pushedPaths.put(dstIpAddress, path);
- }
- path.incrementUsers();
- prefixToPath.put(prefix, path);
- }
+ //For all prefixes we need to add a intent for each of them
+ addRouteIntent(prefix, egressInterface, nextHopMacAddress);
- //For all prefixes we need to add the first-hop mac-rewriting flows
- addPrefixFlows(prefix, egressInterface, nextHopMacAddress);
}
- */
+
}
/**
- * Add a flow to match dst-IP prefix and rewrite MAC for one IP prefix to
- * all other border switches.
+ * Install a flow intent for a prefix.
+ * Intent will match dst IP prefix and rewrite dst MAC address at all other
+ * border switches, then forward packets according to dst MAC address.
+ *
+ * @param prefix IP prefix from BGP route
+ * @param egressInterface egress Interface connected to next hop router
+ * @param nextHopMacAddress MAC address of next hop router
*/
- private void addPrefixFlows(Prefix prefix, Interface egressInterface,
+ private void addRouteIntent(Prefix prefix, Interface egressInterface,
MACAddress nextHopMacAddress) {
- log.debug("Adding flows for prefix {}, next hop mac {}",
+ log.debug("Adding intent for prefix {}, next hop mac {}",
prefix, nextHopMacAddress);
- /*
- FlowPath flowPath = new FlowPath();
- flowPath.setInstallerId(callerId);
+ MultiPointToSinglePointIntent pushedIntent = pushedRouteIntents.get(prefix);
- // Set flowPath FlowPathType and FlowPathUserState
- flowPath.setFlowPathType(FlowPathType.FP_TYPE_SHORTEST_PATH);
- flowPath.setFlowPathUserState(FlowPathUserState.FP_USER_ADD);
+ // Just for testing.
+ if (pushedIntent != null) {
+ log.error("There should not be a pushed intent: {}", pushedIntent);
+ }
- // Insert dst-ip prefix based forwarding and MAC rewrite flow entry
- // only to the first-host switches
- FlowPathFlags flowPathFlags = new FlowPathFlags();
- flowPathFlags.setFlags(FlowPathFlags.KEEP_ONLY_FIRST_HOP_ENTRY);
- flowPath.setFlowPathFlags(flowPathFlags);
+ SwitchPort egressPort = egressInterface.getSwitchPort();
- // Create the DataPath object: dstSwitchPort
- SwitchPort dstPort =
- new SwitchPort(new Dpid(egressInterface.getDpid()),
- new PortNumber(egressInterface.getPort()));
+ Set<SwitchPort> ingressPorts = new HashSet<SwitchPort>();
- // 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())
- && !intf.equals(egressInterface)) {
- srcInterfaces.put(intf.getDpid(), intf);
+ if (!intf.equals(egressInterface)) {
+ SwitchPort srcPort = intf.getSwitchPort();
+ ingressPorts.add(srcPort);
}
}
- for (Interface srcInterface : srcInterfaces.values()) {
- if (egressInterface.equals(srcInterface)) {
- continue;
- }
+ // Match the destination IP prefix at the first hop
+ PacketMatchBuilder builder = new PacketMatchBuilder();
+ builder.setDstIp(new IPv4(InetAddresses
+ .coerceToInteger(prefix.getInetAddress())),
+ (short) prefix.getPrefixLength());
+ PacketMatch packetMatch = builder.build();
- // Create flowPath FlowId
- flowPath.setFlowId(new FlowId());
+ // Rewrite the destination MAC address
+ ModifyDstMacAction modifyDstMacAction =
+ new ModifyDstMacAction(nextHopMacAddress);
- // Create DataPath object: srcSwitchPort
- SwitchPort srcPort =
- new SwitchPort(new Dpid(srcInterface.getDpid()),
- new PortNumber(srcInterface.getPort()));
+ MultiPointToSinglePointIntent intent =
+ new MultiPointToSinglePointIntent(intentIdGenerator.getNewId(),
+ packetMatch, modifyDstMacAction, ingressPorts, egressPort);
- DataPath dataPath = new DataPath();
- dataPath.setSrcPort(srcPort);
- dataPath.setDstPort(dstPort);
- flowPath.setDataPath(dataPath);
+ intentService.submit(intent);
- // Create flow path matching condition(s): IPv4 Prefix
- FlowEntryMatch flowEntryMatch = new FlowEntryMatch();
- flowEntryMatch.enableEthernetFrameType(Ethernet.TYPE_IPV4);
- IPv4Net dstIPv4Net = new IPv4Net(prefix.toString());
- flowEntryMatch.enableDstIPv4Net(dstIPv4Net);
- flowPath.setFlowEntryMatch(flowEntryMatch);
-
- *
- * Create the Flow Entry Action(s): dst-MAC rewrite action
- *
- FlowEntryActions flowEntryActions = new FlowEntryActions();
- FlowEntryAction flowEntryAction1 = new FlowEntryAction();
- flowEntryAction1.setActionSetEthernetDstAddr(nextHopMacAddress);
- // flowEntryAction1.actionSetEthernetDstAddr(nextHopMacAddress);
- flowEntryActions.addAction(flowEntryAction1);
- flowPath.setFlowEntryActions(flowEntryActions);
-
- // Flow Path installation, only to first hop switches
- // TODO: Add the flow by using the new Path Intent framework
- /*
- if (flowManagerService.addFlow(flowPath) == null) {
- log.error("Failed to install flow path to the first hop for " +
- "prefix: {}, nextHopMacAddress: {}", prefix.getAddress(),
- nextHopMacAddress);
- }
- else {
- log.debug("Successfully installed flow path to the first hop " +
- "for prefix: {}, nextHopMacAddress: {}", prefix.getAddress(),
- nextHopMacAddress);
-
- pushedFlowIds.put(prefix, flowPath.flowId());
- }
-
- }
- */
+ // Maintain the Intent
+ pushedRouteIntents.put(prefix, intent);
}
+ /**
+ * Remove prefix from InvertedRadixTree, if success, then try to delete the
+ * relative intent.
+ *
+ * @param update RIB update
+ */
private void processRibDelete(RibUpdate update) {
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
+ // 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.
* If no entry was removed, the <prefix, nexthop> wasn't there so
* it's probably already been removed and we don't need to do anything
*/
- executeDeletePrefix(prefix, update.getRibEntry());
+ executeDeleteRoute(prefix, update.getRibEntry());
+
}
}
}
- private void executeDeletePrefix(Prefix prefix, RibEntry ribEntry) {
- deletePrefixFlows(prefix);
-
- log.debug("Deleting {} to {}", prefix, ribEntry.getNextHop());
-
- if (!bgpPeers.containsKey(ribEntry.getNextHop())) {
- log.debug("Getting path for route with non-peer nexthop");
- 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.decrementUsers();
- if (path.getUsers() <= 0 && !path.isPermanent()) {
- deletePath(path);
- pushedPaths.remove(path.getDstIpAddress());
- }
- }
- }
- }
-
- // TODO have not tested this module
- private void deletePrefixFlows(Prefix prefix) {
- log.debug("Deleting flows for prefix {}", prefix);
-
- //
- // TODO: Delete the flow by using the new Path Intent framework
- // NOTE: During the refactoring of the code below, if obtaining
- // the values of the removed flowIds is needed, the first
- // removeAll() statement should be replaced with the second removeAll()
- // statement.
- //
- // pushedFlowIds.removeAll(prefix);
- /*
- Collection<FlowId> flowIds = pushedFlowIds.removeAll(prefix);
- for (FlowId flowId : flowIds) {
- if (log.isTraceEnabled()) {
- //Trace the flow status by flowPath in the switch before deleting it
- log.trace("Pushing a DELETE flow mod to flowPath : {}",
- flowManagerService.getFlow(flowId).toString());
- }
-
- if( flowManagerService.deleteFlow(flowId))
- {
- log.debug("Successfully deleted FlowId: {}",flowId);
- }
- else
- {
- log.debug("Failed to delete FlowId: {}",flowId);
- }
- }
- */
- }
-
- // TODO need to record the path and then delete here
- private void deletePath(Path path) {
- log.debug("Deleting flows for path to {}",
- path.getDstIpAddress().getHostAddress());
-
- // TODO need update
- /*for (PushedFlowMod pfm : path.getFlowMods()) {
- if (log.isTraceEnabled()) {
- log.trace("Pushing a DELETE flow mod to {}, dst MAC {}",
- new Object[] {HexString.toHexString(pfm.getDpid()),
- HexString.toHexString(pfm.getFlowMod().getMatch().getDataLayerDestination())
- });
- }
-
- sendDeleteFlowMod(pfm.getFlowMod(), pfm.getDpid());
- }*/
- }
-
- // TODO test next-hop changes
- // TODO check delete/add synchronization
-
/**
- * On startup, we need to calculate a full mesh of paths between all gateway
- * switches.
+ * Delete prefix intent installed.
+ *
+ * @param prefix IP prefix withdrew in a rib update announcement
+ * @param ribEntry next hop information
*/
- private void setupFullMesh() {
- // TODO: Fix the code below. Note that "deviceStorage" was removed.
+ private void executeDeleteRoute(Prefix prefix, RibEntry ribEntry) {
+ log.debug("Deleting {} to {}", prefix, ribEntry.getNextHop());
- /*
+ MultiPointToSinglePointIntent intent = pushedRouteIntents.remove(prefix);
- //For each border router, calculate and install a path from every other
- //border switch to said border router. However, don't install the entry
- //in to the first hop switch, as we need to install an entry to rewrite
- //for each prefix received. This will be done later when prefixes have
- //actually been received.
-
- for (BgpPeer peer : bgpPeers.values()) {
- Interface peerInterface = interfaces.get(peer.getInterfaceName());
-
- //We know there's not already a Path here pushed, because this is
- //called before all other routing
- Path path = new Path(peerInterface, peer.getIpAddress());
- path.setPermanent();
-
- //See if we know the MAC address of the peer. If not we can't
- //do anything until we learn it
- MACAddress macAddress = null;
- IDeviceObject nextHopDevice =
- deviceStorage.getDeviceByIP(InetAddresses.coerceToInteger(peer.getIpAddress()));
-
- if(nextHopDevice == null){
- log.debug("There is no DeviceObject for {}", peer.getIpAddress().getHostAddress());
- //Put in the pending paths list first
- pathsWaitingOnArp.put(peer.getIpAddress(), path);
- proxyArp.sendArpRequest(peer.getIpAddress(), this, true);
- continue;
- }
-
- macAddress = MACAddress.valueOf(nextHopDevice.getMACAddress());
-
- if (macAddress == null) {
- log.debug("Don't know MAC for {}", peer.getIpAddress().getHostAddress());
- //Put in the pending paths list first
- pathsWaitingOnArp.put(peer.getIpAddress(), path);
- proxyArp.sendArpRequest(peer.getIpAddress(), this, true);
- continue;
- }
-
- //If we know the MAC, lets go ahead and push the paths to this peer
- calculateAndPushPath(path, macAddress);
+ if (intent == null) {
+ log.debug("There is no intent in pushedRouteIntents to delete for " +
+ "prefix: {}", prefix);
+ } else {
+ intentService.withdraw(intent);
+ log.debug("Deleted the pushedRouteIntent for prefix: {}", prefix);
}
- */
- }
-
- private void calculateAndPushPath(Path path, MACAddress dstMacAddress) {
- // Interface dstInterface = path.getDstInterface();
-
- log.debug("Setting up path to {}, {}", path.getDstIpAddress().getHostAddress(),
- dstMacAddress);
-
- /*
- FlowPath flowPath = new FlowPath();
-
- flowPath.setInstallerId(callerId);
-
- // Set flowPath FlowPathType and FlowPathUserState
- 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
- 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()));
-
- for (Interface srcInterface : interfaces.values()) {
-
- if (dstInterface.equals(srcInterface)) {
- continue;
- }
-
- // Create flowPath FlowId
- flowPath.setFlowId(new FlowId());
-
- // Create the DataPath object: srcSwitchPort
- SwitchPort srcPort =
- new SwitchPort(new Dpid(srcInterface.getDpid()),
- new PortNumber(srcInterface.getPort()));
-
- DataPath dataPath = new DataPath();
- dataPath.setSrcPort(srcPort);
- dataPath.setDstPort(dstPort);
- flowPath.setDataPath(dataPath);
-
- // Create the Flow Path Match condition(s)
- FlowEntryMatch flowEntryMatch = new FlowEntryMatch();
- flowEntryMatch.enableEthernetFrameType(Ethernet.TYPE_IPV4);
- flowEntryMatch.enableDstMac(dstMacAddress);
- 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
- log.debug("FlowPath of MAC based forwarding: {}", flowPath.toString());
- // TODO: Add the flow by using the new Path Intent framework
-
- if (flowManagerService.addFlow(flowPath) == null) {
- log.error("Failed to set up MAC based forwarding path to {}, {}",
- path.getDstIpAddress().getHostAddress(),dstMacAddress);
- }
- else {
- log.debug("Successfully set up MAC based forwarding path to {}, {}",
- path.getDstIpAddress().getHostAddress(),dstMacAddress);
- }
-
- }
- */
- }
-
- @Override
- public void beginRoutingNew() {
- setupBgpPathsNew();
-
- // setupFullMesh();
-
- // Suppress link discovery on external-facing router ports
-
- for (Interface intf : interfaces.values()) {
- linkDiscoveryService.disableDiscoveryOnPort(intf.getDpid(), intf.getPort());
- }
-
- bgpUpdatesExecutor.execute(new Runnable() {
- @Override
- public void run() {
- doUpdatesThread();
- }
- });
}
/**
@@ -847,7 +546,7 @@
* srcPort, long srcMac, int srcIP, long dstSwitch, long dstPort, long
* dstMac, int dstIP
*/
- private void setupBgpPathsNew() {
+ private void setupBgpPaths() {
IntentOperationList operations = new IntentOperationList();
for (BgpPeer bgpPeer : bgpPeers.values()) {
Interface peerInterface = interfaces.get(bgpPeer.getInterfaceName());
@@ -861,9 +560,14 @@
SwitchPort srcPort =
new SwitchPort(bgpdAttachmentPoint.getDpid(),
bgpdAttachmentPoint.getPortNumber());
+ // TODO: replace the code below with peerInterface.getSwitchPort()
+ // when using poingToPointIntent
SwitchPort dstPort =
new SwitchPort(new Dpid(peerInterface.getDpid()),
new PortNumber(peerInterface.getSwitchPort().getPortNumber()));
+
+ // TODO: add TCP port number 179 into intent for BGP
+
ShortestPathIntent fwdIntent = new ShortestPathIntent(fwdIntentId,
srcPort.getDpid().value(), srcPort.getPortNumber().value(),
ShortestPathIntent.EMPTYMACADDRESS, srcIP,
@@ -881,41 +585,26 @@
pathRuntime.executeIntentOperations(operations);
}
+ /**
+ * This method handles the prefixes which are waiting for ARP replies for
+ * MAC addresses of next hops.
+ *
+ * @param ipAddress next hop router IP address, for which we sent ARP request out
+ * @param macAddress MAC address which is relative to the ipAddress
+ */
@Override
public void arpResponse(InetAddress ipAddress, MACAddress macAddress) {
log.debug("Received ARP response: {} => {}",
ipAddress.getHostAddress(), macAddress);
/*
- * We synchronize on this to prevent changes to the ptree while we're pushing
- * flows to the switches. If the ptree changes, the ptree and switches
- * could get out of sync.
+ * We synchronize on this to prevent changes to the InvertedRadixTree
+ * while we're pushing intent. If the InvertedRadixTree changes, the
+ * InvertedRadixTree and intent could get out of sync.
*/
synchronized (this) {
- Path path = pathsWaitingOnArp.remove(ipAddress);
- if (path != null) {
- 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
- // 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.
- if (path.isPermanent()) {
- pushedPaths.get(path.getDstIpAddress()).setPermanent();
- }
- } else {
- calculateAndPushPath(path, macAddress);
- pushedPaths.put(path.getDstIpAddress(), path);
- }
- }
-
- Set<RibUpdate> prefixesToPush = prefixesWaitingOnArp.removeAll(ipAddress);
+ Set<RibUpdate> prefixesToPush = prefixesWaitingOnArp.removeAll(ipAddress);
for (RibUpdate update : prefixesToPush) {
// These will always be adds
@@ -926,22 +615,20 @@
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
- // next hop could have changed.
+ // InvertedRadixTree 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 {
- log.debug("Received ARP response, but {},{} is no longer in ptree",
- update.getPrefix(), update.getRibEntry());
+ log.debug("Received ARP response, but {},{} is no longer in " +
+ "InvertedRadixTree", update.getPrefix(),
+ update.getRibEntry());
}
}
}
}
- // XXX OpenFlow message classes have been removed
+
/*private void setupArpFlows() {
OFMatch match = new OFMatch();
match.setDataLayerType(Ethernet.TYPE_ARP);
@@ -1027,17 +714,18 @@
}
}*/
- private void beginRouting() {
+ /**
+ * The SDN-IP application is started from this method.
+ */
+ @Override
+ public void beginRouting() {
log.debug("Topology is now ready, beginning routing function");
- // TODO: Fix for the new Topology Network Graph
- // topology = topologyNetService.newDatabaseTopology();
- // Wait Pavlin's API. We need the following functions.
+ // TODO
/*setupArpFlows();
setupDefaultDropFlows();*/
- //setupBgpPaths();
- setupFullMesh();
+ setupBgpPaths();
// Suppress link discovery on external-facing router ports
for (Interface intf : interfaces.values()) {
@@ -1052,69 +740,9 @@
});
}
- // Before inserting the paths for BGP traffic, we should check whether
- // all the switches in the configuration file are discovered by ONOS
- private void checkSwitchesConnected() {
- // TODO: Fix the code below after topoSwitchSerice was removed
- /*
- for (String dpid : switches) {
-
- Iterator<ISwitchObject> activeSwitches = topoSwitchService.
- getActiveSwitches().iterator();
- while(activeSwitches.hasNext())
- {
- ISwitchObject switchObject = activeSwitches.next();
- if (switchObject.getDPID().equals(dpid)) {
- break;
- }
- if(activeSwitches.hasNext() == false) {
- log.debug("Not all switches are here yet");
- return;
- }
- }
- }
- switchesConnected = true;
- */
- }
-
- // 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()) {
- if (dstInterface.equals(srcInterface)) {
- continue;
- }
-
- // TODO: Fix for the new Topology Network Graph
- /*
- DataPath shortestPath = topologyNetService.getDatabaseShortestPath(
- srcInterface.getSwitchPort(), dstInterface.getSwitchPort());
-
- if (shortestPath == null){
- log.debug("Shortest path between {} and {} not found",
- srcInterface.getSwitchPort(), dstInterface.getSwitchPort());
- return;
- }
- */
- }
- }
- topologyReady = true;
- }
-
- private void checkStatus() {
- if (!switchesConnected) {
- checkSwitchesConnected();
- }
- boolean oldTopologyReadyStatus = topologyReady;
- if (switchesConnected && !topologyReady) {
- checkTopologyReady();
- }
- if (!oldTopologyReadyStatus && topologyReady) {
- beginRouting();
- }
- }
-
+ /**
+ * Thread for handling RIB updates.
+ */
private void doUpdatesThread() {
boolean interrupted = false;
try {
@@ -1156,6 +784,12 @@
}
}
+ /**
+ * Judge whether a RIB update is in correct order.
+ *
+ * @param update RIB update
+ * @return boolean whether the RIB update is in in correct order
+ */
private boolean validateUpdate(RibUpdate update) {
RibEntry newEntry = update.getRibEntry();
RibEntry oldEntry = bgpRoutes.getValueForExactKey(
@@ -1184,6 +818,13 @@
newEntry.getSequenceNum() > oldEntry.getSequenceNum();
}
+ /**
+ * To find the Interface which has longest matchable IP prefix (sub-network
+ * prefix) to next hop IP address.
+ *
+ * @param address the IP address of next hop router
+ * @return Interface the Interface which has longest matchable IP prefix
+ */
private Interface longestInterfacePrefixMatch(InetAddress address) {
Prefix prefixToSearchFor = new Prefix(address.getAddress(),
Prefix.MAX_PREFIX_LENGTH);
@@ -1199,90 +840,6 @@
return intf;
}
- // The code below should be reimplemented after removal of Floodlight's
- // ITopologyService API. It should be implemented on top of network graph
- // notifications. (It was pretty hacky anyway...)
- /*
- @Override
- public void topologyChanged() {
- if (topologyReady) {
- return;
- }
-
- boolean refreshNeeded = false;
- for (LDUpdate ldu : topologyService.getLastLinkUpdates()){
- if (!ldu.getOperation().equals(ILinkDiscovery.UpdateOperation.LINK_UPDATED)){
- //We don't need to recalculate anything for just link updates
- //They happen very frequently
- refreshNeeded = true;
- }
-
- log.debug("Topo change {}", ldu.getOperation());
-
- if (ldu.getOperation().equals(ILinkDiscovery.UpdateOperation.LINK_ADDED)){
- synchronized (linkUpdates) {
- linkUpdates.add(ldu);
- }
- }
- }
-
- if (refreshNeeded && !topologyReady){
- topologyChangeDetectorTask.reschedule(TOPO_DETECTION_WAIT, TimeUnit.SECONDS);
- }
- }
- */
-
- // ******************
- // IOFSwitchListener
- // ******************
-
- @Override
- 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();
- }
-
- //flowCache.switchConnected(sw);
- }
-
- @Override
- 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 swId, OFPortDesc port, PortChangeType pct) {
- // Not used
- }
-
- @Override
- public String getName() {
- return "sdnip";
- }
-
/*
* IConfigInfoService methods
*/
@@ -1309,6 +866,11 @@
return false;
}
+ /**
+ * To find the relative egress Interface for a next hop IP address.
+ *
+ * @param dstIpAddress the IP address of next hop router
+ */
@Override
public Interface getOutgoingInterface(InetAddress dstIpAddress) {
return longestInterfacePrefixMatch(dstIpAddress);
@@ -1328,4 +890,5 @@
public short getVlan() {
return vlan;
}
+
}
diff --git a/src/main/java/net/onrc/onos/apps/sdnip/web/SdnIpSetup.java b/src/main/java/net/onrc/onos/apps/sdnip/web/SdnIpSetup.java
index d1c4605..4bed425 100644
--- a/src/main/java/net/onrc/onos/apps/sdnip/web/SdnIpSetup.java
+++ b/src/main/java/net/onrc/onos/apps/sdnip/web/SdnIpSetup.java
@@ -13,7 +13,7 @@
public String sdnipSetupMethod() {
ISdnIpService sdnIp = (ISdnIpService) getContext()
.getAttributes().get(ISdnIpService.class.getCanonicalName());
- sdnIp.beginRoutingNew();
+ sdnIp.beginRouting();
return "SdnIp SetupBgpPaths Succeeded";
}
diff --git a/src/test/java/net/onrc/onos/apps/sdnip/SdnIpTest.java b/src/test/java/net/onrc/onos/apps/sdnip/SdnIpTest.java
new file mode 100644
index 0000000..f5a6d31
--- /dev/null
+++ b/src/test/java/net/onrc/onos/apps/sdnip/SdnIpTest.java
@@ -0,0 +1,81 @@
+package net.onrc.onos.apps.sdnip;
+
+import static org.junit.Assert.*;
+
+import java.io.IOException;
+import java.net.InetAddress;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.Map;
+
+import org.junit.Test;
+
+import com.googlecode.concurrenttrees.radix.node.concrete.DefaultByteArrayNodeFactory;
+import com.googlecode.concurrenttrees.radixinverted.ConcurrentInvertedRadixTree;
+import com.googlecode.concurrenttrees.radixinverted.InvertedRadixTree;
+
+public class SdnIpTest {
+
+ private Map<String, Interface> interfaces;
+ private InvertedRadixTree<Interface> interfaceRoutes;
+
+ private Interface longestInterfacePrefixMatch(InetAddress address) {
+ Prefix prefixToSearchFor = new Prefix(address.getAddress(),
+ Prefix.MAX_PREFIX_LENGTH);
+ Iterator<Interface> it =
+ interfaceRoutes.getValuesForKeysPrefixing(
+ prefixToSearchFor.toBinaryString()).iterator();
+ Interface intf = null;
+ // Find the last prefix, which will be the longest prefix
+ while (it.hasNext()) {
+ intf = it.next();
+ }
+
+ return intf;
+ }
+ /**
+ * This is just a test of the InvertedRadixTree, rather than an actual unit
+ * test of SdnIp.
+ *
+ * @throws IOException
+ */
+ @Test
+ public void getOutgoingInterfaceTest() throws IOException {
+
+ interfaces = new HashMap<>();
+ interfaceRoutes = new ConcurrentInvertedRadixTree<>(
+ new DefaultByteArrayNodeFactory());
+
+ Interface interface1 = new Interface("sw3-eth1", "00:00:00:00:00:00:00:a3",
+ (short) 1, "192.168.10.101", 24);
+ interfaces.put(interface1.getName(), interface1);
+ Interface interface2 = new Interface("sw5-eth1", "00:00:00:00:00:00:00:a5",
+ (short) 1, "192.168.20.101", 16);
+ interfaces.put(interface2.getName(), interface2);
+ Interface interface3 = new Interface("sw2-eth1", "00:00:00:00:00:00:00:a2",
+ (short) 1, "192.168.60.101", 16);
+ interfaces.put(interface3.getName(), interface3);
+ Interface interface4 = new Interface("sw6-eth1", "00:00:00:00:00:00:00:a6",
+ (short) 1, "192.168.60.101", 30);
+ interfaces.put(interface4.getName(), interface4);
+ Interface interface5 = new Interface("sw4-eth4", "00:00:00:00:00:00:00:a4",
+ (short) 4, "192.168.60.101", 24);
+ interfaces.put(interface5.getName(), interface5);
+
+ for (Interface intf : interfaces.values()) {
+ Prefix prefix = new Prefix(intf.getIpAddress().getAddress(),
+ intf.getPrefixLength());
+ interfaceRoutes.put(prefix.toBinaryString(), intf);
+ }
+
+ // Check whether the prefix length takes effect
+ InetAddress nextHopAddress = InetAddress.getByName("192.0.0.1");
+ assertNotNull(nextHopAddress);
+ assertNull(longestInterfacePrefixMatch(nextHopAddress));
+
+ // Check whether it returns the longest matchable address
+ nextHopAddress = InetAddress.getByName("192.168.60.101");
+ assertEquals("sw6-eth1", longestInterfacePrefixMatch(nextHopAddress).getName());
+
+ }
+}