- Fix a bug of losing first a few pings
- Add a simple logic to recompute a path when links are removed
Change-Id: I76462dcc1eaf23cd8ae09a69f61f0bb5116f9b16
diff --git a/src/main/java/net/onrc/onos/apps/segmentrouting/ECMPShortestPathGraph.java b/src/main/java/net/onrc/onos/apps/segmentrouting/ECMPShortestPathGraph.java
index 0a66cdd..9451beb 100644
--- a/src/main/java/net/onrc/onos/apps/segmentrouting/ECMPShortestPathGraph.java
+++ b/src/main/java/net/onrc/onos/apps/segmentrouting/ECMPShortestPathGraph.java
@@ -34,7 +34,7 @@
/**
* Constructor
- *
+ *
* @param rootSwitch root of the BFS tree
*/
public ECMPShortestPathGraph(Switch rootSwitch) {
@@ -140,9 +140,18 @@
}
/**
+ * Return root switch for the graph
+ *
+ * @return root switch
+ */
+ public Switch getRootSwitch() {
+ return rootSwitch;
+ }
+
+ /**
* Return the computed ECMP paths from the root switch to a given switch in
* the network
- *
+ *
* @param targetSwitch the target switch
* @return the list of ECMP Paths from the root switch to the target switch
*/
@@ -162,7 +171,7 @@
/**
* Return the complete info of the computed ECMP paths for each switch
* learned in multiple iterations from the root switch
- *
+ *
* @return the hash table of Switches learned in multiple Dijkstra
* iterations and corresponding ECMP paths to it from the root
* switch
@@ -192,7 +201,7 @@
* Switch<>} Iteration2, Switch<> via {Switch<>, Switch<>, Switch<>}
* {Switch<>, Switch<>, Switch<>} Switch<> via {Switch<>, Switch<>,
* Switch<>} }
- *
+ *
* @return the hash table of Switches learned in multiple Dijkstra
* iterations and corresponding ECMP paths in terms of Switches to
* be traversed to it from the root switch
diff --git a/src/main/java/net/onrc/onos/apps/segmentrouting/IcmpHandler.java b/src/main/java/net/onrc/onos/apps/segmentrouting/IcmpHandler.java
index 778658a..56a402b 100644
--- a/src/main/java/net/onrc/onos/apps/segmentrouting/IcmpHandler.java
+++ b/src/main/java/net/onrc/onos/apps/segmentrouting/IcmpHandler.java
@@ -96,7 +96,7 @@
* hosts in subnets of the switches, but if the MAC address is not known,
* then send an ARP request to the subent. If the MAC address is known, then
* set the routing rule to the switch
- *
+ *
* @param sw
* @param inPort
* @param payload
@@ -151,18 +151,18 @@
byte[] destinationMacAddress = host.getMacAddress().toBytes();
srManager.addRouteToHost(sw,
destinationAddress.getInt(), destinationMacAddress);
-
byte[] destIp = destinationAddress.getBytes();
for (IPv4 ipPacket : srManager.getIpPacketFromQueue(destIp)) {
if (ipPacket != null && !inSameSubnet(sw, ipPacket)) {
Ethernet eth = new Ethernet();
- eth.setDestinationMACAddress(payload
- .getSourceMACAddress());
+ eth.setDestinationMACAddress(destinationMacAddress);
eth.setSourceMACAddress(sw
.getStringAttribute("routerMac"));
eth.setEtherType(Ethernet.TYPE_IPV4);
eth.setPayload(ipPacket);
- sendPacketOut(sw, eth, inPort.getSwitchPort(), false);
+ for (Port port: host.getAttachmentPoints())
+ sendPacketOut(sw, eth, new SwitchPort(
+ port.getDpid(),port.getNumber()), false);
}
}
@@ -180,7 +180,7 @@
/**
* Retrieve Gateway IP address of all subnets defined in net config file
- *
+ *
* @param sw Switch to retrieve subnet GW IPs for
* @return list of GW IP addresses for all subnets
*/
@@ -209,7 +209,7 @@
/**
* Send ICMP reply back
- *
+ *
* @param sw Switch
* @param inPort Port the ICMP packet is forwarded from
* @param icmpRequest the ICMP request to handle
@@ -251,7 +251,7 @@
* action, it sends out packet to TABLE port Otherwise, it sends the packet
* to the port the packet came from (in this case, MPLS label is added if
* the packet needs go through transit switches)
- *
+ *
* @param sw Switch the packet came from
* @param packet Ethernet packet to send
* @param switchPort port to send the packet
@@ -328,7 +328,7 @@
/**
* Get MPLS label for the target address from the network config file
- *
+ *
* @param targetAddress - IP address of the target host
* @return MPLS label of the switch to send packets to the target address
*/
@@ -361,7 +361,7 @@
/**
* Get Router MAC Address for the target address from the network config
* file
- *
+ *
* @param targetAddress - IP address of the target host
* @return Router MAC of the switch to send packets to the target address
*/
@@ -392,7 +392,7 @@
/**
* Add a new rule to VLAN table to forward packets from any port to the next
* table It is required to forward packets from controller to pipeline
- *
+ *
* @param sw Switch the packet came from
*/
private void addControlPortInVlanTable(Switch sw) {
@@ -435,7 +435,7 @@
/**
* Check if the source IP and destination IP are in the same subnet
- *
+ *
* @param sw Switch
* @param ipv4 IP address to check
* @return return true if the IP packet is within the same subnet
@@ -454,7 +454,7 @@
/**
* Get router IP address for the given IP address
- *
+ *
* @param sourceAddress
* @return
*/
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 a8f4f1c..513b3d6 100644
--- a/src/main/java/net/onrc/onos/apps/segmentrouting/SegmentRoutingManager.java
+++ b/src/main/java/net/onrc/onos/apps/segmentrouting/SegmentRoutingManager.java
@@ -29,6 +29,7 @@
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;
@@ -86,6 +87,8 @@
private SingletonTask discoveryTask;
private IFloodlightProviderService floodlightProvider;
+ private HashMap<Switch, ECMPShortestPathGraph> graphs;
+
@Override
public Collection<Class<? extends IFloodlightService>> getModuleServices() {
// TODO Auto-generated method stub
@@ -125,6 +128,7 @@
mutableTopology = topologyService.getTopology();
topologyService.addListener(this, false);
ipPacketQueue = new ConcurrentLinkedQueue<IPv4>();
+ graphs = new HashMap<Switch, ECMPShortestPathGraph>();
this.packetService = context.getServiceImpl(IPacketService.class);
packetService.registerPacketListener(this);
@@ -163,7 +167,7 @@
* Update ARP Cache using ARP packets It is used to set destination MAC
* address to forward packets to known hosts. But, it will be replace with
* Host information of Topology service later.
- *
+ *
* @param arp APR packets to use for updating ARP entries
*/
public void updateArpCache(ARP arp) {
@@ -176,7 +180,7 @@
/**
* Get MAC address to known hosts
- *
+ *
* @param destinationAddress IP address to get MAC address
* @return MAC Address to given IP address
*/
@@ -207,11 +211,11 @@
/**
* Send an ARP request via ArpHandler
- *
+ *
* @param destinationAddress
* @param sw
* @param inPort
- *
+ *
*/
public void sendArpRequest(Switch sw, int destinationAddress, Port inPort) {
arpHandler.sendArpRequest(sw, destinationAddress, inPort);
@@ -219,7 +223,7 @@
/**
* Temporary class to to keep ARP entry
- *
+ *
*/
private class ArpEntry {
@@ -234,7 +238,7 @@
/**
* Topology events that have been generated.
- *
+ *
* @param topologyEvents the generated Topology Events
* @see TopologyEvents
*/
@@ -268,25 +272,25 @@
/**
* Report ports newly added to driver
- *
+ *
* @param portEntries
*/
private void processPortAdd(Collection<PortData> portEntries) {
// TODO: do we need to add ports slowly?
for (PortData port : portEntries) {
Dpid dpid = port.getDpid();
- int portNo = (int) port.getPortNumber().value();
- IOF13Switch sw13 = (IOF13Switch) floodlightProvider.getMasterSwitch(
+ IOF13Switch sw = (IOF13Switch) floodlightProvider.getMasterSwitch(
getSwId(port.getDpid().toString()));
- // sw13.addPort(portNo);
- log.debug("Add port {} to switch {}", portNo, dpid.toString());
+ sw.addPortToGroups(port.getPortNumber());
+
+ log.debug("Add port {} to switch {}", port, dpid);
}
}
/**
* Reports ports of new links to driver and recalculate ECMP SPG
- *
+ *
* @param linkEntries
*/
private void processLinkAdd(Collection<LinkData> linkEntries) {
@@ -298,13 +302,13 @@
SwitchPort srcPort = link.getSrc();
SwitchPort dstPort = link.getDst();
- IOF13Switch sw13src = (IOF13Switch) floodlightProvider.getMasterSwitch(
+ IOF13Switch srcSw = (IOF13Switch) floodlightProvider.getMasterSwitch(
getSwId(srcPort.getDpid().toString()));
- IOF13Switch sw13dst = (IOF13Switch) floodlightProvider.getMasterSwitch(
+ IOF13Switch dstSw = (IOF13Switch) floodlightProvider.getMasterSwitch(
getSwId(srcPort.getDpid().toString()));
- // sw13src.addPort(srcPort);
- // sw13dst.addPort(dstPort);
+ srcSw.addPortToGroups(srcPort.getPortNumber());
+ dstSw.addPortToGroups(dstPort.getPortNumber());
}
discoveryTask.reschedule(1, TimeUnit.SECONDS);
}
@@ -313,7 +317,7 @@
* Check if all links are gone b/w the two switches. If all links are gone,
* then we need to recalculate the path. Otherwise, just report link failure
* to the driver.
- *
+ *
* @param linkEntries
*/
private void processLinkRemoval(Collection<LinkData> linkEntries) {
@@ -321,16 +325,21 @@
SwitchPort srcPort = link.getSrc();
SwitchPort dstPort = link.getDst();
- IOF13Switch sw13src = (IOF13Switch) floodlightProvider.getMasterSwitch(
+ IOF13Switch srcSw = (IOF13Switch) floodlightProvider.getMasterSwitch(
getSwId(srcPort.getDpid().toString()));
- IOF13Switch sw13dst = (IOF13Switch) floodlightProvider.getMasterSwitch(
+ IOF13Switch dstSw = (IOF13Switch) floodlightProvider.getMasterSwitch(
getSwId(srcPort.getDpid().toString()));
- // sw13src.addPort(srcPort);
- // sw13dst.addPort(dstPort);
+
+ // TODO: Why do we get null for the master switch??
+ if (srcSw != null)
+ srcSw.removePortFromGroups(srcPort.getPortNumber());
+ if (dstSw != null)
+ dstSw.removePortFromGroups(dstPort.getPortNumber());
Switch srcSwitch = mutableTopology.getSwitch(srcPort.getDpid());
if (srcSwitch.getLinkToNeighbor(dstPort.getDpid()) == null) {
- discoveryTask.reschedule(1, TimeUnit.SECONDS);
+ //discoveryTask.reschedule(1, TimeUnit.SECONDS);
+ modifyEcmpRoutingRules(link);
log.debug("All links are gone b/w {} and {}", srcPort.getDpid(),
srcPort.getDpid());
}
@@ -339,80 +348,166 @@
/**
* report ports removed to the driver immediately
- *
+ *
* @param portEntries
*/
private void processPortRemoval(Collection<PortData> portEntries) {
for (PortData port : portEntries) {
Dpid dpid = port.getDpid();
- int portNo = (int) port.getPortNumber().value();
- IOF13Switch sw13 = (IOF13Switch) floodlightProvider.getMasterSwitch(
+ IOF13Switch sw = (IOF13Switch) floodlightProvider.getMasterSwitch(
getSwId(port.getDpid().toString()));
- // sw13.removePort(portNo);
- log.debug("Remove port {} from switch {}", portNo, dpid.toString());
+ if (sw != null)
+ sw.removePortFromGroups(port.getPortNumber());
+ log.debug("Remove port {} from switch {}", port, dpid);
}
}
/**
* Populate routing rules walking through the ECMP shortest paths
- *
+ *
*/
private void populateEcmpRoutingRules() {
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()));
+ graphs.put(sw, ecmpSPG);
+ //log.debug("ECMPShortestPathGraph is computed for switch {}",
+ // HexString.toHexString(sw.getDpid().value()));
+ populateEcmpRoutingRulesForPath(sw, ecmpSPG);
+ }
+ }
- 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>();
+ private void populateEcmpRoutingRulesForPath(Switch sw,
+ ECMPShortestPathGraph ecmpSPG) {
- 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());
- }
+ 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);
}
- setRoutingRule(targetSw, destSw, fwdToSw);
+ else {
+ fwdToSw.add(via.get(0).toString());
+ }
}
-
- // Send Barrier Message and make sure all rules are set
- // before we set the rules to next routers
- IOF13Switch sw13 = (IOF13Switch) floodlightProvider.getMasterSwitch(
- getSwId(sw.getDpid().toString()));
- try {
- OFBarrierReplyFuture replyFuture = sw13.sendBarrier();
- replyFuture.get(10, TimeUnit.SECONDS);
- } catch (IOException e) {
- e.printStackTrace();
- } catch (InterruptedException | ExecutionException | TimeoutException e) {
- log.error("Barrier message not received for sw: {}", sw.getDpid());
- e.printStackTrace();
- }
+ setRoutingRule(targetSw, destSw, fwdToSw);
}
+
+ // Send Barrier Message and make sure all rules are set
+ // before we set the rules to next routers
+ IOF13Switch sw13 = (IOF13Switch) floodlightProvider.getMasterSwitch(
+ getSwId(sw.getDpid().toString()));
+ try {
+ OFBarrierReplyFuture replyFuture = sw13.sendBarrier();
+ replyFuture.get(10, TimeUnit.SECONDS);
+ } catch (IOException e) {
+ e.printStackTrace();
+ } catch (InterruptedException | ExecutionException | TimeoutException e) {
+ log.error("Barrier message not received for sw: {}", sw.getDpid());
+ e.printStackTrace();
+ }
+ }
+
+ }
+
+
+ private class SwitchPair {
+ private Switch src;
+ private Switch dst;
+
+ public SwitchPair(Switch src, Switch dst) {
+ this.src = src;
+ this.dst = dst;
+ }
+
+ public Switch getSource() {
+ return src;
+ }
+
+ public Switch getDestination() {
+ return dst;
}
}
/**
- *
+ * Modify the routing rules for the lost links
+ * - Recompute the path if the link failed is included in the path
+ * (including src and dest).
+ *
+ * @param newLink
+ */
+ private void modifyEcmpRoutingRules(LinkData linkRemoved) {
+
+ //HashMap<Switch, SwitchPair> linksToRecompute = new HashMap<Switch, SwitchPair>();
+ List<SwitchPair> linksToRecompute = new ArrayList<SwitchPair>();
+
+ for (ECMPShortestPathGraph ecmpSPG : graphs.values()) {
+ Switch rootSw = ecmpSPG.getRootSwitch();
+ HashMap<Integer, HashMap<Switch, ArrayList<Path>>> paths =
+ ecmpSPG.getCompleteLearnedSwitchesAndPaths();
+ for (HashMap<Switch, ArrayList<Path>> p: paths.values()) {
+ for (Switch destSw: p.keySet()) {
+ ArrayList<Path> path = p.get(destSw);
+ if (checkPath(path, linkRemoved)) {
+ linksToRecompute.add(new SwitchPair(rootSw, destSw));
+ }
+ }
+ }
+ }
+
+ // Recompute the path for the specific route
+ for (SwitchPair pair: linksToRecompute) {
+
+ log.debug("Recompute path from {} to {}", pair.getSource(), pair.getDestination());
+ // TODO : we need the following function
+ //ECMPShortestPathGraph ecmpSPG =
+ // new ECMPShortestPathGraph(pair.getSource(), pair.getDestination());
+ ECMPShortestPathGraph ecmpSPG =
+ new ECMPShortestPathGraph(pair.getSource());
+ // TODO: we need to use different function to MODIFY the rules
+ populateEcmpRoutingRulesForPath(pair.getSource(), ecmpSPG);
+ }
+ }
+
+ /**
+ * Check if the path is affected from the link removed
+ *
+ * @param path Path to check
+ * @param linkRemoved link removed
+ * @return true if the path contains the link removed
+ */
+ private boolean checkPath(ArrayList<Path> path, LinkData linkRemoved) {
+
+ for (Path ppp: path) {
+ // TODO: need to check if this is a bidirectional or
+ // unidirectional
+ if (ppp.contains(linkRemoved)) {
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ /**
+ *
* 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
@@ -420,7 +515,7 @@
* 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
@@ -459,6 +554,14 @@
}
+ /**
+ * Set IP forwarding rule to the gateway of each subnet of switches
+ *
+ * @param targetSw Switch to set rules
+ * @param subnets subnet information
+ * @param mplsLabel destination MPLS label
+ * @param fwdToSw router to forward packets to
+ */
private void setIpTableRouterSubnet(Switch targetSw, String subnets,
String mplsLabel, List<String> fwdToSw) {
@@ -491,7 +594,7 @@
/**
* 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
*/
@@ -516,7 +619,7 @@
* 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
@@ -596,7 +699,7 @@
/**
* Convert a string DPID to its Switch Id (integer)
- *
+ *
* @param dpid
* @return
*/
@@ -616,7 +719,7 @@
* 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
@@ -675,7 +778,7 @@
/**
* Debugging function to print out the Match Action Entry
- *
+ *
* @param maEntry
*/
private void printMatchActionOperationEntry(Switch sw,
@@ -736,7 +839,7 @@
/**
* Get MPLS label reading the config file
- *
+ *
* @param dipid DPID of the switch
* @return MPLS label for the switch
*/
@@ -757,7 +860,7 @@
/**
* The function checks if given IP matches to the given subnet mask
- *
+ *
* @param addr - subnet address to match
* @param addr1 - IP address to check
* @return true if the IP address matches to the subnet, otherwise false
@@ -811,7 +914,7 @@
/**
* Add a routing rule for the host
- *
+ *
* @param sw - Switch to add the rule
* @param hostIpAddress Destination host IP address
* @param hostMacAddress Destination host MAC address
@@ -823,7 +926,7 @@
/**
* Add IP packet to a buffer queue
- *
+ *
* @param ipv4
*/
public void addPacket(IPv4 ipv4) {
@@ -832,7 +935,7 @@
/**
* Retrieve all packets whose destination is the given address.
- *
+ *
* @param destIp Destination address of packets to retrieve
*/
public List<IPv4> getIpPacketFromQueue(byte[] destIp) {