[CORD-576] Implements VPWS transport in OFDPA pipelines
Changes:
- Introduces the emulation of the MPLS-ECMP groups for CPQD;
- Adds a couple of check to avoid the creation of MPLS-ECMP for OFDPA;
- Implements the VPWS transport for OFDPA3;
- Implements the VPWS tranposrt for CPQD-OFDPA2;
Change-Id: I8181fceffa35f73f549e3df07fa353642c9d6872
diff --git a/drivers/default/src/main/java/org/onosproject/driver/pipeline/CpqdOfdpa2GroupHandler.java b/drivers/default/src/main/java/org/onosproject/driver/pipeline/CpqdOfdpa2GroupHandler.java
index 39fba4f..7488b32 100644
--- a/drivers/default/src/main/java/org/onosproject/driver/pipeline/CpqdOfdpa2GroupHandler.java
+++ b/drivers/default/src/main/java/org/onosproject/driver/pipeline/CpqdOfdpa2GroupHandler.java
@@ -28,6 +28,7 @@
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.NextObjective;
import org.onosproject.net.group.DefaultGroupBucket;
import org.onosproject.net.group.DefaultGroupDescription;
import org.onosproject.net.group.DefaultGroupKey;
@@ -37,8 +38,13 @@
import org.onosproject.net.group.GroupKey;
import org.slf4j.Logger;
+import java.util.ArrayList;
import java.util.Collections;
+import java.util.Deque;
+import java.util.List;
+import static org.onosproject.driver.pipeline.Ofdpa2GroupHandler.OfdpaMplsGroupSubType.MPLS_ECMP;
+import static org.onosproject.driver.pipeline.Ofdpa2Pipeline.isNotMplsBos;
import static org.slf4j.LoggerFactory.getLogger;
/**
@@ -95,7 +101,7 @@
innerTtb.add(ins);
} else {
log.warn("Driver does not handle this type of TrafficTreatment"
- + " instruction in nextObjectives: {}", ins.type());
+ + " instruction in nextObjectives: {}", ins.type());
}
}
@@ -113,8 +119,8 @@
if (vlanid == null) {
log.error("Driver cannot process an L2/L3 group chain without "
- + "egress vlan information for dev: {} port:{}",
- deviceId, portNum);
+ + "egress vlan information for dev: {} port:{}",
+ deviceId, portNum);
return null;
}
@@ -141,7 +147,7 @@
int mplsInterfaceIndex = getNextAvailableIndex();
int mplsgroupId = MPLS_INTERFACE_TYPE | (SUBTYPE_MASK & mplsInterfaceIndex);
final GroupKey mplsgroupkey = new DefaultGroupKey(
- Ofdpa2Pipeline.appKryo.serialize(mplsInterfaceIndex));
+ Ofdpa2Pipeline.appKryo.serialize(mplsInterfaceIndex));
outerTtb.group(new DefaultGroupId(l2groupId));
// create the mpls-interface group description to wait for the
// l2 interface group to be processed
@@ -156,14 +162,14 @@
mplsgroupId,
appId);
log.debug("Trying MPLS-Interface: device:{} gid:{} gkey:{} nextid:{}",
- deviceId, Integer.toHexString(mplsgroupId),
- mplsgroupkey, nextId);
+ deviceId, Integer.toHexString(mplsgroupId),
+ mplsgroupkey, nextId);
} else {
// outer group is L3Unicast
int l3unicastIndex = getNextAvailableIndex();
int l3groupId = L3_UNICAST_TYPE | (TYPE_MASK & l3unicastIndex);
final GroupKey l3groupkey = new DefaultGroupKey(
- Ofdpa2Pipeline.appKryo.serialize(l3unicastIndex));
+ Ofdpa2Pipeline.appKryo.serialize(l3unicastIndex));
outerTtb.group(new DefaultGroupId(l2groupId));
// create the l3unicast group description to wait for the
// l2 interface group to be processed
@@ -178,8 +184,8 @@
l3groupId,
appId);
log.debug("Trying L3Unicast: device:{} gid:{} gkey:{} nextid:{}",
- deviceId, Integer.toHexString(l3groupId),
- l3groupkey, nextId);
+ deviceId, Integer.toHexString(l3groupId),
+ l3groupkey, nextId);
}
// store l2groupkey with the groupChainElem for the outer-group that depends on it
@@ -199,8 +205,81 @@
l2groupId,
appId);
log.debug("Trying L2Interface: device:{} gid:{} gkey:{} nextId:{}",
- deviceId, Integer.toHexString(l2groupId),
- l2groupkey, nextId);
+ deviceId, Integer.toHexString(l2groupId),
+ l2groupkey, nextId);
return new GroupInfo(l2groupDescription, outerGrpDesc);
}
+
+ @Override
+ public boolean verifyHashedNextObjective(NextObjective nextObjective) {
+ return true;
+ }
+
+ /**
+ * In OFDPA2 we do not support the MPLS-ECMP, while we do in
+ * CPQD implementation.
+ *
+ * @param nextObjective the hashed next objective to support.
+ */
+ @Override
+ protected void processHashedNextObjective(NextObjective nextObjective) {
+ // The case for MPLS-ECMP. For now, we try to create a MPLS-ECMP for
+ // the transport of a VPWS. The necessary info are contained in the
+ // meta selector. In particular we are looking for the case of BoS==False;
+ TrafficSelector metaSelector = nextObjective.meta();
+ if (metaSelector != null && isNotMplsBos(metaSelector)) {
+ // storage for all group keys in the chain of groups created
+ List<Deque<GroupKey>> allGroupKeys = new ArrayList<>();
+ List<GroupInfo> unsentGroups = new ArrayList<>();
+ createHashBucketChains(nextObjective, allGroupKeys, unsentGroups);
+ // now we can create the outermost MPLS ECMP group
+ List<GroupBucket> mplsEcmpGroupBuckets = new ArrayList<>();
+ for (GroupInfo gi : unsentGroups) {
+ // create ECMP bucket to point to the outer group
+ TrafficTreatment.Builder ttb = DefaultTrafficTreatment.builder();
+ ttb.group(new DefaultGroupId(gi.getNextGroupDesc().givenGroupId()));
+ GroupBucket sbucket = DefaultGroupBucket
+ .createSelectGroupBucket(ttb.build());
+ mplsEcmpGroupBuckets.add(sbucket);
+ }
+ int mplsEcmpIndex = getNextAvailableIndex();
+ int mplsEcmpGroupId = makeMplsForwardingGroupId(MPLS_ECMP, mplsEcmpIndex);
+ GroupKey mplsEmpGroupKey = new DefaultGroupKey(
+ Ofdpa2Pipeline.appKryo.serialize(mplsEcmpIndex)
+ );
+ GroupDescription mplsEcmpGroupDesc = new DefaultGroupDescription(
+ deviceId,
+ GroupDescription.Type.SELECT,
+ new GroupBuckets(mplsEcmpGroupBuckets),
+ mplsEmpGroupKey,
+ mplsEcmpGroupId,
+ nextObjective.appId()
+ );
+ GroupChainElem mplsEcmpGce = new GroupChainElem(mplsEcmpGroupDesc,
+ mplsEcmpGroupBuckets.size(),
+ false);
+
+ // create objects for local and distributed storage
+ allGroupKeys.forEach(gkeyChain -> gkeyChain.addFirst(mplsEmpGroupKey));
+ OfdpaNextGroup ofdpaGrp = new OfdpaNextGroup(allGroupKeys, nextObjective);
+
+ // store mplsEcmpGroupKey with the ofdpaGroupChain for the nextObjective
+ // that depends on it
+ updatePendingNextObjective(mplsEmpGroupKey, ofdpaGrp);
+
+ log.debug("Trying MPLS-ECMP: device:{} gid:{} gkey:{} nextId:{}",
+ deviceId, Integer.toHexString(mplsEcmpGroupId),
+ mplsEmpGroupKey, nextObjective.id());
+
+ // finally we are ready to send the innermost groups
+ for (GroupInfo gi : unsentGroups) {
+ log.debug("Sending innermost group {} in group chain on device {} ",
+ Integer.toHexString(gi.getInnerMostGroupDesc().givenGroupId()), deviceId);
+ updatePendingGroups(gi.getNextGroupDesc().appCookie(), mplsEcmpGce);
+ groupService.addGroup(gi.getInnerMostGroupDesc());
+ }
+ return;
+ }
+ super.processHashedNextObjective(nextObjective);
+ }
}
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 60fefcf..2a4de98 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
@@ -77,6 +77,11 @@
import java.util.stream.Collectors;
import static org.onlab.util.Tools.groupedThreads;
+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.VLAN_VID;
+import static org.onosproject.net.flowobjective.NextObjective.Type.HASHED;
import static org.slf4j.LoggerFactory.getLogger;
/**
@@ -185,6 +190,30 @@
groupService.addListener(new InnerGroupListener());
}
+ /**
+ * The purpose of this function is to verify if the hashed next
+ * objective is supported by the current pipeline.
+ *
+ * @param nextObjective the hashed objective to verify
+ * @return true if the hashed objective is supported. Otherwise false.
+ */
+ public boolean verifyHashedNextObjective(NextObjective nextObjective) {
+ // if it is not hashed, there is something wrong;
+ if (nextObjective.type() != HASHED) {
+ return false;
+ }
+ // The case non supported is the MPLS-ECMP. For now, we try
+ // to create a MPLS-ECMP for the transport of a VPWS. The
+ // necessary info are contained in the meta selector. In particular
+ // we are looking for the case of BoS==False;
+ TrafficSelector metaSelector = nextObjective.meta();
+ if (metaSelector != null && isNotMplsBos(metaSelector)) {
+ return false;
+ }
+
+ return true;
+ }
+
//////////////////////////////////////
// Group Creation
//////////////////////////////////////
@@ -206,6 +235,12 @@
processBroadcastNextObjective(nextObjective);
break;
case HASHED:
+ if (!verifyHashedNextObjective(nextObjective)) {
+ log.error("Next Objectives of type hashed not supported. Next Objective Id:{}",
+ nextObjective.id());
+ Ofdpa2Pipeline.fail(nextObjective, ObjectiveError.BADPARAMS);
+ return;
+ }
processHashedNextObjective(nextObjective);
break;
case FAILOVER:
@@ -248,9 +283,14 @@
return;
}
+ boolean isMpls = false;
+ if (nextObj.meta() != null) {
+ isMpls = isNotMplsBos(nextObj.meta());
+ }
+
// break up simple next objective to GroupChain objects
GroupInfo groupInfo = createL2L3Chain(treatment, nextObj.id(),
- nextObj.appId(), false,
+ nextObj.appId(), isMpls,
nextObj.meta());
if (groupInfo == null) {
log.error("Could not process nextObj={} in dev:{}", nextObj.id(), deviceId);
@@ -404,7 +444,7 @@
if (vlanid == null && meta != null) {
// use metadata if available
- Criterion vidCriterion = meta.getCriterion(Criterion.Type.VLAN_VID);
+ Criterion vidCriterion = meta.getCriterion(VLAN_VID);
if (vidCriterion != null) {
vlanid = ((VlanIdCriterion) vidCriterion).vlanId();
}
@@ -746,7 +786,7 @@
*
* @param nextObj the nextObjective of type HASHED
*/
- private void processHashedNextObjective(NextObjective nextObj) {
+ protected void processHashedNextObjective(NextObjective nextObj) {
// storage for all group keys in the chain of groups created
List<Deque<GroupKey>> allGroupKeys = new ArrayList<>();
List<GroupInfo> unsentGroups = new ArrayList<>();
@@ -812,7 +852,7 @@
* @param allGroupKeys a list to store groupKey for each bucket-group-chain
* @param unsentGroups a list to store GroupInfo for each bucket-group-chain
*/
- private void createHashBucketChains(NextObjective nextObj,
+ protected void createHashBucketChains(NextObjective nextObj,
List<Deque<GroupKey>> allGroupKeys,
List<GroupInfo> unsentGroups) {
// break up hashed next objective to multiple groups
@@ -839,9 +879,23 @@
Deque<GroupKey> gkeyChain = new ArrayDeque<>();
// XXX we only deal with 0 and 1 label push right now
if (labelsPushed == 0) {
- GroupInfo nolabelGroupInfo = createL2L3Chain(bucket, nextObj.id(),
- nextObj.appId(), false,
- nextObj.meta());
+ GroupInfo nolabelGroupInfo;
+ TrafficSelector metaSelector = nextObj.meta();
+ if (metaSelector != null) {
+ if (isNotMplsBos(metaSelector)) {
+ nolabelGroupInfo = createL2L3Chain(bucket, nextObj.id(),
+ nextObj.appId(), true,
+ nextObj.meta());
+ } else {
+ nolabelGroupInfo = createL2L3Chain(bucket, nextObj.id(),
+ nextObj.appId(), false,
+ nextObj.meta());
+ }
+ } else {
+ nolabelGroupInfo = createL2L3Chain(bucket, nextObj.id(),
+ nextObj.appId(), false,
+ nextObj.meta());
+ }
if (nolabelGroupInfo == null) {
log.error("Could not process nextObj={} in dev:{}",
nextObj.id(), deviceId);
@@ -1326,7 +1380,7 @@
// Helper Methods and Classes
//////////////////////////////////////
- private void updatePendingNextObjective(GroupKey key, OfdpaNextGroup value) {
+ protected void updatePendingNextObjective(GroupKey key, OfdpaNextGroup value) {
List<OfdpaNextGroup> nextList = new CopyOnWriteArrayList<OfdpaNextGroup>();
nextList.add(value);
List<OfdpaNextGroup> ret = pendingAddNextObjectives.asMap()
@@ -1489,8 +1543,7 @@
* @param portNumber Port number
* @return L2 interface group key
*/
- protected int l2InterfaceGroupKey(
- DeviceId deviceId, VlanId vlanId, long portNumber) {
+ protected int l2InterfaceGroupKey(DeviceId deviceId, VlanId vlanId, long portNumber) {
int portLowerBits = (int) portNumber & PORT_LOWER_BITS_MASK;
long portHigherBits = portNumber & PORT_HIGHER_BITS_MASK;
int hash = Objects.hash(deviceId, vlanId, portHigherBits);
@@ -1544,6 +1597,24 @@
this.innerMostGroupDesc = innerMostGroupDesc;
this.nextGroupDesc = nextGroupDesc;
}
+
+ /**
+ * Getter for innerMostGroupDesc.
+ *
+ * @return the inner most group description
+ */
+ public GroupDescription getInnerMostGroupDesc() {
+ return innerMostGroupDesc;
+ }
+
+ /**
+ * Getter for the next group description.
+ *
+ * @return the next group description
+ */
+ public GroupDescription getNextGroupDesc() {
+ return nextGroupDesc;
+ }
}
/**
@@ -1628,4 +1699,70 @@
" device: " + deviceId);
}
}
+
+ /**
+ * Helper enum to handle the different MPLS group
+ * types.
+ */
+ protected enum OfdpaMplsGroupSubType {
+
+ MPLS_INTF((short) 0),
+
+ L2_VPN((short) 1),
+
+ L3_VPN((short) 2),
+
+ MPLS_TUNNEL_LABEL_1((short) 3),
+
+ MPLS_TUNNEL_LABEL_2((short) 4),
+
+ MPLS_SWAP_LABEL((short) 5),
+
+ MPLS_ECMP((short) 8);
+
+ private short value;
+
+ public static final int OFDPA_GROUP_TYPE_SHIFT = 28;
+ public static final int OFDPA_MPLS_SUBTYPE_SHIFT = 24;
+
+ OfdpaMplsGroupSubType(short value) {
+ this.value = value;
+ }
+
+ /**
+ * Gets the value as an short.
+ *
+ * @return the value as an short
+ */
+ public short getValue() {
+ return this.value;
+ }
+
+ }
+
+ /**
+ * Creates MPLS Label group id given a sub type and
+ * the index.
+ *
+ * @param subType the MPLS Label group sub type
+ * @param index the index of the group
+ * @return the OFDPA group id
+ */
+ public Integer makeMplsLabelGroupId(OfdpaMplsGroupSubType subType, int index) {
+ index = index & 0x00FFFFFF;
+ return index | (9 << OFDPA_GROUP_TYPE_SHIFT) | (subType.value << OFDPA_MPLS_SUBTYPE_SHIFT);
+ }
+
+ /**
+ * Creates MPLS Forwarding group id given a sub type and
+ * the index.
+ *
+ * @param subType the MPLS forwarding group sub type
+ * @param index the index of the group
+ * @return the OFDPA group id
+ */
+ public Integer makeMplsForwardingGroupId(OfdpaMplsGroupSubType subType, int index) {
+ index = index & 0x00FFFFFF;
+ return index | (10 << OFDPA_GROUP_TYPE_SHIFT) | (subType.value << OFDPA_MPLS_SUBTYPE_SHIFT);
+ }
}
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 a12b76f..a49e4fa 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
@@ -94,6 +94,8 @@
import static org.onlab.packet.MacAddress.NONE;
import static org.onlab.util.Tools.groupedThreads;
import static org.slf4j.LoggerFactory.getLogger;
+import static org.onosproject.net.flow.criteria.Criterion.Type.MPLS_BOS;
+import static org.onosproject.net.flowobjective.NextObjective.Type.HASHED;
/**
* Driver for Broadcom's OF-DPA v2.0 TTP.
@@ -107,7 +109,8 @@
protected static final int MULTICAST_ROUTING_TABLE = 40;
protected static final int MPLS_TABLE_0 = 23;
protected static final int MPLS_TABLE_1 = 24;
- protected static final int MPLS_L3_TYPE = 27;
+ protected static final int MPLS_L3_TYPE_TABLE = 27;
+ protected static final int MPLS_TYPE_TABLE = 29;
protected static final int BRIDGING_TABLE = 50;
protected static final int ACL_TABLE = 60;
protected static final int MAC_LEARNING_TABLE = 254;
@@ -750,8 +753,6 @@
return Collections.emptySet();
}
-
-
/**
* In the OF-DPA 2.0 pipeline, versatile forwarding objectives go to the
* ACL table.
@@ -1018,7 +1019,7 @@
.matchMplsLabel(((MplsCriterion)
selector.getCriterion(Criterion.Type.MPLS_LABEL)).label());
MplsBosCriterion bos = (MplsBosCriterion) selector
- .getCriterion(Criterion.Type.MPLS_BOS);
+ .getCriterion(MPLS_BOS);
if (bos != null) {
filteredSelector.matchMplsBos(bos.mplsBos());
}
@@ -1064,6 +1065,13 @@
List<Deque<GroupKey>> gkeys = appKryo.deserialize(next.data());
// we only need the top level group's key to point the flow to it
Group group = groupService.getGroup(deviceId, gkeys.get(0).peekFirst());
+ if (isNotMplsBos(selector) && group.type().equals(HASHED)) {
+ log.warn("SR CONTINUE case cannot be handled as MPLS ECMP "
+ + "is not implemented in OF-DPA yet. Aborting this flow {} -> next:{}"
+ + "in this device {}", fwd.id(), fwd.nextId(), deviceId);
+ fail(fwd, ObjectiveError.FLOWINSTALLATIONFAILED);
+ return Collections.emptySet();
+ }
if (group == null) {
log.warn("Group with key:{} for next-id:{} not found in dev:{}",
gkeys.get(0).peekFirst(), fwd.nextId(), deviceId);
@@ -1086,7 +1094,7 @@
}
if (forTableId == MPLS_TABLE_1) {
- if (mplsNextTable == MPLS_L3_TYPE) {
+ if (mplsNextTable == MPLS_L3_TYPE_TABLE) {
Ofdpa3SetMplsType setMplsType = new Ofdpa3SetMplsType(Ofdpa3MplsType.L3_PHP);
// set mpls type as apply_action
tb.immediate().extension(setMplsType, deviceId);
@@ -1310,6 +1318,16 @@
return mappings;
}
+ static boolean isMplsBos(TrafficSelector selector) {
+ MplsBosCriterion bosCriterion = (MplsBosCriterion) selector.getCriterion(MPLS_BOS);
+ return bosCriterion != null && bosCriterion.mplsBos();
+ }
+
+ static boolean isNotMplsBos(TrafficSelector selector) {
+ MplsBosCriterion bosCriterion = (MplsBosCriterion) selector.getCriterion(MPLS_BOS);
+ return bosCriterion != null && !bosCriterion.mplsBos();
+ }
+
protected static VlanId readVlanFromSelector(TrafficSelector selector) {
if (selector == null) {
return null;
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 966933b..895c187 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
@@ -54,6 +54,9 @@
@Override
protected Collection<FlowRule> processEthTypeSpecific(ForwardingObjective fwd) {
- return processEthTypeSpecificInternal(fwd, true, MPLS_L3_TYPE);
+ if (isNotMplsBos(fwd.selector())) {
+ return processEthTypeSpecificInternal(fwd, true, MPLS_TYPE_TABLE);
+ }
+ return processEthTypeSpecificInternal(fwd, true, MPLS_L3_TYPE_TABLE);
}
}