implementing q-in-q support
implementing filtering objectives for punting rules
Change-Id: I73945df31c4d5aa40ab4b07fc6818edd083178fb
diff --git a/drivers/src/main/java/org/onosproject/driver/pipeline/OltPipeline.java b/drivers/src/main/java/org/onosproject/driver/pipeline/OltPipeline.java
index 8a7b22b..37980f1 100644
--- a/drivers/src/main/java/org/onosproject/driver/pipeline/OltPipeline.java
+++ b/drivers/src/main/java/org/onosproject/driver/pipeline/OltPipeline.java
@@ -15,8 +15,13 @@
*/
package org.onosproject.driver.pipeline;
+import com.google.common.collect.Lists;
+import org.apache.commons.lang3.tuple.ImmutablePair;
+import org.apache.commons.lang3.tuple.Pair;
import org.onlab.osgi.ServiceDirectory;
import org.onlab.packet.EthType;
+import org.onlab.packet.IPv4;
+import org.onlab.packet.VlanId;
import org.onosproject.core.ApplicationId;
import org.onosproject.core.CoreService;
import org.onosproject.net.DefaultAnnotations;
@@ -41,24 +46,38 @@
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.EthTypeCriterion;
+import org.onosproject.net.flow.criteria.IPProtocolCriterion;
+import org.onosproject.net.flow.criteria.PortCriterion;
+import org.onosproject.net.flow.instructions.Instruction;
import org.onosproject.net.flow.instructions.Instructions;
+import org.onosproject.net.flow.instructions.L2ModificationInstruction;
import org.onosproject.net.flowobjective.FilteringObjective;
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.onosproject.net.packet.PacketPriority;
import org.onosproject.net.provider.AbstractProvider;
import org.onosproject.net.provider.ProviderId;
import org.slf4j.Logger;
+import java.util.Collection;
+import java.util.List;
+import java.util.Optional;
+import java.util.stream.Collectors;
+
import static com.google.common.base.Preconditions.checkNotNull;
import static org.slf4j.LoggerFactory.getLogger;
/**
* Pipeliner for OLT device.
*/
+
public class OltPipeline extends AbstractHandlerBehaviour implements Pipeliner {
+ private static final Integer QQ_TABLE = 1;
private final Logger log = getLogger(getClass());
static final ProviderId PID = new ProviderId("olt", "org.onosproject.olt", true);
@@ -75,110 +94,102 @@
private DeviceProvider provider = new AnnotationProvider();
-
@Override
public void init(DeviceId deviceId, PipelinerContext context) {
+ log.debug("Initiate OLT pipeline");
this.serviceDirectory = context.directory();
this.deviceId = deviceId;
DeviceProviderRegistry registry =
- serviceDirectory.get(DeviceProviderRegistry.class);
+ serviceDirectory.get(DeviceProviderRegistry.class);
flowRuleService = serviceDirectory.get(FlowRuleService.class);
coreService = serviceDirectory.get(CoreService.class);
- /*try {
- DeviceProviderService providerService = registry.register(provider);
- providerService.deviceConnected(deviceId,
- description(deviceId, DEVICE, OLT));
- } finally {
- registry.unregister(provider);
- }*/
-
appId = coreService.registerApplication(
"org.onosproject.driver.OLTPipeline");
- TrafficSelector selector = DefaultTrafficSelector.builder()
- .matchEthType(EthType.EtherType.EAPOL.ethType().toShort())
- .build();
-
- TrafficTreatment treatment = DefaultTrafficTreatment.builder()
- .punt()
- .build();
-
- FlowRule flowRule = new DefaultFlowRule(deviceId, selector, treatment,
- PacketPriority.CONTROL.priorityValue(),
- appId, 0, true, null);
-
- //flowRuleService.applyFlowRules(flowRule);
}
@Override
public void filter(FilteringObjective filter) {
- throw new UnsupportedOperationException("OLT does not filter.");
- }
+ Instructions.OutputInstruction output;
- @Override
- public void forward(ForwardingObjective fwd) {
- FlowRuleOperations.Builder flowBuilder = FlowRuleOperations.builder();
+ if (filter.meta() != null && !filter.meta().immediate().isEmpty()) {
+ output = (Instructions.OutputInstruction) filter.meta().immediate().stream()
+ .filter(t -> t.type().equals(Instruction.Type.OUTPUT))
+ .limit(1)
+ .findFirst().get();
- if (fwd.flag() != ForwardingObjective.Flag.VERSATILE) {
- throw new UnsupportedOperationException(
- "Only VERSATILE is supported.");
- }
-
- boolean isPunt = fwd.treatment().immediate().stream().anyMatch(i -> {
- if (i instanceof Instructions.OutputInstruction) {
- Instructions.OutputInstruction out = (Instructions.OutputInstruction) i;
- return out.port().equals(PortNumber.CONTROLLER);
+ if (output != null && !output.port().equals(PortNumber.CONTROLLER)) {
+ fail(filter, ObjectiveError.UNSUPPORTED);
+ return;
}
- return false;
- });
-
- if (isPunt) {
+ } else {
+ fail(filter, ObjectiveError.BADPARAMS);
return;
}
- TrafficSelector selector = fwd.selector();
+ if (filter.key().type() != Criterion.Type.IN_PORT) {
+ fail(filter, ObjectiveError.BADPARAMS);
+ return;
+ }
+
+ EthTypeCriterion ethType = (EthTypeCriterion)
+ filterForCriterion(filter.conditions(), Criterion.Type.ETH_TYPE);
+
+ if (ethType == null) {
+ fail(filter, ObjectiveError.BADPARAMS);
+ return;
+ }
+
+ if (ethType.ethType().equals(EthType.EtherType.EAPOL)) {
+ provisionEapol(filter, ethType, output);
+ } else if (ethType.ethType().equals(EthType.EtherType.IPV4)) {
+ IPProtocolCriterion ipProto = (IPProtocolCriterion)
+ filterForCriterion(filter.conditions(), Criterion.Type.IP_PROTO);
+ if (ipProto.protocol() == IPv4.PROTOCOL_IGMP) {
+ provisionIGMP(filter, ethType, ipProto, output);
+ }
+ } else {
+ fail(filter, ObjectiveError.UNSUPPORTED);
+ }
+
+ }
+
+
+ @Override
+ public void forward(ForwardingObjective fwd) {
TrafficTreatment treatment = fwd.treatment();
- FlowRule.Builder ruleBuilder = DefaultFlowRule.builder()
- .forDevice(deviceId)
- .withSelector(selector)
- .withTreatment(treatment)
- .fromApp(fwd.appId())
- .withPriority(fwd.priority());
+ List<Instruction> instructions = treatment.allInstructions();
- if (fwd.permanent()) {
- ruleBuilder.makePermanent();
+ Optional<Instruction> vlanIntruction = instructions.stream()
+ .filter(i -> i.type() == Instruction.Type.L2MODIFICATION)
+ .filter(i -> ((L2ModificationInstruction) i).subtype() ==
+ L2ModificationInstruction.L2SubType.VLAN_PUSH ||
+ ((L2ModificationInstruction) i).subtype() ==
+ L2ModificationInstruction.L2SubType.VLAN_POP)
+ .findAny();
+
+ if (!vlanIntruction.isPresent()) {
+ fail(fwd, ObjectiveError.BADPARAMS);
+ return;
+ }
+
+ L2ModificationInstruction vlanIns =
+ (L2ModificationInstruction) vlanIntruction.get();
+
+ if (vlanIns.subtype() == L2ModificationInstruction.L2SubType.VLAN_PUSH) {
+ installUpstreamRules(fwd);
+ } else if (vlanIns.subtype() == L2ModificationInstruction.L2SubType.VLAN_POP) {
+ installDownstreamRules(fwd);
} else {
- ruleBuilder.makeTemporary(fwd.timeout());
+ log.error("Unknown OLT operation: {}", fwd);
+ fail(fwd, ObjectiveError.UNSUPPORTED);
+ return;
}
- switch (fwd.op()) {
- case ADD:
- flowBuilder.add(ruleBuilder.build());
- break;
- case REMOVE:
- flowBuilder.remove(ruleBuilder.build());
- break;
- default:
- log.warn("Unknown operation {}", fwd.op());
- }
+ pass(fwd);
- flowRuleService.apply(flowBuilder.build(new FlowRuleOperationsContext() {
- @Override
- public void onSuccess(FlowRuleOperations ops) {
- if (fwd.context().isPresent()) {
- fwd.context().get().onSuccess(fwd);
- }
- }
-
- @Override
- public void onError(FlowRuleOperations ops) {
- if (fwd.context().isPresent()) {
- fwd.context().get().onError(fwd, ObjectiveError.FLOWINSTALLATIONFAILED);
- }
- }
- }));
}
@Override
@@ -186,12 +197,290 @@
throw new UnsupportedOperationException("OLT does not next hop.");
}
+ private void installDownstreamRules(ForwardingObjective fwd) {
+ List<Pair<Instruction, Instruction>> vlanOps =
+ vlanOps(fwd,
+ L2ModificationInstruction.L2SubType.VLAN_POP);
+
+ if (vlanOps == null) {
+ return;
+ }
+
+ Instruction output = fetchOutput(fwd, "downstream");
+
+ if (output == null) {
+ return;
+ }
+
+ Pair<Instruction, Instruction> popAndRewrite = vlanOps.remove(0);
+
+ FlowRule.Builder inner = DefaultFlowRule.builder()
+ .forDevice(deviceId)
+ .fromApp(appId)
+ .makePermanent()
+ .withPriority(fwd.priority())
+ .withSelector(fwd.selector())
+ .withTreatment(buildTreatment(popAndRewrite.getLeft(),
+ Instructions.transition(QQ_TABLE)));
+ PortCriterion inPort = (PortCriterion)
+ fwd.selector().getCriterion(Criterion.Type.IN_PORT);
+
+ FlowRule.Builder outer = DefaultFlowRule.builder()
+ .forDevice(deviceId)
+ .fromApp(appId)
+ .forTable(QQ_TABLE)
+ .makePermanent()
+ .withPriority(fwd.priority())
+ .withSelector(buildSelector(inPort))
+ .withTreatment(buildTreatment(popAndRewrite.getRight(),
+ output));
+
+ applyRules(fwd, inner, outer);
+
+ }
+
+ private void installUpstreamRules(ForwardingObjective fwd) {
+ List<Pair<Instruction, Instruction>> vlanOps =
+ vlanOps(fwd,
+ L2ModificationInstruction.L2SubType.VLAN_PUSH);
+
+ if (vlanOps == null) {
+ return;
+ }
+
+ Instruction output = fetchOutput(fwd, "upstream");
+
+ if (output == null) {
+ return;
+ }
+
+ Pair<Instruction, Instruction> innerPair = vlanOps.remove(0);
+
+ Pair<Instruction, Instruction> outerPair = vlanOps.remove(0);
+
+ FlowRule.Builder inner = DefaultFlowRule.builder()
+ .forDevice(deviceId)
+ .fromApp(appId)
+ .makePermanent()
+ .withPriority(fwd.priority())
+ .withSelector(fwd.selector())
+ .withTreatment(buildTreatment(innerPair.getRight(),
+ Instructions.transition(QQ_TABLE)));
+
+ PortCriterion inPort = (PortCriterion)
+ fwd.selector().getCriterion(Criterion.Type.IN_PORT);
+
+ VlanId cVlanId = ((L2ModificationInstruction.ModVlanIdInstruction)
+ innerPair.getRight()).vlanId();
+
+ FlowRule.Builder outer = DefaultFlowRule.builder()
+ .forDevice(deviceId)
+ .fromApp(appId)
+ .forTable(QQ_TABLE)
+ .makePermanent()
+ .withPriority(fwd.priority())
+ .withSelector(buildSelector(inPort,
+ Criteria.matchVlanId(cVlanId)))
+ .withTreatment(buildTreatment(outerPair.getLeft(),
+ outerPair.getRight(),
+ output));
+
+ applyRules(fwd, inner, outer);
+
+ }
+
+ private Instruction fetchOutput(ForwardingObjective fwd, String direction) {
+ Instruction output = fwd.treatment().allInstructions().stream()
+ .filter(i -> i.type() == Instruction.Type.OUTPUT)
+ .findFirst().orElse(null);
+
+ if (output == null) {
+ log.error("OLT {} rule has no output", direction);
+ fail(fwd, ObjectiveError.BADPARAMS);
+ return null;
+ }
+ return output;
+ }
+
+ private List<Pair<Instruction, Instruction>> vlanOps(ForwardingObjective fwd,
+ L2ModificationInstruction.L2SubType type) {
+
+ List<Pair<Instruction, Instruction>> vlanOps = findVlanOps(
+ fwd.treatment().allInstructions(), type);
+
+ if (vlanOps == null) {
+ String direction = type == L2ModificationInstruction.L2SubType.VLAN_POP
+ ? "downstream" : "upstream";
+ log.error("Missing vlan operations in {} forwarding: {}", direction, fwd);
+ fail(fwd, ObjectiveError.BADPARAMS);
+ return null;
+ }
+ return vlanOps;
+ }
+
+
+ private List<Pair<Instruction, Instruction>> findVlanOps(List<Instruction> instructions,
+ L2ModificationInstruction.L2SubType type) {
+
+ List<Instruction> vlanPushs = findL2Instructions(
+ type,
+ instructions);
+ List<Instruction> vlanSets = findL2Instructions(
+ L2ModificationInstruction.L2SubType.VLAN_ID,
+ instructions);
+
+ if (vlanPushs.size() != vlanSets.size()) {
+ return null;
+ }
+
+ List<Pair<Instruction, Instruction>> pairs = Lists.newArrayList();
+
+ for (int i = 0; i < vlanPushs.size(); i++) {
+ pairs.add(new ImmutablePair<>(vlanPushs.get(i), vlanSets.get(i)));
+ }
+ return pairs;
+ }
+
+ private List<Instruction> findL2Instructions(L2ModificationInstruction.L2SubType subType,
+ List<Instruction> actions) {
+ return actions.stream()
+ .filter(i -> i.type() == Instruction.Type.L2MODIFICATION)
+ .filter(i -> ((L2ModificationInstruction) i).subtype() == subType)
+ .collect(Collectors.toList());
+ }
+
+ private void provisionEapol(FilteringObjective filter,
+ EthTypeCriterion ethType,
+ Instructions.OutputInstruction output) {
+
+ TrafficSelector selector = buildSelector(filter.key(), ethType);
+ TrafficTreatment treatment = buildTreatment(output);
+ buildAndApplyRule(filter, selector, treatment);
+
+ }
+
+ private void provisionIGMP(FilteringObjective filter, EthTypeCriterion ethType,
+ IPProtocolCriterion ipProto,
+ Instructions.OutputInstruction output) {
+ TrafficSelector selector = buildSelector(filter.key(), ethType, ipProto);
+ TrafficTreatment treatment = buildTreatment(output);
+ buildAndApplyRule(filter, selector, treatment);
+ }
+
+ private void buildAndApplyRule(FilteringObjective filter, TrafficSelector selector,
+ TrafficTreatment treatment) {
+ FlowRule rule = DefaultFlowRule.builder()
+ .forDevice(deviceId)
+ .forTable(0)
+ .fromApp(filter.appId())
+ .makePermanent()
+ .withSelector(selector)
+ .withTreatment(treatment)
+ .build();
+
+ FlowRuleOperations.Builder opsBuilder = FlowRuleOperations.builder();
+
+ switch (filter.type()) {
+ case PERMIT:
+ opsBuilder.add(rule);
+ break;
+ case DENY:
+ opsBuilder.remove(rule);
+ break;
+ default:
+ log.warn("Unknown filter type : {}", filter.type());
+ fail(filter, ObjectiveError.UNSUPPORTED);
+ }
+
+ applyFlowRules(opsBuilder, filter);
+ }
+
+ private void applyRules(ForwardingObjective fwd,
+ FlowRule.Builder inner, FlowRule.Builder outer) {
+ FlowRuleOperations.Builder builder = FlowRuleOperations.builder();
+ switch (fwd.op()) {
+ case ADD:
+ builder.add(inner.build()).add(outer.build());
+ break;
+ case REMOVE:
+ builder.remove(inner.build()).remove(outer.build());
+ break;
+ case ADD_TO_EXISTING:
+ break;
+ case REMOVE_FROM_EXISTING:
+ break;
+ default:
+ log.warn("Unknown forwarding operation: {}", fwd.op());
+ }
+
+ applyFlowRules(builder, fwd);
+ }
+
+ private void applyFlowRules(FlowRuleOperations.Builder builder,
+ Objective objective) {
+ flowRuleService.apply(builder.build(new FlowRuleOperationsContext() {
+ @Override
+ public void onSuccess(FlowRuleOperations ops) {
+ pass(objective);
+ }
+
+ @Override
+ public void onError(FlowRuleOperations ops) {
+ fail(objective, ObjectiveError.FLOWINSTALLATIONFAILED);
+ }
+ }));
+ }
+
+ private Criterion filterForCriterion(Collection<Criterion> criteria, Criterion.Type type) {
+ return criteria.stream()
+ .filter(c -> c.type().equals(Criterion.Type.ETH_TYPE))
+ .limit(1)
+ .findFirst().orElse(null);
+ }
+
+ private TrafficSelector buildSelector(Criterion... criteria) {
+
+
+ TrafficSelector.Builder sBuilder = DefaultTrafficSelector.builder();
+
+ for (Criterion c : criteria) {
+ sBuilder.add(c);
+ }
+
+ return sBuilder.build();
+ }
+
+ private TrafficTreatment buildTreatment(Instruction... instructions) {
+
+
+ TrafficTreatment.Builder tBuilder = DefaultTrafficTreatment.builder();
+
+ for (Instruction i : instructions) {
+ tBuilder.add(i);
+ }
+
+ return tBuilder.build();
+ }
+
+
+ private void fail(Objective obj, ObjectiveError error) {
+ if (obj.context().isPresent()) {
+ obj.context().get().onError(obj, error);
+ }
+ }
+
+ private void pass(Objective obj) {
+ if (obj.context().isPresent()) {
+ obj.context().get().onSuccess(obj);
+ }
+ }
+
/**
* Build a device description.
*
* @param deviceId a deviceId
- * @param key the key of the annotation
- * @param value the value for the annotation
+ * @param key the key of the annotation
+ * @param value the value for the annotation
* @return a device description
*/
private DeviceDescription description(DeviceId deviceId, String key, String value) {