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