- Added CopyTtlInAction class
- Added logic to set up routing rules in switches following the ECMP Shortest Path Graph
Change-Id: I4e193401eaf7f9072bfebb604b6f20b16a52facf
diff --git a/src/main/java/net/onrc/onos/apps/segmentrouting/SegmentRoutingManager.java b/src/main/java/net/onrc/onos/apps/segmentrouting/SegmentRoutingManager.java
index b51370f..8482e74 100644
--- a/src/main/java/net/onrc/onos/apps/segmentrouting/SegmentRoutingManager.java
+++ b/src/main/java/net/onrc/onos/apps/segmentrouting/SegmentRoutingManager.java
@@ -9,29 +9,50 @@
import java.util.Iterator;
import java.util.List;
import java.util.Map;
+import java.util.concurrent.ScheduledExecutorService;
+import java.util.concurrent.TimeUnit;
import net.floodlightcontroller.core.IFloodlightProviderService;
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.core.util.SingletonTask;
+import net.floodlightcontroller.threadpool.IThreadPoolService;
import net.onrc.onos.api.packet.IPacketListener;
import net.onrc.onos.api.packet.IPacketService;
import net.onrc.onos.core.flowprogrammer.IFlowPusherService;
-import net.onrc.onos.core.intent.Path;
import net.onrc.onos.core.main.config.IConfigInfoService;
+import net.onrc.onos.core.matchaction.MatchAction;
+import net.onrc.onos.core.matchaction.MatchActionId;
+import net.onrc.onos.core.matchaction.MatchActionOperationEntry;
+import net.onrc.onos.core.matchaction.action.Action;
+import net.onrc.onos.core.matchaction.action.CopyTtlInAction;
+import net.onrc.onos.core.matchaction.action.CopyTtlOutAction;
+import net.onrc.onos.core.matchaction.action.DecMplsTtlAction;
+import net.onrc.onos.core.matchaction.action.DecNwTtlAction;
+import net.onrc.onos.core.matchaction.action.GroupAction;
+import net.onrc.onos.core.matchaction.action.PopMplsAction;
+import net.onrc.onos.core.matchaction.action.PushMplsAction;
+import net.onrc.onos.core.matchaction.action.SetMplsIdAction;
+import net.onrc.onos.core.matchaction.match.Ipv4PacketMatch;
+import net.onrc.onos.core.matchaction.match.Match;
+import net.onrc.onos.core.matchaction.match.MplsMatch;
import net.onrc.onos.core.packet.ARP;
import net.onrc.onos.core.packet.Ethernet;
import net.onrc.onos.core.packet.IPv4;
import net.onrc.onos.core.topology.ITopologyListener;
import net.onrc.onos.core.topology.ITopologyService;
-import net.onrc.onos.core.topology.LinkData;
import net.onrc.onos.core.topology.MutableTopology;
import net.onrc.onos.core.topology.Port;
import net.onrc.onos.core.topology.Switch;
import net.onrc.onos.core.topology.TopologyEvents;
import net.onrc.onos.core.util.Dpid;
+import net.onrc.onos.core.util.IPv4Net;
+import net.onrc.onos.core.util.SwitchPort;
+import org.json.JSONArray;
+import org.json.JSONException;
import org.projectfloodlight.openflow.types.IPv4Address;
import org.projectfloodlight.openflow.util.HexString;
import org.slf4j.Logger;
@@ -50,6 +71,9 @@
private ArpHandler arpHandler;
private GenericIpHandler ipHandler;
private IcmpHandler icmpHandler;
+ private boolean networkConverged;
+ private IThreadPoolService threadPool;
+ private SingletonTask discoveryTask;
@Override
public Collection<Class<? extends IFloodlightService>> getModuleServices() {
@@ -86,6 +110,7 @@
ipHandler = new GenericIpHandler(context, this);
arpEntries = new ArrayList<ArpEntry>();
topologyService = context.getServiceImpl(ITopologyService.class);
+ threadPool = context.getServiceImpl(IThreadPoolService.class);
mutableTopology = topologyService.getTopology();
topologyService.addListener(this, false);
@@ -95,8 +120,18 @@
@Override
public void startUp(FloodlightModuleContext context) throws FloodlightModuleException {
- // TODO Auto-generated method stub
+ networkConverged = false;
+ ScheduledExecutorService ses = threadPool.getScheduledExecutor();
+
+ discoveryTask = new SingletonTask(ses, new Runnable() {
+ @Override
+ public void run() {
+ populateEcmpRoutingRules();
+ }
+ });
+
+ discoveryTask.reschedule(10, TimeUnit.SECONDS);
}
@Override
@@ -191,7 +226,7 @@
{
/**
* Any Link update events, compute the ECMP path graph for all switch nodes
- */
+
if ((topologyEvents.getAddedLinkDataEntries() != null) ||
(topologyEvents.getRemovedLinkDataEntries() != null))
{
@@ -200,19 +235,8 @@
ECMPShortestPathGraph ecmpSPG = new ECMPShortestPathGraph(sw);
log.debug("ECMPShortestPathGraph is computed for switch {}",
HexString.toHexString(sw.getDpid().value()));
- /*
- for (Switch dstSw: mutableTopology.getSwitches()){
- if (sw.getDpid().equals(dstSw.getDpid())){
- continue;
- }
- ArrayList<Path> paths = ecmpSPG.getECMPPaths(dstSw);
- log.debug("ECMPShortestPathGraph:Paths from switch {} to switch {} is {}",
- HexString.toHexString(sw.getDpid().value()),
- HexString.toHexString(dstSw.getDpid().value()), paths);
- //setSegmentRoutingRule(sw, paths);
- }
- */
- /*
+
+
HashMap<Integer, HashMap<Switch,ArrayList<Path>>> pathGraph =
ecmpSPG.getCompleteLearnedSwitchesAndPaths();
for (Integer itrIdx: pathGraph.keySet()){
@@ -231,7 +255,7 @@
}
}
}
- */
+
HashMap<Integer, HashMap<Switch,ArrayList<ArrayList<Dpid>>>> switchVia =
ecmpSPG.getAllLearnedSwitchesAndVia();
for (Integer itrIdx: switchVia.keySet()){
@@ -251,29 +275,313 @@
}
}
}
+
}
}
+ */
+
+ if ((topologyEvents.getAddedLinkDataEntries() != null) ||
+ (topologyEvents.getRemovedLinkDataEntries() != null))
+ {
+
+ if (networkConverged) {
+ populateEcmpRoutingRules();
+ }
+ }
+
}
/**
- * Set segment routing rule to switches in the ECMP shortest path to the switch
+ * Populate routing rules walking through the ECMP shortest paths
*
- * @param sw source switch
- * @param paths ECMP path
*/
- private void setSegmentRoutingRule(Switch sw, ArrayList<Path> paths) {
+ private void populateEcmpRoutingRules() {
- log.debug("Set routing info for {} to .. ", sw.getDpid());
- for (Path path: paths) {
+ Iterable<Switch> switches= mutableTopology.getSwitches();
+ for (Switch sw : switches) {
+ ECMPShortestPathGraph ecmpSPG = new ECMPShortestPathGraph(sw);
+ log.debug("ECMPShortestPathGraph is computed for switch {}",
+ HexString.toHexString(sw.getDpid().value()));
- for (Object obj : path.toArray()) {
- LinkData link = (LinkData)obj;
- String destMplsLabel = getMplslabel(link.getDst().getDpid());
- String targetMplsLabel = getMplslabel(sw.getDpid());
- if (destMplsLabel != null && targetMplsLabel != null)
- setTransitRouterRule(targetMplsLabel, destMplsLabel);
+ HashMap<Integer, HashMap<Switch,ArrayList<ArrayList<Dpid>>>> switchVia =
+ ecmpSPG.getAllLearnedSwitchesAndVia();
+ for (Integer itrIdx: switchVia.keySet()){
+ log.debug("ECMPShortestPathGraph:Switches learned in "
+ + "Iteration{} from switch {}:",
+ itrIdx,
+ HexString.toHexString(sw.getDpid().value()));
+ HashMap<Switch, ArrayList<ArrayList<Dpid>>> swViaMap =
+ switchVia.get(itrIdx);
+ for (Switch targetSw: swViaMap.keySet()){
+ log.debug("ECMPShortestPathGraph:****switch {} via:",
+ HexString.toHexString(targetSw.getDpid().value()));
+ String destSw = sw.getDpid().toString();
+ List<String> fwdToSw = new ArrayList<String>();
+
+ int i=0;
+ for (ArrayList<Dpid> via:swViaMap.get(targetSw)){
+ log.debug("ECMPShortestPathGraph:******{}) {}",++i,via);
+ if (via.isEmpty()) {
+ fwdToSw.add(destSw);
+ }
+ else {
+ fwdToSw.add(via.get(0).toString());
+ }
+ }
+ setRoutingRule(targetSw, destSw, fwdToSw);
+ }
}
}
+
+ networkConverged = true;
+ }
+
+ /**
+ *
+ * Set routing rules in targetSw
+ * {forward packets to fwdToSw switches in order to send packets to destSw}
+ * - If the target switch is an edge router and final destnation switch is also
+ * an edge router, then set IP forwarding rules to subnets
+ * - If only the target switch is an edge router, then set IP forwarding rule to
+ * the transit router loopback IP address
+ * - If the target is a transit router, then just set the MPLS forwarding rule
+ *
+ * @param targetSw Switch to set the rules
+ * @param destSw Final destination switches
+ * @param fwdToSw next hop switches
+ */
+ private void setRoutingRule(Switch targetSw, String destSw, List<String> fwdToSw) {
+
+
+ if (fwdToSw.isEmpty()) {
+ fwdToSw.add(destSw);
+ }
+
+ // if it is an edge router, then set IP table
+ if (IsEdgeRouter(targetSw.getDpid().toString()) &&
+ IsEdgeRouter(destSw)) {
+ // We assume that there is at least one transit router b/w edge routers
+ Switch destSwitch = mutableTopology.getSwitch(new Dpid(destSw));
+ String subnets = destSwitch.getStringAttribute("subnets");
+ try {
+ JSONArray arry = new JSONArray(subnets);
+ for (int i = 0; i < arry.length(); i++) {
+ String subnetIp = (String) arry.getJSONObject(i).get("subnetIp");
+ setIpTableRouter(targetSw, subnetIp, getMplsLabel(destSw)
+ ,fwdToSw);
+
+ }
+ } catch (JSONException e) {
+ // TODO Auto-generated catch block
+ e.printStackTrace();
+ }
+ String routerIp = destSwitch.getStringAttribute("routerIp");
+ setIpTableRouter(targetSw, routerIp, getMplsLabel(destSw), fwdToSw);
+ }
+ // if the target switch is the edge router, then set the IP rule to router IPs
+ else if (IsEdgeRouter(targetSw.getDpid().toString())) {
+ // We assume that there is at least one transit router b/w edge routers
+ Switch destSwitch = mutableTopology.getSwitch(new Dpid(destSw));
+ String routerIp = destSwitch.getStringAttribute("routerIp");
+ setIpTableRouter(targetSw, routerIp, getMplsLabel(destSw), fwdToSw);
+ }
+ // if it is a transit router, then set rules in the MPLS table
+ else {
+ setMplsTable(targetSw, getMplsLabel(destSw), fwdToSw);
+ }
+
+ }
+
+ /**
+ * Check if the switch is the edge router or not
+ * If any subnet information is defined in the config file, the we assume
+ * it is an edge router
+ *
+ * @param dpid Dpid of the switch to check
+ * @return true if it is an edge router, otherwise false
+ */
+ private boolean IsEdgeRouter(String dpid) {
+
+ for (Switch sw: mutableTopology.getSwitches()) {
+ String dpidStr = sw.getDpid().toString();
+ if (dpid.equals(dpidStr)) {
+ String subnetInfo = sw.getStringAttribute("subnets");
+ if (subnetInfo == null || subnetInfo.equals("[]")) {
+ return false;
+ }
+ else
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ /**
+ * Set IP forwarding rule
+ * - If the destination is the next hop, then do not push MPLS,
+ * just decrease the NW TTL
+ * - Otherwise, push MPLS label and set the MPLS ID
+ *
+ * @param sw target switch to set rules
+ * @param subnetIp Match IP address
+ * @param mplsLabel MPLS label of final destination router
+ * @param fwdToSws next hop routers
+ */
+ private void setIpTableRouter(Switch sw, String subnetIp, String mplsLabel,
+ List<String> fwdToSws) {
+
+ Ipv4PacketMatch ipMatch = new Ipv4PacketMatch(subnetIp);
+ List<Action> actions = new ArrayList<>();
+
+ // If destination SW is the same as the fwd SW, then do not push MPLS label
+ if (fwdToSws.size() == 1) {
+ String fwdToSw = fwdToSws.get(0);
+ if (getMplsLabel(fwdToSw).equals(mplsLabel)) {
+ DecNwTtlAction decTtlAction = new DecNwTtlAction(1);
+ actions.add(decTtlAction);
+ }
+ }
+ else {
+ PushMplsAction pushMplsAction = new PushMplsAction();
+ SetMplsIdAction setIdAction = new SetMplsIdAction(Integer.parseInt(mplsLabel));
+ CopyTtlOutAction copyTtlOutAction = new CopyTtlOutAction();
+
+ actions.add(pushMplsAction);
+ actions.add(setIdAction);
+ actions.add(copyTtlOutAction);
+ }
+
+ GroupAction groupAction = new GroupAction();
+
+ for (String fwdSw : fwdToSws) {
+ groupAction.addSwitch(new Dpid(fwdSw));
+ }
+ actions.add(groupAction);
+
+ //MatchAction matchAction = new MatchAction(maIdGenerator.getNewId(),
+ MatchAction matchAction = new MatchAction(new MatchActionId(0),
+ new SwitchPort((long)0,(short)0), ipMatch, actions);
+
+ MatchActionOperationEntry maEntry =
+ new MatchActionOperationEntry(
+ net.onrc.onos.core.matchaction.MatchActionOperations.Operator.ADD,
+ matchAction);
+
+ MatchActionOperationEntry(maEntry);
+
+ }
+
+
+ /**
+ * Set MPLS forwarding rules to MPLS table
+ * - If the destination is the same as the next hop to forward packets
+ * then, pop the MPLS label according to PHP rule
+ * - Otherwise, just forward packets to next hops using Group action
+ *
+ * @param sw Switch to set the rules
+ * @param mplsLabel destination MPLS label
+ * @param fwdSws next hop switches
+ */
+ private void setMplsTable(Switch sw, String mplsLabel, List<String> fwdSws) {
+
+ MplsMatch mplsMatch = new MplsMatch(Integer.parseInt(mplsLabel));
+
+ List<Action> actions = new ArrayList<Action>();
+ // Either when packet is forwarded to edge router or the dest is the router
+
+ if (fwdSws.size() == 1) {
+ String fwdSw = fwdSws.get(0);
+ if (mplsLabel.equals(getMplsLabel(fwdSw))) {
+ PopMplsAction popAction = new PopMplsAction();
+ CopyTtlInAction copyTtlInAction = new CopyTtlInAction();
+
+ actions.add(popAction);
+ actions.add(copyTtlInAction);
+ }
+ }
+ else {
+ DecMplsTtlAction decMplsTtlAction = new DecMplsTtlAction(1);
+ actions.add(decMplsTtlAction);
+ }
+
+ GroupAction groupAction = new GroupAction();
+ for (String fwdSw: fwdSws)
+ groupAction.addSwitch(new Dpid(fwdSw));
+ actions.add(groupAction);
+
+ MatchAction matchAction = new MatchAction(new MatchActionId(0),
+ new SwitchPort((long)0,(short)0), mplsMatch, actions);
+
+ MatchActionOperationEntry maEntry =
+ new MatchActionOperationEntry(
+ net.onrc.onos.core.matchaction.MatchActionOperations.Operator.ADD,
+ matchAction);
+
+ MatchActionOperationEntry(maEntry);
+
+ }
+
+
+ /**
+ * Debugging function to print out the Match Action Entry
+ *
+ * @param maEntry
+ */
+ private void MatchActionOperationEntry(MatchActionOperationEntry maEntry) {
+
+ StringBuilder logStr = new StringBuilder();
+
+ MatchAction ma = maEntry.getTarget();
+ Match m = ma.getMatch();
+ List<Action> actions = ma.getActions();
+
+ if (m instanceof Ipv4PacketMatch) {
+ logStr.append("If the IP matches with ");
+ IPv4Net ip = ((Ipv4PacketMatch) m).getDestination();
+ logStr.append(ip.toString());
+ logStr.append(" then ");
+ }
+ else if (m instanceof MplsMatch) {
+ logStr.append("If the MPLS label matches with ");
+ int mplsLabel = ((MplsMatch) m).getMplsLabel();
+ logStr.append(mplsLabel);
+ logStr.append(" then ");
+ }
+
+ logStr.append(" do { ");
+ for (Action action: actions) {
+ if (action instanceof CopyTtlInAction) {
+ logStr.append("copy ttl In, ");
+ }
+ else if (action instanceof CopyTtlOutAction) {
+ logStr.append("copy ttl Out, ");
+ }
+ else if (action instanceof DecMplsTtlAction) {
+ logStr.append("Dec MPLS TTL , ");
+ }
+ else if (action instanceof GroupAction) {
+ logStr.append("Forward packet to < ");
+ List<Dpid> dpids = ((GroupAction)action).getDpids();
+ for (Dpid dpid: dpids) {
+ logStr.append(dpid.toString() + ",");
+ }
+ }
+ else if (action instanceof PopMplsAction) {
+ logStr.append("Pop MPLS label, ");
+ }
+ else if (action instanceof PushMplsAction) {
+ logStr.append("Push MPLS label, ");
+ }
+ else if (action instanceof SetMplsIdAction) {
+ int id = ((SetMplsIdAction)action).getMplsId();
+ logStr.append("Set MPLS ID as " + id + ", ");
+
+ }
+ }
+
+ log.debug(logStr.toString());
+
}
/**
@@ -283,12 +591,12 @@
* @return MPLS label for the switch
*/
- private String getMplslabel(Dpid dpid) {
+ private String getMplsLabel(String dpid) {
String mplsLabel = null;
for (Switch sw: mutableTopology.getSwitches()) {
- String dpidStr = sw.getStringAttribute("nodeDpid");
- if (dpid.toString().endsWith(dpidStr)) {
+ String dpidStr = sw.getDpid().toString();
+ if (dpid.equals(dpidStr)) {
mplsLabel = sw.getStringAttribute("nodeSid");
break;
}
@@ -297,27 +605,6 @@
return mplsLabel;
}
- /**
- * Test function
- *
- *
- */
- private void setTransitRouterRule(String targetMplsLabel, String destMplsLabel) {
-
- log.debug("Match: MPLS label {}, action: forward to {}", targetMplsLabel, destMplsLabel);
-
- }
-
- /**
- * Test function
- *
- */
- private void setBorderRouterRule() {
-
-
-
- }
-
/**
diff --git a/src/main/java/net/onrc/onos/core/matchaction/action/CopyTtlInAction.java b/src/main/java/net/onrc/onos/core/matchaction/action/CopyTtlInAction.java
new file mode 100644
index 0000000..49109c5
--- /dev/null
+++ b/src/main/java/net/onrc/onos/core/matchaction/action/CopyTtlInAction.java
@@ -0,0 +1,8 @@
+package net.onrc.onos.core.matchaction.action;
+
+public class CopyTtlInAction implements Action {
+
+ public CopyTtlInAction() {
+ }
+
+}
diff --git a/src/main/java/net/onrc/onos/core/matchaction/action/GroupAction.java b/src/main/java/net/onrc/onos/core/matchaction/action/GroupAction.java
index ba0c866..77dd7ff 100644
--- a/src/main/java/net/onrc/onos/core/matchaction/action/GroupAction.java
+++ b/src/main/java/net/onrc/onos/core/matchaction/action/GroupAction.java
@@ -12,7 +12,7 @@
fwdSws = new ArrayList<Dpid>();
}
- public void AddSwitch(Dpid d) {
+ public void addSwitch(Dpid d) {
fwdSws.add(d);
}
diff --git a/src/main/java/net/onrc/onos/core/matchaction/match/IpPacketMatch.java b/src/main/java/net/onrc/onos/core/matchaction/match/Ipv4PacketMatch.java
similarity index 70%
rename from src/main/java/net/onrc/onos/core/matchaction/match/IpPacketMatch.java
rename to src/main/java/net/onrc/onos/core/matchaction/match/Ipv4PacketMatch.java
index 58d7546..7182ca1 100644
--- a/src/main/java/net/onrc/onos/core/matchaction/match/IpPacketMatch.java
+++ b/src/main/java/net/onrc/onos/core/matchaction/match/Ipv4PacketMatch.java
@@ -2,11 +2,11 @@
import net.onrc.onos.core.util.IPv4Net;
-public class IpPacketMatch implements Match {
+public class Ipv4PacketMatch implements Match {
IPv4Net dstIp;
- public IpPacketMatch(String ipAddressSlash) {
+ public Ipv4PacketMatch(String ipAddressSlash) {
this.dstIp = new IPv4Net(ipAddressSlash);
}
diff --git a/src/main/java/net/onrc/onos/core/matchaction/match/MplsPacketMatch.java b/src/main/java/net/onrc/onos/core/matchaction/match/MplsMatch.java
similarity index 68%
rename from src/main/java/net/onrc/onos/core/matchaction/match/MplsPacketMatch.java
rename to src/main/java/net/onrc/onos/core/matchaction/match/MplsMatch.java
index cf4e8d4..c0cf410 100644
--- a/src/main/java/net/onrc/onos/core/matchaction/match/MplsPacketMatch.java
+++ b/src/main/java/net/onrc/onos/core/matchaction/match/MplsMatch.java
@@ -1,10 +1,10 @@
package net.onrc.onos.core.matchaction.match;
-public class MplsPacketMatch implements Match {
+public class MplsMatch implements Match {
private final int mplsLabel;
- public MplsPacketMatch(int label) {
+ public MplsMatch(int label) {
this.mplsLabel = label;
}