ONOS-686, 687, 1344 : The first commit for the Segment Routing application
- ICMP/ARP/IP handlers are implemented as a part of the application for now
- Default routing and link add/failure/recovery are also supprted
- Temporary NetworkConfigHandler, which is hardcoded to support only 6 router FISH topology, is used for test
- Some fixes on GroupHanlder app to support transit routers
- Supports multi-instance (tested with two instances)
Change-Id: Idfa67903e59e1c4cac4da430f89cd4c50e821420
diff --git a/apps/segmentrouting/src/main/java/org/onosproject/segmentrouting/DefaultRoutingHandler.java b/apps/segmentrouting/src/main/java/org/onosproject/segmentrouting/DefaultRoutingHandler.java
new file mode 100644
index 0000000..3684ec9
--- /dev/null
+++ b/apps/segmentrouting/src/main/java/org/onosproject/segmentrouting/DefaultRoutingHandler.java
@@ -0,0 +1,228 @@
+/*
+ * Copyright 2015 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.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.apache.org/licenses/LICENSE-2.0
+ *
+ * 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.onosproject.segmentrouting;
+
+import org.onlab.packet.IpPrefix;
+import org.onosproject.net.Device;
+import org.onosproject.net.DeviceId;
+import org.onosproject.net.MastershipRole;
+import org.onosproject.net.flow.FlowRule;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Set;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+
+public class DefaultRoutingHandler {
+
+ private static Logger log = LoggerFactory.getLogger(DefaultRoutingHandler.class);
+
+ private SegmentRoutingManager srManager;
+ private RoutingRulePopulator rulePopulator;
+ private NetworkConfigHandler config;
+ private Status populationStatus;
+
+ /**
+ * Represents the default routing population status.
+ */
+ public enum Status {
+ // population process is not started yet.
+ IDLE,
+
+ // population process started.
+ STARTED,
+
+ // population process was aborted due to errors, mostly for groups not found.
+ ABORTED,
+
+ // population process was finished successfully.
+ SUCCEEDED
+ }
+
+ /**
+ * Creates a DefaultRoutingHandler object.
+ *
+ * @param srManager SegmentRoutingManager object
+ */
+ public DefaultRoutingHandler(SegmentRoutingManager srManager) {
+ this.srManager = srManager;
+ this.rulePopulator = checkNotNull(srManager.routingRulePopulator);
+ this.config = checkNotNull(srManager.networkConfigHandler);
+ this.populationStatus = Status.IDLE;
+ }
+
+ /**
+ * Populates all routing rules to all connected routers, including default
+ * routing rules, adjacency rules, and policy rules if any.
+ *
+ * @return true if it succeeds in populating all rules, otherwise false
+ */
+ public boolean populateAllRoutingRules() {
+
+ populationStatus = Status.STARTED;
+ log.info("Starts to populate routing rules");
+
+ for (Device sw : srManager.deviceService.getDevices()) {
+ if (srManager.mastershipService.
+ getLocalRole(sw.id()) != MastershipRole.MASTER) {
+ continue;
+ }
+
+ ECMPShortestPathGraph ecmpSPG = new ECMPShortestPathGraph(sw.id(), srManager);
+ if (!populateEcmpRoutingRules(sw, ecmpSPG)) {
+ populationStatus = Status.ABORTED;
+ log.debug("Abort routing rule population");
+ return false;
+ }
+
+ // TODO: Set adjacency routing rule for all switches
+ }
+
+ populationStatus = Status.SUCCEEDED;
+ log.info("Completes routing rule population");
+ return true;
+ }
+
+ private boolean populateEcmpRoutingRules(Device sw,
+ ECMPShortestPathGraph ecmpSPG) {
+
+ HashMap<Integer, HashMap<DeviceId, ArrayList<ArrayList<DeviceId>>>> switchVia =
+ ecmpSPG.getAllLearnedSwitchesAndVia();
+ for (Integer itrIdx : switchVia.keySet()) {
+ HashMap<DeviceId, ArrayList<ArrayList<DeviceId>>> swViaMap =
+ switchVia.get(itrIdx);
+ for (DeviceId targetSw : swViaMap.keySet()) {
+ DeviceId destSw = sw.id();
+ Set<DeviceId> nextHops = new HashSet<>();
+
+ for (ArrayList<DeviceId> via : swViaMap.get(targetSw)) {
+ if (via.isEmpty()) {
+ nextHops.add(destSw);
+ } else {
+ nextHops.add(via.get(0));
+ }
+ }
+ if (!populateEcmpRoutingRulePartial(targetSw, destSw, nextHops)) {
+ return false;
+ }
+ }
+ }
+
+ return true;
+ }
+
+ private boolean populateEcmpRoutingRulePartial(DeviceId targetSw, DeviceId destSw,
+ Set<DeviceId> nextHops) {
+ boolean result;
+
+ if (nextHops.isEmpty()) {
+ nextHops.add(destSw);
+ }
+
+ // If both target switch and dest switch are edge routers, then set IP rule
+ // for both subnet and router IP.
+ if (config.isEdgeRouter(targetSw) && config.isEdgeRouter(destSw)) {
+ String subnets = config.getSubnetInfo(destSw);
+ result = rulePopulator.populateIpRuleForSubnet(targetSw, subnets, destSw, nextHops);
+ if (!result) {
+ return false;
+ }
+
+ IpPrefix routerIp = config.getRouterIpAddress(destSw);
+ result = rulePopulator.populateIpRuleForRouter(targetSw, routerIp, destSw, nextHops);
+ if (!result) {
+ return false;
+ }
+
+ // If the target switch is an edge router, then set IP rules for the router IP.
+ } else if (config.isEdgeRouter(targetSw)) {
+ IpPrefix routerIp = config.getRouterIpAddress(destSw);
+ result = rulePopulator.populateIpRuleForRouter(targetSw, routerIp, destSw, nextHops);
+ if (!result) {
+ return false;
+ }
+
+ // If the target switch is an transit router, then set MPLS rules only.
+ } else if (config.isTransitRouter(targetSw)) {
+ result = rulePopulator.populateMplsRule(targetSw, destSw, nextHops);
+ if (!result) {
+ return false;
+ }
+ } else {
+ log.warn("The switch {} is neither an edge router nor a transit router.", targetSw);
+ return false;
+ }
+
+ return true;
+ }
+
+ /**
+ * Populates table miss entries for all tables, and pipeline rules for
+ * VLAN and TACM tables.
+ *
+ * @param deviceId Switch ID to set the rules
+ */
+ public void populateTtpRules(DeviceId deviceId) {
+
+ rulePopulator.populateTableMissEntry(deviceId, FlowRule.Type.VLAN,
+ true, false, false, FlowRule.Type.DEFAULT);
+ rulePopulator.populateTableMissEntry(deviceId, FlowRule.Type.ETHER,
+ true, false, false, FlowRule.Type.DEFAULT);
+ rulePopulator.populateTableMissEntry(deviceId, FlowRule.Type.IP,
+ false, true, true, FlowRule.Type.ACL);
+ rulePopulator.populateTableMissEntry(deviceId, FlowRule.Type.MPLS,
+ false, true, true, FlowRule.Type.ACL);
+ rulePopulator.populateTableMissEntry(deviceId, FlowRule.Type.ACL,
+ false, false, false, FlowRule.Type.DEFAULT);
+
+ rulePopulator.populateTableVlan(deviceId);
+ rulePopulator.populateTableTMac(deviceId);
+ }
+
+ /**
+ * Start the flow rule population process if it was never started.
+ * The process finishes successfully when all flow rules are set and
+ * stops with ABORTED status when any groups required for flows is not
+ * set yet.
+ */
+ public void startPopulationProcess() {
+ synchronized (populationStatus) {
+ if (populationStatus == Status.IDLE ||
+ populationStatus == Status.SUCCEEDED) {
+ populationStatus = Status.STARTED;
+ populateAllRoutingRules();
+ }
+ }
+ }
+
+ /**
+ * Resume the flow rule population process if it was aborted for any reason.
+ * Mostly the process is aborted when the groups required are not set yet.
+ */
+ public void resumePopulationProcess() {
+ synchronized (populationStatus) {
+ if (populationStatus == Status.ABORTED) {
+ populationStatus = Status.STARTED;
+ // TODO: we need to restart from the point aborted instead of restarting.
+ populateAllRoutingRules();
+ }
+ }
+ }
+}