blob: 1c3033383a4ac2885d4139f629dba5ddd2af6a82 [file] [log] [blame]
/*
* 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
}