| /* |
| * 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; |
| |
| 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 |
| } |