| /* |
| * Copyright 2016-present Open Networking Laboratory |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| package org.onosproject.driver.pipeline.ofdpa; |
| |
| import org.onlab.packet.MacAddress; |
| import org.onlab.packet.VlanId; |
| import org.onosproject.core.ApplicationId; |
| import org.onosproject.core.GroupId; |
| import org.onosproject.net.flow.DefaultTrafficTreatment; |
| import org.onosproject.net.flow.TrafficSelector; |
| import org.onosproject.net.flow.TrafficTreatment; |
| import org.onosproject.net.flow.criteria.Criterion; |
| import org.onosproject.net.flow.criteria.VlanIdCriterion; |
| 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; |
| 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.ArrayList; |
| import java.util.Collections; |
| import java.util.Deque; |
| import java.util.List; |
| |
| import static org.onosproject.driver.pipeline.ofdpa.OfdpaGroupHandlerUtility.*; |
| import static org.slf4j.LoggerFactory.getLogger; |
| |
| /** |
| * Group handler for CpqD OFDPA pipeline. |
| */ |
| public class CpqdOfdpa2GroupHandler extends Ofdpa2GroupHandler { |
| private final Logger log = getLogger(getClass()); |
| |
| @Override |
| protected GroupInfo createL2L3Chain(TrafficTreatment treatment, int nextId, |
| ApplicationId appId, boolean mpls, |
| TrafficSelector meta) { |
| // 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(); |
| TrafficTreatment.Builder innerTtb = DefaultTrafficTreatment.builder(); |
| VlanId vlanid = null; |
| long portNum = 0; |
| boolean setVlan = false, popVlan = false; |
| MacAddress srcMac = MacAddress.ZERO; |
| MacAddress dstMac = MacAddress.ZERO; |
| for (Instruction ins : treatment.allInstructions()) { |
| if (ins.type() == Instruction.Type.L2MODIFICATION) { |
| L2ModificationInstruction l2ins = (L2ModificationInstruction) ins; |
| switch (l2ins.subtype()) { |
| case ETH_DST: |
| dstMac = ((L2ModificationInstruction.ModEtherInstruction) l2ins).mac(); |
| outerTtb.setEthDst(dstMac); |
| break; |
| case ETH_SRC: |
| srcMac = ((L2ModificationInstruction.ModEtherInstruction) l2ins).mac(); |
| outerTtb.setEthSrc(srcMac); |
| break; |
| case VLAN_ID: |
| vlanid = ((L2ModificationInstruction.ModVlanIdInstruction) l2ins).vlanId(); |
| outerTtb.setVlanId(vlanid); |
| setVlan = true; |
| break; |
| case VLAN_POP: |
| innerTtb.popVlan(); |
| popVlan = true; |
| break; |
| case DEC_MPLS_TTL: |
| case MPLS_LABEL: |
| case MPLS_POP: |
| case MPLS_PUSH: |
| case VLAN_PCP: |
| case VLAN_PUSH: |
| default: |
| break; |
| } |
| } else if (ins.type() == Instruction.Type.OUTPUT) { |
| portNum = ((Instructions.OutputInstruction) ins).port().toLong(); |
| innerTtb.add(ins); |
| } else { |
| log.warn("Driver does not handle this type of TrafficTreatment" |
| + " instruction in nextObjectives: {}", ins.type()); |
| } |
| } |
| |
| if (vlanid == null && meta != null) { |
| // use metadata if available |
| Criterion vidCriterion = meta.getCriterion(Criterion.Type.VLAN_VID); |
| if (vidCriterion != null) { |
| vlanid = ((VlanIdCriterion) vidCriterion).vlanId(); |
| } |
| // if vlan is not set, use the vlan in metadata for outerTtb |
| if (vlanid != null && !setVlan) { |
| outerTtb.setVlanId(vlanid); |
| } |
| } |
| |
| if (vlanid == null) { |
| log.error("Driver cannot process an L2/L3 group chain without " |
| + "egress vlan information for dev: {} port:{}", |
| deviceId, portNum); |
| return null; |
| } |
| |
| if (!setVlan && !popVlan) { |
| // untagged outgoing port |
| TrafficTreatment.Builder temp = DefaultTrafficTreatment.builder(); |
| temp.popVlan(); |
| innerTtb.build().allInstructions().forEach(i -> temp.add(i)); |
| innerTtb = temp; |
| } |
| |
| // assemble information for ofdpa l2interface group |
| int l2groupId = L2_INTERFACE_TYPE | (vlanid.toShort() << 16) | (int) portNum; |
| // a globally unique groupkey that is different for ports in the same device, |
| // 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); |
| final GroupKey l2groupkey = new DefaultGroupKey(Ofdpa2Pipeline.appKryo.serialize(l2gk)); |
| |
| // assemble information for outer group |
| GroupDescription outerGrpDesc = null; |
| if (mpls) { |
| // outer group is MPLSInteface |
| int mplsInterfaceIndex = getNextAvailableIndex(); |
| int mplsgroupId = MPLS_INTERFACE_TYPE | (SUBTYPE_MASK & mplsInterfaceIndex); |
| final GroupKey mplsgroupkey = new DefaultGroupKey( |
| Ofdpa2Pipeline.appKryo.serialize(mplsInterfaceIndex)); |
| outerTtb.group(new GroupId(l2groupId)); |
| // create the mpls-interface group description to wait for the |
| // l2 interface group to be processed |
| GroupBucket mplsinterfaceGroupBucket = |
| DefaultGroupBucket.createIndirectGroupBucket(outerTtb.build()); |
| outerGrpDesc = new DefaultGroupDescription( |
| deviceId, |
| GroupDescription.Type.INDIRECT, |
| new GroupBuckets(Collections.singletonList( |
| mplsinterfaceGroupBucket)), |
| mplsgroupkey, |
| mplsgroupId, |
| appId); |
| log.debug("Trying MPLS-Interface: device:{} gid:{} gkey:{} 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)); |
| outerTtb.group(new GroupId(l2groupId)); |
| // create the l3unicast group description to wait for the |
| // l2 interface group to be processed |
| GroupBucket l3unicastGroupBucket = |
| DefaultGroupBucket.createIndirectGroupBucket(outerTtb.build()); |
| outerGrpDesc = new DefaultGroupDescription( |
| deviceId, |
| GroupDescription.Type.INDIRECT, |
| new GroupBuckets(Collections.singletonList( |
| l3unicastGroupBucket)), |
| l3groupkey, |
| l3groupId, |
| appId); |
| log.debug("Trying L3Unicast: device:{} gid:{} gkey:{} nextid:{}", |
| deviceId, Integer.toHexString(l3groupId), |
| l3groupkey, nextId); |
| } |
| |
| // store l2groupkey with the groupChainElem for the outer-group that depends on it |
| GroupChainElem gce = new GroupChainElem(outerGrpDesc, |
| 1, |
| false, |
| deviceId); |
| updatePendingGroups(l2groupkey, gce); |
| |
| // create group description for the inner l2interfacegroup |
| GroupBucket l2InterfaceGroupBucket = |
| DefaultGroupBucket.createIndirectGroupBucket(innerTtb.build()); |
| GroupDescription l2groupDescription = |
| new DefaultGroupDescription( |
| deviceId, |
| GroupDescription.Type.INDIRECT, |
| new GroupBuckets(Collections.singletonList( |
| l2InterfaceGroupBucket)), |
| l2groupkey, |
| l2groupId, |
| appId); |
| log.debug("Trying L2Interface: device:{} gid:{} gkey:{} nextId:{}", |
| deviceId, Integer.toHexString(l2groupId), |
| l2groupkey, nextId); |
| return new GroupInfo(l2groupDescription, outerGrpDesc); |
| } |
| |
| /** |
| * 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 && Ofdpa2Pipeline.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 GroupId(gi.nextGroupDesc().givenGroupId())); |
| GroupBucket sbucket = DefaultGroupBucket |
| .createSelectGroupBucket(ttb.build()); |
| mplsEcmpGroupBuckets.add(sbucket); |
| } |
| int mplsEcmpIndex = getNextAvailableIndex(); |
| int mplsEcmpGroupId = makeMplsForwardingGroupId(OfdpaMplsGroupSubType.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, |
| deviceId); |
| |
| // 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.innerMostGroupDesc().givenGroupId()), deviceId); |
| updatePendingGroups(gi.nextGroupDesc().appCookie(), mplsEcmpGce); |
| groupService.addGroup(gi.innerMostGroupDesc()); |
| } |
| return; |
| } |
| super.processHashedNextObjective(nextObjective); |
| } |
| } |