CORD-455 Implement multicast support in OFDPA driver
Also refactor Ofdpa2GroupHandler
Change-Id: Id6a9224cab663f57edb8e85a0e7d81e7da3df132
diff --git a/drivers/default/src/main/java/org/onosproject/driver/pipeline/CpqdOfdpa2Pipeline.java b/drivers/default/src/main/java/org/onosproject/driver/pipeline/CpqdOfdpa2Pipeline.java
index 955dfe7..57e0c4b 100644
--- a/drivers/default/src/main/java/org/onosproject/driver/pipeline/CpqdOfdpa2Pipeline.java
+++ b/drivers/default/src/main/java/org/onosproject/driver/pipeline/CpqdOfdpa2Pipeline.java
@@ -26,6 +26,7 @@
import java.util.concurrent.ConcurrentHashMap;
import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableSet;
import org.onlab.packet.Ethernet;
import org.onlab.packet.MacAddress;
import org.onlab.packet.IpPrefix;
@@ -99,7 +100,8 @@
// convert filtering conditions for switch-intfs into flowrules
FlowRuleOperations.Builder ops = FlowRuleOperations.builder();
for (Criterion criterion : filt.conditions()) {
- if (criterion.type() == Criterion.Type.ETH_DST) {
+ if (criterion.type() == Criterion.Type.ETH_DST ||
+ criterion.type() == Criterion.Type.ETH_DST_MASKED) {
ethCriterion = (EthCriterion) criterion;
} else if (criterion.type() == Criterion.Type.VLAN_VID) {
vidCriterion = (VlanIdCriterion) criterion;
@@ -294,6 +296,11 @@
return processEthDstOnlyFilter(ethCriterion, applicationId);
}
+ // Multicast MAC
+ if (ethCriterion.mask() != null) {
+ return processMcastEthDstFilter(ethCriterion, applicationId);
+ }
+
//handling untagged packets via assigned VLAN
if (vidCriterion.vlanId() == VlanId.NONE) {
vidCriterion = (VlanIdCriterion) Criteria.matchVlanId(assignedVlan);
@@ -416,19 +423,37 @@
*/
if (ethType.ethType().toShort() == Ethernet.TYPE_IPV4) {
IpPrefix ipv4Dst = ((IPCriterion) selector.getCriterion(Criterion.Type.IPV4_DST)).ip();
- if (ipv4Dst.prefixLength() > 0) {
- filteredSelector.matchEthType(Ethernet.TYPE_IPV4)
- .matchIPDst(ipv4Dst);
+ if (ipv4Dst.isMulticast()) {
+ if (ipv4Dst.prefixLength() != 32) {
+ log.warn("Multicast specific forwarding objective can only be /32");
+ fail(fwd, ObjectiveError.BADPARAMS);
+ return ImmutableSet.of();
+ }
+ VlanId assignedVlan = readVlanFromSelector(fwd.meta());
+ if (assignedVlan == null) {
+ log.warn("VLAN ID required by multicast specific fwd obj is missing. Abort.");
+ fail(fwd, ObjectiveError.BADPARAMS);
+ return ImmutableSet.of();
+ }
+ filteredSelector.matchVlanId(assignedVlan);
+ filteredSelector.matchEthType(Ethernet.TYPE_IPV4).matchIPDst(ipv4Dst);
+ forTableId = MULTICAST_ROUTING_TABLE;
+ log.debug("processing IPv4 multicast specific forwarding objective {} -> next:{}"
+ + " in dev:{}", fwd.id(), fwd.nextId(), deviceId);
} else {
- filteredSelector.matchEthType(Ethernet.TYPE_IPV4)
- .matchIPDst(IpPrefix.valueOf("0.0.0.0/1"));
- complementarySelector.matchEthType(Ethernet.TYPE_IPV4)
- .matchIPDst(IpPrefix.valueOf("128.0.0.0/1"));
- defaultRule = true;
+ if (ipv4Dst.prefixLength() > 0) {
+ filteredSelector.matchEthType(Ethernet.TYPE_IPV4).matchIPDst(ipv4Dst);
+ } else {
+ filteredSelector.matchEthType(Ethernet.TYPE_IPV4)
+ .matchIPDst(IpPrefix.valueOf("0.0.0.0/1"));
+ complementarySelector.matchEthType(Ethernet.TYPE_IPV4)
+ .matchIPDst(IpPrefix.valueOf("128.0.0.0/1"));
+ defaultRule = true;
+ }
+ forTableId = UNICAST_ROUTING_TABLE;
+ log.debug("processing IPv4 unicast specific forwarding objective {} -> next:{}"
+ + " in dev:{}", fwd.id(), fwd.nextId(), deviceId);
}
- forTableId = UNICAST_ROUTING_TABLE;
- log.debug("processing IPv4 specific forwarding objective {} -> next:{}"
- + " in dev:{}", fwd.id(), fwd.nextId(), deviceId);
} else {
filteredSelector
.matchEthType(Ethernet.MPLS_UNICAST)
diff --git a/drivers/default/src/main/java/org/onosproject/driver/pipeline/CpqdOfdpa2VlanPipeline.java b/drivers/default/src/main/java/org/onosproject/driver/pipeline/CpqdOfdpa2VlanPipeline.java
index f8625aa..a835f18 100644
--- a/drivers/default/src/main/java/org/onosproject/driver/pipeline/CpqdOfdpa2VlanPipeline.java
+++ b/drivers/default/src/main/java/org/onosproject/driver/pipeline/CpqdOfdpa2VlanPipeline.java
@@ -83,6 +83,11 @@
return processEthDstOnlyFilter(ethCriterion, applicationId);
}
+ // Multicast MAC
+ if (ethCriterion.mask() != null) {
+ return processMcastEthDstFilter(ethCriterion, applicationId);
+ }
+
//handling untagged packets via assigned VLAN
if (vidCriterion.vlanId() == VlanId.NONE) {
vidCriterion = (VlanIdCriterion) Criteria.matchVlanId(assignedVlan);
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 f19fa90..30a9c32 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
@@ -4,7 +4,10 @@
import com.google.common.cache.CacheBuilder;
import com.google.common.cache.RemovalCause;
import com.google.common.cache.RemovalNotification;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.Lists;
import org.onlab.osgi.ServiceDirectory;
+import org.onlab.packet.IpPrefix;
import org.onlab.packet.MacAddress;
import org.onlab.packet.MplsLabel;
import org.onlab.packet.VlanId;
@@ -74,7 +77,9 @@
* L3 VPN Groups have <4bits-9><4bits-2><24bits-index>
*/
private static final int L2_INTERFACE_TYPE = 0x00000000;
+ private static final int L3_INTERFACE_TYPE = 0x50000000;
private static final int L3_UNICAST_TYPE = 0x20000000;
+ private static final int L3_MULTICAST_TYPE = 0x60000000;
private static final int MPLS_INTERFACE_TYPE = 0x90000000;
private static final int MPLS_L3VPN_SUBTYPE = 0x92000000;
private static final int L3_ECMP_TYPE = 0x70000000;
@@ -82,6 +87,7 @@
private static final int TYPE_MASK = 0x0fffffff;
private static final int SUBTYPE_MASK = 0x00ffffff;
+ private static final int TYPE_VLAN_MASK = 0x0000ffff;
private static final int PORT_LOWER_BITS_MASK = 0x3f;
private static final long PORT_HIGHER_BITS_MASK = ~PORT_LOWER_BITS_MASK;
@@ -210,19 +216,19 @@
}
// create object for local and distributed storage
Deque<GroupKey> gkeyChain = new ArrayDeque<>();
- gkeyChain.addFirst(groupInfo.innerGrpDesc.appCookie());
- gkeyChain.addFirst(groupInfo.outerGrpDesc.appCookie());
+ 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.outerGrpDesc.appCookie(), ofdpaGrp);
+ 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.innerGrpDesc);
+ groupService.addGroup(groupInfo.innerMostGroupDesc);
}
/**
@@ -231,71 +237,30 @@
* @param nextObj the next Objective
*/
private void createL2InterfaceGroup(NextObjective nextObj) {
- // only allowed actions are vlan pop and outport
- TrafficTreatment.Builder ttb = DefaultTrafficTreatment.builder();
- PortNumber portNum = null;
- for (Instruction ins : nextObj.next().iterator().next().allInstructions()) {
- if (ins.type() == Instruction.Type.L2MODIFICATION) {
- L2ModificationInstruction l2ins = (L2ModificationInstruction) ins;
- switch (l2ins.subtype()) {
- case VLAN_POP:
- ttb.add(l2ins);
- break;
- default:
- break;
- }
- } else if (ins.type() == Instruction.Type.OUTPUT) {
- portNum = ((Instructions.OutputInstruction) ins).port();
- ttb.add(ins);
- } else {
- log.warn("Driver does not handle this type of TrafficTreatment"
- + " instruction in simple nextObjectives: {}", ins.type());
- }
- }
-
- VlanId vlanId = readVlanFromMeta(nextObj);
- if (vlanId == null) {
- log.error("Driver cannot process an L2/L3 group chain without "
- + "egress vlan information for dev: {} port:{}",
- deviceId, portNum);
+ VlanId assignedVlan = Ofdpa2Pipeline.readVlanFromSelector(nextObj.meta());
+ if (assignedVlan == null) {
+ log.warn("VLAN ID required by simple next obj is missing. Abort.");
+ Ofdpa2Pipeline.fail(nextObj, ObjectiveError.BADPARAMS);
return;
}
- // assemble information for ofdpa l2interface group
- int l2groupId = L2_INTERFACE_TYPE | (vlanId.toShort() << 16) | (int) portNum.toLong();
- // a globally unique groupkey that is different for ports in the same devices
- // but different for the same portnumber on different devices. Also different
- // for the various group-types created out of the same next objective.
- int l2gk = l2InterfaceGroupKey(deviceId, vlanId, portNum.toLong());
- final GroupKey l2groupkey = new DefaultGroupKey(Ofdpa2Pipeline.appKryo.serialize(l2gk));
+ List<GroupInfo> groupInfos = prepareL2InterfaceGroup(nextObj, assignedVlan);
- // create group description for the l2interfacegroup
- GroupBucket l2interfaceGroupBucket =
- DefaultGroupBucket.createIndirectGroupBucket(ttb.build());
- GroupDescription l2groupDescription =
- new DefaultGroupDescription(
- deviceId,
- GroupDescription.Type.INDIRECT,
- new GroupBuckets(Collections.singletonList(
- l2interfaceGroupBucket)),
- l2groupkey,
- l2groupId,
- nextObj.appId());
- log.debug("Trying L2Interface: device:{} gid:{} gkey:{} nextId:{}",
- deviceId, Integer.toHexString(l2groupId),
- l2groupkey, nextObj.id());
+ // There is only one L2 interface group in this case
+ GroupDescription l2InterfaceGroupDesc = groupInfos.get(0).innerMostGroupDesc;
- // create object for local and distributed storage
- Deque<GroupKey> singleKey = new ArrayDeque<>();
- singleKey.addFirst(l2groupkey);
- OfdpaNextGroup ofdpaGrp = new OfdpaNextGroup(
- Collections.singletonList(singleKey),
- nextObj);
+ // Put all dependency information into allGroupKeys
+ List<Deque<GroupKey>> allGroupKeys = Lists.newArrayList();
+ Deque<GroupKey> gkeyChain = new ArrayDeque<>();
+ gkeyChain.addFirst(l2InterfaceGroupDesc.appCookie());
+ allGroupKeys.add(gkeyChain);
- // store l2groupkey for the nextObjective that depends on it
- updatePendingNextObjective(l2groupkey, ofdpaGrp);
- // send the group description to the group service
- groupService.addGroup(l2groupDescription);
+ // Point the next objective to this group
+ OfdpaNextGroup ofdpaGrp = new OfdpaNextGroup(allGroupKeys, nextObj);
+ updatePendingNextObjective(l2InterfaceGroupDesc.appCookie(), ofdpaGrp);
+
+ // Start installing the inner-most group
+ groupService.addGroup(l2InterfaceGroupDesc);
}
/**
@@ -454,14 +419,14 @@
updatePendingGroups(l2groupkey, gce);
// create group description for the inner l2interfacegroup
- GroupBucket l2interfaceGroupBucket =
+ GroupBucket l2InterfaceGroupBucket =
DefaultGroupBucket.createIndirectGroupBucket(innerTtb.build());
GroupDescription l2groupDescription =
new DefaultGroupDescription(
deviceId,
GroupDescription.Type.INDIRECT,
new GroupBuckets(Collections.singletonList(
- l2interfaceGroupBucket)),
+ l2InterfaceGroupBucket)),
l2groupkey,
l2groupId,
appId);
@@ -481,21 +446,40 @@
* @param nextObj the nextObjective of type BROADCAST
*/
private void processBroadcastNextObjective(NextObjective nextObj) {
- // break up broadcast next objective to multiple groups
- Collection<TrafficTreatment> buckets = nextObj.next();
-
- VlanId vlanId = readVlanFromMeta(nextObj);
- if (vlanId == null) {
- log.warn("Required VLAN ID info in nextObj metadata but not found. Aborting");
+ VlanId assignedVlan = Ofdpa2Pipeline.readVlanFromSelector(nextObj.meta());
+ if (assignedVlan == null) {
+ log.warn("VLAN ID required by broadcast next obj is missing. Abort.");
+ Ofdpa2Pipeline.fail(nextObj, ObjectiveError.BADPARAMS);
return;
}
+ List<GroupInfo> groupInfos = prepareL2InterfaceGroup(nextObj, assignedVlan);
+
+ IpPrefix ipDst = Ofdpa2Pipeline.readIpDstFromSelector(nextObj.meta());
+ if (ipDst != null) {
+ if (ipDst.isMulticast()) {
+ createL3MulticastGroup(nextObj, assignedVlan, groupInfos);
+ } else {
+ log.warn("Broadcast NextObj with non-multicast IP address {}", nextObj);
+ Ofdpa2Pipeline.fail(nextObj, ObjectiveError.BADPARAMS);
+ return;
+ }
+ } else {
+ createL2FloodGroup(nextObj, assignedVlan, groupInfos);
+ }
+ }
+
+ private List<GroupInfo> prepareL2InterfaceGroup(NextObjective nextObj, VlanId assignedVlan) {
+ ImmutableList.Builder<GroupInfo> groupInfoBuilder = ImmutableList.builder();
+
+ // break up broadcast next objective to multiple groups
+ Collection<TrafficTreatment> buckets = nextObj.next();
+
// each treatment is converted to an L2 interface group
- List<GroupDescription> l2interfaceGroupDescs = new ArrayList<>();
- List<Deque<GroupKey>> allGroupKeys = new ArrayList<>();
for (TrafficTreatment treatment : buckets) {
TrafficTreatment.Builder newTreatment = DefaultTrafficTreatment.builder();
PortNumber portNum = null;
+ VlanId egressVlan = null;
// ensure that the only allowed treatments are pop-vlan and output
for (Instruction ins : treatment.allInstructions()) {
if (ins.type() == Instruction.Type.L2MODIFICATION) {
@@ -504,6 +488,9 @@
case VLAN_POP:
newTreatment.add(l2ins);
break;
+ case VLAN_ID:
+ egressVlan = ((L2ModificationInstruction.ModVlanIdInstruction) l2ins).vlanId();
+ break;
default:
log.debug("action {} not permitted for broadcast nextObj",
l2ins.subtype());
@@ -519,46 +506,51 @@
}
// assemble info for l2 interface group
- int l2gk = l2InterfaceGroupKey(deviceId, vlanId, portNum.toLong());
- final GroupKey l2groupkey = new DefaultGroupKey(Ofdpa2Pipeline.appKryo.serialize(l2gk));
- int l2groupId = L2_INTERFACE_TYPE | (vlanId.toShort() << 16) |
+ VlanId l2InterfaceGroupVlan =
+ (egressVlan != null && !assignedVlan.equals(egressVlan)) ?
+ egressVlan : assignedVlan;
+ int l2gk = l2InterfaceGroupKey(deviceId, l2InterfaceGroupVlan, portNum.toLong());
+ final GroupKey l2InterfaceGroupKey =
+ new DefaultGroupKey(Ofdpa2Pipeline.appKryo.serialize(l2gk));
+ int l2InterfaceGroupId = L2_INTERFACE_TYPE | (l2InterfaceGroupVlan.toShort() << 16) |
(int) portNum.toLong();
- GroupBucket l2interfaceGroupBucket =
+ GroupBucket l2InterfaceGroupBucket =
DefaultGroupBucket.createIndirectGroupBucket(newTreatment.build());
- GroupDescription l2interfaceGroupDescription =
+ GroupDescription l2InterfaceGroupDescription =
new DefaultGroupDescription(
deviceId,
GroupDescription.Type.INDIRECT,
new GroupBuckets(Collections.singletonList(
- l2interfaceGroupBucket)),
- l2groupkey,
- l2groupId,
+ l2InterfaceGroupBucket)),
+ l2InterfaceGroupKey,
+ l2InterfaceGroupId,
nextObj.appId());
log.debug("Trying L2-Interface: device:{} gid:{} gkey:{} nextid:{}",
- deviceId, Integer.toHexString(l2groupId),
- l2groupkey, nextObj.id());
+ deviceId, Integer.toHexString(l2InterfaceGroupId),
+ l2InterfaceGroupKey, nextObj.id());
- Deque<GroupKey> gkeyChain = new ArrayDeque<>();
- gkeyChain.addFirst(l2groupkey);
-
- // store the info needed to create this group
- l2interfaceGroupDescs.add(l2interfaceGroupDescription);
- allGroupKeys.add(gkeyChain);
+ groupInfoBuilder.add(new GroupInfo(l2InterfaceGroupDescription,
+ l2InterfaceGroupDescription));
}
+ return groupInfoBuilder.build();
+ }
+ private void createL2FloodGroup(NextObjective nextObj, VlanId vlanId, List<GroupInfo> groupInfos) {
// assemble info for l2 flood group
// since there can be only one flood group for a vlan, its index is always the same - 0
Integer l2floodgroupId = L2_FLOOD_TYPE | (vlanId.toShort() << 16);
int l2floodgk = getNextAvailableIndex();
final GroupKey l2floodgroupkey = new DefaultGroupKey(Ofdpa2Pipeline.appKryo.serialize(l2floodgk));
+
// collection of group buckets pointing to all the l2 interface groups
- List<GroupBucket> l2floodBuckets = new ArrayList<>();
- for (GroupDescription l2intGrpDesc : l2interfaceGroupDescs) {
+ List<GroupBucket> l2floodBuckets = Lists.newArrayList();
+ groupInfos.forEach(groupInfo -> {
+ GroupDescription l2intGrpDesc = groupInfo.nextGroupDesc;
TrafficTreatment.Builder ttb = DefaultTrafficTreatment.builder();
ttb.group(new DefaultGroupId(l2intGrpDesc.givenGroupId()));
GroupBucket abucket = DefaultGroupBucket.createAllGroupBucket(ttb.build());
l2floodBuckets.add(abucket);
- }
+ });
// create the l2flood group-description to wait for all the
// l2interface groups to be processed
GroupDescription l2floodGroupDescription =
@@ -569,28 +561,91 @@
l2floodgroupkey,
l2floodgroupId,
nextObj.appId());
- GroupChainElem gce = new GroupChainElem(l2floodGroupDescription,
- l2interfaceGroupDescs.size(),
- false);
log.debug("Trying L2-Flood: device:{} gid:{} gkey:{} nextid:{}",
deviceId, Integer.toHexString(l2floodgroupId),
l2floodgroupkey, nextObj.id());
- // create objects for local and distributed storage
- allGroupKeys.forEach(gkeyChain -> gkeyChain.addFirst(l2floodgroupkey));
- OfdpaNextGroup ofdpaGrp = new OfdpaNextGroup(allGroupKeys, nextObj);
+ // Put all dependency information into allGroupKeys
+ List<Deque<GroupKey>> allGroupKeys = Lists.newArrayList();
+ groupInfos.forEach(groupInfo -> {
+ Deque<GroupKey> gkeyChain = new ArrayDeque<>();
+ // In this case we should have L2 interface group only
+ gkeyChain.addFirst(groupInfo.nextGroupDesc.appCookie());
+ gkeyChain.addFirst(l2floodgroupkey);
+ allGroupKeys.add(gkeyChain);
+ });
- // store l2floodgroupkey with the ofdpaGroupChain for the nextObjective
- // that depends on it
+ // Point the next objective to this group
+ OfdpaNextGroup ofdpaGrp = new OfdpaNextGroup(allGroupKeys, nextObj);
updatePendingNextObjective(l2floodgroupkey, ofdpaGrp);
- for (GroupDescription l2intGrpDesc : l2interfaceGroupDescs) {
- // store all l2groupkeys with the groupChainElem for the l2floodgroup
- // that depends on it
- updatePendingGroups(l2intGrpDesc.appCookie(), gce);
- // send groups for all l2 interface groups
- groupService.addGroup(l2intGrpDesc);
- }
+ GroupChainElem gce = new GroupChainElem(l2floodGroupDescription,
+ groupInfos.size(), false);
+ groupInfos.forEach(groupInfo -> {
+ // Point this group to the next group
+ updatePendingGroups(groupInfo.nextGroupDesc.appCookie(), gce);
+ // Start installing the inner-most group
+ groupService.addGroup(groupInfo.innerMostGroupDesc);
+ });
+ }
+
+ private void createL3MulticastGroup(NextObjective nextObj, VlanId vlanId, List<GroupInfo> groupInfos) {
+ List<GroupBucket> l3McastBuckets = new ArrayList<>();
+ groupInfos.forEach(groupInfo -> {
+ // Points to L3 interface group if there is one.
+ // Otherwise points to L2 interface group directly.
+ GroupDescription nextGroupDesc = (groupInfo.nextGroupDesc != null) ?
+ groupInfo.nextGroupDesc : groupInfo.innerMostGroupDesc;
+ TrafficTreatment.Builder ttb = DefaultTrafficTreatment.builder();
+ ttb.group(new DefaultGroupId(nextGroupDesc.givenGroupId()));
+ GroupBucket abucket = DefaultGroupBucket.createAllGroupBucket(ttb.build());
+ l3McastBuckets.add(abucket);
+ });
+
+ int l3MulticastIndex = getNextAvailableIndex();
+ int l3MulticastGroupId = L3_MULTICAST_TYPE | vlanId.toShort() << 16 | (TYPE_VLAN_MASK & l3MulticastIndex);
+ final GroupKey l3MulticastGroupKey = new DefaultGroupKey(Ofdpa2Pipeline.appKryo.serialize(l3MulticastIndex));
+
+ GroupDescription l3MulticastGroupDesc = new DefaultGroupDescription(deviceId,
+ GroupDescription.Type.ALL,
+ new GroupBuckets(l3McastBuckets),
+ l3MulticastGroupKey,
+ l3MulticastGroupId,
+ nextObj.appId());
+
+ // Put all dependency information into allGroupKeys
+ List<Deque<GroupKey>> allGroupKeys = Lists.newArrayList();
+ groupInfos.forEach(groupInfo -> {
+ Deque<GroupKey> gkeyChain = new ArrayDeque<>();
+ gkeyChain.addFirst(groupInfo.innerMostGroupDesc.appCookie());
+ // Add L3 interface group to the chain if there is one.
+ if (!groupInfo.nextGroupDesc.equals(groupInfo.innerMostGroupDesc)) {
+ gkeyChain.addFirst(groupInfo.nextGroupDesc.appCookie());
+ }
+ gkeyChain.addFirst(l3MulticastGroupKey);
+ allGroupKeys.add(gkeyChain);
+ });
+
+ // Point the next objective to this group
+ OfdpaNextGroup ofdpaGrp = new OfdpaNextGroup(allGroupKeys, nextObj);
+ updatePendingNextObjective(l3MulticastGroupKey, ofdpaGrp);
+
+ GroupChainElem outerGce = new GroupChainElem(l3MulticastGroupDesc,
+ groupInfos.size(), false);
+ groupInfos.forEach(groupInfo -> {
+ // Point this group (L3 multicast) to the next group
+ updatePendingGroups(groupInfo.nextGroupDesc.appCookie(), outerGce);
+
+ // Point next group to inner-most group, if any
+ if (!groupInfo.nextGroupDesc.equals(groupInfo.innerMostGroupDesc)) {
+ GroupChainElem innerGce = new GroupChainElem(groupInfo.nextGroupDesc,
+ 1, false);
+ updatePendingGroups(groupInfo.innerMostGroupDesc.appCookie(), innerGce);
+ }
+
+ // Start installing the inner-most group
+ groupService.addGroup(groupInfo.innerMostGroupDesc);
+ });
}
/**
@@ -619,7 +674,7 @@
for (GroupInfo gi : unsentGroups) {
// create ECMP bucket to point to the outer group
TrafficTreatment.Builder ttb = DefaultTrafficTreatment.builder();
- ttb.group(new DefaultGroupId(gi.outerGrpDesc.givenGroupId()));
+ ttb.group(new DefaultGroupId(gi.nextGroupDesc.givenGroupId()));
GroupBucket sbucket = DefaultGroupBucket
.createSelectGroupBucket(ttb.build());
l3ecmpGroupBuckets.add(sbucket);
@@ -654,9 +709,9 @@
// 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.innerGrpDesc.givenGroupId()), deviceId);
- updatePendingGroups(gi.outerGrpDesc.appCookie(), l3ecmpGce);
- groupService.addGroup(gi.innerGrpDesc);
+ Integer.toHexString(gi.innerMostGroupDesc.givenGroupId()), deviceId);
+ updatePendingGroups(gi.nextGroupDesc.appCookie(), l3ecmpGce);
+ groupService.addGroup(gi.innerMostGroupDesc);
}
}
@@ -709,8 +764,8 @@
nextObj.id(), deviceId);
return;
}
- gkeyChain.addFirst(nolabelGroupInfo.innerGrpDesc.appCookie());
- gkeyChain.addFirst(nolabelGroupInfo.outerGrpDesc.appCookie());
+ gkeyChain.addFirst(nolabelGroupInfo.innerMostGroupDesc.appCookie());
+ gkeyChain.addFirst(nolabelGroupInfo.nextGroupDesc.appCookie());
// we can't send the inner group description yet, as we have to
// create the dependent ECMP group first. So we store..
@@ -732,7 +787,7 @@
.setMplsBos(true)
.copyTtlOut()
.group(new DefaultGroupId(
- onelabelGroupInfo.outerGrpDesc.givenGroupId()));
+ onelabelGroupInfo.nextGroupDesc.givenGroupId()));
GroupBucket l3vpnGrpBkt =
DefaultGroupBucket.createIndirectGroupBucket(l3vpnTtb.build());
int l3vpnIndex = getNextAvailableIndex();
@@ -749,14 +804,14 @@
l3vpngroupId,
nextObj.appId());
GroupChainElem l3vpnGce = new GroupChainElem(l3vpnGroupDesc, 1, false);
- updatePendingGroups(onelabelGroupInfo.outerGrpDesc.appCookie(), l3vpnGce);
+ updatePendingGroups(onelabelGroupInfo.nextGroupDesc.appCookie(), l3vpnGce);
- gkeyChain.addFirst(onelabelGroupInfo.innerGrpDesc.appCookie());
- gkeyChain.addFirst(onelabelGroupInfo.outerGrpDesc.appCookie());
+ gkeyChain.addFirst(onelabelGroupInfo.innerMostGroupDesc.appCookie());
+ gkeyChain.addFirst(onelabelGroupInfo.nextGroupDesc.appCookie());
gkeyChain.addFirst(l3vpngroupkey);
//now we can replace the outerGrpDesc with the one we just created
- onelabelGroupInfo.outerGrpDesc = l3vpnGroupDesc;
+ onelabelGroupInfo.nextGroupDesc = l3vpnGroupDesc;
// we can't send the innermost group yet, as we have to create
// the dependent ECMP group first. So we store ...
@@ -805,7 +860,7 @@
// now we can create the outermost L3 ECMP group bucket to add
GroupInfo gi = unsentGroups.get(0); // only one bucket, so only one group-chain
TrafficTreatment.Builder ttb = DefaultTrafficTreatment.builder();
- ttb.group(new DefaultGroupId(gi.outerGrpDesc.givenGroupId()));
+ ttb.group(new DefaultGroupId(gi.nextGroupDesc.givenGroupId()));
GroupBucket sbucket = DefaultGroupBucket.createSelectGroupBucket(ttb.build());
// recreate the original L3 ECMP group id and description
@@ -839,9 +894,9 @@
l3ecmpGroupKey, nextObjective.id());
// send the innermost group
log.debug("Sending innermost group {} in group chain on device {} ",
- Integer.toHexString(gi.innerGrpDesc.givenGroupId()), deviceId);
- updatePendingGroups(gi.outerGrpDesc.appCookie(), l3ecmpGce);
- groupService.addGroup(gi.innerGrpDesc);
+ Integer.toHexString(gi.innerMostGroupDesc.givenGroupId()), deviceId);
+ updatePendingGroups(gi.nextGroupDesc.appCookie(), l3ecmpGce);
+ groupService.addGroup(gi.innerMostGroupDesc);
}
@@ -1072,13 +1127,6 @@
}
}
- private VlanId readVlanFromMeta(NextObjective nextObj) {
- TrafficSelector metadata = nextObj.meta();
- Criterion criterion = metadata.getCriterion(Criterion.Type.VLAN_VID);
- return (criterion == null)
- ? null : ((VlanIdCriterion) criterion).vlanId();
- }
-
private int getNextAvailableIndex() {
return (int) nextIndex.incrementAndGet();
}
@@ -1106,12 +1154,22 @@
* Utility class for moving group information around.
*/
private class GroupInfo {
- private GroupDescription innerGrpDesc;
- private GroupDescription outerGrpDesc;
+ /**
+ * Description of the inner-most group of the group chain.
+ * It is always an L2 interface group.
+ */
+ private GroupDescription innerMostGroupDesc;
- GroupInfo(GroupDescription innerGrpDesc, GroupDescription outerGrpDesc) {
- this.innerGrpDesc = innerGrpDesc;
- this.outerGrpDesc = outerGrpDesc;
+ /**
+ * Description of the next group in the group chain.
+ * It can be L2 interface, L3 interface, L3 unicast, L3 VPN group.
+ * It is possible that nextGroup is the same as the innerMostGroup.
+ */
+ private GroupDescription nextGroupDesc;
+
+ GroupInfo(GroupDescription innerMostGroupDesc, GroupDescription nextGroupDesc) {
+ this.innerMostGroupDesc = innerMostGroupDesc;
+ this.nextGroupDesc = nextGroupDesc;
}
}
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 2408bb6..1492ac1 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
@@ -28,6 +28,7 @@
import java.util.concurrent.ConcurrentHashMap;
import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableSet;
import org.onlab.osgi.ServiceDirectory;
import org.onlab.packet.Ethernet;
import org.onlab.packet.IpPrefix;
@@ -297,7 +298,8 @@
// convert filtering conditions for switch-intfs into flowrules
FlowRuleOperations.Builder ops = FlowRuleOperations.builder();
for (Criterion criterion : filt.conditions()) {
- if (criterion.type() == Criterion.Type.ETH_DST) {
+ if (criterion.type() == Criterion.Type.ETH_DST ||
+ criterion.type() == Criterion.Type.ETH_DST_MASKED) {
ethCriterion = (EthCriterion) criterion;
} else if (criterion.type() == Criterion.Type.VLAN_VID) {
vidCriterion = (VlanIdCriterion) criterion;
@@ -559,6 +561,11 @@
return processEthDstOnlyFilter(ethCriterion, applicationId);
}
+ // Multicast MAC
+ if (ethCriterion.mask() != null) {
+ return processMcastEthDstFilter(ethCriterion, applicationId);
+ }
+
//handling untagged packets via assigned VLAN
if (vidCriterion.vlanId() == VlanId.NONE) {
vidCriterion = (VlanIdCriterion) Criteria.matchVlanId(assignedVlan);
@@ -635,6 +642,24 @@
return ImmutableList.<FlowRule>builder().add(rule).build();
}
+ protected List<FlowRule> processMcastEthDstFilter(EthCriterion ethCriterion,
+ ApplicationId applicationId) {
+ TrafficSelector.Builder selector = DefaultTrafficSelector.builder();
+ TrafficTreatment.Builder treatment = DefaultTrafficTreatment.builder();
+ selector.matchEthType(Ethernet.TYPE_IPV4);
+ selector.matchEthDstMasked(ethCriterion.mac(), ethCriterion.mask());
+ treatment.transition(MULTICAST_ROUTING_TABLE);
+ FlowRule rule = DefaultFlowRule.builder()
+ .forDevice(deviceId)
+ .withSelector(selector.build())
+ .withTreatment(treatment.build())
+ .withPriority(DEFAULT_PRIORITY)
+ .fromApp(applicationId)
+ .makePermanent()
+ .forTable(TMAC_TABLE).build();
+ return ImmutableList.<FlowRule>builder().add(rule).build();
+ }
+
private Collection<FlowRule> processForward(ForwardingObjective fwd) {
switch (fwd.flag()) {
case SPECIFIC:
@@ -802,19 +827,38 @@
*/
if (ethType.ethType().toShort() == Ethernet.TYPE_IPV4) {
IpPrefix ipv4Dst = ((IPCriterion) selector.getCriterion(Criterion.Type.IPV4_DST)).ip();
- if (ipv4Dst.prefixLength() > 0) {
- filteredSelector.matchEthType(Ethernet.TYPE_IPV4)
- .matchIPDst(ipv4Dst);
+ if (ipv4Dst.isMulticast()) {
+ if (ipv4Dst.prefixLength() != 32) {
+ log.warn("Multicast specific forwarding objective can only be /32");
+ fail(fwd, ObjectiveError.BADPARAMS);
+ return ImmutableSet.of();
+ }
+ VlanId assignedVlan = readVlanFromSelector(fwd.meta());
+ if (assignedVlan == null) {
+ log.warn("VLAN ID required by multicast specific fwd obj is missing. Abort.");
+ fail(fwd, ObjectiveError.BADPARAMS);
+ return ImmutableSet.of();
+ }
+ OfdpaMatchVlanVid ofdpaMatchVlanVid = new OfdpaMatchVlanVid(assignedVlan);
+ filteredSelector.extension(ofdpaMatchVlanVid, deviceId);
+ filteredSelector.matchEthType(Ethernet.TYPE_IPV4).matchIPDst(ipv4Dst);
+ forTableId = MULTICAST_ROUTING_TABLE;
+ log.debug("processing IPv4 multicast specific forwarding objective {} -> next:{}"
+ + " in dev:{}", fwd.id(), fwd.nextId(), deviceId);
} else {
- filteredSelector.matchEthType(Ethernet.TYPE_IPV4)
- .matchIPDst(IpPrefix.valueOf("0.0.0.0/1"));
- complementarySelector.matchEthType(Ethernet.TYPE_IPV4)
- .matchIPDst(IpPrefix.valueOf("128.0.0.0/1"));
- defaultRule = true;
+ if (ipv4Dst.prefixLength() > 0) {
+ filteredSelector.matchEthType(Ethernet.TYPE_IPV4).matchIPDst(ipv4Dst);
+ } else {
+ filteredSelector.matchEthType(Ethernet.TYPE_IPV4)
+ .matchIPDst(IpPrefix.valueOf("0.0.0.0/1"));
+ complementarySelector.matchEthType(Ethernet.TYPE_IPV4)
+ .matchIPDst(IpPrefix.valueOf("128.0.0.0/1"));
+ defaultRule = true;
+ }
+ forTableId = UNICAST_ROUTING_TABLE;
+ log.debug("processing IPv4 unicast specific forwarding objective {} -> next:{}"
+ + " in dev:{}", fwd.id(), fwd.nextId(), deviceId);
}
- forTableId = UNICAST_ROUTING_TABLE;
- log.debug("processing IPv4 specific forwarding objective {} -> next:{}"
- + " in dev:{}", fwd.id(), fwd.nextId(), deviceId);
if (fwd.treatment() != null) {
for (Instruction instr : fwd.treatment().allInstructions()) {
@@ -1057,4 +1101,15 @@
}
return mappings;
}
+
+ protected static VlanId readVlanFromSelector(TrafficSelector selector) {
+ Criterion criterion = selector.getCriterion(Criterion.Type.VLAN_VID);
+ return (criterion == null)
+ ? null : ((VlanIdCriterion) criterion).vlanId();
+ }
+
+ protected static IpPrefix readIpDstFromSelector(TrafficSelector selector) {
+ Criterion criterion = selector.getCriterion(Criterion.Type.IPV4_DST);
+ return (criterion == null) ? null : ((IPCriterion) criterion).ip();
+ }
}