[ONOS-7285][ONOS-7263] VLAN support by fabric.p4
Change-Id: I9ea460bca2698eb74f0d4988830a1e7cc7bc2768
diff --git a/pipelines/fabric/src/main/java/org/onosproject/pipelines/fabric/pipeliner/FabricNextPipeliner.java b/pipelines/fabric/src/main/java/org/onosproject/pipelines/fabric/pipeliner/FabricNextPipeliner.java
index c7c7647..12602b4 100644
--- a/pipelines/fabric/src/main/java/org/onosproject/pipelines/fabric/pipeliner/FabricNextPipeliner.java
+++ b/pipelines/fabric/src/main/java/org/onosproject/pipelines/fabric/pipeliner/FabricNextPipeliner.java
@@ -16,16 +16,21 @@
package org.onosproject.pipelines.fabric.pipeliner;
+import org.onlab.packet.VlanId;
import org.onosproject.net.DeviceId;
+import org.onosproject.net.PortNumber;
import org.onosproject.net.driver.Driver;
import org.onosproject.net.flow.DefaultFlowRule;
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.Criterion;
import org.onosproject.net.flow.criteria.PiCriterion;
+import org.onosproject.net.flow.criteria.VlanIdCriterion;
import org.onosproject.net.flow.instructions.Instruction;
import org.onosproject.net.flow.instructions.Instructions.OutputInstruction;
+import org.onosproject.net.flow.instructions.L2ModificationInstruction;
import org.onosproject.net.flowobjective.DefaultNextObjective;
import org.onosproject.net.flowobjective.NextObjective;
import org.onosproject.net.flowobjective.Objective;
@@ -44,8 +49,11 @@
import static org.onosproject.pipelines.fabric.FabricConstants.ACT_PRF_FABRICINGRESS_NEXT_ECMP_SELECTOR_ID;
import static org.onosproject.pipelines.fabric.FabricConstants.HF_FABRIC_METADATA_NEXT_ID_ID;
+import static org.onosproject.pipelines.fabric.FabricConstants.HF_STANDARD_METADATA_EGRESS_PORT_ID;
+import static org.onosproject.pipelines.fabric.FabricConstants.TBL_EGRESS_VLAN_ID;
import static org.onosproject.pipelines.fabric.FabricConstants.TBL_HASHED_ID;
import static org.onosproject.pipelines.fabric.FabricConstants.TBL_SIMPLE_ID;
+import static org.onosproject.pipelines.fabric.FabricConstants.TBL_VLAN_META_ID;
import static org.slf4j.LoggerFactory.getLogger;
/**
@@ -66,6 +74,8 @@
public PipelinerTranslationResult next(NextObjective nextObjective) {
PipelinerTranslationResult.Builder resultBuilder = PipelinerTranslationResult.builder();
+ processNextVlanMeta(nextObjective, resultBuilder);
+
switch (nextObjective.type()) {
case SIMPLE:
processSimpleNext(nextObjective, resultBuilder);
@@ -82,6 +92,38 @@
return resultBuilder.build();
}
+ private void processNextVlanMeta(NextObjective next,
+ PipelinerTranslationResult.Builder resultBuilder) {
+ TrafficSelector meta = next.meta();
+ if (meta == null) {
+ // do nothing if there is no metadata in the next objective.
+ return;
+ }
+ VlanIdCriterion vlanIdCriterion =
+ (VlanIdCriterion) meta.getCriterion(Criterion.Type.VLAN_VID);
+
+ if (vlanIdCriterion == null) {
+ // do nothing if we can't find vlan from next objective metadata.
+ return;
+ }
+
+ VlanId vlanId = vlanIdCriterion.vlanId();
+ TrafficSelector selector = buildNextIdSelector(next.id());
+ TrafficTreatment treatment = DefaultTrafficTreatment.builder()
+ .setVlanId(vlanId)
+ .build();
+
+ resultBuilder.addFlowRule(DefaultFlowRule.builder()
+ .withSelector(selector)
+ .withTreatment(treatment)
+ .forTable(TBL_VLAN_META_ID)
+ .makePermanent()
+ .withPriority(next.priority())
+ .forDevice(deviceId)
+ .fromApp(next.appId())
+ .build());
+ }
+
private void processSimpleNext(NextObjective next,
PipelinerTranslationResult.Builder resultBuilder) {
@@ -93,18 +135,14 @@
TrafficSelector selector = buildNextIdSelector(next.id());
TrafficTreatment treatment = next.next().iterator().next();
- OutputInstruction outputInst = treatment.allInstructions()
- .stream()
- .filter(inst -> inst.type() == Instruction.Type.OUTPUT)
- .map(inst -> (OutputInstruction) inst)
- .findFirst()
- .orElse(null);
+ PortNumber outputPort = getOutputPort(treatment);
- if (outputInst == null) {
+ if (outputPort == null) {
log.warn("At least one output instruction in simple next objective");
resultBuilder.setError(ObjectiveError.BADPARAMS);
return;
}
+
resultBuilder.addFlowRule(DefaultFlowRule.builder()
.withSelector(selector)
.withTreatment(treatment)
@@ -114,29 +152,81 @@
.forDevice(deviceId)
.fromApp(next.appId())
.build());
+
+ if (includesPopVlanInst(treatment)) {
+ processVlanPopRule(outputPort, next, resultBuilder);
+ }
}
- private void processHashedNext(NextObjective nextObjective, PipelinerTranslationResult.Builder resultBuilder) {
+ private PortNumber getOutputPort(TrafficTreatment treatment) {
+ return treatment.allInstructions()
+ .stream()
+ .filter(inst -> inst.type() == Instruction.Type.OUTPUT)
+ .map(inst -> (OutputInstruction) inst)
+ .findFirst()
+ .map(OutputInstruction::port)
+ .orElse(null);
+ }
+
+ private boolean includesPopVlanInst(TrafficTreatment treatment) {
+ return treatment.allInstructions()
+ .stream()
+ .filter(inst -> inst.type() == Instruction.Type.L2MODIFICATION)
+ .map(inst -> (L2ModificationInstruction) inst)
+ .anyMatch(inst -> inst.subtype() == L2ModificationInstruction.L2SubType.VLAN_POP);
+ }
+
+ private void processVlanPopRule(PortNumber port, NextObjective next,
+ PipelinerTranslationResult.Builder resultBuilder) {
+ TrafficSelector meta = next.meta();
+ VlanIdCriterion vlanIdCriterion =
+ (VlanIdCriterion) meta.getCriterion(Criterion.Type.VLAN_VID);
+ VlanId vlanId = vlanIdCriterion.vlanId();
+
+ PiCriterion egressVlanTableMatch = PiCriterion.builder()
+ .matchExact(HF_STANDARD_METADATA_EGRESS_PORT_ID,
+ (short) port.toLong())
+ .build();
+ // Add VLAN pop rule to egress pipeline table
+ TrafficSelector selector = DefaultTrafficSelector.builder()
+ .matchPi(egressVlanTableMatch)
+ .matchVlanId(vlanId)
+ .build();
+ TrafficTreatment treatment = DefaultTrafficTreatment.builder()
+ .popVlan()
+ .build();
+ resultBuilder.addFlowRule(DefaultFlowRule.builder()
+ .withSelector(selector)
+ .withTreatment(treatment)
+ .forTable(TBL_EGRESS_VLAN_ID)
+ .makePermanent()
+ .withPriority(next.priority())
+ .forDevice(deviceId)
+ .fromApp(next.appId())
+ .build());
+ }
+
+ private void processHashedNext(NextObjective next, PipelinerTranslationResult.Builder resultBuilder) {
boolean noHashedTable = Boolean.parseBoolean(driver.getProperty(NO_HASHED_TABLE));
if (noHashedTable) {
- if (nextObjective.next().isEmpty()) {
+ if (next.next().isEmpty()) {
return;
}
// use first action if not support hashed group
- TrafficTreatment treatment = nextObjective.next().iterator().next();
+ TrafficTreatment treatment = next.next().iterator().next();
NextObjective.Builder simpleNext = DefaultNextObjective.builder()
.addTreatment(treatment)
- .withId(nextObjective.id())
- .fromApp(nextObjective.appId())
+ .withId(next.id())
+ .fromApp(next.appId())
.makePermanent()
- .withMeta(nextObjective.meta())
- .withPriority(nextObjective.priority())
+ .withMeta(next.meta())
+ .withPriority(next.priority())
.withType(NextObjective.Type.SIMPLE);
- if (nextObjective.context().isPresent()) {
- processSimpleNext(simpleNext.add(nextObjective.context().get()), resultBuilder);
+ if (next.context().isPresent()) {
+ processSimpleNext(simpleNext.add(next.context().get()), resultBuilder);
} else {
processSimpleNext(simpleNext.add(), resultBuilder);
}
@@ -144,15 +234,23 @@
}
// create hash groups
- int groupId = nextObjective.id();
- List<GroupBucket> bucketList = nextObjective.next().stream()
+ int groupId = next.id();
+ List<GroupBucket> bucketList = next.next().stream()
.map(DefaultGroupBucket::createSelectGroupBucket)
.collect(Collectors.toList());
- if (bucketList.size() != nextObjective.next().size()) {
+ // Egress VLAN handling
+ next.next().forEach(treatment -> {
+ PortNumber outputPort = getOutputPort(treatment);
+ if (includesPopVlanInst(treatment) && outputPort != null) {
+ processVlanPopRule(outputPort, next, resultBuilder);
+ }
+ });
+
+ if (bucketList.size() != next.next().size()) {
// some action not converted
// set error
- log.warn("Expected bucket size {}, got {}", nextObjective.next().size(), bucketList.size());
+ log.warn("Expected bucket size {}, got {}", next.next().size(), bucketList.size());
resultBuilder.setError(ObjectiveError.BADPARAMS);
return;
}
@@ -167,18 +265,18 @@
buckets,
groupKey,
groupId,
- nextObjective.appId()));
+ next.appId()));
// flow
// If operation is ADD_TO_EXIST or REMOVE_FROM_EXIST, means we modify
// group buckets only, no changes for flow rule
- if (nextObjective.op() == Objective.Operation.ADD_TO_EXISTING ||
- nextObjective.op() == Objective.Operation.REMOVE_FROM_EXISTING) {
+ if (next.op() == Objective.Operation.ADD_TO_EXISTING ||
+ next.op() == Objective.Operation.REMOVE_FROM_EXISTING) {
return;
}
- TrafficSelector selector = buildNextIdSelector(nextObjective.id());
+ TrafficSelector selector = buildNextIdSelector(next.id());
TrafficTreatment treatment = DefaultTrafficTreatment.builder()
- .piTableAction(PiActionGroupId.of(nextObjective.id()))
+ .piTableAction(PiActionGroupId.of(next.id()))
.build();
resultBuilder.addFlowRule(DefaultFlowRule.builder()
@@ -186,9 +284,9 @@
.withTreatment(treatment)
.forTable(TBL_HASHED_ID)
.makePermanent()
- .withPriority(nextObjective.priority())
+ .withPriority(next.priority())
.forDevice(deviceId)
- .fromApp(nextObjective.appId())
+ .fromApp(next.appId())
.build());
}
diff --git a/pipelines/fabric/src/main/java/org/onosproject/pipelines/fabric/pipeliner/FabricPipeliner.java b/pipelines/fabric/src/main/java/org/onosproject/pipelines/fabric/pipeliner/FabricPipeliner.java
index 5c66474..363a95c 100644
--- a/pipelines/fabric/src/main/java/org/onosproject/pipelines/fabric/pipeliner/FabricPipeliner.java
+++ b/pipelines/fabric/src/main/java/org/onosproject/pipelines/fabric/pipeliner/FabricPipeliner.java
@@ -263,7 +263,12 @@
};
FlowRuleOperations ops = buildFlowRuleOps(objective, flowRules, ctx);
- flowRuleService.apply(ops);
+ if (ops != null) {
+ flowRuleService.apply(ops);
+ } else {
+ // remove pendings
+ flowRules.forEach(flowRule -> pendingInstallObjectiveFlows.remove(flowRule.id()));
+ }
}
private void installGroups(Objective objective, Collection<GroupDescription> groups) {
@@ -317,8 +322,13 @@
case REMOVE:
flowRules.forEach(ops::remove);
break;
+ case ADD_TO_EXISTING:
+ case REMOVE_FROM_EXISTING:
+ // Next objective may use ADD_TO_EXIST or REMOVE_FROM_EXIST op
+ // No need to update FlowRuls for vlan_meta table.
+ return null;
default:
- log.warn("Unsupported op {} for {}", objective);
+ log.warn("Unsupported op {} for {}", objective.op(), objective);
fail(objective, ObjectiveError.BADPARAMS);
return null;
}