blob: 4e13a74d094f666a370850377d2bc6229818b955 [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.ImmutableMap;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Lists;
import com.google.common.collect.Sets;
import org.onosproject.net.flow.criteria.Criterion;
import org.onosproject.net.flowobjective.ForwardingObjective;
import org.slf4j.Logger;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;
import static org.onosproject.net.flow.criteria.Criterion.Type.ETH_DST;
import static org.onosproject.net.flow.criteria.Criterion.Type.ETH_TYPE;
import static org.onosproject.net.flow.criteria.Criterion.Type.IPV4_DST;
import static org.onosproject.net.flow.criteria.Criterion.Type.IPV6_DST;
import static org.onosproject.net.flow.criteria.Criterion.Type.MPLS_BOS;
import static org.onosproject.net.flow.criteria.Criterion.Type.MPLS_LABEL;
import static org.onosproject.net.flow.criteria.Criterion.Type.VLAN_VID;
import static org.onosproject.pipelines.fabric.pipeliner.ForwardingFunctionTypeCommons.MATCH_ETH_DST_NONE;
import static org.onosproject.pipelines.fabric.pipeliner.ForwardingFunctionTypeCommons.MATCH_ETH_TYPE_IPV4;
import static org.onosproject.pipelines.fabric.pipeliner.ForwardingFunctionTypeCommons.MATCH_ETH_TYPE_IPV6;
import static org.onosproject.pipelines.fabric.pipeliner.ForwardingFunctionTypeCommons.MATCH_ETH_TYPE_MPLS;
import static org.onosproject.pipelines.fabric.pipeliner.ForwardingFunctionTypeCommons.MATCH_MPLS_BOS_FALSE;
import static org.onosproject.pipelines.fabric.pipeliner.ForwardingFunctionTypeCommons.MATCH_MPLS_BOS_TRUE;
import static org.slf4j.LoggerFactory.getLogger;
/**
* Forwarding function types (FFTs) that can represent a given forwarding
* objective. Each FFT is defined by a subset of criterion types expected to be
* found in the selector of the given objective, and, optionally, by their
* respective values (criterion instances) to match or to mismatch.
*/
enum ForwardingFunctionType {
/**
* L2 unicast.
*/
L2_UNICAST(
Sets.newHashSet(VLAN_VID, ETH_DST), // Expected criterion types.
Collections.emptyList(), // Criteria to match.
Lists.newArrayList(MATCH_ETH_DST_NONE)), // Criteria NOT to match.
/**
* L2 broadcast.
*/
L2_BROADCAST(
Sets.newHashSet(VLAN_VID, ETH_DST),
Lists.newArrayList(MATCH_ETH_DST_NONE),
Collections.emptyList()),
L2_BROADCAST_ALIAS(
Sets.newHashSet(VLAN_VID),
Collections.emptyList(),
Collections.emptyList(),
L2_BROADCAST), // (Optional) FFT to return if selected.
/**
* IPv4 unicast.
*/
IPV4_ROUTING(
Sets.newHashSet(ETH_TYPE, IPV4_DST),
Lists.newArrayList(MATCH_ETH_TYPE_IPV4),
Collections.emptyList()),
/**
* IPv4 multicast.
*/
IPV4_ROUTING_MULTICAST(
Sets.newHashSet(ETH_TYPE, VLAN_VID, IPV4_DST),
Lists.newArrayList(MATCH_ETH_TYPE_IPV4),
Collections.emptyList()),
/**
* IPv6 unicast.
*/
IPV6_ROUTING(
Sets.newHashSet(ETH_TYPE, IPV6_DST),
Lists.newArrayList(MATCH_ETH_TYPE_IPV6),
Collections.emptyList()),
/**
* IPv6 multicast.
*/
IPV6_ROUTING_MULTICAST(
Sets.newHashSet(ETH_TYPE, VLAN_VID, IPV6_DST),
Lists.newArrayList(MATCH_ETH_TYPE_IPV6),
Collections.emptyList()),
/**
* MPLS segment routing.
*/
MPLS_SEGMENT_ROUTING(
Sets.newHashSet(ETH_TYPE, MPLS_LABEL, MPLS_BOS),
Lists.newArrayList(MATCH_ETH_TYPE_MPLS, MATCH_MPLS_BOS_TRUE),
Collections.emptyList()),
/**
* Pseudo-wire.
*/
PSEUDO_WIRE(
Sets.newHashSet(ETH_TYPE, MPLS_LABEL, MPLS_BOS),
Lists.newArrayList(MATCH_ETH_TYPE_MPLS, MATCH_MPLS_BOS_FALSE),
Collections.emptyList()),
/**
* Unsupported type.
*/
UNKNOWN(
Collections.emptySet(),
Collections.emptyList(),
Collections.emptyList());
private static final Logger log = getLogger(ForwardingFunctionType.class);
private final Set<Criterion.Type> expectedCriterionTypes;
private final Map<Criterion.Type, List<Criterion>> matchCriteria;
private final Map<Criterion.Type, List<Criterion>> mismatchCriteria;
private final ForwardingFunctionType originalType;
/**
* Creates a new FFT.
*
* @param expectedCriterionTypes expected criterion types
* @param matchCriteria criterion instances to match
* @param mismatchCriteria criterion instance not to be matched
*/
ForwardingFunctionType(Set<Criterion.Type> expectedCriterionTypes,
Collection<Criterion> matchCriteria,
Collection<Criterion> mismatchCriteria) {
this(expectedCriterionTypes, matchCriteria, mismatchCriteria, null);
}
/**
* Creates a new alias FFT that if matched, should return the given original
* FFT.
*
* @param expectedCriterionTypes expected criterion types
* @param matchCriteria criterion instances to match
* @param mismatchCriteria criterion instance not to be matched
* @param original original FFT to return
*/
ForwardingFunctionType(Set<Criterion.Type> expectedCriterionTypes,
Collection<Criterion> matchCriteria,
Collection<Criterion> mismatchCriteria,
ForwardingFunctionType original) {
this.expectedCriterionTypes = ImmutableSet.copyOf(expectedCriterionTypes);
this.matchCriteria = typeToCriteriaMap(matchCriteria);
this.mismatchCriteria = typeToCriteriaMap(mismatchCriteria);
this.originalType = original == null ? this : original;
}
/**
* Attempts to guess the forwarding function type of the given forwarding
* objective.
*
* @param fwd the forwarding objective
* @return forwarding function type. {@link #UNKNOWN} if the FFT cannot be
* determined.
*/
public static ForwardingFunctionType getForwardingFunctionType(ForwardingObjective fwd) {
final Set<Criterion> criteria = criteriaIncludingMeta(fwd);
final Set<Criterion.Type> criterionTypes = criteria.stream()
.map(Criterion::type).collect(Collectors.toSet());
final List<ForwardingFunctionType> candidates = Arrays.stream(ForwardingFunctionType.values())
// Keep FFTs which expected criterion types are the same found
// in the fwd objective.
.filter(fft -> fft.expectedCriterionTypes.equals(criterionTypes))
// Keep FFTs which match criteria are found in the fwd objective.
.filter(fft -> matchFft(criteria, fft))
// Keep FFTs which mismatch criteria are NOT found in the objective.
.filter(fft -> mismatchFft(criteria, fft))
.collect(Collectors.toList());
switch (candidates.size()) {
case 1:
return candidates.get(0).originalType;
case 0:
return UNKNOWN;
default:
log.warn("Multiple FFT candidates found: {} [{}]", candidates, fwd);
return UNKNOWN;
}
}
private static boolean matchFft(Collection<Criterion> criteria, ForwardingFunctionType fft) {
return matchOrMismatchFft(criteria, fft.matchCriteria, false);
}
private static boolean mismatchFft(Collection<Criterion> criteria, ForwardingFunctionType fft) {
return matchOrMismatchFft(criteria, fft.mismatchCriteria, true);
}
private static boolean matchOrMismatchFft(
Collection<Criterion> criteria,
Map<Criterion.Type, List<Criterion>> criteriaToMatch,
boolean mismatch) {
final Map<Criterion.Type, Criterion> givenCriteria = typeToCriterionMap(criteria);
for (Criterion.Type typeToMatch : criteriaToMatch.keySet()) {
if (!givenCriteria.containsKey(typeToMatch)) {
return false;
}
final boolean matchFound = criteriaToMatch.get(typeToMatch).stream()
.anyMatch(c -> mismatch != givenCriteria.get(c.type()).equals(c));
if (!matchFound) {
return false;
}
}
return true;
}
private static Set<Criterion> criteriaIncludingMeta(ForwardingObjective fwd) {
final Set<Criterion> criteria = Sets.newHashSet();
criteria.addAll(fwd.selector().criteria());
// FIXME: Is this really needed? Meta is such an ambiguous field...
if (fwd.meta() != null) {
criteria.addAll(fwd.meta().criteria());
}
return criteria;
}
private static Map<Criterion.Type, List<Criterion>> typeToCriteriaMap(Collection<Criterion> criteria) {
return criteria.stream().collect(Collectors.groupingBy(Criterion::type));
}
private static Map<Criterion.Type, Criterion> typeToCriterionMap(Collection<Criterion> criteria) {
final ImmutableMap.Builder<Criterion.Type, Criterion> mapBuilder = ImmutableMap.builder();
criteria.forEach(c -> mapBuilder.put(c.type(), c));
return mapBuilder.build();
}
}