blob: 67d7b3a7f92a130ce02431131a9d74f8f3fec5ce [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.onosproject.net.DeviceId;
import org.onosproject.net.PortNumber;
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.FabricCapabilities;
import org.onosproject.pipelines.fabric.FabricConstants;
import java.util.Collection;
import java.util.List;
import static java.lang.String.format;
import static org.onosproject.pipelines.fabric.FabricUtils.criterion;
/**
* ObjectiveTranslator implementation for FilteringObjective.
*/
class FilteringObjectiveTranslator
extends AbstractObjectiveTranslator<FilteringObjective> {
// Forwarding types from fabric.p4.
static final byte FWD_MPLS = 1;
static final byte FWD_IPV4_ROUTING = 2;
static final byte FWD_IPV6_ROUTING = 3;
private static final byte[] ONE = new byte[]{1};
private static final byte[] ZERO = new byte[]{0};
private static final PiAction DENY = PiAction.builder()
.withId(FabricConstants.FABRIC_INGRESS_FILTERING_DENY)
.build();
private static final PiCriterion VLAN_VALID = PiCriterion.builder()
.matchExact(FabricConstants.HDR_VLAN_IS_VALID, ONE)
.build();
private static final PiCriterion VLAN_INVALID = PiCriterion.builder()
.matchExact(FabricConstants.HDR_VLAN_IS_VALID, ZERO)
.build();
FilteringObjectiveTranslator(DeviceId deviceId, FabricCapabilities capabilities) {
super(deviceId, capabilities);
}
@Override
public ObjectiveTranslation doTranslate(FilteringObjective obj)
throws FabricPipelinerException {
final ObjectiveTranslation.Builder resultBuilder =
ObjectiveTranslation.builder();
if (obj.key() == null || obj.key().type() != Criterion.Type.IN_PORT) {
throw new FabricPipelinerException(
format("Unsupported or missing filtering key: key=%s", obj.key()),
ObjectiveError.BADPARAMS);
}
final PortCriterion inPort = (PortCriterion) obj.key();
final VlanIdCriterion vlan = (VlanIdCriterion) criterion(
obj.conditions(), Criterion.Type.VLAN_VID);
final EthCriterion ethDst = (EthCriterion) criterion(
obj.conditions(), Criterion.Type.ETH_DST);
final EthCriterion ethDstMasked = (EthCriterion) criterion(
obj.conditions(), Criterion.Type.ETH_DST_MASKED);
ingressPortVlanRule(obj, inPort, vlan, resultBuilder);
fwdClassifierRules(obj, inPort, ethDst, ethDstMasked, resultBuilder);
return resultBuilder.build();
}
private void ingressPortVlanRule(
FilteringObjective obj,
Criterion inPortCriterion,
VlanIdCriterion vlanCriterion,
ObjectiveTranslation.Builder resultBuilder)
throws FabricPipelinerException {
final boolean vlanValid = vlanCriterion != null
&& !vlanCriterion.vlanId().equals(VlanId.NONE);
final TrafficSelector.Builder selector = DefaultTrafficSelector.builder()
.add(inPortCriterion)
.add(vlanValid ? VLAN_VALID : VLAN_INVALID);
if (vlanValid) {
selector.add(vlanCriterion);
}
final TrafficTreatment treatment;
if (obj.type().equals(FilteringObjective.Type.DENY)) {
treatment = DefaultTrafficTreatment.builder()
.piTableAction(DENY)
.build();
} else {
treatment = obj.meta() == null
? DefaultTrafficTreatment.emptyTreatment() : obj.meta();
}
resultBuilder.addFlowRule(flowRule(
obj, FabricConstants.FABRIC_INGRESS_FILTERING_INGRESS_PORT_VLAN,
selector.build(), treatment));
}
private void fwdClassifierRules(
FilteringObjective obj,
PortCriterion inPortCriterion,
EthCriterion ethDstCriterion,
EthCriterion ethDstMaskedCriterion,
ObjectiveTranslation.Builder resultBuilder)
throws FabricPipelinerException {
final List<FlowRule> flowRules = Lists.newArrayList();
final PortNumber inPort = inPortCriterion.port();
if (ethDstCriterion == null) {
if (ethDstMaskedCriterion == null) {
// No match. Do bridging (default action).
return;
}
// Masked fwd classifier rule
final MacAddress dstMac = ethDstMaskedCriterion.mac();
final MacAddress dstMacMask = ethDstMaskedCriterion.mask();
flowRules.add(maskedFwdClassifierRule(inPort, dstMac, dstMacMask, obj));
} else {
final MacAddress dstMac = ethDstCriterion.mac();
flowRules.addAll(ipFwdClassifierRules(inPort, dstMac, obj));
flowRules.add(mplsFwdClassifierRule(inPort, dstMac, obj));
}
for (FlowRule f : flowRules) {
resultBuilder.addFlowRule(f);
}
}
private FlowRule maskedFwdClassifierRule(
PortNumber inPort, MacAddress dstMac, MacAddress dstMacMask,
FilteringObjective obj)
throws FabricPipelinerException {
final TrafficTreatment treatment;
final short ethType;
if (dstMac.equals(MacAddress.IPV4_MULTICAST)
&& dstMacMask.equals(MacAddress.IPV4_MULTICAST_MASK)) {
treatment = fwdClassifierTreatment(FWD_IPV4_ROUTING);
ethType = Ethernet.TYPE_IPV4;
} else if (dstMac.equals(MacAddress.IPV6_MULTICAST)
&& dstMacMask.equals(MacAddress.IPV6_MULTICAST_MASK)) {
treatment = fwdClassifierTreatment(FWD_IPV6_ROUTING);
ethType = Ethernet.TYPE_IPV6;
} else {
throw new FabricPipelinerException(format(
"Unsupported masked Ethernet address for fwd " +
"classifier rule (mac=%s, mask=%s)",
dstMac, dstMacMask));
}
return fwdClassifierRule(inPort, ethType, dstMac, dstMacMask, treatment, obj);
}
private Collection<FlowRule> ipFwdClassifierRules(
PortNumber inPort, MacAddress dstMac, FilteringObjective obj)
throws FabricPipelinerException {
final Collection<FlowRule> flowRules = Lists.newArrayList();
flowRules.add(fwdClassifierRule(
inPort, Ethernet.TYPE_IPV4, dstMac, null,
fwdClassifierTreatment(FWD_IPV4_ROUTING), obj));
flowRules.add(fwdClassifierRule(
inPort, Ethernet.TYPE_IPV6, dstMac, null,
fwdClassifierTreatment(FWD_IPV6_ROUTING), obj));
return flowRules;
}
private FlowRule mplsFwdClassifierRule(
PortNumber inPort, MacAddress dstMac, FilteringObjective obj)
throws FabricPipelinerException {
return fwdClassifierRule(
inPort, Ethernet.MPLS_UNICAST, dstMac, null,
fwdClassifierTreatment(FWD_MPLS), obj);
}
private FlowRule fwdClassifierRule(
PortNumber inPort, short ethType, MacAddress dstMac, MacAddress dstMacMask,
TrafficTreatment treatment, FilteringObjective obj)
throws FabricPipelinerException {
final TrafficSelector selector = DefaultTrafficSelector.builder()
.matchInPort(inPort)
.matchEthType(ethType)
.matchEthDstMasked(dstMac, dstMacMask == null
? MacAddress.EXACT_MASK : dstMacMask)
.build();
return flowRule(
obj, FabricConstants.FABRIC_INGRESS_FILTERING_FWD_CLASSIFIER,
selector, treatment);
}
private TrafficTreatment fwdClassifierTreatment(byte fwdType) {
final PiActionParam param = new PiActionParam(FabricConstants.FWD_TYPE, fwdType);
final PiAction action = PiAction.builder()
.withId(FabricConstants.FABRIC_INGRESS_FILTERING_SET_FORWARDING_TYPE)
.withParameter(param)
.build();
return DefaultTrafficTreatment.builder()
.piTableAction(action)
.build();
}
}