Implements [CORD-96] and [CORD-410]
Changes:
- Introduces L2TunnelHandler for managing the pws;
- Supports pws initiation and pws policy for olt<->vsg communication;
- Supports teardown and update;
Change-Id: If51272c91445f618727434606edd2491f93cc4dd
diff --git a/drivers/default/src/main/java/org/onosproject/driver/pipeline/Ofdpa3Pipeline.java b/drivers/default/src/main/java/org/onosproject/driver/pipeline/Ofdpa3Pipeline.java
index 895c187..38fe384 100644
--- a/drivers/default/src/main/java/org/onosproject/driver/pipeline/Ofdpa3Pipeline.java
+++ b/drivers/default/src/main/java/org/onosproject/driver/pipeline/Ofdpa3Pipeline.java
@@ -16,21 +16,57 @@
package org.onosproject.driver.pipeline;
+import com.google.common.collect.ImmutableList;
import org.onlab.packet.VlanId;
import org.onosproject.core.ApplicationId;
+import org.onosproject.driver.extensions.Ofdpa3MatchMplsL2Port;
+import org.onosproject.driver.extensions.Ofdpa3MatchOvid;
+import org.onosproject.driver.extensions.Ofdpa3SetMplsL2Port;
+import org.onosproject.driver.extensions.Ofdpa3SetMplsType;
+import org.onosproject.driver.extensions.Ofdpa3SetOvid;
+import org.onosproject.driver.extensions.Ofdpa3SetQosIndex;
+import org.onosproject.driver.extensions.OfdpaMatchVlanVid;
+import org.onosproject.net.behaviour.NextGroup;
import org.onosproject.net.behaviour.PipelinerContext;
+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.TrafficSelector;
+import org.onosproject.net.flow.TrafficTreatment;
+import org.onosproject.net.flow.criteria.Criterion;
import org.onosproject.net.flow.criteria.PortCriterion;
+import org.onosproject.net.flow.criteria.TunnelIdCriterion;
import org.onosproject.net.flow.criteria.VlanIdCriterion;
+import org.onosproject.net.flow.instructions.L2ModificationInstruction;
+import org.onosproject.net.flow.instructions.L2ModificationInstruction.L2SubType;
+import org.onosproject.net.flowobjective.FilteringObjective;
import org.onosproject.net.flowobjective.ForwardingObjective;
+import org.onosproject.net.flowobjective.ObjectiveError;
+import org.onosproject.net.group.Group;
+import org.onosproject.net.group.GroupKey;
+import org.slf4j.Logger;
import java.util.Collection;
+import java.util.Collections;
+import java.util.Deque;
import java.util.List;
+import static org.onosproject.driver.extensions.Ofdpa3MplsType.VPWS;
+import static org.onosproject.net.flow.criteria.Criterion.Type.*;
+import static org.onosproject.net.flow.instructions.Instruction.Type.L2MODIFICATION;
+import static org.onosproject.net.flow.instructions.L2ModificationInstruction.ModTunnelIdInstruction;
+import static org.slf4j.LoggerFactory.getLogger;
+
/**
* Pipeliner for Broadcom OF-DPA 3.0 TTP.
*/
public class Ofdpa3Pipeline extends Ofdpa2Pipeline {
+
+ private final Logger log = getLogger(getClass());
+
@Override
protected void initDriverId() {
driverId = coreService.registerApplication(
@@ -44,6 +80,180 @@
}
@Override
+ protected void processFilter(FilteringObjective filteringObjective,
+ boolean install,
+ ApplicationId applicationId) {
+ // We are looking for inner vlan id criterion. We use this
+ // to identify the pseudo wire flows. In future we can enforce
+ // using also the tunnel id in the meta.
+ VlanIdCriterion innerVlanIdCriterion = null;
+ for (Criterion criterion : filteringObjective.conditions()) {
+ if (criterion.type() == INNER_VLAN_VID) {
+ innerVlanIdCriterion = (VlanIdCriterion) criterion;
+ break;
+ }
+ }
+ if (innerVlanIdCriterion != null) {
+ FlowRuleOperations.Builder ops = FlowRuleOperations.builder();
+ PortCriterion portCriterion;
+ VlanIdCriterion outerVlanIdCriterion = null;
+ // We extract the expected port criterion in the key.
+ portCriterion = (PortCriterion) filteringObjective.key();
+ // We extract the outer vlan id criterion.
+ for (Criterion criterion : filteringObjective.conditions()) {
+ if (criterion.type() == VLAN_VID) {
+ outerVlanIdCriterion = (VlanIdCriterion) criterion;
+ break;
+ }
+ }
+ // We extract the tunnel id.
+ long tunnelId;
+ if (filteringObjective.meta() != null &&
+ filteringObjective.meta().allInstructions().size() != 1) {
+ log.warn("Bad filtering objective from app: {}. Not"
+ + "processing filtering objective", applicationId);
+ fail(filteringObjective, ObjectiveError.BADPARAMS);
+ return;
+ } else if (filteringObjective.meta() != null &&
+ filteringObjective.meta().allInstructions().size() == 1 &&
+ filteringObjective.meta().allInstructions().get(0).type() == L2MODIFICATION) {
+ L2ModificationInstruction l2instruction = (L2ModificationInstruction)
+ filteringObjective.meta().allInstructions().get(0);
+ if (l2instruction.subtype() != L2SubType.TUNNEL_ID) {
+ log.warn("Bad filtering objective from app: {}. Not"
+ + "processing filtering objective", applicationId);
+ fail(filteringObjective, ObjectiveError.BADPARAMS);
+ return;
+ } else {
+ tunnelId = ((ModTunnelIdInstruction) l2instruction).tunnelId();
+ }
+ } else {
+ log.warn("Bad filtering objective from app: {}. Not"
+ + "processing filtering objective", applicationId);
+ fail(filteringObjective, ObjectiveError.BADPARAMS);
+ return;
+ }
+ // Mpls tunnel ids according to the OFDPA manual have to be
+ // in the range [2^17-1, 2^16].
+ tunnelId = MPLS_TUNNEL_ID_BASE | tunnelId;
+ // Sanity check for the filtering objective.
+ if (portCriterion == null ||
+ outerVlanIdCriterion == null ||
+ tunnelId > MPLS_TUNNEL_ID_MAX) {
+ log.warn("Bad filtering objective from app: {}. Not"
+ + "processing filtering objective", applicationId);
+ fail(filteringObjective, ObjectiveError.BADPARAMS);
+ return;
+ }
+ // 0x0000XXXX is UNI interface.
+ if (portCriterion.port().toLong() > 0x0000FFFF) {
+ log.error("Filering Objective invalid logical port {}",
+ portCriterion.port().toLong());
+ fail(filteringObjective, ObjectiveError.BADPARAMS);
+ return;
+ }
+ // We create the flows.
+ List<FlowRule> pwRules = processPwFilter(portCriterion,
+ innerVlanIdCriterion,
+ outerVlanIdCriterion,
+ tunnelId,
+ applicationId
+ );
+ // We tag the flow for adding or for removing.
+ for (FlowRule pwRule : pwRules) {
+ log.debug("adding filtering rule in VLAN tables: {} for dev: {}",
+ pwRule, deviceId);
+ ops = install ? ops.add(pwRule) : ops.remove(pwRule);
+ }
+ // We push the filtering rules for the pw.
+ flowRuleService.apply(ops.build(new FlowRuleOperationsContext() {
+ @Override
+ public void onSuccess(FlowRuleOperations ops) {
+ log.info("Applied {} filtering rules in device {}",
+ ops.stages().get(0).size(), deviceId);
+ pass(filteringObjective);
+ }
+
+ @Override
+ public void onError(FlowRuleOperations ops) {
+ log.info("Failed to apply all filtering rules in dev {}", deviceId);
+ fail(filteringObjective, ObjectiveError.FLOWINSTALLATIONFAILED);
+ }
+ }));
+
+ return;
+ }
+ // If it is not a pseudo wire flow we fall back
+ // to the OFDPA 2.0 pipeline.
+ super.processFilter(filteringObjective, install, applicationId);
+ }
+
+ /**
+ * Method to process the pw related filtering objectives.
+ *
+ * @param portCriterion the in port match
+ * @param innerVlanIdCriterion the inner vlan match
+ * @param outerVlanIdCriterion the outer vlan match
+ * @param tunnelId the tunnel id
+ * @param applicationId the application id
+ * @return a list of flow rules to install
+ */
+ private List<FlowRule> processPwFilter(PortCriterion portCriterion,
+ VlanIdCriterion innerVlanIdCriterion,
+ VlanIdCriterion outerVlanIdCriterion,
+ long tunnelId,
+ ApplicationId applicationId) {
+ // As first we create the flow rule for the vlan 1 table.
+ FlowRule vlan1FlowRule;
+ int mplsLogicalPort = ((int) portCriterion.port().toLong());
+ // We have to match on the inner vlan and outer vlan at the same time.
+ // Ofdpa supports this through the OVID meta-data type.
+ TrafficSelector.Builder vlan1Selector = DefaultTrafficSelector.builder()
+ .matchInPort(portCriterion.port())
+ .extension(new OfdpaMatchVlanVid(innerVlanIdCriterion.vlanId()), deviceId)
+ .extension(new Ofdpa3MatchOvid(outerVlanIdCriterion.vlanId()), deviceId);
+ // TODO understand for the future how to manage the vlan rewrite.
+ TrafficTreatment.Builder vlan1Treatment = DefaultTrafficTreatment.builder()
+ .pushVlan()
+ .setVlanId(outerVlanIdCriterion.vlanId())
+ .extension(new Ofdpa3SetMplsType(VPWS), deviceId)
+ .extension(new Ofdpa3SetMplsL2Port(mplsLogicalPort), deviceId)
+ .setTunnelId(tunnelId)
+ .transition(MPLS_L2_PORT_FLOW_TABLE);
+ vlan1FlowRule = DefaultFlowRule.builder()
+ .forDevice(deviceId)
+ .withSelector(vlan1Selector.build())
+ .withTreatment(vlan1Treatment.build())
+ .withPriority(DEFAULT_PRIORITY)
+ .fromApp(applicationId)
+ .makePermanent()
+ .forTable(VLAN_1_TABLE)
+ .build();
+ // Finally we create the flow rule for the vlan table.
+ FlowRule vlanFlowRule;
+ // We have to match on the outer vlan.
+ TrafficSelector.Builder vlanSelector = DefaultTrafficSelector.builder()
+ .matchInPort(portCriterion.port())
+ .extension(new OfdpaMatchVlanVid(outerVlanIdCriterion.vlanId()), deviceId);
+ // TODO understand for the future how to manage the vlan rewrite.
+ TrafficTreatment.Builder vlanTreatment = DefaultTrafficTreatment.builder()
+ .popVlan()
+ .extension(new Ofdpa3SetOvid(outerVlanIdCriterion.vlanId()), deviceId)
+ .transition(VLAN_1_TABLE);
+ vlanFlowRule = DefaultFlowRule.builder()
+ .forDevice(deviceId)
+ .withSelector(vlanSelector.build())
+ .withTreatment(vlanTreatment.build())
+ .withPriority(DEFAULT_PRIORITY)
+ .fromApp(applicationId)
+ .makePermanent()
+ .forTable(VLAN_TABLE)
+ .build();
+
+ return ImmutableList.of(vlan1FlowRule, vlanFlowRule);
+ }
+
+ @Override
protected List<FlowRule> processVlanIdFilter(PortCriterion portCriterion,
VlanIdCriterion vidCriterion,
VlanId assignedVlan,
@@ -59,4 +269,104 @@
}
return processEthTypeSpecificInternal(fwd, true, MPLS_L3_TYPE_TABLE);
}
+
+ @Override
+ protected Collection<FlowRule> processVersatile(ForwardingObjective fwd) {
+ // We use the tunnel id to identify pw related flows.
+ TunnelIdCriterion tunnelIdCriterion = (TunnelIdCriterion) fwd.selector()
+ .getCriterion(TUNNEL_ID);
+ if (tunnelIdCriterion != null) {
+ return processPwVersatile(fwd);
+ }
+ // If it is not a pseudo wire flow we fall back
+ // to the OFDPA 2.0 pipeline.
+ return super.processVersatile(fwd);
+ }
+
+ /**
+ * Helper method to process the pw forwarding objectives.
+ *
+ * @param forwardingObjective the fw objective to process
+ * @return a singleton list of flow rule
+ */
+ private Collection<FlowRule> processPwVersatile(ForwardingObjective forwardingObjective) {
+ // We retrieve the matching criteria for mpls l2 port.
+ TunnelIdCriterion tunnelIdCriterion = (TunnelIdCriterion) forwardingObjective.selector()
+ .getCriterion(TUNNEL_ID);
+ PortCriterion portCriterion = (PortCriterion) forwardingObjective.selector()
+ .getCriterion(IN_PORT);
+ TrafficSelector.Builder selector = DefaultTrafficSelector.builder();
+ TrafficTreatment.Builder treatment = DefaultTrafficTreatment.builder();
+ int mplsLogicalPort;
+ long tunnelId;
+ // Mpls tunnel ids according to the OFDPA manual have to be
+ // in the range [2^17-1, 2^16].
+ tunnelId = MPLS_TUNNEL_ID_BASE | tunnelIdCriterion.tunnelId();
+ if (tunnelId > MPLS_TUNNEL_ID_MAX) {
+ log.error("Pw Versatile Forwarding Objective must include tunnel id < {}",
+ MPLS_TUNNEL_ID_MAX);
+ fail(forwardingObjective, ObjectiveError.BADPARAMS);
+ return Collections.emptySet();
+ }
+ // Port has not been null.
+ if (portCriterion == null) {
+ log.error("Pw Versatile Forwarding Objective must include port");
+ fail(forwardingObjective, ObjectiveError.BADPARAMS);
+ return Collections.emptySet();
+ }
+ // 0x0000XXXX is UNI interface.
+ if (portCriterion.port().toLong() > 0x0000FFFF) {
+ log.error("Pw Versatile Forwarding Objective invalid logical port {}",
+ portCriterion.port().toLong());
+ fail(forwardingObjective, ObjectiveError.BADPARAMS);
+ return Collections.emptySet();
+ }
+ mplsLogicalPort = ((int) portCriterion.port().toLong());
+ if (forwardingObjective.nextId() == null) {
+ log.error("Pw Versatile Forwarding Objective must contain nextId ",
+ forwardingObjective.nextId());
+ fail(forwardingObjective, ObjectiveError.BADPARAMS);
+ return Collections.emptySet();
+ }
+ // We don't expect a treatment.
+ if (forwardingObjective.treatment() != null &&
+ !forwardingObjective.treatment().equals(DefaultTrafficTreatment.emptyTreatment())) {
+ log.error("Pw Versatile Forwarding Objective cannot contain a treatment ",
+ forwardingObjective.nextId());
+ fail(forwardingObjective, ObjectiveError.BADPARAMS);
+ return Collections.emptySet();
+ }
+ // We retrieve the l2 vpn group and point the mpls
+ // l2 port to this.
+ NextGroup next = getGroupForNextObjective(forwardingObjective.nextId());
+ if (next == null) {
+ log.warn("next-id:{} not found in dev:{}", forwardingObjective.nextId(), deviceId);
+ fail(forwardingObjective, ObjectiveError.GROUPMISSING);
+ return Collections.emptySet();
+ }
+ List<Deque<GroupKey>> gkeys = appKryo.deserialize(next.data());
+ Group group = groupService.getGroup(deviceId, gkeys.get(0).peekFirst());
+ if (group == null) {
+ log.warn("Group with key:{} for next-id:{} not found in dev:{}",
+ gkeys.get(0).peekFirst(), forwardingObjective.nextId(), deviceId);
+ fail(forwardingObjective, ObjectiveError.GROUPMISSING);
+ return Collections.emptySet();
+ }
+ // We prepare the flow rule for the mpls l2 port table.
+ selector.matchTunnelId(tunnelId);
+ selector.extension(new Ofdpa3MatchMplsL2Port(mplsLogicalPort), deviceId);
+ // This should not be necessary but without we receive an error
+ treatment.extension(new Ofdpa3SetQosIndex(0), deviceId);
+ treatment.transition(MPLS_L2_PORT_PCP_TRUST_FLOW_TABLE);
+ treatment.deferred().group(group.id());
+ FlowRule.Builder ruleBuilder = DefaultFlowRule.builder()
+ .fromApp(forwardingObjective.appId())
+ .withPriority(MPLS_L2_PORT_PRIORITY)
+ .forDevice(deviceId)
+ .withSelector(selector.build())
+ .withTreatment(treatment.build())
+ .makePermanent()
+ .forTable(MPLS_L2_PORT_FLOW_TABLE);
+ return Collections.singletonList(ruleBuilder.build());
+ }
}