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/drivers/src/main/java/org/onosproject/driver/pipeline/SoftRouterPipeline.java | |
parent | 6139282e1e93c2322076de4b91b1c85d0bc4a8b3 (diff) |
ONOS checkin based on commit tag e796610b1f721d02f9b0e213cf6f7790c10ecd60
Change-Id: Ife8810491034fe7becdba75dda20de4267bd15cd
Diffstat (limited to 'framework/src/onos/drivers/src/main/java/org/onosproject/driver/pipeline/SoftRouterPipeline.java')
-rw-r--r-- | framework/src/onos/drivers/src/main/java/org/onosproject/driver/pipeline/SoftRouterPipeline.java | 529 |
1 files changed, 529 insertions, 0 deletions
diff --git a/framework/src/onos/drivers/src/main/java/org/onosproject/driver/pipeline/SoftRouterPipeline.java b/framework/src/onos/drivers/src/main/java/org/onosproject/driver/pipeline/SoftRouterPipeline.java new file mode 100644 index 00000000..220be864 --- /dev/null +++ b/framework/src/onos/drivers/src/main/java/org/onosproject/driver/pipeline/SoftRouterPipeline.java @@ -0,0 +1,529 @@ +package org.onosproject.driver.pipeline; + +import static org.slf4j.LoggerFactory.getLogger; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.concurrent.ConcurrentHashMap; + +import org.onlab.osgi.ServiceDirectory; +import org.onlab.packet.Ethernet; +import org.onlab.packet.MacAddress; +import org.onlab.packet.VlanId; +import org.onlab.util.KryoNamespace; +import org.onosproject.core.ApplicationId; +import org.onosproject.core.CoreService; +import org.onosproject.net.DeviceId; +import org.onosproject.net.PortNumber; +import org.onosproject.net.behaviour.NextGroup; +import org.onosproject.net.behaviour.Pipeliner; +import org.onosproject.net.behaviour.PipelinerContext; +import org.onosproject.net.driver.AbstractHandlerBehaviour; +import org.onosproject.net.flow.DefaultFlowRule; +import org.onosproject.net.flow.DefaultTrafficSelector; +import org.onosproject.net.flow.DefaultTrafficTreatment; +import org.onosproject.net.flow.FlowRule; +import org.onosproject.net.flow.FlowRuleOperations; +import org.onosproject.net.flow.FlowRuleOperationsContext; +import org.onosproject.net.flow.FlowRuleService; +import org.onosproject.net.flow.TrafficSelector; +import org.onosproject.net.flow.TrafficTreatment; +import org.onosproject.net.flow.criteria.Criteria; +import org.onosproject.net.flow.criteria.Criterion; +import org.onosproject.net.flow.criteria.EthCriterion; +import org.onosproject.net.flow.criteria.EthTypeCriterion; +import org.onosproject.net.flow.criteria.IPCriterion; +import org.onosproject.net.flow.criteria.PortCriterion; +import org.onosproject.net.flow.criteria.VlanIdCriterion; +import org.onosproject.net.flowobjective.FilteringObjective; +import org.onosproject.net.flowobjective.FlowObjectiveStore; +import org.onosproject.net.flowobjective.ForwardingObjective; +import org.onosproject.net.flowobjective.NextObjective; +import org.onosproject.net.flowobjective.Objective; +import org.onosproject.net.flowobjective.ObjectiveError; +import org.slf4j.Logger; +import org.onosproject.store.serializers.KryoNamespaces; + +/** + * Simple 2-Table Pipeline for Software/NPU based routers. This pipeline + * does not forward IP traffic to next-hop groups. Instead it forwards traffic + * using OF FlowMod actions. + */ +public class SoftRouterPipeline extends AbstractHandlerBehaviour implements Pipeliner { + + protected static final int FILTER_TABLE = 0; + protected static final int FIB_TABLE = 1; + + private static final int DROP_PRIORITY = 0; + private static final int DEFAULT_PRIORITY = 0x8000; + private static final int HIGHEST_PRIORITY = 0xffff; + + private ServiceDirectory serviceDirectory; + protected FlowRuleService flowRuleService; + private CoreService coreService; + private FlowObjectiveStore flowObjectiveStore; + protected DeviceId deviceId; + protected ApplicationId appId; + private ApplicationId driverId; + private Collection<Filter> filters; + private Collection<ForwardingObjective> pendingVersatiles; + + private KryoNamespace appKryo = new KryoNamespace.Builder() + .register(DummyGroup.class) + .register(KryoNamespaces.API) + .register(byte[].class) + .build(); + + private final Logger log = getLogger(getClass()); + + @Override + public void init(DeviceId deviceId, PipelinerContext context) { + this.serviceDirectory = context.directory(); + this.deviceId = deviceId; + coreService = serviceDirectory.get(CoreService.class); + flowRuleService = serviceDirectory.get(FlowRuleService.class); + flowObjectiveStore = context.store(); + driverId = coreService.registerApplication( + "org.onosproject.driver.OVSCorsaPipeline"); + filters = Collections.newSetFromMap(new ConcurrentHashMap<Filter, Boolean>()); + pendingVersatiles = Collections.newSetFromMap( + new ConcurrentHashMap<ForwardingObjective, Boolean>()); + initializePipeline(); + } + + @Override + public void filter(FilteringObjective filteringObjective) { + if (filteringObjective.type() == FilteringObjective.Type.PERMIT) { + processFilter(filteringObjective, + filteringObjective.op() == Objective.Operation.ADD, + filteringObjective.appId()); + } else { + fail(filteringObjective, ObjectiveError.UNSUPPORTED); + } + } + + @Override + public void forward(ForwardingObjective fwd) { + Collection<FlowRule> rules; + FlowRuleOperations.Builder flowOpsBuilder = FlowRuleOperations.builder(); + + rules = processForward(fwd); + switch (fwd.op()) { + case ADD: + rules.stream() + .filter(rule -> rule != null) + .forEach(flowOpsBuilder::add); + break; + case REMOVE: + rules.stream() + .filter(rule -> rule != null) + .forEach(flowOpsBuilder::remove); + break; + default: + fail(fwd, ObjectiveError.UNKNOWN); + log.warn("Unknown forwarding type {}", fwd.op()); + } + + + flowRuleService.apply(flowOpsBuilder.build(new FlowRuleOperationsContext() { + @Override + public void onSuccess(FlowRuleOperations ops) { + pass(fwd); + } + + @Override + public void onError(FlowRuleOperations ops) { + fail(fwd, ObjectiveError.FLOWINSTALLATIONFAILED); + } + })); + + } + + @Override + public void next(NextObjective nextObjective) { + switch (nextObjective.type()) { + case SIMPLE: + Collection<TrafficTreatment> treatments = nextObjective.next(); + if (treatments.size() != 1) { + log.error("Next Objectives of type Simple should only have a " + + "single Traffic Treatment. Next Objective Id:{}", nextObjective.id()); + fail(nextObjective, ObjectiveError.BADPARAMS); + return; + } + processSimpleNextObjective(nextObjective); + break; + case HASHED: + case BROADCAST: + case FAILOVER: + fail(nextObjective, ObjectiveError.UNSUPPORTED); + log.warn("Unsupported next objective type {}", nextObjective.type()); + break; + default: + fail(nextObjective, ObjectiveError.UNKNOWN); + log.warn("Unknown next objective type {}", nextObjective.type()); + } + } + + private void pass(Objective obj) { + if (obj.context().isPresent()) { + obj.context().get().onSuccess(obj); + } + } + + private void fail(Objective obj, ObjectiveError error) { + if (obj.context().isPresent()) { + obj.context().get().onError(obj, error); + } + } + + + private void initializePipeline() { + //Drop rules for both tables + TrafficSelector.Builder selector = DefaultTrafficSelector.builder(); + TrafficTreatment.Builder treatment = DefaultTrafficTreatment.builder(); + FlowRuleOperations.Builder ops = FlowRuleOperations.builder(); + + treatment.drop(); + + FlowRule rule = DefaultFlowRule.builder() + .forDevice(deviceId) + .withSelector(selector.build()) + .withTreatment(treatment.build()) + .withPriority(DROP_PRIORITY) + .fromApp(driverId) + .makePermanent() + .forTable(FILTER_TABLE) + .build(); + ops = ops.add(rule); + + rule = DefaultFlowRule.builder().forDevice(deviceId) + .withSelector(selector.build()) + .withTreatment(treatment.build()) + .withPriority(DROP_PRIORITY) + .fromApp(driverId) + .makePermanent() + .forTable(FIB_TABLE) + .build(); + ops = ops.add(rule); + + flowRuleService.apply(ops.build(new FlowRuleOperationsContext() { + @Override + public void onSuccess(FlowRuleOperations ops) { + log.info("Provisioned drop rules in both tables"); + } + + @Override + public void onError(FlowRuleOperations ops) { + log.info("Failed to provision drop rules"); + } + })); + } + + private void processFilter(FilteringObjective filt, boolean install, + ApplicationId applicationId) { + // This driver only processes filtering criteria defined with switch + // ports as the key + PortCriterion p; EthCriterion e = null; VlanIdCriterion v = null; + Collection<IPCriterion> ips = new ArrayList<IPCriterion>(); + if (!filt.key().equals(Criteria.dummy()) && + filt.key().type() == Criterion.Type.IN_PORT) { + p = (PortCriterion) filt.key(); + } else { + log.warn("No key defined in filtering objective from app: {}. Not" + + "processing filtering objective", applicationId); + fail(filt, ObjectiveError.UNKNOWN); + return; + } + + // convert filtering conditions for switch-intfs into flowrules + FlowRuleOperations.Builder ops = FlowRuleOperations.builder(); + for (Criterion c : filt.conditions()) { + if (c.type() == Criterion.Type.ETH_DST) { + e = (EthCriterion) c; + } else if (c.type() == Criterion.Type.VLAN_VID) { + v = (VlanIdCriterion) c; + } else if (c.type() == Criterion.Type.IPV4_DST) { + ips.add((IPCriterion) c); + } else { + log.error("Unsupported filter {}", c); + fail(filt, ObjectiveError.UNSUPPORTED); + return; + } + } + + log.debug("adding Port/VLAN/MAC filtering rules in filter table: {}/{}/{}", + p.port(), v.vlanId(), e.mac()); + TrafficSelector.Builder selector = DefaultTrafficSelector.builder(); + TrafficTreatment.Builder treatment = DefaultTrafficTreatment.builder(); + selector.matchInPort(p.port()); + selector.matchVlanId(v.vlanId()); + selector.matchEthDst(e.mac()); + selector.matchEthType(Ethernet.TYPE_IPV4); + treatment.popVlan(); + treatment.transition(FIB_TABLE); // all other IPs to the FIB table + FlowRule rule = DefaultFlowRule.builder() + .forDevice(deviceId) + .withSelector(selector.build()) + .withTreatment(treatment.build()) + .withPriority(DEFAULT_PRIORITY) + .fromApp(applicationId) + .makePermanent() + .forTable(FILTER_TABLE).build(); + ops = ops.add(rule); + + for (IPCriterion ipaddr : ips) { + log.debug("adding IP filtering rules in FILTER table: {}", ipaddr.ip()); + selector = DefaultTrafficSelector.builder(); + treatment = DefaultTrafficTreatment.builder(); + selector.matchInPort(p.port()); + selector.matchVlanId(v.vlanId()); + selector.matchEthDst(e.mac()); + selector.matchEthType(Ethernet.TYPE_IPV4); + selector.matchIPDst(ipaddr.ip()); // router IPs to the controller + treatment.setOutput(PortNumber.CONTROLLER); + rule = DefaultFlowRule.builder() + .forDevice(deviceId) + .withSelector(selector.build()) + .withTreatment(treatment.build()) + .withPriority(HIGHEST_PRIORITY) + .fromApp(applicationId) + .makePermanent() + .forTable(FILTER_TABLE).build(); + ops = ops.add(rule); + } + + // cache for later use + Filter filter = new Filter(p, e, v, ips); + filters.add(filter); + // apply any pending versatile forwarding objectives + for (ForwardingObjective fwd : pendingVersatiles) { + Collection<FlowRule> ret = processVersatilesWithFilters(filter, fwd); + for (FlowRule fr : ret) { + ops.add(fr); + } + } + + ops = install ? ops.add(rule) : ops.remove(rule); + // apply filtering flow rules + flowRuleService.apply(ops.build(new FlowRuleOperationsContext() { + @Override + public void onSuccess(FlowRuleOperations ops) { + log.info("Applied filtering rules"); + pass(filt); + } + + @Override + public void onError(FlowRuleOperations ops) { + log.info("Failed to apply filtering rules"); + fail(filt, ObjectiveError.FLOWINSTALLATIONFAILED); + } + })); + } + + private Collection<FlowRule> processForward(ForwardingObjective fwd) { + switch (fwd.flag()) { + case SPECIFIC: + return processSpecific(fwd); + case VERSATILE: + return processVersatile(fwd); + default: + fail(fwd, ObjectiveError.UNKNOWN); + log.warn("Unknown forwarding flag {}", fwd.flag()); + } + return Collections.emptySet(); + } + + /** + * SoftRouter has a single versatile table - the filter table. All versatile + * flow rules must include the filtering rules. + * + * @param fwd The forwarding objective of type versatile + * @return A collection of flow rules meant to be delivered to the flowrule + * subsystem. May return empty collection in case of failures. + */ + private Collection<FlowRule> processVersatile(ForwardingObjective fwd) { + if (filters.isEmpty()) { + pendingVersatiles.add(fwd); + return Collections.emptySet(); + } + Collection<FlowRule> flowrules = new ArrayList<FlowRule>(); + for (Filter filter : filters) { + flowrules.addAll(processVersatilesWithFilters(filter, fwd)); + } + return flowrules; + } + + private Collection<FlowRule> processVersatilesWithFilters( + Filter filt, ForwardingObjective fwd) { + log.info("Processing versatile forwarding objective"); + Collection<FlowRule> flows = new ArrayList<FlowRule>(); + TrafficSelector match = fwd.selector(); + EthTypeCriterion ethType = + (EthTypeCriterion) match.getCriterion(Criterion.Type.ETH_TYPE); + if (ethType == null) { + log.error("Versatile forwarding objective must include ethType"); + fail(fwd, ObjectiveError.UNKNOWN); + return Collections.emptySet(); + } + + if (ethType.ethType().toShort() == Ethernet.TYPE_ARP) { + // need to install ARP request & reply flow rules for each interface filter + + // rule for ARP replies + TrafficSelector.Builder selector = DefaultTrafficSelector.builder(); + selector.matchInPort(filt.port()); + selector.matchVlanId(filt.vlanId()); + selector.matchEthDst(filt.mac()); + selector.matchEthType(Ethernet.TYPE_ARP); + FlowRule.Builder ruleBuilder = DefaultFlowRule.builder() + .fromApp(fwd.appId()) + .withPriority(fwd.priority()) + .forDevice(deviceId) + .withSelector(selector.build()) + .withTreatment(fwd.treatment()) + .makePermanent() + .forTable(FILTER_TABLE); + flows.add(ruleBuilder.build()); + + //rule for ARP requests + selector = DefaultTrafficSelector.builder(); + selector.matchInPort(filt.port()); + selector.matchVlanId(filt.vlanId()); + selector.matchEthDst(MacAddress.BROADCAST); + selector.matchEthType(Ethernet.TYPE_ARP); + ruleBuilder = DefaultFlowRule.builder() + .fromApp(fwd.appId()) + .withPriority(fwd.priority()) + .forDevice(deviceId) + .withSelector(selector.build()) + .withTreatment(fwd.treatment()) + .makePermanent() + .forTable(FILTER_TABLE); + flows.add(ruleBuilder.build()); + + return flows; + } + // not handling other versatile flows + return Collections.emptySet(); + } + + /** + * SoftRouter has a single specific table - the FIB Table. It emulates + * LPM matching of dstIP by using higher priority flows for longer prefixes. + * Flows are forwarded using flow-actions + * + * @param fwd The forwarding objective of type simple + * @return A collection of flow rules meant to be delivered to the flowrule + * subsystem. Typically the returned collection has a single flowrule. + * May return empty collection in case of failures. + * + */ + private Collection<FlowRule> processSpecific(ForwardingObjective fwd) { + log.debug("Processing specific forwarding objective"); + TrafficSelector selector = fwd.selector(); + EthTypeCriterion ethType = + (EthTypeCriterion) selector.getCriterion(Criterion.Type.ETH_TYPE); + // XXX currently supporting only the L3 unicast table + if (ethType == null || ethType.ethType().toShort() != Ethernet.TYPE_IPV4) { + fail(fwd, ObjectiveError.UNSUPPORTED); + return Collections.emptySet(); + } + + TrafficSelector filteredSelector = + DefaultTrafficSelector.builder() + .matchEthType(Ethernet.TYPE_IPV4) + .matchIPDst(((IPCriterion) + selector.getCriterion(Criterion.Type.IPV4_DST)).ip()) + .build(); + + TrafficTreatment tt = null; + if (fwd.nextId() != null) { + NextGroup next = flowObjectiveStore.getNextGroup(fwd.nextId()); + if (next == null) { + log.error("next-id {} does not exist in store", fwd.nextId()); + return Collections.emptySet(); + } + tt = appKryo.deserialize(next.data()); + if (tt == null) { + log.error("Error in deserializing next-id {}", fwd.nextId()); + return Collections.emptySet(); + } + } + + FlowRule.Builder ruleBuilder = DefaultFlowRule.builder() + .fromApp(fwd.appId()) + .withPriority(fwd.priority()) + .forDevice(deviceId) + .withSelector(filteredSelector) + .withTreatment(tt); + + if (fwd.permanent()) { + ruleBuilder.makePermanent(); + } else { + ruleBuilder.makeTemporary(fwd.timeout()); + } + + ruleBuilder.forTable(FIB_TABLE); + return Collections.singletonList(ruleBuilder.build()); + } + + /** + * Next Objectives are stored as dummy groups for retrieval later + * when Forwarding Objectives reference the next objective id. At that point + * the dummy group is fetched from the distributed store and the enclosed + * treatment is applied as a flow rule action. + * + * @param nextObj the next objective of type simple + */ + private void processSimpleNextObjective(NextObjective nextObj) { + // Simple next objective has a single treatment (not a collection) + TrafficTreatment treatment = nextObj.next().iterator().next(); + flowObjectiveStore.putNextGroup(nextObj.id(), + new DummyGroup(treatment)); + } + + private class Filter { + private PortCriterion port; + private VlanIdCriterion vlan; + private EthCriterion eth; + + @SuppressWarnings("unused") + private Collection<IPCriterion> ips; + + public Filter(PortCriterion p, EthCriterion e, VlanIdCriterion v, + Collection<IPCriterion> ips) { + this.eth = e; + this.port = p; + this.vlan = v; + this.ips = ips; + } + + public PortNumber port() { + return port.port(); + } + + public VlanId vlanId() { + return vlan.vlanId(); + } + + public MacAddress mac() { + return eth.mac(); + } + } + + private class DummyGroup implements NextGroup { + TrafficTreatment nextActions; + + public DummyGroup(TrafficTreatment next) { + this.nextActions = next; + } + + @Override + public byte[] data() { + return appKryo.serialize(nextActions); + } + + } + +} |