blob: 154127e72d966acede7f49f57497f0a8f9233a1b [file] [log] [blame]
/*
* 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.ForwardingObjective.Builder;
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.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());
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.decNwTtl();
ns = new NeighborSet(nextHops);
} else {
tbuilder.copyTtlOut();
ns = new NeighborSet(nextHops, config.getSegmentId(destSw));
}
TrafficTreatment treatment = tbuilder.build();
TrafficSelector selector = sbuilder.build();
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());
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])) {
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 {
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);
log.debug("Installing MPLS forwarding objective in switch {}",
deviceId);
srManager.flowObjectiveService.forward(deviceId,
fwdObjBuilder.add());
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.copyTtlIn();
if (isBos) {
tbuilder.popMpls(Ethernet.TYPE_IPV4).decNwTtl();
} else {
tbuilder.popMpls(Ethernet.MPLS_UNICAST).decMplsTtl();
}
} else {
log.debug("getMplsForwardingObjective: php not required");
tbuilder.decMplsTtl();
}
if (!isECMPSupportedInTransitRouter() && !config.isEdgeDevice(deviceId)) {
Link link = selectOneLink(deviceId, nextHops);
DeviceId nextHop = (DeviceId) nextHops.toArray()[0];
if (link == null) {
log.warn("No link from {} to {}", deviceId, nextHops);
return null;
}
tbuilder.setEthSrc(config.getDeviceMac(deviceId))
.setEthDst(config.getDeviceMac(nextHop))
.setOutput(link.src().port());
fwdBuilder.withTreatment(tbuilder.build());
} else {
NeighborSet ns = new NeighborSet(nextHops);
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());
}
/**
* 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());
}
private Link selectOneLink(DeviceId srcId, Set<DeviceId> destIds) {
Set<Link> links = srManager.linkService.getDeviceEgressLinks(srcId);
DeviceId destId = (DeviceId) destIds.toArray()[0];
for (Link link : links) {
if (link.dst().deviceId().equals(destId)) {
return link;
}
}
return null;
}
}