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/apps/segmentrouting/src/main/java/org/onosproject/segmentrouting/SegmentRoutingManager.java b/apps/segmentrouting/src/main/java/org/onosproject/segmentrouting/SegmentRoutingManager.java
index 2d4cc45..1423e06 100644
--- a/apps/segmentrouting/src/main/java/org/onosproject/segmentrouting/SegmentRoutingManager.java
+++ b/apps/segmentrouting/src/main/java/org/onosproject/segmentrouting/SegmentRoutingManager.java
@@ -74,6 +74,7 @@
import org.onosproject.net.packet.PacketContext;
import org.onosproject.net.packet.PacketProcessor;
import org.onosproject.net.packet.PacketService;
+import org.onosproject.net.topology.PathService;
import org.onosproject.net.topology.TopologyService;
import org.onosproject.routing.config.RouterConfig;
import org.onosproject.segmentrouting.config.DeviceConfigNotFoundException;
@@ -134,6 +135,9 @@
private NeighbourResolutionService neighbourResolutionService;
@Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+ public PathService pathService;
+
+ @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
CoreService coreService;
@Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
@@ -146,16 +150,16 @@
DeviceService deviceService;
@Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
- FlowObjectiveService flowObjectiveService;
+ public FlowObjectiveService flowObjectiveService;
@Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
LinkService linkService;
@Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
- MastershipService mastershipService;
+ public MastershipService mastershipService;
@Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
- StorageService storageService;
+ public StorageService storageService;
@Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
MulticastRouteService multicastRouteService;
@@ -180,7 +184,7 @@
IpHandler ipHandler = null;
RoutingRulePopulator routingRulePopulator = null;
public ApplicationId appId;
- protected DeviceConfiguration deviceConfiguration = null;
+ public DeviceConfiguration deviceConfiguration = null;
DefaultRoutingHandler defaultRoutingHandler = null;
private TunnelHandler tunnelHandler = null;
diff --git a/apps/segmentrouting/src/main/java/org/onosproject/segmentrouting/pwaas/L2TunnelHandler.java b/apps/segmentrouting/src/main/java/org/onosproject/segmentrouting/pwaas/L2TunnelHandler.java
index 96fe6c9..5f0ed35 100644
--- a/apps/segmentrouting/src/main/java/org/onosproject/segmentrouting/pwaas/L2TunnelHandler.java
+++ b/apps/segmentrouting/src/main/java/org/onosproject/segmentrouting/pwaas/L2TunnelHandler.java
@@ -16,36 +16,166 @@
package org.onosproject.segmentrouting.pwaas;
+import com.google.common.collect.Iterables;
+import com.google.common.collect.Lists;
+import org.apache.commons.lang3.RandomUtils;
+import org.onlab.packet.MacAddress;
+import org.onlab.packet.MplsLabel;
+import org.onlab.packet.VlanId;
+import org.onlab.util.KryoNamespace;
+import org.onosproject.net.ConnectPoint;
+import org.onosproject.net.DeviceId;
+import org.onosproject.net.DisjointPath;
+import org.onosproject.net.Link;
+import org.onosproject.net.PortNumber;
import org.onosproject.net.config.NetworkConfigEvent;
+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.DefaultNextObjective;
+import org.onosproject.net.flowobjective.DefaultObjectiveContext;
+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.ObjectiveContext;
+import org.onosproject.net.flowobjective.ObjectiveError;
import org.onosproject.segmentrouting.SegmentRoutingManager;
+import org.onosproject.segmentrouting.SegmentRoutingService;
+import org.onosproject.segmentrouting.config.DeviceConfigNotFoundException;
import org.onosproject.segmentrouting.config.PwaasConfig;
+import org.onosproject.store.serializers.KryoNamespaces;
+import org.onosproject.store.service.ConsistentMap;
+import org.onosproject.store.service.Serializer;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
+import java.util.List;
+import java.util.Set;
+import java.util.concurrent.CompletableFuture;
+import java.util.stream.Collectors;
+
+import static com.google.common.base.Preconditions.checkState;
+import static org.onosproject.net.flowobjective.ForwardingObjective.Flag.VERSATILE;
+import static org.onosproject.segmentrouting.pwaas.L2Mode.TAGGED;
+import static org.onosproject.segmentrouting.pwaas.L2TunnelHandler.Pipeline.INITIATION;
+import static org.onosproject.segmentrouting.pwaas.L2TunnelHandler.Result.*;
+
/**
* Handles pwaas related events.
*/
public class L2TunnelHandler {
+
private static final Logger log = LoggerFactory.getLogger(L2TunnelHandler.class);
- private static final String CONFIG_NOT_FOUND = "Pwaas config not found";
+
+ private static final String FWD = "f";
+ private static final String REV = "r";
+
private static final String NOT_MASTER = "Not master controller";
+ private static final String WRONG_TOPOLOGY = "Path in leaf-spine topology" +
+ " should always be two hops: ";
+
private final SegmentRoutingManager srManager;
- public L2TunnelHandler(SegmentRoutingManager srManager) {
- this.srManager = srManager;
+ private final ConsistentMap<String, NextObjective> l2InitiationNextObjStore;
+
+ /**
+ * TODO a proper store is necessary to handle the policies and collisions.
+ */
+ private final KryoNamespace.Builder l2TunnelKryo;
+
+ /**
+ * Create a l2 tunnel handler for the deploy and
+ * for the tear down of pseudo wires.
+ *
+ * @param segmentRoutingManager the segment routing manager
+ */
+ public L2TunnelHandler(SegmentRoutingManager segmentRoutingManager) {
+ srManager = segmentRoutingManager;
+ l2TunnelKryo = new KryoNamespace.Builder()
+ .register(KryoNamespaces.API);
+
+ l2InitiationNextObjStore = srManager.storageService
+ .<String, NextObjective>consistentMapBuilder()
+ .withName("onos-l2initiation-nextobj-store")
+ .withSerializer(Serializer.using(l2TunnelKryo.build()))
+ .build();
}
/**
* Processes Pwaas Config added event.
*
- * @param event network config added event
+ * @param event network config add event
*/
public void processPwaasConfigAdded(NetworkConfigEvent event) {
log.info("Processing Pwaas CONFIG_ADDED");
PwaasConfig config = (PwaasConfig) event.config().get();
- config.getPwIds().forEach(pwId -> {
- log.info("{}", config.getPwDescription(pwId));
- });
+ Set<DefaultL2TunnelDescription> pwToAdd = config.getPwIds()
+ .stream()
+ .map(config::getPwDescription)
+ .collect(Collectors.toSet());
+ // We deploy all the pseudo wire deployed
+ deploy(pwToAdd);
+ }
+
+ private void deploy(Set<DefaultL2TunnelDescription> pwToAdd) {
+ Result result;
+ long l2TunnelId;
+ for (DefaultL2TunnelDescription currentL2Tunnel : pwToAdd) {
+ l2TunnelId = currentL2Tunnel.l2Tunnel().tunnelId();
+ // The tunnel id cannot be 0.
+ if (l2TunnelId == 0) {
+ log.warn("Tunnel id cannot be 0");
+ continue;
+ }
+ // We do a sanity check of the pseudo wire.
+ result = verifyPseudoWire(currentL2Tunnel);
+ if (result != SUCCESS) {
+ continue;
+ }
+ // We establish the tunnel.
+ result = deployPseudoWire(
+ currentL2Tunnel.l2Tunnel(),
+ currentL2Tunnel.l2TunnelPolicy().cP1(),
+ currentL2Tunnel.l2TunnelPolicy().cP2(),
+ FWD
+ );
+ if (result != SUCCESS) {
+ continue;
+ }
+ // We create the policy.
+ result = deployPolicy(
+ l2TunnelId,
+ currentL2Tunnel.l2TunnelPolicy().cP1(),
+ currentL2Tunnel.l2TunnelPolicy().cP1InnerTag(),
+ currentL2Tunnel.l2TunnelPolicy().cP1OuterTag(),
+ result.nextId
+ );
+ if (result != SUCCESS) {
+ continue;
+ }
+ // We establish the reverse tunnel.
+ result = deployPseudoWire(
+ currentL2Tunnel.l2Tunnel(),
+ currentL2Tunnel.l2TunnelPolicy().cP2(),
+ currentL2Tunnel.l2TunnelPolicy().cP1(),
+ REV
+ );
+ if (result != SUCCESS) {
+ continue;
+ }
+ deployPolicy(
+ l2TunnelId,
+ currentL2Tunnel.l2TunnelPolicy().cP2(),
+ currentL2Tunnel.l2TunnelPolicy().cP2InnerTag(),
+ currentL2Tunnel.l2TunnelPolicy().cP2OuterTag(),
+ result.nextId
+ );
+ }
}
/**
@@ -55,9 +185,161 @@
*/
public void processPwaasConfigUpdated(NetworkConfigEvent event) {
log.info("Processing Pwaas CONFIG_UPDATED");
+ // We retrieve the old pseudo wires.
+ PwaasConfig prevConfig = (PwaasConfig) event.prevConfig().get();
+ Set<Long> prevPws = prevConfig.getPwIds();
+ // We retrieve the new pseudo wires.
PwaasConfig config = (PwaasConfig) event.config().get();
- config.getPwIds().forEach(pwId -> {
- log.info("{}", config.getPwDescription(pwId));
+ Set<Long> newPws = config.getPwIds();
+ // We compute the pseudo wires to update.
+ Set<Long> updPws = newPws.stream()
+ .filter(tunnelId -> prevPws.contains(tunnelId) &&
+ !config.getPwDescription(tunnelId).equals(prevConfig.getPwDescription(tunnelId)))
+ .collect(Collectors.toSet());
+ // The pseudo wires to remove.
+ Set<DefaultL2TunnelDescription> pwToRemove = prevPws.stream()
+ .filter(tunnelId -> !newPws.contains(tunnelId))
+ .map(prevConfig::getPwDescription)
+ .collect(Collectors.toSet());
+ tearDown(pwToRemove);
+ // The pseudo wires to add.
+ Set<DefaultL2TunnelDescription> pwToAdd = newPws.stream()
+ .filter(tunnelId -> !prevPws.contains(tunnelId))
+ .map(config::getPwDescription)
+ .collect(Collectors.toSet());
+ deploy(pwToAdd);
+ // The pseudo wires to update.
+ updPws.forEach(tunnelId -> {
+ updatePw(
+ prevConfig.getPwDescription(tunnelId),
+ config.getPwDescription(tunnelId)
+ );
+ });
+ }
+
+ /**
+ * Helper function to update a pw.
+ *
+ * @param oldPw the pseudo wire to remove
+ * @param newPw the pseudo wirte to add
+ */
+ private void updatePw(DefaultL2TunnelDescription oldPw,
+ DefaultL2TunnelDescription newPw) {
+
+ long tunnelId = oldPw.l2Tunnel().tunnelId();
+ String fwdKey = generateKey(tunnelId, FWD);
+ String revKey = generateKey(tunnelId, REV);
+ Result result;
+ NextObjective fwdNextObjective;
+ NextObjective revNextObjective;
+ // The async tasks to orchestrate the next and
+ // forwarding update.
+ CompletableFuture<ObjectiveError> revPolicyFuture = new CompletableFuture<>();
+ CompletableFuture<ObjectiveError> fwdInitNextFuture = new CompletableFuture<>();
+ CompletableFuture<ObjectiveError> revInitNextFuture = new CompletableFuture<>();
+ CompletableFuture<ObjectiveError> newPwFuture = new CompletableFuture<>();
+
+ result = verifyPseudoWire(newPw);
+ if (result != SUCCESS) {
+ return;
+ }
+ if (!l2InitiationNextObjStore.containsKey(fwdKey)) {
+ log.warn("NextObj for {} does not exist in the store.", fwdKey);
+ return;
+ }
+ fwdNextObjective = l2InitiationNextObjStore.get(fwdKey).value();
+ if (!l2InitiationNextObjStore.containsKey(revKey)) {
+ log.warn("NextObj for {} does not exist in the store.", revKey);
+ return;
+ }
+ // First we remove both policy.
+ revNextObjective = l2InitiationNextObjStore.get(revKey).value();
+ log.debug("Start deleting fwd policy for {}", tunnelId);
+ deletePolicy(
+ tunnelId,
+ oldPw.l2TunnelPolicy().cP1(),
+ oldPw.l2TunnelPolicy().cP1InnerTag(),
+ oldPw.l2TunnelPolicy().cP1OuterTag(),
+ fwdNextObjective.id(),
+ revPolicyFuture
+ );
+ revPolicyFuture.thenAcceptAsync(status -> {
+ if (status == null) {
+ log.debug("Fwd policy removed. Now remove rev policy for {}", tunnelId);
+ deletePolicy(
+ tunnelId,
+ oldPw.l2TunnelPolicy().cP2(),
+ oldPw.l2TunnelPolicy().cP2InnerTag(),
+ oldPw.l2TunnelPolicy().cP2OuterTag(),
+ revNextObjective.id(),
+ fwdInitNextFuture
+ );
+ }
+ });
+ // Finally we remove both the tunnels.
+ fwdInitNextFuture.thenAcceptAsync(status -> {
+ if (status == null) {
+ log.debug("Rev policy removed. Now remove fwd pw for {}", tunnelId);
+ tearDownPseudoWire(
+ fwdKey,
+ fwdNextObjective,
+ oldPw.l2TunnelPolicy().cP1(),
+ oldPw.l2TunnelPolicy().cP2(),
+ revInitNextFuture
+ );
+ }
+ });
+ revInitNextFuture.thenAcceptAsync(status -> {
+ if (status == null) {
+ log.debug("Fwd tunnel removed. Now remove rev pw for {}", tunnelId);
+ tearDownPseudoWire(
+ revKey,
+ revNextObjective,
+ oldPw.l2TunnelPolicy().cP2(),
+ oldPw.l2TunnelPolicy().cP1(),
+ newPwFuture
+ );
+
+ }
+ });
+ // At the end we install the new pw.
+ newPwFuture.thenAcceptAsync(status -> {
+ if (status == null) {
+ log.debug("Deploying new fwd pw for {}", tunnelId);
+ Result lamdaResult = deployPseudoWire(
+ newPw.l2Tunnel(),
+ newPw.l2TunnelPolicy().cP1(),
+ newPw.l2TunnelPolicy().cP2(),
+ FWD
+ );
+ if (lamdaResult != SUCCESS) {
+ return;
+ }
+ lamdaResult = deployPolicy(
+ tunnelId,
+ newPw.l2TunnelPolicy().cP1(),
+ newPw.l2TunnelPolicy().cP1InnerTag(),
+ newPw.l2TunnelPolicy().cP1OuterTag(),
+ lamdaResult.nextId
+ );
+ log.debug("Deploying new rev pw for {}", tunnelId);
+ lamdaResult = deployPseudoWire(
+ newPw.l2Tunnel(),
+ newPw.l2TunnelPolicy().cP2(),
+ newPw.l2TunnelPolicy().cP1(),
+ REV
+ );
+ if (lamdaResult != SUCCESS) {
+ return;
+ }
+ lamdaResult = deployPolicy(
+ tunnelId,
+ newPw.l2TunnelPolicy().cP2(),
+ newPw.l2TunnelPolicy().cP2InnerTag(),
+ newPw.l2TunnelPolicy().cP2OuterTag(),
+ lamdaResult.nextId
+ );
+ }
});
}
@@ -68,9 +350,685 @@
*/
public void processPwaasConfigRemoved(NetworkConfigEvent event) {
log.info("Processing Pwaas CONFIG_REMOVED");
- PwaasConfig config = (PwaasConfig) event.config().get();
- config.getPwIds().forEach(pwId -> {
- log.info("{}", config.getPwDescription(pwId));
- });
+ PwaasConfig config = (PwaasConfig) event.prevConfig().get();
+ Set<DefaultL2TunnelDescription> pwToRemove = config.getPwIds()
+ .stream()
+ .map(config::getPwDescription)
+ .collect(Collectors.toSet());
+ // We teardown all the pseudo wire deployed
+ tearDown(pwToRemove);
}
+
+ /**
+ * Helper function to handle the pw removal.
+ *
+ * @param pwToRemove the pseudo wires to remove
+ */
+ private void tearDown(Set<DefaultL2TunnelDescription> pwToRemove) {
+ Result result;
+ int nextId;
+ NextObjective nextObjective;
+ long l2TunnelId;
+ // We remove all the pw in the configuration
+ // file.
+ for (DefaultL2TunnelDescription currentL2Tunnel : pwToRemove) {
+ l2TunnelId = currentL2Tunnel.l2Tunnel().tunnelId();
+ if (l2TunnelId == 0) {
+ log.warn("Tunnel id cannot be 0");
+ continue;
+ }
+ result = verifyPseudoWire(currentL2Tunnel);
+ if (result != SUCCESS) {
+ continue;
+ }
+ String key = generateKey(l2TunnelId, FWD);
+ if (!l2InitiationNextObjStore.containsKey(key)) {
+ log.warn("NextObj for {} does not exist in the store.", key);
+ continue;
+ }
+ nextObjective = l2InitiationNextObjStore.get(key).value();
+ nextId = nextObjective.id();
+ // First all we have to delete the policy.
+ deletePolicy(
+ l2TunnelId,
+ currentL2Tunnel.l2TunnelPolicy().cP1(),
+ currentL2Tunnel.l2TunnelPolicy().cP1InnerTag(),
+ currentL2Tunnel.l2TunnelPolicy().cP1OuterTag(),
+ nextId,
+ null
+ );
+ // Finally we will tear down the pseudo wire.
+ tearDownPseudoWire(
+ key,
+ nextObjective,
+ currentL2Tunnel.l2TunnelPolicy().cP1(),
+ currentL2Tunnel.l2TunnelPolicy().cP2(),
+ null
+ );
+ // We do the same operations on the reverse side.
+ key = generateKey(l2TunnelId, REV);
+ if (!l2InitiationNextObjStore.containsKey(key)) {
+ log.warn("NextObj for {} does not exist in the store.", key);
+ continue;
+ }
+ nextObjective = l2InitiationNextObjStore.get(key).value();
+ nextId = nextObjective.id();
+ deletePolicy(
+ l2TunnelId,
+ currentL2Tunnel.l2TunnelPolicy().cP2(),
+ currentL2Tunnel.l2TunnelPolicy().cP2InnerTag(),
+ currentL2Tunnel.l2TunnelPolicy().cP2OuterTag(),
+ nextId,
+ null
+ );
+ tearDownPseudoWire(
+ key,
+ nextObjective,
+ currentL2Tunnel.l2TunnelPolicy().cP2(),
+ currentL2Tunnel.l2TunnelPolicy().cP1(),
+ null
+ );
+ }
+
+ }
+
+ /**
+ * Helper method to verify the integrity of the pseudo wire.
+ *
+ * @param l2TunnelDescription the pseudo wire description
+ * @return the result of the check
+ */
+ private Result verifyPseudoWire(DefaultL2TunnelDescription l2TunnelDescription) {
+ Result result;
+ DefaultL2Tunnel l2Tunnel = l2TunnelDescription.l2Tunnel();
+ DefaultL2TunnelPolicy l2TunnelPolicy = l2TunnelDescription.l2TunnelPolicy();
+ result = verifyTunnel(l2Tunnel);
+ if (result != SUCCESS) {
+ log.warn("Tunnel {} did not pass the validation", l2Tunnel.tunnelId());
+ return result;
+ }
+ result = verifyPolicy(
+ l2TunnelPolicy.isAllVlan(),
+ l2TunnelPolicy.cP1InnerTag(),
+ l2TunnelPolicy.cP1OuterTag(),
+ l2TunnelPolicy.cP2InnerTag(),
+ l2TunnelPolicy.cP2OuterTag()
+ );
+ if (result != SUCCESS) {
+ log.warn("Policy for tunnel {} did not pass the validation", l2Tunnel.tunnelId());
+ return result;
+ }
+
+ return SUCCESS;
+ }
+
+ /**
+ * TODO Operation on the policies store.
+ *
+ * Handles the policy establishment which consists in
+ * create the filtering and forwarding objectives related
+ * to the initiation and termination.
+ *
+ * @param tunnelId the tunnel id
+ * @param ingress the ingress point
+ * @param ingressInner the ingress inner tag
+ * @param ingressOuter the ingress outer tag
+ * @param nextId the next objective id
+ * @return SUCCESS if the policy has been deployed.
+ * Otherwise an error according to the failure
+ * scenario.
+ */
+ private Result deployPolicy(long tunnelId,
+ ConnectPoint ingress,
+ VlanId ingressInner,
+ VlanId ingressOuter,
+ int nextId) {
+
+ ForwardingObjective.Builder fwdBuilder;
+ FilteringObjective.Builder filtBuilder;
+ List<Objective> objectives = Lists.newArrayList();
+ if (!srManager.mastershipService.isLocalMaster(ingress.deviceId())) {
+ log.info("Abort creation of policy for L2 tunnel {}: {}", tunnelId, NOT_MASTER);
+ return SUCCESS;
+ }
+ // We create the forwarding objective for supporting
+ // the l2 tunnel.
+ fwdBuilder = createFwdObjective(
+ INITIATION,
+ tunnelId,
+ ingress.port(),
+ nextId
+ );
+ // We create and add objective context.
+ ObjectiveContext context = new DefaultObjectiveContext(
+ (objective)
+ -> log.debug("FwdObj for tunnel {} populated", tunnelId),
+ (objective, error)
+ -> log.warn("Failed to populate fwdrObj for tunnel {}", tunnelId, error));
+ objectives.add(fwdBuilder.add(context));
+ // We create the filtering objective to define the
+ // permit traffic in the switch
+ filtBuilder = createFiltObjective(
+ ingress.port(),
+ ingressInner,
+ ingressOuter
+ );
+ // We add the metadata.
+ TrafficTreatment.Builder treatment = DefaultTrafficTreatment.builder()
+ .setTunnelId(tunnelId);
+ filtBuilder.withMeta(treatment.build());
+ // We create and add objective context.
+ context = new DefaultObjectiveContext(
+ (objective)
+ -> log.debug("FilterObj for tunnel {} populated", tunnelId),
+ (objective, error)
+ -> log.warn("Failed to populate filterObj for tunnel {}", tunnelId, error));
+ objectives.add(filtBuilder.add(context));
+
+ for (Objective objective : objectives) {
+ if (objective instanceof ForwardingObjective) {
+ srManager.flowObjectiveService.forward(ingress.deviceId(), (ForwardingObjective) objective);
+ log.debug("Creating new FwdObj for NextObj with id={} for tunnel {}", nextId, tunnelId);
+ } else {
+ srManager.flowObjectiveService.filter(ingress.deviceId(), (FilteringObjective) objective);
+ log.debug("Creating new FiltObj for tunnel {}", tunnelId);
+ }
+ }
+ return SUCCESS;
+ }
+
+ /**
+ * Helper method to verify if the policy is whether or not
+ * supported.
+ *
+ * @param isAllVlan all vlan mode
+ * @param ingressInner the ingress inner tag
+ * @param ingressOuter the ingress outer tag
+ * @param egressInner the egress inner tag
+ * @param egressOuter the egress outer tag
+ * @return the result of verification
+ */
+ private Result verifyPolicy(boolean isAllVlan,
+ VlanId ingressInner,
+ VlanId ingressOuter,
+ VlanId egressInner,
+ VlanId egressOuter) {
+ // AllVlan mode is not supported yet.
+ if (isAllVlan) {
+ log.warn("AllVlan not supported yet");
+ return UNSUPPORTED;
+ }
+ // The vlan tags for cP1 and cP2 have to be different from
+ // vlan none.
+ if (ingressInner.equals(VlanId.NONE) ||
+ ingressOuter.equals(VlanId.NONE) ||
+ egressInner.equals(VlanId.NONE) ||
+ egressOuter.equals(VlanId.NONE)) {
+ log.warn("The vlan tags for the connect point have to be" +
+ "different from vlan none");
+ return WRONG_PARAMETERS;
+ }
+ return SUCCESS;
+ }
+
+ /**
+ * TODO Operation on the policies store.
+ *
+ * Handles the tunnel establishment which consists in
+ * create the next objectives related to the initiation
+ * and termination.
+ *
+ * @param l2Tunnel the tunnel to deploy
+ * @param ingress the ingress connect point
+ * @param egress the egress connect point
+ * @param direction the direction of the pw
+ * @return SUCCESS if the tunnel has been created.
+ * Otherwise an error according to the failure
+ * scenario
+ */
+ private Result deployPseudoWire(DefaultL2Tunnel l2Tunnel,
+ ConnectPoint ingress,
+ ConnectPoint egress,
+ String direction) {
+ Link nextHop;
+ NextObjective.Builder nextObjectiveBuilder;
+ NextObjective nextObjective;
+ int nextId;
+ Result result;
+ if (!srManager.mastershipService.isLocalMaster(ingress.deviceId())) {
+ log.info("Abort initiation creation of L2 tunnel {}: {}",
+ l2Tunnel.tunnelId(), NOT_MASTER);
+ return SUCCESS;
+ }
+ // We need at least a path between ingress and egress.
+ nextHop = getNextHop(ingress, egress);
+ if (nextHop == null) {
+ log.warn("No path between ingress and egress");
+ return WRONG_PARAMETERS;
+ }
+ // We create the next objective without the metadata
+ // context and id. We check if it already exists in the
+ // store. If not we store as it is in the store ?
+ nextObjectiveBuilder = createNextObjective(
+ INITIATION,
+ nextHop,
+ l2Tunnel,
+ egress.deviceId()
+ );
+ if (nextObjectiveBuilder == null) {
+ return INTERNAL_ERROR;
+ }
+ // We set the metadata. We will use this metadata
+ // to inform the driver we are doing a l2 tunnel.
+ TrafficSelector metadata = DefaultTrafficSelector
+ .builder()
+ .matchTunnelId(l2Tunnel.tunnelId())
+ .build();
+ nextObjectiveBuilder.withMeta(metadata);
+ nextId = srManager.flowObjectiveService.allocateNextId();
+ if (nextId < 0) {
+ log.warn("Not able to allocate a next id for initiation");
+ return INTERNAL_ERROR;
+ }
+ nextObjectiveBuilder.withId(nextId);
+ String key = generateKey(l2Tunnel.tunnelId(), direction);
+ l2InitiationNextObjStore.put(key, nextObjectiveBuilder.add());
+ ObjectiveContext context = new DefaultObjectiveContext(
+ (objective)
+ -> log.debug("Initiation l2 tunnel rule for {} populated",
+ l2Tunnel.tunnelId()),
+ (objective, error)
+ -> log.warn("Failed to populate Initiation l2 tunnel rule for {}: {}",
+ l2Tunnel.tunnelId(), error));
+ nextObjective = nextObjectiveBuilder.add(context);
+ srManager.flowObjectiveService.next(ingress.deviceId(), nextObjective);
+ log.debug("Initiation next objective for {} not found. Creating new NextObj with id={}",
+ l2Tunnel.tunnelId(),
+ nextObjective.id()
+ );
+ result = SUCCESS;
+ result.nextId = nextObjective.id();
+ return result;
+ }
+
+ /**
+ * Helper method to verify if the tunnel is whether or not
+ * supported.
+ *
+ * @param l2Tunnel the tunnel to verify
+ * @return the result of the verification
+ */
+ private Result verifyTunnel(DefaultL2Tunnel l2Tunnel) {
+ // Service delimiting tag not supported yet.
+ if (!l2Tunnel.sdTag().equals(VlanId.NONE)) {
+ log.warn("Service delimiting tag not supported yet");
+ return UNSUPPORTED;
+ }
+ // Tag mode not supported yet.
+ if (l2Tunnel.pwMode() == TAGGED) {
+ log.warn("Tagged mode not supported yet");
+ return UNSUPPORTED;
+ }
+ // Raw mode without service delimiting tag
+ // is the only mode supported for now.
+ return SUCCESS;
+ }
+
+ /**
+ * Create the filtering objective according to a given policy.
+ *
+ * @param inPort the in port
+ * @param innerTag the inner vlan tag
+ * @param outerTag the outer vlan tag
+ * @return the filtering objective
+ */
+ private FilteringObjective.Builder createFiltObjective(PortNumber inPort,
+ VlanId innerTag,
+ VlanId outerTag) {
+ return DefaultFilteringObjective.builder()
+ .withKey(Criteria.matchInPort(inPort))
+ .addCondition(Criteria.matchInnerVlanId(innerTag))
+ .addCondition(Criteria.matchVlanId(outerTag))
+ .withPriority(SegmentRoutingService.DEFAULT_PRIORITY)
+ .permit()
+ .fromApp(srManager.appId);
+ }
+
+ /**
+ * Create the forwarding objective according to a given pipeline.
+ *
+ * @param pipeline the pipeline
+ * @param tunnelId the tunnel id
+ * @param nextId the next step
+ * @return the forwarding objective to support the pipeline.
+ */
+ private ForwardingObjective.Builder createFwdObjective(Pipeline pipeline,
+ long tunnelId,
+ PortNumber inPort,
+ int nextId) {
+ ForwardingObjective.Builder fwdBuilder = null;
+ TrafficSelector.Builder trafficSelector = DefaultTrafficSelector
+ .builder();
+ if (pipeline == INITIATION) {
+ // The flow has to match on the mpls logical
+ // port and the tunnel id.
+ trafficSelector.matchTunnelId(tunnelId);
+ trafficSelector.matchInPort(inPort);
+ fwdBuilder = DefaultForwardingObjective.builder()
+ .fromApp(srManager.appId)
+ .makePermanent()
+ .nextStep(nextId)
+ .withPriority(SegmentRoutingService.DEFAULT_PRIORITY)
+ .withSelector(trafficSelector.build())
+ .withFlag(VERSATILE);
+ }
+ return fwdBuilder;
+
+ }
+
+ /**
+ * Creates the next objective according to a given
+ * pipeline. We don't set the next id and we don't
+ * create the final meta to check if we are re-using
+ * the same next objective for different tunnels.
+ *
+ * @param pipeline the pipeline to support
+ * @param nextHop the next hop towards the destination
+ * @param l2Tunnel the tunnel to support
+ * @param egressId the egress device id
+ * @return the next objective to support the pipeline
+ */
+ private NextObjective.Builder createNextObjective(Pipeline pipeline,
+ Link nextHop,
+ DefaultL2Tunnel l2Tunnel,
+ DeviceId egressId) {
+ NextObjective.Builder nextObjBuilder;
+ TrafficTreatment.Builder treatmentBuilder = DefaultTrafficTreatment.builder();
+ if (pipeline == INITIATION) {
+ nextObjBuilder = DefaultNextObjective
+ .builder()
+ .withType(NextObjective.Type.SIMPLE)
+ .fromApp(srManager.appId);
+ // The pw label is the bottom of stack. It has to
+ // be different -1.
+ if (l2Tunnel.pwLabel().toInt() == MplsLabel.MAX_MPLS) {
+ log.warn("Pw label not configured");
+ return null;
+ }
+ treatmentBuilder.pushMpls();
+ treatmentBuilder.setMpls(l2Tunnel.pwLabel());
+ treatmentBuilder.setMplsBos(true);
+ treatmentBuilder.copyTtlOut();
+ // If the inter-co label is present we have to set the label.
+ if (l2Tunnel.interCoLabel().toInt() != MplsLabel.MAX_MPLS) {
+ treatmentBuilder.pushMpls();
+ treatmentBuilder.setMpls(l2Tunnel.interCoLabel());
+ treatmentBuilder.setMplsBos(false);
+ treatmentBuilder.copyTtlOut();
+ }
+ // We retrieve the sr label from the config
+ // using the egress leaf device id.
+ MplsLabel srLabel;
+ try {
+ srLabel = MplsLabel.mplsLabel(
+ srManager.deviceConfiguration.getIPv4SegmentId(egressId)
+ );
+ } catch (DeviceConfigNotFoundException e) {
+ log.warn("Sr label not configured");
+ return null;
+ }
+ treatmentBuilder.pushMpls();
+ treatmentBuilder.setMpls(srLabel);
+ treatmentBuilder.setMplsBos(false);
+ treatmentBuilder.copyTtlOut();
+ // We have to rewrite the src and dst mac address.
+ MacAddress ingressMac;
+ try {
+ ingressMac = srManager
+ .deviceConfiguration
+ .getDeviceMac(nextHop.src().deviceId());
+ } catch (DeviceConfigNotFoundException e) {
+ log.warn("Was not able to find the ingress mac");
+ return null;
+ }
+ treatmentBuilder.setEthSrc(ingressMac);
+ MacAddress neighborMac;
+ try {
+ neighborMac = srManager
+ .deviceConfiguration
+ .getDeviceMac(nextHop.dst().deviceId());
+ } catch (DeviceConfigNotFoundException e) {
+ log.warn("Was not able to find the neighbor mac");
+ return null;
+ }
+ treatmentBuilder.setEthDst(neighborMac);
+ } else {
+ nextObjBuilder = DefaultNextObjective
+ .builder()
+ .withType(NextObjective.Type.SIMPLE)
+ .fromApp(srManager.appId);
+
+ }
+ treatmentBuilder.setOutput(nextHop.src().port());
+ nextObjBuilder.addTreatment(treatmentBuilder.build());
+ return nextObjBuilder;
+ }
+
+ /**
+ * Returns the next hop.
+ *
+ * @param srcCp the ingress connect point
+ * @param dstCp the egress connect point
+ * @return the next hop
+ */
+ private Link getNextHop(ConnectPoint srcCp, ConnectPoint dstCp) {
+ // We retrieve a set of disjoint paths.
+ Set<DisjointPath> paths = srManager.pathService.getDisjointPaths(
+ srcCp.elementId(),
+ dstCp.elementId()
+ );
+ // We randmly pick a path.
+ if (paths.isEmpty()) {
+ return null;
+ }
+ int size = paths.size();
+ int index = RandomUtils.nextInt(0, size);
+ // We verify if the path is ok and there is not
+ // a misconfiguration.
+ List<Link> links = Iterables.get(paths, index).links();
+ checkState(links.size() == 2, WRONG_TOPOLOGY, links);
+ return links.get(0);
+ }
+
+ /**
+ * TODO Operation on the store.
+ * Deletes a given policy using the parameter supplied.
+ *
+ * @param tunnelId the tunnel id
+ * @param ingress the ingress point
+ * @param ingressInner the ingress inner vlan id
+ * @param ingressOuter the ingress outer vlan id
+ * @param nextId the next objective id
+ */
+ private void deletePolicy(long tunnelId,
+ ConnectPoint ingress,
+ VlanId ingressInner,
+ VlanId ingressOuter,
+ int nextId,
+ CompletableFuture<ObjectiveError> fwdFuture) {
+
+ ForwardingObjective.Builder fwdBuilder;
+ FilteringObjective.Builder filtBuilder;
+ List<Objective> objectives = Lists.newArrayList();
+ if (!srManager.mastershipService.isLocalMaster(ingress.deviceId())) {
+ log.info("Abort delete of policy for L2 tunnel {}: {}", tunnelId, NOT_MASTER);
+ return;
+ }
+ // We create the forwarding objective.
+ fwdBuilder = createFwdObjective(
+ INITIATION,
+ tunnelId,
+ ingress.port(),
+ nextId
+ );
+ ObjectiveContext context = new ObjectiveContext() {
+ @Override
+ public void onSuccess(Objective objective) {
+ log.debug("Previous FwdObj for policy {} removed", tunnelId);
+ if (fwdFuture != null) {
+ fwdFuture.complete(null);
+ }
+ }
+
+ @Override
+ public void onError(Objective objective, ObjectiveError error) {
+ log.warn("Failed to remove previous FwdObj for policy {}: {}", tunnelId, error);
+ if (fwdFuture != null) {
+ fwdFuture.complete(error);
+ }
+ }
+ };
+ objectives.add(fwdBuilder.remove(context));
+ // We create the filtering objective to define the
+ // permit traffic in the switch
+ filtBuilder = createFiltObjective(
+ ingress.port(),
+ ingressInner,
+ ingressOuter
+ );
+ TrafficTreatment.Builder treatment = DefaultTrafficTreatment.builder()
+ .setTunnelId(tunnelId);
+ filtBuilder.withMeta(treatment.build());
+ context = new DefaultObjectiveContext(
+ (objective)
+ -> log.debug("FilterObj for policy {} revoked", tunnelId),
+ (objective, error)
+ -> log.warn("Failed to revoke filterObj for policy {}", tunnelId, error));
+ objectives.add(filtBuilder.remove(context));
+
+ for (Objective objective : objectives) {
+ if (objective instanceof ForwardingObjective) {
+ srManager.flowObjectiveService.forward(ingress.deviceId(), (ForwardingObjective) objective);
+ } else {
+ srManager.flowObjectiveService.filter(ingress.deviceId(), (FilteringObjective) objective);
+ }
+ }
+ }
+
+ /**
+ * TODO Operation on the store.
+ * Deletes a given pseudo wire using the parameter supplied.
+ *
+ * @param key the key of the store
+ * @param nextObjective the next objective representing the pw
+ * @param ingress the ingress connect point
+ * @param egress the egress connect point
+ */
+ private void tearDownPseudoWire(String key,
+ NextObjective nextObjective,
+ ConnectPoint ingress,
+ ConnectPoint egress,
+ CompletableFuture<ObjectiveError> nextFutureForInit) {
+ if (!srManager.mastershipService.isLocalMaster(ingress.deviceId())) {
+ log.info("Abort delete of {} for {}: {}", INITIATION, key, NOT_MASTER);
+ return;
+ }
+ ObjectiveContext context = new ObjectiveContext() {
+ @Override
+ public void onSuccess(Objective objective) {
+ log.debug("Previous {} NextObj for {} removed", INITIATION, key);
+ if (nextFutureForInit != null) {
+ nextFutureForInit.complete(null);
+ }
+ }
+
+ @Override
+ public void onError(Objective objective, ObjectiveError error) {
+ log.warn("Failed to remove previous {} NextObj for {}: {}", INITIATION, key, error);
+ if (nextFutureForInit != null) {
+ nextFutureForInit.complete(error);
+ }
+ }
+ };
+ srManager.flowObjectiveService.next(
+ ingress.deviceId(),
+ (NextObjective) nextObjective.copy().remove(context)
+ );
+ l2InitiationNextObjStore.remove(key);
+ }
+
+ /**
+ * Utilities to generate pw key.
+ *
+ * @param tunnelId the tunnel id
+ * @param direction the direction of the pw
+ * @return the key of the store
+ */
+ private String generateKey(long tunnelId, String direction) {
+ return String.format("%s-%s", tunnelId, direction);
+ }
+
+ /**
+ * VPWS pipelines.
+ */
+ protected enum Pipeline {
+ /**
+ * The initiation pipeline.
+ */
+ INITIATION,
+ /**
+ * The termination pipeline.
+ */
+ TERMINATION;
+ }
+
+ /**
+ * Enum helper to carry the outcomes of an operation.
+ */
+ public enum Result {
+ /**
+ * Happy ending scenario it has been created.
+ */
+ SUCCESS(0, "It has been Created"),
+ /**
+ * We have problems with the supplied parameters.
+ */
+ WRONG_PARAMETERS(1, "Wrong parameters"),
+ /**
+ * It already exists.
+ */
+ ID_EXISTS(2, "The id already exists"),
+ /**
+ * We have an internal error during the deployment
+ * phase.
+ */
+ INTERNAL_ERROR(3, "Internal error"),
+ /**
+ * The operation is not supported.
+ */
+ UNSUPPORTED(4, "Unsupported");
+
+ private final int code;
+ private final String description;
+ private int nextId;
+
+ private Result(int code, String description) {
+ this.code = code;
+ this.description = description;
+ }
+
+ public String getDescription() {
+ return description;
+ }
+
+ public int getCode() {
+ return code;
+ }
+
+ @Override
+ public String toString() {
+ return code + ": " + description;
+ }
+ }
+
}
diff --git a/drivers/default/src/main/java/org/onosproject/driver/pipeline/Ofdpa2GroupHandler.java b/drivers/default/src/main/java/org/onosproject/driver/pipeline/Ofdpa2GroupHandler.java
index 2a4de98..9e3161a 100644
--- a/drivers/default/src/main/java/org/onosproject/driver/pipeline/Ofdpa2GroupHandler.java
+++ b/drivers/default/src/main/java/org/onosproject/driver/pipeline/Ofdpa2GroupHandler.java
@@ -37,6 +37,7 @@
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.TunnelIdCriterion;
import org.onosproject.net.flow.criteria.VlanIdCriterion;
import org.onosproject.net.flow.instructions.Instruction;
import org.onosproject.net.flow.instructions.Instructions;
@@ -80,6 +81,7 @@
import static org.onosproject.driver.pipeline.Ofdpa2GroupHandler.OfdpaMplsGroupSubType.OFDPA_GROUP_TYPE_SHIFT;
import static org.onosproject.driver.pipeline.Ofdpa2GroupHandler.OfdpaMplsGroupSubType.OFDPA_MPLS_SUBTYPE_SHIFT;
import static org.onosproject.driver.pipeline.Ofdpa2Pipeline.isNotMplsBos;
+import static org.onosproject.net.flow.criteria.Criterion.Type.TUNNEL_ID;
import static org.onosproject.net.flow.criteria.Criterion.Type.VLAN_VID;
import static org.onosproject.net.flowobjective.NextObjective.Type.HASHED;
import static org.slf4j.LoggerFactory.getLogger;
@@ -284,32 +286,50 @@
}
boolean isMpls = false;
+ // In order to understand if it is a pseudo wire related
+ // next objective we look for the tunnel id in the meta.
+ boolean isPw = false;
if (nextObj.meta() != null) {
isMpls = isNotMplsBos(nextObj.meta());
+
+ TunnelIdCriterion tunnelIdCriterion = (TunnelIdCriterion) nextObj
+ .meta()
+ .getCriterion(TUNNEL_ID);
+ if (tunnelIdCriterion != null) {
+ isPw = true;
+ }
+
}
- // break up simple next objective to GroupChain objects
- GroupInfo groupInfo = createL2L3Chain(treatment, nextObj.id(),
- nextObj.appId(), isMpls,
- nextObj.meta());
- if (groupInfo == null) {
- log.error("Could not process nextObj={} in dev:{}", nextObj.id(), deviceId);
- return;
+ if (!isPw) {
+ // break up simple next objective to GroupChain objects
+ GroupInfo groupInfo = createL2L3Chain(treatment, nextObj.id(),
+ nextObj.appId(), isMpls,
+ nextObj.meta());
+ if (groupInfo == null) {
+ log.error("Could not process nextObj={} in dev:{}", nextObj.id(), deviceId);
+ return;
+ }
+ // create object for local and distributed storage
+ Deque<GroupKey> gkeyChain = new ArrayDeque<>();
+ gkeyChain.addFirst(groupInfo.innerMostGroupDesc.appCookie());
+ gkeyChain.addFirst(groupInfo.nextGroupDesc.appCookie());
+ OfdpaNextGroup ofdpaGrp =
+ new OfdpaNextGroup(Collections.singletonList(gkeyChain), nextObj);
+
+ // store l3groupkey with the ofdpaNextGroup for the nextObjective that depends on it
+ updatePendingNextObjective(groupInfo.nextGroupDesc.appCookie(), ofdpaGrp);
+
+ // now we are ready to send the l2 groupDescription (inner), as all the stores
+ // that will get async replies have been updated. By waiting to update
+ // the stores, we prevent nasty race conditions.
+ groupService.addGroup(groupInfo.innerMostGroupDesc);
+ } else {
+ // We handle the pseudo wire with a different a procedure.
+ // This procedure is meant to handle both initiation and
+ // termination of the pseudo wire.
+ processPwNextObjective(nextObj);
}
- // create object for local and distributed storage
- Deque<GroupKey> gkeyChain = new ArrayDeque<>();
- gkeyChain.addFirst(groupInfo.innerMostGroupDesc.appCookie());
- gkeyChain.addFirst(groupInfo.nextGroupDesc.appCookie());
- OfdpaNextGroup ofdpaGrp =
- new OfdpaNextGroup(Collections.singletonList(gkeyChain), nextObj);
-
- // store l3groupkey with the ofdpaNextGroup for the nextObjective that depends on it
- updatePendingNextObjective(groupInfo.nextGroupDesc.appCookie(), ofdpaGrp);
-
- // now we are ready to send the l2 groupDescription (inner), as all the stores
- // that will get async replies have been updated. By waiting to update
- // the stores, we prevent nasty race conditions.
- groupService.addGroup(groupInfo.innerMostGroupDesc);
}
/**
@@ -387,8 +407,8 @@
* error in processing the chain
*/
protected GroupInfo createL2L3ChainInternal(TrafficTreatment treatment, int nextId,
- ApplicationId appId, boolean mpls,
- TrafficSelector meta, boolean useSetVlanExtension) {
+ ApplicationId appId, boolean mpls,
+ TrafficSelector meta, boolean useSetVlanExtension) {
// for the l2interface group, get vlan and port info
// for the outer group, get the src/dst mac, and vlan info
TrafficTreatment.Builder outerTtb = DefaultTrafficTreatment.builder();
@@ -973,6 +993,18 @@
}
}
+ /**
+ * Processes the pseudo wire related next objective.
+ * This procedure try to reuse the mpls label groups,
+ * the mpls interface group and the l2 interface group.
+ *
+ * @param nextObjective the objective to process.
+ */
+ protected void processPwNextObjective(NextObjective nextObjective) {
+ log.warn("Pseudo wire extensions are not support for the OFDPA 2.0 {}", nextObjective.id());
+ return;
+ }
+
//////////////////////////////////////
// Group Editing
//////////////////////////////////////
diff --git a/drivers/default/src/main/java/org/onosproject/driver/pipeline/Ofdpa2Pipeline.java b/drivers/default/src/main/java/org/onosproject/driver/pipeline/Ofdpa2Pipeline.java
index a49e4fa..e32f5d5 100644
--- a/drivers/default/src/main/java/org/onosproject/driver/pipeline/Ofdpa2Pipeline.java
+++ b/drivers/default/src/main/java/org/onosproject/driver/pipeline/Ofdpa2Pipeline.java
@@ -102,8 +102,12 @@
*
*/
public class Ofdpa2Pipeline extends AbstractHandlerBehaviour implements Pipeliner {
+
protected static final int PORT_TABLE = 0;
protected static final int VLAN_TABLE = 10;
+ protected static final int VLAN_1_TABLE = 11;
+ protected static final int MPLS_L2_PORT_FLOW_TABLE = 13;
+ protected static final int MPLS_L2_PORT_PCP_TRUST_FLOW_TABLE = 16;
protected static final int TMAC_TABLE = 20;
protected static final int UNICAST_ROUTING_TABLE = 30;
protected static final int MULTICAST_ROUTING_TABLE = 40;
@@ -120,6 +124,11 @@
protected static final int DEFAULT_PRIORITY = 0x8000;
protected static final int LOWEST_PRIORITY = 0x0;
+ protected static final int MPLS_L2_PORT_PRIORITY = 2;
+
+ protected static final int MPLS_TUNNEL_ID_BASE = 0x10000;
+ protected static final int MPLS_TUNNEL_ID_MAX = 0x1FFFF;
+
private final Logger log = getLogger(getClass());
protected ServiceDirectory serviceDirectory;
protected FlowRuleService flowRuleService;
@@ -225,6 +234,7 @@
rules.stream()
.filter(Objects::nonNull)
.forEach(flowOpsBuilder::remove);
+ log.debug("Deleting a flow rule to sw:{}", deviceId);
break;
default:
fail(fwd, ObjectiveError.UNKNOWN);
diff --git a/drivers/default/src/main/java/org/onosproject/driver/pipeline/Ofdpa3GroupHandler.java b/drivers/default/src/main/java/org/onosproject/driver/pipeline/Ofdpa3GroupHandler.java
index 5ba0446..1c30333 100644
--- a/drivers/default/src/main/java/org/onosproject/driver/pipeline/Ofdpa3GroupHandler.java
+++ b/drivers/default/src/main/java/org/onosproject/driver/pipeline/Ofdpa3GroupHandler.java
@@ -16,18 +16,338 @@
package org.onosproject.driver.pipeline;
+import com.google.common.collect.Lists;
+import org.onlab.packet.VlanId;
import org.onosproject.core.ApplicationId;
+import org.onosproject.core.DefaultGroupId;
+import org.onosproject.driver.extensions.Ofdpa3PushCw;
+import org.onosproject.driver.extensions.Ofdpa3PushL2Header;
+import org.onosproject.net.flow.DefaultTrafficTreatment;
import org.onosproject.net.flow.TrafficSelector;
import org.onosproject.net.flow.TrafficTreatment;
+import org.onosproject.net.flow.instructions.Instruction;
+import org.onosproject.net.flow.instructions.L2ModificationInstruction;
+import org.onosproject.net.flow.instructions.L3ModificationInstruction;
+import org.onosproject.net.flowobjective.NextObjective;
+import org.onosproject.net.flowobjective.ObjectiveError;
+import org.onosproject.net.group.DefaultGroupBucket;
+import org.onosproject.net.group.DefaultGroupDescription;
+import org.onosproject.net.group.DefaultGroupKey;
+import org.onosproject.net.group.GroupBucket;
+import org.onosproject.net.group.GroupBuckets;
+import org.onosproject.net.group.GroupDescription;
+import org.onosproject.net.group.GroupKey;
+import org.slf4j.Logger;
+
+import java.util.ArrayDeque;
+import java.util.Collections;
+import java.util.Deque;
+import java.util.List;
+
+import static org.onosproject.driver.pipeline.Ofdpa2GroupHandler.OfdpaMplsGroupSubType.*;
+import static org.onosproject.net.flow.instructions.L3ModificationInstruction.L3SubType.TTL_OUT;
+import static org.onosproject.net.group.GroupDescription.Type.INDIRECT;
+import static org.slf4j.LoggerFactory.getLogger;
/**
* Group handler for OFDPA2 pipeline.
*/
public class Ofdpa3GroupHandler extends Ofdpa2GroupHandler {
+
+ private static final int PW_INTERNAL_VLAN = 4094;
+ private static final int MAX_DEPTH_UNPROTECTED_PW = 3;
+
+ private final Logger log = getLogger(getClass());
+
@Override
protected GroupInfo createL2L3Chain(TrafficTreatment treatment, int nextId,
ApplicationId appId, boolean mpls,
TrafficSelector meta) {
return createL2L3ChainInternal(treatment, nextId, appId, mpls, meta, false);
}
+
+ @Override
+ protected void processPwNextObjective(NextObjective nextObjective) {
+ TrafficTreatment treatment = nextObjective.next().iterator().next();
+ Deque<GroupKey> gkeyChain = new ArrayDeque<>();
+ GroupChainElem groupChainElem;
+ GroupKey groupKey;
+ GroupDescription groupDescription;
+ // Now we separate the mpls actions from the l2/l3 actions
+ TrafficTreatment.Builder l2L3Treatment = DefaultTrafficTreatment.builder();
+ TrafficTreatment.Builder mplsTreatment = DefaultTrafficTreatment.builder();
+ createL2L3AndMplsTreatments(treatment, l2L3Treatment, mplsTreatment);
+ // We create the chain from mpls intf group to
+ // l2 intf group.
+ GroupInfo groupInfo = createL2L3ChainInternal(
+ l2L3Treatment.build(),
+ nextObjective.id(),
+ nextObjective.appId(),
+ true,
+ nextObjective.meta(),
+ false
+ );
+ if (groupInfo == null) {
+ log.error("Could not process nextObj={} in dev:{}", nextObjective.id(), deviceId);
+ Ofdpa2Pipeline.fail(nextObjective, ObjectiveError.GROUPINSTALLATIONFAILED);
+ return;
+ }
+ // We update the chain with the last two groups;
+ gkeyChain.addFirst(groupInfo.getInnerMostGroupDesc().appCookie());
+ gkeyChain.addFirst(groupInfo.getNextGroupDesc().appCookie());
+ // We retrieve also all mpls instructions.
+ List<List<Instruction>> mplsInstructionSets = Lists.newArrayList();
+ List<Instruction> mplsInstructionSet = Lists.newArrayList();
+ L3ModificationInstruction l3Ins;
+ for (Instruction ins : treatment.allInstructions()) {
+ // Each mpls instruction set is delimited by a
+ // copy ttl outward action.
+ mplsInstructionSet.add(ins);
+ if (ins.type() == Instruction.Type.L3MODIFICATION) {
+ l3Ins = (L3ModificationInstruction) ins;
+ if (l3Ins.subtype() == TTL_OUT) {
+ mplsInstructionSets.add(mplsInstructionSet);
+ mplsInstructionSet = Lists.newArrayList();
+ }
+
+ }
+ }
+ if (mplsInstructionSets.size() > MAX_DEPTH_UNPROTECTED_PW) {
+ log.error("Next Objective for pseudo wire should have at "
+ + "most {} mpls instruction sets. Next Objective Id:{}",
+ MAX_DEPTH_UNPROTECTED_PW, nextObjective.id());
+ Ofdpa2Pipeline.fail(nextObjective, ObjectiveError.BADPARAMS);
+ return;
+ }
+ int nextGid = groupInfo.getNextGroupDesc().givenGroupId();
+ int index;
+ // We create the mpls tunnel label groups.
+ // In this case we need to use also the
+ // tunnel label group 2;
+ if (mplsInstructionSets.size() == MAX_DEPTH_UNPROTECTED_PW) {
+ // We deal with the label 2 group.
+ index = getNextAvailableIndex();
+ groupDescription = createMplsTunnelLabelGroup(
+ nextGid,
+ MPLS_TUNNEL_LABEL_2,
+ index,
+ mplsInstructionSets.get(2),
+ nextObjective.appId()
+ );
+ groupKey = new DefaultGroupKey(
+ Ofdpa2Pipeline.appKryo.serialize(index)
+ );
+ // We update the chain.
+ groupChainElem = new GroupChainElem(groupDescription, 1, false);
+ updatePendingGroups(
+ groupInfo.getNextGroupDesc().appCookie(),
+ groupChainElem
+ );
+ gkeyChain.addFirst(groupKey);
+ // We have to create tunnel label group and
+ // l2 vpn group before to send the inner most
+ // group. We update the nextGid.
+ nextGid = groupDescription.givenGroupId();
+ groupInfo = new GroupInfo(groupInfo.getInnerMostGroupDesc(), groupDescription);
+
+ log.debug("Trying Label 2 Group: device:{} gid:{} gkey:{} nextId:{}",
+ deviceId, Integer.toHexString(nextGid),
+ groupKey, nextObjective.id());
+ }
+ // We deal with the label 1 group.
+ index = getNextAvailableIndex();
+ groupDescription = createMplsTunnelLabelGroup(
+ nextGid,
+ MPLS_TUNNEL_LABEL_1,
+ index,
+ mplsInstructionSets.get(1),
+ nextObjective.appId()
+ );
+ groupKey = new DefaultGroupKey(
+ Ofdpa2Pipeline.appKryo.serialize(index)
+ );
+ groupChainElem = new GroupChainElem(groupDescription, 1, false);
+ updatePendingGroups(
+ groupInfo.getNextGroupDesc().appCookie(),
+ groupChainElem
+ );
+ gkeyChain.addFirst(groupKey);
+ // We have to create the l2 vpn group before
+ // to send the inner most group.
+ nextGid = groupDescription.givenGroupId();
+ groupInfo = new GroupInfo(groupInfo.getInnerMostGroupDesc(), groupDescription);
+
+ log.debug("Trying Label 1 Group: device:{} gid:{} gkey:{} nextId:{}",
+ deviceId, Integer.toHexString(nextGid),
+ groupKey, nextObjective.id());
+ // Finally we create the l2 vpn group.
+ index = getNextAvailableIndex();
+ groupDescription = createMplsL2VpnGroup(
+ nextGid,
+ index,
+ mplsInstructionSets.get(0),
+ nextObjective.appId()
+ );
+ groupKey = new DefaultGroupKey(
+ Ofdpa2Pipeline.appKryo.serialize(index)
+ );
+ groupChainElem = new GroupChainElem(groupDescription, 1, false);
+ updatePendingGroups(
+ groupInfo.getNextGroupDesc().appCookie(),
+ groupChainElem
+ );
+ gkeyChain.addFirst(groupKey);
+ OfdpaNextGroup ofdpaGrp = new OfdpaNextGroup(
+ Collections.singletonList(gkeyChain),
+ nextObjective
+ );
+ updatePendingNextObjective(groupKey, ofdpaGrp);
+
+ log.debug("Trying L2 Vpn Group: device:{} gid:{} gkey:{} nextId:{}",
+ deviceId, Integer.toHexString(nextGid),
+ groupKey, nextObjective.id());
+ // Finally we send the innermost group.
+ log.debug("Sending innermost group {} in group chain on device {} ",
+ Integer.toHexString(groupInfo.getInnerMostGroupDesc().givenGroupId()), deviceId);
+ groupService.addGroup(groupInfo.getInnerMostGroupDesc());
+ }
+
+ /**
+ * Helper method to create a mpls tunnel label group.
+ *
+ * @param nextGroupId the next group in the chain
+ * @param subtype the mpls tunnel label group subtype
+ * @param index the index of the group
+ * @param instructions the instructions to push
+ * @param applicationId the application id
+ * @return the group description
+ */
+ private GroupDescription createMplsTunnelLabelGroup(int nextGroupId,
+ OfdpaMplsGroupSubType subtype,
+ int index,
+ List<Instruction> instructions,
+ ApplicationId applicationId) {
+ TrafficTreatment.Builder treatment = DefaultTrafficTreatment.builder();
+ // We add all the instructions.
+ instructions.forEach(treatment::add);
+ // We point the group to the next group.
+ treatment.group(new DefaultGroupId(nextGroupId));
+ GroupBucket groupBucket = DefaultGroupBucket
+ .createIndirectGroupBucket(treatment.build());
+ // Finally we build the group description.
+ int groupId = makeMplsLabelGroupId(subtype, index);
+ GroupKey groupKey = new DefaultGroupKey(
+ Ofdpa2Pipeline.appKryo.serialize(index)
+ );
+ return new DefaultGroupDescription(
+ deviceId,
+ INDIRECT,
+ new GroupBuckets(Collections.singletonList(groupBucket)),
+ groupKey,
+ groupId,
+ applicationId
+ );
+ }
+
+ /**
+ * Helper method to create a mpls l2 vpn group.
+ *
+ * @param nextGroupId the next group in the chain
+ * @param index the index of the group
+ * @param instructions the instructions to push
+ * @param applicationId the application id
+ * @return the group description
+ */
+ private GroupDescription createMplsL2VpnGroup(int nextGroupId,
+ int index,
+ List<Instruction> instructions,
+ ApplicationId applicationId) {
+ TrafficTreatment.Builder treatment = DefaultTrafficTreatment.builder();
+ // We add the extensions and the instructions.
+ treatment.extension(new Ofdpa3PushL2Header(), deviceId);
+ treatment.pushVlan();
+ instructions.forEach(treatment::add);
+ treatment.extension(new Ofdpa3PushCw(), deviceId);
+ // We point the group to the next group.
+ treatment.group(new DefaultGroupId(nextGroupId));
+ GroupBucket groupBucket = DefaultGroupBucket
+ .createIndirectGroupBucket(treatment.build());
+ // Finally we build the group description.
+ int groupId = makeMplsLabelGroupId(L2_VPN, index);
+ GroupKey groupKey = new DefaultGroupKey(
+ Ofdpa2Pipeline.appKryo.serialize(index)
+ );
+ return new DefaultGroupDescription(
+ deviceId,
+ INDIRECT,
+ new GroupBuckets(Collections.singletonList(groupBucket)),
+ groupKey,
+ groupId,
+ applicationId
+ );
+ }
+
+ /**
+ * Helper method for dividing the l2/l3 instructions from the mpls
+ * instructions.
+ *
+ * @param treatment the treatment to analyze
+ * @param l2L3Treatment the l2/l3 treatment builder
+ * @param mplsTreatment the mpls treatment builder
+ */
+ private void createL2L3AndMplsTreatments(TrafficTreatment treatment,
+ TrafficTreatment.Builder l2L3Treatment,
+ TrafficTreatment.Builder mplsTreatment) {
+
+ for (Instruction ins : treatment.allInstructions()) {
+
+ if (ins.type() == Instruction.Type.L2MODIFICATION) {
+ L2ModificationInstruction l2ins = (L2ModificationInstruction) ins;
+ switch (l2ins.subtype()) {
+ // These instructions have to go in the l2/l3 treatment.
+ case ETH_DST:
+ case ETH_SRC:
+ case VLAN_ID:
+ case VLAN_POP:
+ l2L3Treatment.add(ins);
+ break;
+ // These instructions have to go in the mpls treatment.
+ case MPLS_BOS:
+ case DEC_MPLS_TTL:
+ case MPLS_LABEL:
+ case MPLS_PUSH:
+ mplsTreatment.add(ins);
+ break;
+ default:
+ log.warn("Driver does not handle this type of TrafficTreatment"
+ + " instruction in nextObjectives: {} - {}",
+ ins.type(), ins);
+ break;
+ }
+ } else if (ins.type() == Instruction.Type.OUTPUT) {
+ // The output goes in the l2/l3 treatment.
+ l2L3Treatment.add(ins);
+ } else if (ins.type() == Instruction.Type.L3MODIFICATION) {
+ // We support partially the l3 instructions.
+ L3ModificationInstruction l3ins = (L3ModificationInstruction) ins;
+ switch (l3ins.subtype()) {
+ case TTL_OUT:
+ mplsTreatment.add(ins);
+ break;
+ default:
+ log.warn("Driver does not handle this type of TrafficTreatment"
+ + " instruction in nextObjectives: {} - {}",
+ ins.type(), ins);
+ }
+
+ } else {
+ log.warn("Driver does not handle this type of TrafficTreatment"
+ + " instruction in nextObjectives: {} - {}",
+ ins.type(), ins);
+ }
+ }
+ // We add in a transparent way the set vlan to 4094.
+ l2L3Treatment.setVlanId(VlanId.vlanId((short) PW_INTERNAL_VLAN));
+ }
+ // TODO Introduce in the future an inner class to return two treatments
}
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());
+ }
}