blob: 7d6ad40ae5d2c8959924a65e62a9a0cf7017f81e [file] [log] [blame]
/*
* Copyright 2017-present Open Networking Foundation
*
* 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.pipelines.fabric.pipeliner;
import com.google.common.collect.Lists;
import org.onlab.packet.Ethernet;
import org.onlab.packet.MacAddress;
import org.onlab.packet.VlanId;
import org.onlab.util.ImmutableByteSequence;
import org.onosproject.net.DeviceId;
import org.onosproject.net.PortNumber;
import org.onosproject.net.flow.DefaultFlowRule;
import org.onosproject.net.flow.DefaultTrafficSelector;
import org.onosproject.net.flow.DefaultTrafficTreatment;
import org.onosproject.net.flow.FlowRule;
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.EthCriterion;
import org.onosproject.net.flow.criteria.PiCriterion;
import org.onosproject.net.flow.criteria.PortCriterion;
import org.onosproject.net.flow.criteria.VlanIdCriterion;
import org.onosproject.net.flowobjective.FilteringObjective;
import org.onosproject.net.flowobjective.ObjectiveError;
import org.onosproject.net.pi.runtime.PiAction;
import org.onosproject.net.pi.runtime.PiActionParam;
import org.onosproject.pipelines.fabric.FabricConstants;
import org.slf4j.Logger;
import java.util.Collection;
import static org.slf4j.LoggerFactory.getLogger;
/**
* Handling filtering objective for fabric pipeliner.
*/
public class FabricFilteringPipeliner {
private static final Logger log = getLogger(FabricFilteringPipeliner.class);
// Forwarding types
static final byte FWD_BRIDGING = 0;
static final byte FWD_MPLS = 1;
static final byte FWD_IPV4_ROUTING = 2;
static final byte FWD_IPV6_ROUTING = 3;
private static final PiCriterion VLAN_VALID = PiCriterion.builder()
.matchExact(FabricConstants.HDR_VLAN_TAG_IS_VALID, new byte[]{1})
.build();
private static final PiCriterion VLAN_INVALID = PiCriterion.builder()
.matchExact(FabricConstants.HDR_VLAN_TAG_IS_VALID, new byte[]{0})
.build();
protected DeviceId deviceId;
public FabricFilteringPipeliner(DeviceId deviceId) {
this.deviceId = deviceId;
}
/**
* Translates filtering objective to flows and groups.
*
* @param filterObjective the filtering objective
* @return translation result, contains flows, groups or error it generated
*/
public PipelinerTranslationResult filter(FilteringObjective filterObjective) {
PipelinerTranslationResult.Builder resultBuilder = PipelinerTranslationResult.builder();
// maps selector and treatment from filtering objective to filtering
// control block.
if (filterObjective.type() == FilteringObjective.Type.DENY) {
log.warn("Unsupported filtering objective type {}", filterObjective.type());
resultBuilder.setError(ObjectiveError.UNSUPPORTED);
return resultBuilder.build();
}
if (filterObjective.key() == null ||
filterObjective.key().type() != Criterion.Type.IN_PORT) {
log.warn("Unsupported filter key {}", filterObjective.key());
resultBuilder.setError(ObjectiveError.BADPARAMS);
return resultBuilder.build();
}
PortCriterion inPortCriterion = (PortCriterion) filterObjective.key();
VlanIdCriterion vlanCriterion = filterObjective.conditions().stream()
.filter(criterion -> criterion.type() == Criterion.Type.VLAN_VID)
.map(criterion -> (VlanIdCriterion) criterion)
.findFirst()
.orElse(null);
EthCriterion ethDstCriterion = filterObjective.conditions().stream()
.filter(criterion -> criterion.type() == Criterion.Type.ETH_DST)
.map(criterion -> (EthCriterion) criterion)
.findFirst()
.orElse(null);
EthCriterion ethDstMaskedCriterion = filterObjective.conditions().stream()
.filter(criterion -> criterion.type() == Criterion.Type.ETH_DST_MASKED)
.map(criterion -> (EthCriterion) criterion)
.findFirst()
.orElse(null);
FlowRule inPortVlanTableRule = createInPortVlanTable(inPortCriterion, vlanCriterion,
filterObjective);
Collection<FlowRule> fwdClassifierRules = createFwdClassifierRules(inPortCriterion, ethDstCriterion,
ethDstMaskedCriterion, filterObjective);
resultBuilder.addFlowRule(inPortVlanTableRule);
fwdClassifierRules.forEach(resultBuilder::addFlowRule);
return resultBuilder.build();
}
private FlowRule createInPortVlanTable(Criterion inPortCriterion,
VlanIdCriterion vlanCriterion,
FilteringObjective filterObjective) {
Criterion vlanIsVlalidCriterion;
TrafficSelector.Builder selector = DefaultTrafficSelector.builder()
.add(inPortCriterion);
VlanId vlanId = null;
if (vlanCriterion != null) {
vlanId = vlanCriterion.vlanId();
}
vlanIsVlalidCriterion = VLAN_VALID;
if (vlanId == null || vlanId.equals(VlanId.NONE)) {
// untag vlan, match in port only
vlanIsVlalidCriterion = VLAN_INVALID;
}
selector.add(vlanIsVlalidCriterion);
// TODO: check if this treatment is valid or not
TrafficTreatment treatment = filterObjective.meta();
if (treatment == null) {
treatment = DefaultTrafficTreatment.emptyTreatment();
}
return DefaultFlowRule.builder()
.fromApp(filterObjective.appId())
.withPriority(filterObjective.priority())
.withSelector(selector.build())
.withTreatment(treatment)
.withPriority(filterObjective.priority())
.forTable(FabricConstants.FABRIC_INGRESS_FILTERING_INGRESS_PORT_VLAN)
.forDevice(deviceId)
.makePermanent()
.build();
}
private Collection<FlowRule> createFwdClassifierRules(PortCriterion inPortCriterion,
EthCriterion ethDstCriterion,
EthCriterion ethDstMaskedCriterion,
FilteringObjective filterObjective) {
PortNumber port = inPortCriterion.port();
Collection<FlowRule> flowRules = Lists.newArrayList();
if (ethDstCriterion == null) {
if (ethDstMaskedCriterion == null) {
// Bridging table, do nothing
return flowRules;
}
// Masked fwd classifier rule
MacAddress dstMac = ethDstMaskedCriterion.mac();
MacAddress dstMacMask = ethDstMaskedCriterion.mask();
FlowRule flow = createMaskedFwdClassifierRule(port, dstMac, dstMacMask, filterObjective);
if (flow != null) {
flowRules.add(flow);
}
return flowRules;
}
MacAddress dstMac = ethDstCriterion.mac();
flowRules.addAll(createIpFwdClassifierRules(port, dstMac, filterObjective));
flowRules.add(createMplsFwdClassifierRule(port, dstMac, filterObjective));
return flowRules;
}
private FlowRule createMaskedFwdClassifierRule(PortNumber inPort, MacAddress dstMac, MacAddress dstMacMask,
FilteringObjective filterObjective) {
TrafficTreatment treatment;
short ethType;
if (dstMac.equals(MacAddress.IPV4_MULTICAST) && dstMacMask.equals(MacAddress.IPV4_MULTICAST_MASK)) {
treatment = createFwdClassifierTreatment(FWD_IPV4_ROUTING);
ethType = Ethernet.TYPE_IPV4;
} else if (dstMac.equals(MacAddress.IPV6_MULTICAST) && dstMacMask.equals(MacAddress.IPV6_MULTICAST_MASK)) {
treatment = createFwdClassifierTreatment(FWD_IPV6_ROUTING);
ethType = Ethernet.TYPE_IPV6;
} else {
log.warn("Unsupported masked fwd classifier rule. mac={}. mask={}", dstMac, dstMacMask);
return null;
}
return createFwdClassifierRule(inPort, ethType, dstMac, dstMacMask, treatment, filterObjective);
}
private Collection<FlowRule> createIpFwdClassifierRules(PortNumber inPort,
MacAddress dstMac,
FilteringObjective filterObjective) {
Collection<FlowRule> flowRules = Lists.newArrayList();
TrafficTreatment treatment;
treatment = createFwdClassifierTreatment(FWD_IPV4_ROUTING);
flowRules.add(createFwdClassifierRule(inPort, Ethernet.TYPE_IPV4, dstMac, null, treatment, filterObjective));
treatment = createFwdClassifierTreatment(FWD_IPV6_ROUTING);
flowRules.add(createFwdClassifierRule(inPort, Ethernet.TYPE_IPV6, dstMac, null, treatment, filterObjective));
return flowRules;
}
private FlowRule createMplsFwdClassifierRule(PortNumber inPort,
MacAddress dstMac,
FilteringObjective filterObjective) {
TrafficTreatment treatment = createFwdClassifierTreatment(FWD_MPLS);
return createFwdClassifierRule(inPort, Ethernet.MPLS_UNICAST, dstMac, null, treatment, filterObjective);
}
private FlowRule createFwdClassifierRule(PortNumber inPort,
short ethType,
MacAddress dstMac,
MacAddress dstMacMask,
TrafficTreatment treatment,
FilteringObjective filterObjective) {
TrafficSelector.Builder selector = DefaultTrafficSelector.builder()
.matchInPort(inPort)
.matchEthType(ethType);
if (dstMacMask != null) {
selector.matchEthDstMasked(dstMac, dstMacMask);
} else {
selector.matchEthDstMasked(dstMac, MacAddress.EXACT_MASK);
}
return DefaultFlowRule.builder()
.withSelector(selector.build())
.withTreatment(treatment)
.fromApp(filterObjective.appId())
.withPriority(filterObjective.priority())
.forDevice(deviceId)
.makePermanent()
.forTable(FabricConstants.FABRIC_INGRESS_FILTERING_FWD_CLASSIFIER)
.build();
}
private TrafficTreatment createFwdClassifierTreatment(byte fwdType) {
PiActionParam param = new PiActionParam(FabricConstants.FWD_TYPE,
ImmutableByteSequence.copyFrom(fwdType));
PiAction action = PiAction.builder()
.withId(FabricConstants.FABRIC_INGRESS_FILTERING_SET_FORWARDING_TYPE)
.withParameter(param)
.build();
return DefaultTrafficTreatment.builder()
.piTableAction(action)
.build();
}
}