diff options
author | Ashlee Young <ashlee@onosfw.com> | 2015-09-09 22:15:21 -0700 |
---|---|---|
committer | Ashlee Young <ashlee@onosfw.com> | 2015-09-09 22:15:21 -0700 |
commit | 13d05bc8458758ee39cb829098241e89616717ee (patch) | |
tree | 22a4d1ce65f15952f07a3df5af4b462b4697cb3a /framework/src/onos/apps/segmentrouting/src/main/java/org/onosproject/segmentrouting/RoutingRulePopulator.java | |
parent | 6139282e1e93c2322076de4b91b1c85d0bc4a8b3 (diff) |
ONOS checkin based on commit tag e796610b1f721d02f9b0e213cf6f7790c10ecd60
Change-Id: Ife8810491034fe7becdba75dda20de4267bd15cd
Diffstat (limited to 'framework/src/onos/apps/segmentrouting/src/main/java/org/onosproject/segmentrouting/RoutingRulePopulator.java')
-rw-r--r-- | framework/src/onos/apps/segmentrouting/src/main/java/org/onosproject/segmentrouting/RoutingRulePopulator.java | 430 |
1 files changed, 430 insertions, 0 deletions
diff --git a/framework/src/onos/apps/segmentrouting/src/main/java/org/onosproject/segmentrouting/RoutingRulePopulator.java b/framework/src/onos/apps/segmentrouting/src/main/java/org/onosproject/segmentrouting/RoutingRulePopulator.java new file mode 100644 index 00000000..59fc4ca7 --- /dev/null +++ b/framework/src/onos/apps/segmentrouting/src/main/java/org/onosproject/segmentrouting/RoutingRulePopulator.java @@ -0,0 +1,430 @@ +/* + * 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.Ethernet; +import org.onlab.packet.Ip4Address; +import org.onlab.packet.Ip4Prefix; +import org.onlab.packet.IpPrefix; +import org.onlab.packet.MacAddress; +import org.onlab.packet.MplsLabel; +import org.onlab.packet.VlanId; +import org.onosproject.segmentrouting.grouphandler.NeighborSet; +import org.onosproject.net.DeviceId; +import org.onosproject.net.Link; +import org.onosproject.net.PortNumber; +import org.onosproject.net.flow.DefaultTrafficSelector; +import org.onosproject.net.flow.DefaultTrafficTreatment; +import org.onosproject.net.flow.TrafficSelector; +import org.onosproject.net.flow.TrafficTreatment; +import org.onosproject.net.flow.criteria.Criteria; +import org.onosproject.net.flowobjective.DefaultFilteringObjective; +import org.onosproject.net.flowobjective.DefaultForwardingObjective; +import org.onosproject.net.flowobjective.FilteringObjective; +import org.onosproject.net.flowobjective.ForwardingObjective; +import org.onosproject.net.flowobjective.Objective; +import org.onosproject.net.flowobjective.ObjectiveError; +import org.onosproject.net.flowobjective.ForwardingObjective.Builder; +import org.onosproject.net.flowobjective.ObjectiveContext; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.util.ArrayList; +import java.util.List; +import java.util.Set; +import java.util.concurrent.atomic.AtomicLong; + +import static com.google.common.base.Preconditions.checkNotNull; + +public class RoutingRulePopulator { + + private static final Logger log = LoggerFactory + .getLogger(RoutingRulePopulator.class); + + private AtomicLong rulePopulationCounter; + private SegmentRoutingManager srManager; + private DeviceConfiguration config; + /** + * Creates a RoutingRulePopulator object. + * + * @param srManager segment routing manager reference + */ + public RoutingRulePopulator(SegmentRoutingManager srManager) { + this.srManager = srManager; + this.config = checkNotNull(srManager.deviceConfiguration); + this.rulePopulationCounter = new AtomicLong(0); + } + + /** + * Resets the population counter. + */ + public void resetCounter() { + rulePopulationCounter.set(0); + } + + /** + * Returns the number of rules populated. + * + * @return number of rules + */ + public long getCounter() { + return rulePopulationCounter.get(); + } + + /** + * Populates IP flow rules for specific hosts directly connected to the + * switch. + * + * @param deviceId switch ID to set the rules + * @param hostIp host IP address + * @param hostMac host MAC address + * @param outPort port where the host is connected + */ + public void populateIpRuleForHost(DeviceId deviceId, Ip4Address hostIp, + MacAddress hostMac, PortNumber outPort) { + TrafficSelector.Builder sbuilder = DefaultTrafficSelector.builder(); + TrafficTreatment.Builder tbuilder = DefaultTrafficTreatment.builder(); + + sbuilder.matchIPDst(IpPrefix.valueOf(hostIp, 32)); + sbuilder.matchEthType(Ethernet.TYPE_IPV4); + + tbuilder.deferred() + .setEthDst(hostMac) + .setEthSrc(config.getDeviceMac(deviceId)) + .setOutput(outPort); + + TrafficTreatment treatment = tbuilder.build(); + TrafficSelector selector = sbuilder.build(); + + ForwardingObjective.Builder fwdBuilder = DefaultForwardingObjective + .builder().fromApp(srManager.appId).makePermanent() + .withSelector(selector).withTreatment(treatment) + .withPriority(100).withFlag(ForwardingObjective.Flag.SPECIFIC); + + log.debug("Installing IPv4 forwarding objective " + + "for host {} in switch {}", hostIp, deviceId); + srManager.flowObjectiveService. + forward(deviceId, + fwdBuilder. + add(new SRObjectiveContext(deviceId, + SRObjectiveContext.ObjectiveType.FORWARDING))); + rulePopulationCounter.incrementAndGet(); + } + + /** + * Populates IP flow rules for the subnets of the destination router. + * + * @param deviceId switch ID to set the rules + * @param subnets subnet information + * @param destSw destination switch ID + * @param nextHops next hop switch ID list + * @return true if all rules are set successfully, false otherwise + */ + public boolean populateIpRuleForSubnet(DeviceId deviceId, + List<Ip4Prefix> subnets, + DeviceId destSw, + Set<DeviceId> nextHops) { + + for (IpPrefix subnet : subnets) { + if (!populateIpRuleForRouter(deviceId, subnet, destSw, nextHops)) { + return false; + } + } + + return true; + } + + /** + * Populates IP flow rules for the router IP address. + * + * @param deviceId device ID to set the rules + * @param ipPrefix the IP address of the destination router + * @param destSw device ID of the destination router + * @param nextHops next hop switch ID list + * @return true if all rules are set successfully, false otherwise + */ + public boolean populateIpRuleForRouter(DeviceId deviceId, + IpPrefix ipPrefix, DeviceId destSw, + Set<DeviceId> nextHops) { + + TrafficSelector.Builder sbuilder = DefaultTrafficSelector.builder(); + TrafficTreatment.Builder tbuilder = DefaultTrafficTreatment.builder(); + + sbuilder.matchIPDst(ipPrefix); + sbuilder.matchEthType(Ethernet.TYPE_IPV4); + + NeighborSet ns = null; + + // If the next hop is the same as the final destination, then MPLS label + // is not set. + if (nextHops.size() == 1 && nextHops.toArray()[0].equals(destSw)) { + tbuilder.deferred().decNwTtl(); + ns = new NeighborSet(nextHops); + } else { + tbuilder.deferred().copyTtlOut(); + ns = new NeighborSet(nextHops, config.getSegmentId(destSw)); + } + + TrafficTreatment treatment = tbuilder.build(); + TrafficSelector selector = sbuilder.build(); + + if (srManager.getNextObjectiveId(deviceId, ns) <= 0) { + log.warn("No next objective in {} for ns: {}", deviceId, ns); + return false; + } + + ForwardingObjective.Builder fwdBuilder = DefaultForwardingObjective + .builder() + .fromApp(srManager.appId) + .makePermanent() + .nextStep(srManager.getNextObjectiveId(deviceId, ns)) + .withTreatment(treatment) + .withSelector(selector) + .withPriority(100) + .withFlag(ForwardingObjective.Flag.SPECIFIC); + log.debug("Installing IPv4 forwarding objective " + + "for router IP/subnet {} in switch {}", + ipPrefix, + deviceId); + srManager.flowObjectiveService. + forward(deviceId, + fwdBuilder. + add(new SRObjectiveContext(deviceId, + SRObjectiveContext.ObjectiveType.FORWARDING))); + rulePopulationCounter.incrementAndGet(); + + return true; + } + + /** + * Populates MPLS flow rules to all transit routers. + * + * @param deviceId device ID of the switch to set the rules + * @param destSwId destination switch device ID + * @param nextHops next hops switch ID list + * @return true if all rules are set successfully, false otherwise + */ + public boolean populateMplsRule(DeviceId deviceId, DeviceId destSwId, + Set<DeviceId> nextHops) { + + TrafficSelector.Builder sbuilder = DefaultTrafficSelector.builder(); + List<ForwardingObjective.Builder> fwdObjBuilders = new ArrayList<ForwardingObjective.Builder>(); + + // TODO Handle the case of Bos == false + sbuilder.matchMplsLabel(MplsLabel.mplsLabel(config.getSegmentId(destSwId))); + sbuilder.matchEthType(Ethernet.MPLS_UNICAST); + + // If the next hop is the destination router, do PHP + if (nextHops.size() == 1 && destSwId.equals(nextHops.toArray()[0])) { + log.debug("populateMplsRule: Installing MPLS forwarding objective for " + + "label {} in switch {} with PHP", + config.getSegmentId(destSwId), + deviceId); + + ForwardingObjective.Builder fwdObjBosBuilder = + getMplsForwardingObjective(deviceId, + destSwId, + nextHops, + true, + true); + // TODO: Check with Sangho on why we need this + ForwardingObjective.Builder fwdObjNoBosBuilder = + getMplsForwardingObjective(deviceId, + destSwId, + nextHops, + true, + false); + if (fwdObjBosBuilder != null) { + fwdObjBuilders.add(fwdObjBosBuilder); + } else { + log.warn("Failed to set MPLS rules."); + return false; + } + } else { + log.debug("Installing MPLS forwarding objective for " + + "label {} in switch {} without PHP", + config.getSegmentId(destSwId), + deviceId); + + ForwardingObjective.Builder fwdObjBosBuilder = + getMplsForwardingObjective(deviceId, + destSwId, + nextHops, + false, + true); + // TODO: Check with Sangho on why we need this + ForwardingObjective.Builder fwdObjNoBosBuilder = + getMplsForwardingObjective(deviceId, + destSwId, + nextHops, + false, + false); + if (fwdObjBosBuilder != null) { + fwdObjBuilders.add(fwdObjBosBuilder); + } else { + log.warn("Failed to set MPLS rules."); + return false; + } + } + + TrafficSelector selector = sbuilder.build(); + for (ForwardingObjective.Builder fwdObjBuilder : fwdObjBuilders) { + ((Builder) ((Builder) fwdObjBuilder.fromApp(srManager.appId) + .makePermanent()).withSelector(selector) + .withPriority(100)) + .withFlag(ForwardingObjective.Flag.SPECIFIC); + srManager.flowObjectiveService. + forward(deviceId, + fwdObjBuilder. + add(new SRObjectiveContext(deviceId, + SRObjectiveContext.ObjectiveType.FORWARDING))); + rulePopulationCounter.incrementAndGet(); + } + + return true; + } + + private ForwardingObjective.Builder getMplsForwardingObjective(DeviceId deviceId, + DeviceId destSw, + Set<DeviceId> nextHops, + boolean phpRequired, + boolean isBos) { + + ForwardingObjective.Builder fwdBuilder = DefaultForwardingObjective + .builder().withFlag(ForwardingObjective.Flag.SPECIFIC); + + TrafficTreatment.Builder tbuilder = DefaultTrafficTreatment.builder(); + + if (phpRequired) { + log.debug("getMplsForwardingObjective: php required"); + tbuilder.deferred().copyTtlIn(); + if (isBos) { + tbuilder.deferred().popMpls(Ethernet.TYPE_IPV4).decNwTtl(); + } else { + tbuilder.deferred().popMpls(Ethernet.MPLS_UNICAST).decMplsTtl(); + } + } else { + log.debug("getMplsForwardingObjective: php not required"); + tbuilder.deferred().decMplsTtl(); + } + + if (!isECMPSupportedInTransitRouter() && !config.isEdgeDevice(deviceId)) { + PortNumber port = selectOnePort(deviceId, nextHops); + DeviceId nextHop = (DeviceId) nextHops.toArray()[0]; + if (port == null) { + log.warn("No link from {} to {}", deviceId, nextHops); + return null; + } + tbuilder.deferred() + .setEthSrc(config.getDeviceMac(deviceId)) + .setEthDst(config.getDeviceMac(nextHop)) + .setOutput(port); + fwdBuilder.withTreatment(tbuilder.build()); + } else { + NeighborSet ns = new NeighborSet(nextHops); + fwdBuilder.withTreatment(tbuilder.build()); + fwdBuilder.nextStep(srManager + .getNextObjectiveId(deviceId, ns)); + } + + return fwdBuilder; + } + + private boolean isECMPSupportedInTransitRouter() { + + // TODO: remove this function when objectives subsystem is supported. + return false; + } + + /** + * Populates VLAN flows rules. All packets are forwarded to TMAC table. + * + * @param deviceId switch ID to set the rules + */ + public void populateTableVlan(DeviceId deviceId) { + FilteringObjective.Builder fob = DefaultFilteringObjective.builder(); + fob.withKey(Criteria.matchInPort(PortNumber.ALL)) + .addCondition(Criteria.matchVlanId(VlanId.NONE)); + fob.permit().fromApp(srManager.appId); + log.debug("populateTableVlan: Installing filtering objective for untagged packets"); + srManager.flowObjectiveService. + filter(deviceId, + fob.add(new SRObjectiveContext(deviceId, + SRObjectiveContext.ObjectiveType.FILTER))); + } + + /** + * Populates TMAC table rules. IP packets are forwarded to IP table. MPLS + * packets are forwarded to MPLS table. + * + * @param deviceId switch ID to set the rules + */ + public void populateTableTMac(DeviceId deviceId) { + + FilteringObjective.Builder fob = DefaultFilteringObjective.builder(); + fob.withKey(Criteria.matchInPort(PortNumber.ALL)) + .addCondition(Criteria.matchEthDst(config + .getDeviceMac(deviceId))); + fob.permit().fromApp(srManager.appId); + log.debug("populateTableVlan: Installing filtering objective for router mac"); + srManager.flowObjectiveService. + filter(deviceId, + fob.add(new SRObjectiveContext(deviceId, + SRObjectiveContext.ObjectiveType.FILTER))); + } + + private PortNumber selectOnePort(DeviceId srcId, Set<DeviceId> destIds) { + + Set<Link> links = srManager.linkService.getDeviceLinks(srcId); + for (DeviceId destId: destIds) { + for (Link link : links) { + if (link.dst().deviceId().equals(destId)) { + return link.src().port(); + } else if (link.src().deviceId().equals(destId)) { + return link.dst().port(); + } + } + } + + return null; + } + + private static class SRObjectiveContext implements ObjectiveContext { + enum ObjectiveType { + FILTER, + FORWARDING + } + final DeviceId deviceId; + final ObjectiveType type; + + SRObjectiveContext(DeviceId deviceId, ObjectiveType type) { + this.deviceId = deviceId; + this.type = type; + } + @Override + public void onSuccess(Objective objective) { + log.debug("{} objective operation successful in device {}", + type.name(), deviceId); + } + + @Override + public void onError(Objective objective, ObjectiveError error) { + log.warn("{} objective {} operation failed with error: {} in device {}", + type.name(), objective, error, deviceId); + } + } + +} |