/* * Copyright (c) 2013 Big Switch Networks, Inc. * * Licensed under the Eclipse Public License, Version 1.0 (the * "License"); you may not use this file except in compliance with the * License. You may obtain a copy of the License at * * http://www.eclipse.org/legal/epl-v10.html * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or * implied. See the License for the specific language governing * permissions and limitations under the License. */ package org.sdnplatform.topology; import java.util.ArrayList; import java.util.Collections; import java.util.HashMap; import java.util.HashSet; import java.util.LinkedList; import java.util.List; import java.util.Map; import java.util.PriorityQueue; import java.util.Queue; import java.util.Set; import org.openflow.util.HexString; import org.sdnplatform.core.IControllerService; import org.sdnplatform.routing.BroadcastTree; import org.sdnplatform.routing.Link; import org.sdnplatform.routing.Route; import org.sdnplatform.routing.RouteId; import org.sdnplatform.tunnelmanager.ITunnelManagerService; import org.slf4j.Logger; import org.slf4j.LoggerFactory; public class BetterTopologyInstance extends TopologyInstance { protected static Logger log = LoggerFactory.getLogger(BetterTopologyInstance.class); protected Set<BroadcastDomain> broadcastDomains; // set of broadcast domains protected Map<NodePortTuple, BroadcastDomain> nodePortBroadcastDomainMap; // node port tuple to broadcast domain map protected Map<Long, Long> clusterKeyMap; // map from a cluster to its key in the nodeMap protected Map<BroadcastDomain, Long> broadcastDomainKeyMap; // map from a broadcast domain to its key in the nodeMap protected Map<Long, Object> htNodes; // Long is the id, Object could be either broadcast domain or cluster protected Map<Long, Set<Long>> htNeighbors; // set of Neighboring Nodes protected Map<Long, Map<Long, Long>> htNextHop; // for every source, a map // of next hop for every // destination. protected Map<Long, Long> clusterL2DomainIdMap; // Mapping of a cluster to // an L2 domain id. // protected Set<NodePair> allowedNodePairs; // node pairs that can // communicate in teh higher layer. protected Map<OrderedNodePair, Set<NodePortTuple>> allowedUnicastPorts; protected Map<OrderedNodePair, NodePortTuple> allowedIncomingBroadcastPorts; protected Map<NodePortTuple, Set<Long>> permittedSwitches; protected Map<NodePortTuple, Set<Long>> permittedPortToBroadcastDomains; // routing tree for multipath routing protected Map<Long, BroadcastTreeMultipath> destinationRootedTreesMultipath; // Tunnel domain identifier. Long tunnelDomain; IControllerService controllerProvider; ITunnelManagerService tunnelManager; private final long LONG_PRIME = 304250263527209L; public BetterTopologyInstance(Map<Long, Set<Short>> switchPorts, Set<NodePortTuple> blockedPorts, Map<NodePortTuple, Set<Link>> switchPortLinks, Set<NodePortTuple> broadcastDomainPorts, Set<NodePortTuple> tunnelPorts, Set<BroadcastDomain> bDomains, Long tunnelDomain, IControllerService controllerProvider, ITunnelManagerService tunnelManager) { super(switchPorts, blockedPorts, switchPortLinks, broadcastDomainPorts, tunnelPorts); destinationRootedTreesMultipath = new HashMap<Long, BroadcastTreeMultipath>(); broadcastDomains = new HashSet<BroadcastDomain>(); nodePortBroadcastDomainMap = new HashMap<NodePortTuple, BroadcastDomain>(); clusterKeyMap = new HashMap<Long, Long>(); broadcastDomainKeyMap = new HashMap<BroadcastDomain, Long>(); htNodes = new HashMap<Long, Object>(); htNeighbors = new HashMap<Long, Set<Long>>(); htNextHop = new HashMap<Long, Map<Long, Long>>(); clusterL2DomainIdMap = new HashMap<Long, Long>(); allowedUnicastPorts = new HashMap<OrderedNodePair, Set<NodePortTuple>>(); allowedIncomingBroadcastPorts = new HashMap<OrderedNodePair, NodePortTuple>(); permittedSwitches = new HashMap<NodePortTuple, Set<Long>>(); permittedPortToBroadcastDomains = new HashMap<NodePortTuple, Set<Long>>(); // Copy the broadcast domains. if (bDomains != null) { for (BroadcastDomain bd : bDomains) { BroadcastDomain copyBd = new BroadcastDomain(); copyBd.setId(bd.getId()); broadcastDomains.add(copyBd); Set<NodePortTuple> nptList = bd.getPorts(); if (nptList == null) continue; for (NodePortTuple npt : nptList) { copyBd.add(npt); nodePortBroadcastDomainMap.put(npt, copyBd); } } } this.tunnelDomain = tunnelDomain; this.controllerProvider = controllerProvider; this.tunnelManager = tunnelManager; } @Override public boolean isBroadcastDomainPort(NodePortTuple npt) { return nodePortBroadcastDomainMap.containsKey(npt); } @Override public void compute() { // Step 0. Create tunnel domain links. createTunnelDomainLinks(); // Step 1. Compute clusters ignoring broadcast domain links // Create nodes for clusters in the higher level topology // Ignores blocked ports. identifyOpenflowDomains(); // Step 3. Create inter-cluster links in the higher-level // topology. This step also assigns intra-cluster links to clusters. // Ignores blocked ports. addLinksToOpenflowDomains(); // Step 6. Add cluster nodes to higher-level topology. // Create L2 domain. // Ignores blocked ports. createHigherLevelTopology(); // Step 7. Given the higher-level topology, compute forest // in the higher-level topology. If the higher level topology // is connected, then this step will result in a tree. // Blocked ports are not relevant here. createForestInHigherLevelTopology(); computeClusterL2DomainMap(); // Step 8. Classify the switch ports by ordered node pairs // in the higher-level topology. // Ignores blocked ports. classifyPorts(); /* **************************************************************** */ // Step 4. Compute shortest path trees in each cluster for // unicast routing. The trees are rooted at the destination. // Cost for tunnel links and direct links are the same. // Blocked ports not relevant. calculateShortestPathTreeInClusters(); // Step 5. Compute broadcast tree in each cluster. // Cost for tunnel links are high to discourage use of // tunnel links. The cost is set to the number of nodes // in the cluster + 1, to use as minimum number of // clusters as possible. // Blocked ports not relevant. calculateBroadcastNodePortsInClusters(); /* **************************************************************** */ // Step 9. Compute switches and broadcast switches assigned // Blocked ports are not relevant as this procedure // considers only those switchports that belong to allowedUnicastPort // mapping. calculateSwitchPortMappings(); // Step 10. print topology. if (log.isTraceEnabled()) { printTopology(); } } @Override public void printTopology() { super.printTopology(); if (log.isTraceEnabled()) { log.trace("-----------------------------------------------"); log.trace("destinationRootedTreesMultipath: {}", destinationRootedTreesMultipath); log.trace("Computed Broadcast Domains: {}", broadcastDomains); log.trace("Higher Topology (HT) Nodes: {}", htNodes); log.trace("HT neighbors: ", htNeighbors); log.trace("HT Reachability: {}", htNextHop); log.trace("L2 Domain Ids: {}", clusterL2DomainIdMap); log.trace("Allowed Ports for Unicast: {}", allowedUnicastPorts); log.trace("Allowed Ports for Broadcast: {}", allowedIncomingBroadcastPorts); log.trace("permitted switches:"); for (NodePortTuple npt : permittedSwitches.keySet()) { log.trace(" Port: {}", npt); for (long swid : permittedSwitches.get(npt)) { log.trace(" {}", HexString.toHexString(swid)); } } log.trace("permitted ports to broadcast domain: {}", permittedPortToBroadcastDomains); log.trace("-----------------------------------------------"); } } /** * Given the tunnel domain switches, this procedure creates * bidirectional links between the tunnel domain and other * switches that are tunnel enabled. */ private void createTunnelDomainLinks() { if (tunnelDomain == null) return; if (switches == null) { log.trace("No switches to create tunnel links."); } short tport = 1; for(NodePortTuple npt: tunnelPorts) { addTunnelLinks(npt.getNodeId(), npt.getPortId(), tunnelDomain.longValue(), tport); tport++; } // Add all the tunnel domain ports to tunnel ports. for (short i=1; i<tport; i++) { NodePortTuple npt = new NodePortTuple(tunnelDomain.longValue(), i); tunnelPorts.add(npt); } } private void addTunnelLinks(long sw1, short port1, long sw2, short port2) { NodePortTuple npt1 = new NodePortTuple(sw1, port1); NodePortTuple npt2 = new NodePortTuple(sw2, port2); // if switches doesn't contain a switch, then add it there. // It is possible that switches don't exist as there wer no // links identified. if (!switches.contains(sw1)) { switches.add(sw1); } if (!switches.contains(sw2)) { switches.add(sw2); } // if switchPorts don't contain the ports, add the ports. if (!switchPorts.containsKey(sw1)) { switchPorts.put(sw1, new HashSet<Short>()); } if (!switchPorts.containsKey(sw2)) { switchPorts.put(sw2, new HashSet<Short>()); } switchPorts.get(sw1).add(port1); switchPorts.get(sw2).add(port2); // if switchportLinks doesn't contain the keys, add the keys. if (!switchPortLinks.containsKey(npt1)) { switchPortLinks.put(npt1, new HashSet<Link>()); } if (!switchPortLinks.containsKey(npt2)) { switchPortLinks.put(npt2, new HashSet<Link>()); } Link link12 = new Link(sw1, port1, sw2, port2); Link link21 = new Link(sw2, port2, sw1, port1); // Add the two links to both switch ports. switchPortLinks.get(npt1).add(link12); switchPortLinks.get(npt2).add(link12); switchPortLinks.get(npt1).add(link21); switchPortLinks.get(npt2).add(link21); } @Override protected Set<NodePortTuple> getBroadcastNodePortsInCluster(long sw) { long clusterId = getOpenflowDomainId(sw); Set<NodePortTuple> ports = clusterBroadcastNodePorts.get(clusterId); ports.removeAll(tunnelPorts); return ports; } private static void addToPortMapping(Map<NodePortTuple, Set<Long>> permitted, NodePortTuple p, long s) { if (permitted.containsKey(p) == false) { permitted.put(p, new HashSet<Long>()); } permitted.get(p).add(s); } private void calculateSwitchPortMappings() { int cost; NodePortTuple resultOut; NodePortTuple resultIn; // for every cluster. for(Cluster c: clusters) { long n = clusterKeyMap.get(c.getId()); for(long nbr: htNeighbors.get(n)) { OrderedNodePair onp = new OrderedNodePair(n, nbr); // We need to compute the assignments only if the // nodes are allowed to communicate in the higher level topology // If a port was blocked here, then it woudln't have been added // to the allowedUnicastPorts. Thus, we don't need to take care // of it here. if (allowedUnicastPorts.containsKey(onp) == false) continue; // map every switch in the cluster to a port. for (long s : c.getNodes()) { cost = MAX_PATH_WEIGHT; resultOut = null; for (NodePortTuple npt : allowedUnicastPorts.get(onp)) { if (this.getCost(s, npt.getNodeId()) < cost) { cost = this.getCost(s, npt.getNodeId()); resultOut = npt; Long temp = new Long(s * npt.getNodeId() * npt.getPortId() * LONG_PRIME); temp.hashCode(); } } if (resultOut != null) { addToPortMapping(permittedSwitches, resultOut, s); } } // map every other cluster/broadcast domain to the switch ports for (long othernbr : htNeighbors.get(n)) { // the switch-port assignment is decided by the node // with the shortest id in the higher level topology if (othernbr >= nbr) continue; OrderedNodePair otheronp = new OrderedNodePair(othernbr, n); // If a port was blocked here, then it woudln't have been // added // to the allowedUnicastPorts. Thus, we don't need to take // care // of it here. if (allowedUnicastPorts.containsKey(otheronp) == false) continue; cost = MAX_PATH_WEIGHT; resultOut = null; resultIn = null; for (NodePortTuple input : allowedUnicastPorts.get(otheronp)) { for (NodePortTuple output : allowedUnicastPorts.get(onp)) { if (this.getCost(input.getNodeId(), output.getNodeId()) < cost) { cost = this.getCost(input.getNodeId(), output.getNodeId()); resultOut = output; resultIn = input; Long temp = new Long(input.getNodeId() * input.getPortId() * output.getNodeId() * output.getPortId() * LONG_PRIME); temp.hashCode(); } } } if (resultOut != null) { addToPortMapping(permittedPortToBroadcastDomains, resultOut, othernbr); addToPortMapping(permittedPortToBroadcastDomains, resultIn, nbr); } } } } } /** * This method classifies all the ports as either allowedUnicast ports or * blocked ports. In addition, it identifies one of the unicast ports to * accept incoming broadcast traffic from a broadcast domain. */ protected void classifyPorts() { for (long s : switches) { if (switchPorts.get(s) == null) continue; for (short p : switchPorts.get(s)) { NodePortTuple np = new NodePortTuple(s, p); if (switchPortLinks.get(np) == null) continue; for (Link l : switchPortLinks.get(np)) { if (isBroadcastDomainLink(l)) { // The link is in broadcast domain, so create Cluster c1 = switchClusterMap.get(l.getSrc()); Cluster c2 = switchClusterMap.get(l.getDst()); long n1 = clusterKeyMap.get(c1.getId()); long n2 = clusterKeyMap.get(c2.getId()); NodePortTuple npt1 = new NodePortTuple( l.getSrc(), l.getSrcPort()); NodePortTuple npt2 = new NodePortTuple( l.getDst(), l.getDstPort()); BroadcastDomain bd1 = nodePortBroadcastDomainMap.get(npt1); BroadcastDomain bd2 = nodePortBroadcastDomainMap.get(npt2); if (bd1 == null) { if (log.isTraceEnabled()) { log.trace("No broadcastDomain for a " + "broadcastPort, {}", npt1); } bd1 = bd2; } if (bd2 == null) { if (log.isTraceEnabled()) { log.trace("No broadcastDomain for a broadcastPort, " + "{}", npt2); } } if (bd1 != bd2) { if (log.isTraceEnabled()) { log.trace("BroadcastDomainLink {} connects " + "to two different broadcastDomains {} {}", new Object[] { l, bd1, bd2 }); } } long z = broadcastDomainKeyMap.get(bd1); OrderedNodePair onp1z = new OrderedNodePair(n1, z); OrderedNodePair onpz1 = new OrderedNodePair(z, n1); if (allowedUnicastPorts.containsKey(onp1z)) { allowedUnicastPorts.get(onp1z).add(npt1); allowedUnicastPorts.get(onpz1).add(npt1); NodePortTuple prev = allowedIncomingBroadcastPorts.get(onp1z); if (prev == null || npt1.compareTo(prev) < 0) { allowedIncomingBroadcastPorts.put(onp1z, npt1); allowedIncomingBroadcastPorts.put(onpz1, npt1); } } else { // if the allowed unicast port doesn't contain // this higher level topology link, this switch // port must be blocked. this.blockedPorts.add(npt1); } OrderedNodePair onp2z = new OrderedNodePair(n2, z); OrderedNodePair onpz2 = new OrderedNodePair(z, n2); if (allowedUnicastPorts.containsKey(onp2z)) { allowedUnicastPorts.get(onp2z).add(npt2); allowedUnicastPorts.get(onpz2).add(npt2); NodePortTuple prev = allowedIncomingBroadcastPorts.get(onp2z); if (prev == null || npt2.compareTo(prev) < 0) { allowedIncomingBroadcastPorts.put(onp2z, npt2); allowedIncomingBroadcastPorts.put(onpz2, npt2); } } else { // if the allowed unicast port doesn't contain // this higher level topology link, this switch // port must be blocked. this.blockedPorts.add(npt2); } } else { Cluster c1 = switchClusterMap.get(l.getSrc()); Cluster c2 = switchClusterMap.get(l.getDst()); if (c1 == c2) continue; long n1 = clusterKeyMap.get(c1.getId()); long n2 = clusterKeyMap.get(c2.getId()); NodePortTuple npt1 = new NodePortTuple( l.getSrc(), l.getSrcPort()); NodePortTuple npt2 = new NodePortTuple( l.getDst(), l.getDstPort()); OrderedNodePair onp1 = new OrderedNodePair(n1, n2); OrderedNodePair onp2 = new OrderedNodePair(n2, n1); if (allowedUnicastPorts.containsKey(onp1)) { allowedUnicastPorts.get(onp1).add(npt1); allowedUnicastPorts.get(onp2).add(npt2); NodePortTuple prev = allowedIncomingBroadcastPorts.get(onp1); if (prev == null || npt1.compareTo(prev) < 0) { allowedIncomingBroadcastPorts.put(onp1, npt1); } prev = allowedIncomingBroadcastPorts.get(onp2); if (prev == null || npt2.compareTo(prev) < 0) { allowedIncomingBroadcastPorts.put(onp2, npt2); } } else { // if the allowed unicast port doesn't contain // this higher level topology link, this switch // port must be blocked. this.blockedPorts.add(npt1); this.blockedPorts.add(npt2); } } } } } } /** * This method computes higher level topology involving clusters and * broadcast domains. */ private void createHigherLevelTopology() { // Create cluster nodes. for (Cluster c : clusters) { long nid = htNodes.size() + 1; htNodes.put(nid, c); clusterKeyMap.put(c.getId(), nid); } // Create broadcast domain nodes. for (BroadcastDomain bd : broadcastDomains) { long nid = htNodes.size() + 1; htNodes.put(nid, bd); broadcastDomainKeyMap.put(bd, nid); } // clear the neighbors list. htNeighbors.clear(); // create an entry for every node in the higher level topology. for (long nid : htNodes.keySet()) { htNeighbors.put(nid, new HashSet<Long>()); } // Create links between cluster nodes through unidirectional link for (long s : switches) { if (switchPorts.get(s) == null) continue; for (short p : switchPorts.get(s)) { NodePortTuple np = new NodePortTuple(s, p); if (switchPortLinks.get(np) == null) continue; if (isBroadcastDomainPort(np)) continue; for (Link l : switchPortLinks.get(np)) { // Ignore blocked links. if (isBlockedLink(l)) continue; if (isBroadcastDomainLink(l)) continue; Cluster c1 = switchClusterMap.get(l.getSrc()); Cluster c2 = switchClusterMap.get(l.getDst()); if (c1 != c2) { long n1 = clusterKeyMap.get(c1.getId()); long n2 = clusterKeyMap.get(c2.getId()); htNeighbors.get(n1).add(n2); htNeighbors.get(n2).add(n1); } } } } // Create links between broadcastdomains and clusters for (NodePortTuple npt : nodePortBroadcastDomainMap.keySet()) { Cluster c = switchClusterMap.get(npt.getNodeId()); long cid = clusterKeyMap.get(c.getId()); BroadcastDomain bd = nodePortBroadcastDomainMap.get(npt); long nid = broadcastDomainKeyMap.get(bd); htNeighbors.get(cid).add(nid); htNeighbors.get(nid).add(cid); } } @Override protected void calculateBroadcastTreeInClusters() { clusterBroadcastTrees.clear(); // Make every tunnel link have a weight that's more than the // number of switches in the network. Map<Link, Integer> linkCost = new HashMap<Link, Integer>(); int tunnel_weight = switchPorts.size() + 1; for (NodePortTuple npt : tunnelPorts) { if (switchPortLinks.get(npt) == null) continue; for (Link link : switchPortLinks.get(npt)) { if (link == null) continue; linkCost.put(link, tunnel_weight); } } for (Cluster c : clusters) { BroadcastTree tree = dijkstra(c, c.getId(), linkCost, true); clusterBroadcastTrees.put(c.getId(), tree); } } /** * For every node (cluster) in the higher level topology, this method * computes the smallest cluster id that is connected to it. This is the * equivalent of clusterId, considering non-openflow links as well. The id * is the smallest switch id that to which a switch has L2 connectivity to. */ private void computeClusterL2DomainMap() { // For every node (cluster) in the higher level topology, compute // what is the smallest cluster Id it is connected to. clusterL2DomainIdMap.clear(); for (long n : htNodes.keySet()) { long l2DomainId; // ignore non-openflow domains. if (htNodes.get(n) instanceof BroadcastDomain) continue; Cluster c = (Cluster) htNodes.get(n); // We have computed the L2 domain id already. if (clusterL2DomainIdMap.containsKey(n)) continue; l2DomainId = c.getId(); // get the minimum among all the connected openflow clusters for (long nbr : htNextHop.get(n).keySet()) { if (htNodes.get(nbr) instanceof BroadcastDomain) continue; Cluster nbrCluster = (Cluster) htNodes.get(nbr); if (nbrCluster.getId() < l2DomainId) l2DomainId = nbrCluster.getId(); } // Add to the L2DomainId Map. // note that n is also in its neighbor list, so n will also // get assigned here. for (long nbr : htNextHop.get(n).keySet()) { if (htNodes.get(nbr) instanceof BroadcastDomain) continue; clusterL2DomainIdMap.put(nbr, l2DomainId); } } } protected void createForestInHigherLevelTopology() { // Using only neighbors, we have to pull out a // forests. Set<Long> visitedNodes = new HashSet<Long>(); Queue<Long> queue = new LinkedList<Long>(); // Let's do a breadth-first search here. for (long s : htNeighbors.keySet()) { if (visitedNodes.contains(s)) continue; queue.clear(); queue.add(s); visitedNodes.add(s); while (queue.peek() != null) { long u = queue.remove(); for (long nbr : htNeighbors.get(u)) { if (visitedNodes.contains(nbr) == false) { visitedNodes.add(nbr); queue.add(nbr); OrderedNodePair onp1 = new OrderedNodePair(u, nbr); OrderedNodePair onp2 = new OrderedNodePair(nbr, u); if (allowedUnicastPorts.containsKey(onp1) == false) { allowedUnicastPorts.put(onp1, new HashSet<NodePortTuple>()); } if (allowedUnicastPorts.containsKey(onp2) == false) { allowedUnicastPorts.put(onp2, new HashSet<NodePortTuple>()); } } } } } // Once the forest is computed, now compute how every node can reach // every other node using only the forest computed in the higher // level topology. for (long s : htNeighbors.keySet()) { Map<Long, Long> nh = new HashMap<Long, Long>(); htNextHop.put(s, nh); nh.put(s, s); // self for (long nbr : htNeighbors.get(s)) { // ignore reachability through this neighbor if // the node pair is not in the allowedports key OrderedNodePair onp_s_nbr = new OrderedNodePair(s, nbr); if (allowedUnicastPorts.containsKey(onp_s_nbr) == false) { continue; } nh.put(nbr, nbr); // add the first hop neighbor // the following two statements could be put outside the // for(long:nbr.. loop as well. // add node s to the visitedNodes to ignore nbr -> s. visitedNodes.clear(); visitedNodes.add(s); // run a breadth-first search from nbr, considering // all neighbors except s. [s is already added to visited.] visitedNodes.add(nbr); queue.clear(); queue.add(nbr); while (queue.peek() != null) { long u = queue.remove(); for (long v : htNeighbors.get(u)) { // ignore reachability through this neighbor if // the node pair is not in the allowedPorts key OrderedNodePair onp_uv = new OrderedNodePair(u, v); if (allowedUnicastPorts.containsKey(onp_uv) == false) { continue; } if (visitedNodes.contains(v)) continue; visitedNodes.add(v); queue.add(v); nh.put(v, nbr); // v can be reached through nbr from s. } } } } } private long getHTNodeId(long sw, short port) { long result = -1; NodePortTuple npt = new NodePortTuple(sw, port); if (isBroadcastDomainPort(npt)) { // this is a broadcast domain port BroadcastDomain bd = nodePortBroadcastDomainMap.get(npt); result = broadcastDomainKeyMap.get(bd); } else if (switchPortLinks.containsKey(npt)) { // there is another cluster this link connects to // we need the other cluster id // look at the first link and get the other switch port // and find out where it belongs to. NodePortTuple otherNpt; for (Link l : switchPortLinks.get(npt)) { if (l.getSrc() == npt.getNodeId() && l.getSrcPort() == npt.getPortId()) { otherNpt = new NodePortTuple(l.getDst(), l.getDstPort()); } else { otherNpt = new NodePortTuple(l.getSrc(), l.getSrcPort()); } // otherNpt cannot belong to broadcast doamain. // as we have already computed broadcast domain ports // based on reachability Cluster c = switchClusterMap.get(otherNpt.getNodeId()); if (c==null) result = -1; else result = clusterKeyMap.get(c.getId()); } } else { // this is not a port known to topology Cluster c = switchClusterMap.get(sw); if (c==null) result = -1; else result = clusterKeyMap.get(c.getId()); } return result; } // // Public methods for accessing some data structures // protected Set<BroadcastDomain> getBroadcastDomains() { return broadcastDomains; } protected Set<NodePortTuple> getBroadcastDomainPorts(NodePortTuple npt) { if (npt == null) return null; BroadcastDomain bd = nodePortBroadcastDomainMap.get(npt); if (bd == null) return null; return (bd.getPorts()); } protected Map<Long, Object> getHTNodes() { return htNodes; } protected Map<Long, Set<Long>> getHTNeighbors() { return htNeighbors; } protected Map<Long, Map<Long, Long>> getHTNextHops() { return htNextHop; } protected Set<OrderedNodePair> getAllowedNodePairs() { return allowedUnicastPorts.keySet(); } protected Map<Long, Map<Long, Long>> getNextHopMap() { return htNextHop; } protected Map<OrderedNodePair, Set<NodePortTuple>> getAllowedPorts() { return allowedUnicastPorts; } protected Map<OrderedNodePair, NodePortTuple> getAllowedIncomingBroadcastPorts() { return allowedIncomingBroadcastPorts; } protected Map<Long, Long> getL2DomainIds() { return clusterL2DomainIdMap; } protected Map<NodePortTuple, Set<Long>> getAllowedPortsToBroadcastDomains() { return permittedPortToBroadcastDomains; } // // ITopologyService interface method helpers // @Override public boolean isAttachmentPointPort(long switchid, short port) { NodePortTuple npt = new NodePortTuple(switchid, port); // if the switchport is not known to topology, then it is an // attachment point port (i.e. if the port is not an internal port - a // port on direct OF links) if (switchPortLinks.containsKey(npt) == false) return true; // the switchport is known to topology. // A switch port belonging to a broadcast domain is an // attachment point port. if (isBroadcastDomainPort(npt)) return true; // switch port is not in broadcast domain. // Thus, the switch port has either one direct link or two // direct links. // If it has only one link, then we need to check if the // other switch belongs to the same broadcast domain or not. // Even though this is a for loop, there's only one element // in the set. if (switchPortLinks.get(npt).size() == 1) { for (Link link : switchPortLinks.get(npt)) { if (inSameOpenflowDomain(link.getSrc(), link.getDst()) == false) return true; } } return false; } @Override protected Set<Long> getSwitchesInOpenflowDomain(long switchId) { Cluster c = switchClusterMap.get(switchId); if (c == null) { // The switch is not known to topology as there // are no links connected to it. Set<Long> nodes = new HashSet<Long>(); nodes.add(switchId); return nodes; } Set<Long> nodes = c.getNodes(); nodes.remove(tunnelDomain); return (nodes); } /** * Indicates if the given switch port is allowed for sending/receiving * packets by the higher-level topology or not. */ @Override public boolean isAllowed(long sw, short portId) { NodePortTuple npt = new NodePortTuple(sw, portId); if (blockedPorts.contains(npt)) return false; /* * if (nodePortBroadcastDomainMap.containsKey(npt)) { BroadcastDomain bd * = nodePortBroadcastDomainMap.get(npt); long n1 = * broadcastDomainKeyMap.get(bd); long n = * clusterKeyMap.get(switchClusterMap.get(sw)); OrderedNodePair onp = * new OrderedNodePair(n, n1); return * (allowedUnicastPorts.containsKey(onp)); } */ return true; } @Override protected boolean isIncomingBroadcastAllowedOnSwitchPort(long sw, short portId) { NodePortTuple npt = new NodePortTuple(sw, portId); if (isInternalToOpenflowDomain(sw, portId)) { long clusterId = getOpenflowDomainId(sw); if (clusterBroadcastNodePorts.get(clusterId).contains(npt)) return true; else return false; } else if (nodePortBroadcastDomainMap.containsKey(npt)) { long n = clusterKeyMap.get(switchClusterMap.get(sw).getId()); for(long nbr: htNeighbors.get(n)) { OrderedNodePair onp = new OrderedNodePair(n, nbr); NodePortTuple othernpt = allowedIncomingBroadcastPorts.get(onp); if (othernpt != null) { if (othernpt.equals(npt)) return true; } } return false; } return true; } private NodePortTuple getPermitted(OrderedNodePair onp, Map<NodePortTuple, Set<Long>> permitted, long s) { if (allowedUnicastPorts.get(onp) != null) { for (NodePortTuple npt : allowedUnicastPorts.get(onp)) { Set<Long> pSet = permitted.get(npt); if (pSet == null) continue; if (pSet.contains(s)) return npt; } } return null; } @Override public NodePortTuple getIncomingSwitchPort(long src, short srcPort, long dst, short dstPort) { List<NodePortTuple> nptList = buildNodePortList(src, srcPort, dst, dstPort, 0); //cookie = 0 for now as it does not alter incoming port if (nptList == null || nptList.size() == 0) { // if the nptList is null or nptList is zero size, then // a path doesn't exist, if the two switches are different. if (src != dst) { return null; } else return new NodePortTuple(src, srcPort); } else return nptList.get(0); } @Override public NodePortTuple getOutgoingSwitchPort(long src, short srcPort, long dst, short dstPort) { List<NodePortTuple> nptList = buildNodePortList(src, srcPort, dst, dstPort, 0); //cookie = 0 for now as it does not alter incoming port if (nptList == null || nptList.size() == 0) { // if the nptList is null or nptList is zero size, then // a path doesn't exist, if the two switches are different. if (src != dst) { return null; } else return new NodePortTuple(dst, dstPort); } else return nptList.get(nptList.size() - 1); } @Override protected long getL2DomainId(long switchId) { // return getOpenflowDomainId(switchId); Cluster c = switchClusterMap.get(switchId); // if c is null, then this is a stand-alone switch with no links. if (c == null) return switchId; Long n = clusterKeyMap.get(c.getId()); return clusterL2DomainIdMap.get(n); } @Override public boolean inSameL2Domain(long switch1, long switch2) { // These are in the same island, if the clusters to // which the two switches belong to are connected in // the higher-level topology. if (switch1 == switch2) return true; Cluster c1 = switchClusterMap.get(switch1); Cluster c2 = switchClusterMap.get(switch2); if (c1 == null || c2 == null) return false; long n1 = clusterKeyMap.get(c1.getId()); long n2 = clusterKeyMap.get(c2.getId()); Map<Long, Long> rMap = htNextHop.get(n1); if (rMap == null) return false; if (rMap.containsKey(n2) == false) return false; return true; } @Override public boolean inSameBroadcastDomain(long s1, short p1, long s2, short p2) { NodePortTuple npt1 = new NodePortTuple(s1, p1); NodePortTuple npt2 = new NodePortTuple(s2, p2); BroadcastDomain bd1, bd2; bd1 = nodePortBroadcastDomainMap.get(npt1); bd2 = nodePortBroadcastDomainMap.get(npt2); if (bd1 == null || bd2 == null) return (s1 == s2 && p1 == p2); else return (bd1 == bd2); } public Set<Short> getBroadcastDomainPorts(long sw) { Set<Short> result = new HashSet<Short>(); for (NodePortTuple npt : nodePortBroadcastDomainMap.keySet()) { if (npt.getNodeId() == sw) { result.add(npt.getPortId()); } } return result; } /** * Given a target switch and a source attachment point (src, srcPort), * compute the ports on the targetSw where this packet should be broadcast. * Note that src,srcPort may not be in the same openflow domain as the * target switch. */ @Override public Set<Short> getBroadcastPorts(long targetSw, long src, short srcPort) { Set<Short> result = new HashSet<Short>(); NodePortTuple nptSrc; long n1; // Get the consistent attachment point for the source in the // cluster containing targetSw. nptSrc = getConsistentBroadcastAttachmentPoint(targetSw, src, srcPort); if (nptSrc == null) return result; if (inSameOpenflowDomain(targetSw, nptSrc.getNodeId()) == false) return result; /* // Add the internal broadcast ports long clusterId = getOpenflowDomainId(targetSw); if (clusterBroadcastNodePorts.get(clusterId) != null) for(NodePortTuple npt: clusterBroadcastNodePorts.get(clusterId)) { if (npt.getNodeId() == targetSw) { result.add(npt.getPortId()); } } */ // Add external broadcast ports if (isBroadcastDomainPort(nptSrc)) { BroadcastDomain bd = nodePortBroadcastDomainMap.get(nptSrc); long bdId = broadcastDomainKeyMap.get(bd); Cluster c = switchClusterMap.get(targetSw); if (c!=null) { n1 = clusterKeyMap.get(c.getId()); if (htNeighbors.get(n1) != null) for (long nbr : htNeighbors.get(n1)) { // for each higher-level // node OrderedNodePair onp = new OrderedNodePair( n1, nbr); NodePortTuple x = getPermitted(onp, permittedPortToBroadcastDomains, bdId); if (x == null) continue; if (x.getNodeId() == targetSw) result.add(x.getPortId()); } } } else { // if the srcPort is not a broadcast domain port. Cluster c = switchClusterMap.get(src); if (c!=null) { n1 = clusterKeyMap.get(c.getId()); if (htNeighbors.get(n1) != null) for (long nbr : htNeighbors.get(n1)) { // for each higher-level // node OrderedNodePair onp = new OrderedNodePair( n1, nbr); NodePortTuple x = getPermitted(onp, permittedSwitches, src); if (x == null) continue; if (x.getNodeId() == targetSw) result.add(x.getPortId()); } } } return result; } @Override public NodePortTuple getAllowedIncomingBroadcastPort(long src, short srcPort) { NodePortTuple resultNpt = null; NodePortTuple srcNpt = new NodePortTuple(src, srcPort); /** * If the src is not a broadcast domain port, return null. */ if (!nodePortBroadcastDomainMap.containsKey(srcNpt)) { return null; } Cluster srcCluster = switchClusterMap.get(src); if (srcCluster == null) return null; long htSrcClusterId = clusterKeyMap.get(srcCluster.getId()); long htSrc = getHTNodeId(src, srcPort); if (htSrc < 0) return null; OrderedNodePair onp = new OrderedNodePair(htSrc, htSrcClusterId); if (htSrc == htSrcClusterId) { /** * This should not happen since src is a broadcast domain port, * which connects a broadcast domain to the local cluster. */ log.warn("BroadcastDomain port {} {} without any broadcast domain", HexString.toHexString(src), srcPort); resultNpt = srcNpt; } else { resultNpt = allowedIncomingBroadcastPorts.get(onp); } return resultNpt; } @Override public NodePortTuple getAllowedOutgoingBroadcastPort(long src, short srcPort, long dst, short dstPort) { NodePortTuple resultNpt = null; Cluster srcCluster = switchClusterMap.get(src); if (srcCluster == null) { NodePortTuple dstNpt = new NodePortTuple(dst, dstPort); if (src == dst) return dstNpt; else return null; } long htSrcClusterId = clusterKeyMap.get(srcCluster.getId()); long htSrc = getHTNodeId(src, srcPort); long htDst = getHTNodeId(dst, dstPort); if (htSrc < 0 || htDst < 0) return null; /** * in the higher level topology htSrc is trying to reach htDst through * the cluster htSrcClusterId htSrc may be the same as htSrcClusterId * The packet-in is from cluster htSrcClusterId If we have to go from * htSrcClusterId to htDst, what's the nexthop? */ Map<Long, Long> rMap = htNextHop.get(htSrcClusterId); if (rMap == null) return null; if (rMap.containsKey(htDst) == false) return null; long nextHopNodeId = rMap.get(htDst); OrderedNodePair onp = new OrderedNodePair(htSrcClusterId, nextHopNodeId); if (htSrcClusterId == htDst) { NodePortTuple dstNpt = new NodePortTuple(dst, dstPort); if (!isBroadcastDomainPort(dstNpt)) { return dstNpt; } } if (htSrc == htSrcClusterId) { /** * if src switch is an internal switch, get the permitted outgoing * port to the destination broadcast domain. */ resultNpt = getPermitted(onp, permittedSwitches, src); } else { /** * If src switch belongs to a broadcast domain, x, and dst belongs * to a different broadcast domain, y, then get the permitted * incoming nodePort, z, for traffic from x -> y, then look up the * permitted outgoing port to y for z. */ resultNpt = getPermitted(onp, permittedPortToBroadcastDomains, htSrc); if (resultNpt != null) { resultNpt = getPermitted(onp, permittedSwitches, resultNpt.getNodeId()); } } return resultNpt; } @Override public boolean isConsistent(long oldSw, short oldPort, long newSw, short newPort) { NodePortTuple newNpt = new NodePortTuple(newSw, newPort); if (oldSw == newSw && oldPort == newPort) return true; if (isInternalToOpenflowDomain(newSw, newPort)) return true; if (!isBroadcastDomainPort(newNpt)) return false; Cluster newCluster = switchClusterMap.get(newSw); if (newCluster == null) return false; long htNewClusterId = clusterKeyMap.get(newCluster.getId()); long htOld = getHTNodeId(oldSw, oldPort); long htNew = getHTNodeId(newSw, newPort); if (htOld < 0 || htNew < 0) return false; Map<Long, Long> rMap = htNextHop.get(htNewClusterId); if (rMap == null) return false; if (rMap.containsKey(htOld) == false) return false; long nextHopNodeId = rMap.get(htOld); return (htNew == nextHopNodeId); } /** * Returns the path from the higher level topology * * @param htSrc * @param htDst * @return */ List<Long> getHTPath(long htSrc, long htDst) { List<Long> result = new ArrayList<Long>(); if (htNextHop.get(htSrc) == null) return null; if (htNextHop.get(htSrc).get(htDst) == null) return null; result.add(htSrc); long currNode = htSrc; while (currNode != htDst) { currNode = htNextHop.get(currNode).get(htDst); result.add(currNode); } return result; } private void addNodePortsToList(List<NodePortTuple> nptList, Route route) { if (route == null) return; /* * List<Link> links = route.getPath(); for(int i=0; i<links.size(); ++i) * { NodePortTuple npt; npt = new NodePortTuple(links.get(i).getSrc(), * links.get(i).getSrcPort()); nptList.add(npt); npt = new * NodePortTuple(links.get(i).getDst(), links.get(i).getDstPort()); * nptList.add(npt); } */ nptList.addAll(route.getPath()); } protected List<NodePortTuple> getFirstHopRoute(long sw, long htNode, long cookie) { // The goal here is to compute the path from the switch // to the output port connected to the htNode. List<NodePortTuple> nptList = new ArrayList<NodePortTuple>(); // get the output nodeport tuple. Cluster c1 = switchClusterMap.get(sw); if (c1 == null) return null; long n1 = clusterKeyMap.get(c1.getId()); OrderedNodePair onp = new OrderedNodePair(n1, htNode); NodePortTuple dstNpt = getPermitted(onp, permittedSwitches, sw); if (dstNpt == null) return null; // get the path from sw to dstNpt.node Route route = getRoute(sw, dstNpt.getNodeId(), cookie); addNodePortsToList(nptList, route); nptList.add(dstNpt); return nptList; } /** * This method computes the last hop route to the destination switch. htNode * is the top-level node that is before the cluster to which switch sw * belongs to. * * @param htNode * @param sw * @return */ protected List<NodePortTuple> getLastHopRoute(long htNode, long sw, long cookie) { // The goal here is to compute the path from the switch // to the output port connected to the htNode. List<NodePortTuple> nptList = new ArrayList<NodePortTuple>(); // get the output nodeport tuple. Cluster c1 = switchClusterMap.get(sw); if (c1 == null) return null; long n1 = clusterKeyMap.get(c1.getId()); OrderedNodePair onp = new OrderedNodePair(n1, htNode); NodePortTuple srcNpt = getPermitted(onp, permittedSwitches, sw); if (srcNpt == null) return null; nptList.add(srcNpt); // get the path from sw to dstNpt.node Route route = getRoute(srcNpt.getNodeId(), sw, cookie); addNodePortsToList(nptList, route); return nptList; } /** * This method computes the route from [fromNode] to [toNode] in the higher * level topology through the [throughNode] in the higher level topology * (which is a cluster). The route is a sequence of nodeports, which * includes the correct ingress and egress nodeports to be used for the * traffic to enter and exit. This method is intended to be used for only * adjacent nodes in the higher-level topology. Thus, if [fromNode] and * [toNode] are not connected to the cluster [throughNode], it will return * null. * * @param fromNode * @param toNode * @param throughNode * @return */ protected List<NodePortTuple> getRouteThroughCluster(long fromNode, long toNode, long throughNode, long cookie) { List<NodePortTuple> nptList = new ArrayList<NodePortTuple>(); OrderedNodePair onp1 = new OrderedNodePair(throughNode, toNode); OrderedNodePair onp2 = new OrderedNodePair(throughNode, fromNode); // get the ingress switchport. NodePortTuple dstNpt = getPermitted(onp1, permittedPortToBroadcastDomains, fromNode); NodePortTuple srcNpt = getPermitted(onp2, permittedPortToBroadcastDomains, toNode); if (srcNpt == null || dstNpt == null) return null; // add the source nodeports. nptList.add(srcNpt); // Get the route and add intermediate nodeports. Route route = getRoute(srcNpt.getNodeId(), dstNpt.getNodeId(), cookie); addNodePortsToList(nptList, route); // Add the egress switch port. nptList.add(dstNpt); return nptList; } protected List<NodePortTuple> multiroute(long srcId, long dstId, long cookie) { return multiroute(srcId, false, dstId, false, cookie); } // Overrides the one in TopologyInstance. protected List<NodePortTuple> multiroute(long srcId, boolean srcBDFlag, long dstId, boolean dstBDFlag, long cookie) { List<NodePortTuple> nptList = new ArrayList<NodePortTuple>(); long n1, n2; Cluster c1 = null, c2 = null; // if the two switches are not in the same L2 domain, // there should not be a path. if (srcBDFlag == false && dstBDFlag == false && inSameL2Domain(srcId, dstId) == false) { return null; } if (srcBDFlag == false) { c1 = switchClusterMap.get(srcId); if (c1 == null) return null; n1 = clusterKeyMap.get(c1.getId()); } else { if (htNodes.get(srcId) instanceof BroadcastDomain == false) return null; BroadcastDomain bd = (BroadcastDomain) htNodes.get(srcId); n1 = broadcastDomainKeyMap.get(bd); } if (dstBDFlag == false) { c2 = switchClusterMap.get(dstId); if (c2 == null) return null; n2 = clusterKeyMap.get(c2.getId()); } else { if (htNodes.get(dstId) instanceof BroadcastDomain == false) return null; BroadcastDomain bd = (BroadcastDomain) htNodes.get(dstId); n2 = broadcastDomainKeyMap.get(bd); } // Both are switches and are in the same openflow domain. if (srcBDFlag == false && dstBDFlag == false && c1.getId() == c2.getId()) // get the direct path. { Route route = getRoute(srcId, dstId, cookie); addNodePortsToList(nptList, route); return nptList; } List<Long> htPath = getHTPath(n1, n2); // if the first ht-node is broadcast domain, we don't have // to anything. // if the first ht-node is a cluster, then we have to compute // a path from the switch to the 2nd switch. if (htNodes.get(htPath.get(0)) instanceof BroadcastDomain == false) { nptList.addAll(getFirstHopRoute(srcId, htPath.get(1), cookie)); } // skip the first and the last nodes. for (int i = 1; i < htPath.size() - 1; ++i) { if (htNodes.get(htPath.get(i)) instanceof BroadcastDomain) continue; List<NodePortTuple> list; list = getRouteThroughCluster(htPath.get(i - 1), htPath.get(i + 1), htPath.get(i), cookie); if (list != null) nptList.addAll(list); else if (log.isTraceEnabled()) { log.trace("No route found src id {}, dst id {}", HexString.toHexString(srcId), HexString.toHexString(dstId)); } } if (htNodes.get(htPath.get(htPath.size() - 1)) instanceof BroadcastDomain == false) { nptList.addAll(getLastHopRoute(htPath.get(htPath.size() - 2), dstId, cookie)); } // At this point, we have the entire route that we need. // we can create a route. return nptList; } @Override protected Route getRoute(long srcId, short srcPort, long dstId, short dstPort, long cookie) { // Return null the route source and desitnation are the // same switchports. if (srcId == dstId && srcPort == dstPort) return null; List<NodePortTuple> nptList = buildNodePortList(srcId, srcPort, dstId, dstPort, cookie); // nptList should already include source-destination ports. if (nptList == null) return null; RouteId id = new RouteId(srcId, dstId); return new Route(id, nptList); } /** * The route is from a source switch port to a destination port. * * @param srcId * @param srcPort * @param dstId * @param dstPort * @return */ protected List<NodePortTuple> buildNodePortList(long srcId, short srcPort, long dstId, short dstPort, long cookie) { long n1, n2; boolean srcBDFlag, dstBDFlag; NodePortTuple srcNpt = new NodePortTuple(srcId, srcPort); NodePortTuple dstNpt = new NodePortTuple(dstId, dstPort); if (isBroadcastDomainPort(srcNpt) == true) { BroadcastDomain bd = nodePortBroadcastDomainMap.get(srcNpt); n1 = broadcastDomainKeyMap.get(bd); srcBDFlag = true; } else { n1 = srcId; srcBDFlag = false; } if (isBroadcastDomainPort(dstNpt) == true) { BroadcastDomain bd = nodePortBroadcastDomainMap.get(dstNpt); n2 = broadcastDomainKeyMap.get(bd); dstBDFlag = true; } else { n2 = dstId; dstBDFlag = false; } List<NodePortTuple> nptList = multiroute(n1, srcBDFlag, n2, dstBDFlag, cookie); if (nptList == null && srcId != dstId) return null; if (nptList == null) { nptList = new ArrayList<NodePortTuple>(); } if (srcBDFlag == false) { nptList.add(0, srcNpt); } if (dstBDFlag == false) { nptList.add(dstNpt); } return nptList; } /** * This method returns the attachment for the device on the cluster in the * cluster where targetSw is present. * * @param apSwitch * @param apPort * @param targetSw * @return */ protected NodePortTuple getConsistentBroadcastAttachmentPoint(long targetSwitch, long apSwitch, short apPort) { if (isAttachmentPointPort(apSwitch, apPort) == false) return null; long apNode, targetNode; Cluster targetCluster = switchClusterMap.get(targetSwitch); NodePortTuple npt = new NodePortTuple(apSwitch, apPort); if (nodePortBroadcastDomainMap.containsKey(npt)) { // the node port belongs to a broadcast domain. if (targetCluster == null) return null; BroadcastDomain bd = nodePortBroadcastDomainMap.get(npt); apNode = this.broadcastDomainKeyMap.get(bd); } else { // the node port belongs to a cluster. Cluster apCluster = switchClusterMap.get(apSwitch); if (apSwitch == targetSwitch && apCluster == null) { return npt; } // if either one is null, then return null if (apCluster == null || targetCluster == null) return null; // if targetCluster and apCluster are the same, then // apSwitch, apPort should be the attachment point port // in that cluster. if (targetCluster.getId() == apCluster.getId()) { if (this.isAttachmentPointPort(apSwitch, apPort)) return npt; else return null; } apNode = clusterKeyMap.get(apCluster.getId()); } // at this time, we have the apNode. // get the targetNode. targetNode = clusterKeyMap.get(targetCluster.getId()); // Get the higher-level path from apNode to targetNode List<Long> htPath = this.getHTPath(apNode, targetNode); // These two are not in the same L2 domain. if (htPath == null) return null; // the path should consist of at least two nodes. int pathLength = htPath.size(); // we need to get the correct switchport to go from // htPath[pathlength-2] to htPath[pathlength-1]. // Since we are dealing with two nodes in higher level // topology, we can get the right switchport from // the broadcast domain. OrderedNodePair onp = new OrderedNodePair( htPath.get(pathLength - 1), htPath.get(pathLength - 2)); return this.allowedIncomingBroadcastPorts.get(onp); } protected BroadcastTreeMultipath dijkstraMultipath(Cluster c, Long root, Map<Link, Integer> linkCost, boolean isDstRooted) { HashMap<Long, ArrayList<Link>> nexthoplinks = new HashMap<Long, ArrayList<Link>>(); HashMap<Long, Integer> cost = new HashMap<Long, Integer>(); int w; for (Long node : c.getLinks().keySet()) { nexthoplinks.put(node, new ArrayList<Link>()); cost.put(node, MAX_PATH_WEIGHT); } // HashMap<Long, Boolean> seen = new HashMap<Long, Boolean>(); PriorityQueue<NodeDist> nodeq = new PriorityQueue<NodeDist>(); nodeq.add(new NodeDist(root, 0)); cost.put(root, 0); while (nodeq.peek() != null) { NodeDist n = nodeq.poll(); Long cnode = n.getNode(); int cdist = n.getDist(); if (cdist >= MAX_PATH_WEIGHT) break; // not checking seen nodes can cause sub-optimal tree // in some cases // if (seen.containsKey(cnode)) // continue; // seen.put(cnode, true); for (Link link : c.getLinks().get(cnode)) { Long neighbor; if (isDstRooted == true) neighbor = link.getSrc(); else neighbor = link.getDst(); if (linkCost == null || linkCost.get(link) == null) w = 1; else w = linkCost.get(link); int ndist = cdist + w; // the weight of the link, always 1 in // current version of sdnplatform. if (ndist < cost.get(neighbor)) { cost.put(neighbor, ndist); nexthoplinks.get(neighbor).clear(); nexthoplinks.get(neighbor).add(link); nodeq.add(new NodeDist(neighbor, ndist)); } else if (ndist == cost.get(neighbor)) { nexthoplinks.get(neighbor).add(link); nodeq.add(new NodeDist(neighbor, ndist)); } } } // sorting multipath links for (Long node : c.getLinks().keySet()) { Collections.sort(nexthoplinks.get(node)); } BroadcastTreeMultipath ret = new BroadcastTreeMultipath(nexthoplinks, cost); return ret; } @Override protected void calculateShortestPathTreeInClusters() { pathcache.invalidateAll(); destinationRootedTrees.clear(); destinationRootedTreesMultipath.clear(); Map<Link, Integer> linkCost = new HashMap<Link, Integer>(); int tunnel_weight = switchPorts.size() + 1; for (NodePortTuple npt : tunnelPorts) { if (switchPortLinks.get(npt) == null) continue; for (Link link : switchPortLinks.get(npt)) { if (link == null) continue; linkCost.put(link, tunnel_weight); } } for (Cluster c : clusters) { for (Long node : c.getLinks().keySet()) { BroadcastTree tree = dijkstra(c, node, linkCost, true); BroadcastTreeMultipath treeMultipath = dijkstraMultipath(c, node, linkCost, true); destinationRootedTrees.put(node, tree); destinationRootedTreesMultipath.put(node, treeMultipath); } } } @Override protected Route buildroute(RouteId id) { NodePortTuple npt; int routeCount = 0; long srcId = id.getSrc(); long dstId = id.getDst(); long cookie = id.getCookie(); LinkedList<NodePortTuple> switchPorts = new LinkedList<NodePortTuple>(); if (destinationRootedTreesMultipath == null) return null; if (destinationRootedTreesMultipath.get(dstId) == null) return null; HashMap<Long, ArrayList<Link>> nexthoplinks = destinationRootedTreesMultipath.get(dstId) .getLinks(); if (log.isTraceEnabled()) { log.trace("buildrouteMultipath: find multipath for srcId {} to dstId {} cookie " + cookie, HexString.toHexString(srcId), HexString.toHexString(dstId)); } if (!switches.contains(srcId) || !switches.contains(dstId)) { // This is a switch that is not connected to any other switch // hence there was no update for links (and hence it is not // in the network) if (log.isTraceEnabled()) { log.info("buildrouteMultipath: Standalone switch: {}", srcId); } // The only possible non-null path for this case is // if srcId equals dstId --- and that too is an 'empty' path [] } else if ((nexthoplinks != null) && (nexthoplinks.get(srcId) != null)) { while (srcId != dstId) { int choicesThisHop = nexthoplinks.get(srcId).size(); long linkIndex = cookie % choicesThisHop; Link l = nexthoplinks.get(srcId).get((int) linkIndex); routeCount = (routeCount > choicesThisHop) ? routeCount : choicesThisHop; npt = new NodePortTuple(l.getSrc(), l.getSrcPort()); switchPorts.addLast(npt); npt = new NodePortTuple(l.getDst(), l.getDstPort()); switchPorts.addLast(npt); // proceed to next hop srcId = l.getDst(); } } // else, no path exists, and path equals null // Eliminate any tunnel domain switch ports from the list. LinkedList<NodePortTuple> resultPorts = new LinkedList<NodePortTuple>(); for(int i=0; i<switchPorts.size(); ++i) { NodePortTuple sp = switchPorts.get(i); if (sp.getNodeId() == tunnelDomain.longValue()) continue; resultPorts.addLast(sp); } switchPorts = resultPorts; // tunnel domain switch ports eliminated. Route result = null; if (switchPorts != null && !switchPorts.isEmpty()) { result = new Route(id, switchPorts); // set routeCount to record total available routes; useful for // purpose like REST API retrieval result.setRouteCount(routeCount); } if (log.isTraceEnabled()) { log.trace("buildrouteMultipath: {}", result); } return result; } // cookie based getRoute, needed by multipath // The only difference between this and the super method is that // the route id uses cookie here, the super does not. // NOTE: Return a null route if srcId equals dstId. The null route // need not be stored in the cache. Moreover, the LoadingCache will // throw an exception if null route is returned. @Override protected Route getRoute(long srcId, long dstId, long cookie) { cookie=0; // Return null route if srcId equals dstId if (srcId == dstId) return null; RouteId id = new RouteId(srcId, dstId, cookie); Route result = null; try { result = pathcache.get(id); } catch (Exception e) { log.error("{}", e); } if (log.isTraceEnabled()) { log.trace("getRoute: {} -> {} cookie: " + cookie, id, result); } return result; } public ArrayList<Route> getRoutes(long srcDpid, long dstDpid) { ArrayList<Route> routes = new ArrayList<Route>(); Route firstRoute = getRoute(srcDpid, dstDpid, 0); if(firstRoute != null) { routes.add(firstRoute); for (int i=1; i < firstRoute.getRouteCount(); i++) routes.add(getRoute(srcDpid, dstDpid, i)); } return routes; } }