blob: 74397b01491aa40eb8ffa258ecad806fe30f837e [file] [log] [blame]
/*
* Copyright 2015 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.net.flowobjective.impl.composition;
import org.onlab.packet.IpPrefix;
import org.onosproject.net.flow.DefaultTrafficSelector;
import org.onosproject.net.flow.DefaultTrafficTreatment;
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.LambdaCriterion;
import org.onosproject.net.flow.criteria.OchSignalCriterion;
import org.onosproject.net.flow.criteria.EthCriterion;
import org.onosproject.net.flow.criteria.VlanIdCriterion;
import org.onosproject.net.flow.criteria.VlanPcpCriterion;
import org.onosproject.net.flow.criteria.MplsCriterion;
import org.onosproject.net.flow.criteria.IPCriterion;
import org.onosproject.net.flow.criteria.IPv6FlowLabelCriterion;
import org.onosproject.net.flow.criteria.OduSignalIdCriterion;
import org.onosproject.net.flow.criteria.Criteria;
import org.onosproject.net.flow.instructions.Instruction;
import org.onosproject.net.flow.instructions.L0ModificationInstruction;
import org.onosproject.net.flow.instructions.L1ModificationInstruction;
import org.onosproject.net.flow.instructions.L2ModificationInstruction;
import org.onosproject.net.flow.instructions.L3ModificationInstruction;
import org.onosproject.net.flowobjective.DefaultForwardingObjective;
import org.onosproject.net.flowobjective.ForwardingObjective;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.Stack;
/**
* Provide util functions for FlowObjectiveComposition.
*/
public final class FlowObjectiveCompositionUtil {
private FlowObjectiveCompositionUtil() {}
// only work with VERSATILE
public static ForwardingObjective composeParallel(ForwardingObjective fo1, ForwardingObjective fo2) {
TrafficSelector trafficSelector = intersectTrafficSelector(fo1.selector(), fo2.selector());
if (trafficSelector == null) {
return null;
}
TrafficTreatment trafficTreatment = unionTrafficTreatment(fo1.treatment(), fo2.treatment());
return DefaultForwardingObjective.builder()
.fromApp(fo1.appId())
.makePermanent()
.withFlag(ForwardingObjective.Flag.VERSATILE)
.withPriority(fo1.priority() + fo2.priority())
.withSelector(trafficSelector)
.withTreatment(trafficTreatment)
.add();
}
public static ForwardingObjective composeSequential(ForwardingObjective fo1,
ForwardingObjective fo2,
int priorityMultiplier) {
TrafficSelector revertTrafficSelector = revertTreatmentSelector(fo1.treatment(), fo2.selector());
if (revertTrafficSelector == null) {
return null;
}
TrafficSelector trafficSelector = intersectTrafficSelector(fo1.selector(), revertTrafficSelector);
if (trafficSelector == null) {
return null;
}
TrafficTreatment trafficTreatment = unionTrafficTreatment(fo1.treatment(), fo2.treatment());
return DefaultForwardingObjective.builder()
.fromApp(fo1.appId())
.makePermanent()
.withFlag(ForwardingObjective.Flag.VERSATILE)
.withPriority(fo1.priority() * priorityMultiplier + fo2.priority())
.withSelector(trafficSelector)
.withTreatment(trafficTreatment)
.add();
}
public static ForwardingObjective composeOverride(ForwardingObjective fo, int priorityAddend) {
return DefaultForwardingObjective.builder()
.fromApp(fo.appId())
.makePermanent()
.withFlag(fo.flag())
.withPriority(fo.priority() + priorityAddend)
.withSelector(fo.selector())
.withTreatment(fo.treatment())
.add();
}
public static TrafficSelector intersectTrafficSelector(TrafficSelector ts1, TrafficSelector ts2) {
TrafficSelector.Builder selectorBuilder = DefaultTrafficSelector.builder();
Set<Criterion.Type> ts1IntersectTs2 = getTypeSet(ts1);
ts1IntersectTs2.retainAll(getTypeSet(ts2));
for (Criterion.Type type : ts1IntersectTs2) {
Criterion criterion = intersectCriterion(ts1.getCriterion(type), ts2.getCriterion(type));
if (criterion == null) {
return null;
} else {
selectorBuilder.add(criterion);
}
}
Set<Criterion.Type> ts1MinusTs2 = getTypeSet(ts1);
ts1MinusTs2.removeAll(getTypeSet(ts2));
for (Criterion.Type type : ts1MinusTs2) {
selectorBuilder.add(ts1.getCriterion(type));
}
Set<Criterion.Type> ts2MinusTs1 = getTypeSet(ts2);
ts2MinusTs1.removeAll(getTypeSet(ts1));
for (Criterion.Type type : ts2MinusTs1) {
selectorBuilder.add(ts2.getCriterion(type));
}
return selectorBuilder.build();
}
public static TrafficTreatment unionTrafficTreatment(TrafficTreatment tt1, TrafficTreatment tt2) {
TrafficTreatment.Builder treatmentBuilder = DefaultTrafficTreatment.builder();
for (Instruction instruction : tt1.allInstructions()) {
treatmentBuilder.add(instruction);
}
for (Instruction instruction : tt2.allInstructions()) {
treatmentBuilder.add(instruction);
}
return treatmentBuilder.build();
}
//CHECKSTYLE:OFF
public static TrafficSelector revertTreatmentSelector(TrafficTreatment trafficTreatment,
TrafficSelector trafficSelector) {
TrafficSelector.Builder selectorBuilder = DefaultTrafficSelector.builder();
Map<Criterion.Type, Criterion> criterionMap = new HashMap<>();
for (Criterion criterion : trafficSelector.criteria()) {
criterionMap.put(criterion.type(), criterion);
}
for (Instruction instruction : trafficTreatment.allInstructions()) {
switch (instruction.type()) {
case OUTPUT:
break;
case GROUP:
break;
case L0MODIFICATION: {
L0ModificationInstruction l0 = (L0ModificationInstruction) instruction;
switch (l0.subtype()) {
case LAMBDA:
if (criterionMap.containsKey(Criterion.Type.OCH_SIGID)) {
if (((LambdaCriterion) criterionMap.get((Criterion.Type.OCH_SIGID))).lambda()
== ((L0ModificationInstruction.ModLambdaInstruction) l0).lambda()) {
criterionMap.remove(Criterion.Type.OCH_SIGID);
} else {
return null;
}
} else {
break;
}
case OCH:
if (criterionMap.containsKey(Criterion.Type.OCH_SIGID)) {
if (((OchSignalCriterion) criterionMap.get((Criterion.Type.OCH_SIGID))).lambda()
.equals(((L0ModificationInstruction.ModOchSignalInstruction) l0).lambda())) {
criterionMap.remove(Criterion.Type.OCH_SIGID);
} else {
return null;
}
}
default:
break;
}
break;
}
case L1MODIFICATION: {
L1ModificationInstruction l1 = (L1ModificationInstruction) instruction;
switch (l1.subtype()) {
case ODU_SIGID:
if (criterionMap.containsKey(Criterion.Type.ODU_SIGID)) {
if (((OduSignalIdCriterion) criterionMap.get((Criterion.Type.ODU_SIGID))).oduSignalId()
.equals(((L1ModificationInstruction.ModOduSignalIdInstruction) l1)
.oduSignalId())) {
criterionMap.remove(Criterion.Type.ODU_SIGID);
} else {
return null;
}
}
default:
break;
}
break;
}
case L2MODIFICATION: {
L2ModificationInstruction l2 = (L2ModificationInstruction) instruction;
switch (l2.subtype()) {
case ETH_SRC:
if (criterionMap.containsKey(Criterion.Type.ETH_SRC)) {
if (((EthCriterion) criterionMap.get((Criterion.Type.ETH_SRC))).mac()
.equals(((L2ModificationInstruction.ModEtherInstruction) l2).mac())) {
criterionMap.remove(Criterion.Type.ETH_SRC);
} else {
return null;
}
} else {
break;
}
case ETH_DST:
if (criterionMap.containsKey(Criterion.Type.ETH_DST)) {
if (((EthCriterion) criterionMap.get((Criterion.Type.ETH_DST))).mac()
.equals(((L2ModificationInstruction.ModEtherInstruction) l2).mac())) {
criterionMap.remove(Criterion.Type.ETH_DST);
} else {
return null;
}
} else {
break;
}
case VLAN_ID:
if (criterionMap.containsKey(Criterion.Type.VLAN_VID)) {
if (((VlanIdCriterion) criterionMap.get((Criterion.Type.VLAN_VID))).vlanId()
.equals(((L2ModificationInstruction.ModVlanIdInstruction) l2).vlanId())) {
criterionMap.remove(Criterion.Type.VLAN_VID);
} else {
return null;
}
} else {
break;
}
case VLAN_PCP:
if (criterionMap.containsKey(Criterion.Type.VLAN_PCP)) {
if (((VlanPcpCriterion) criterionMap.get((Criterion.Type.VLAN_PCP))).priority()
== ((L2ModificationInstruction.ModVlanPcpInstruction) l2).vlanPcp()) {
criterionMap.remove(Criterion.Type.VLAN_PCP);
} else {
return null;
}
} else {
break;
}
case MPLS_LABEL:
if (criterionMap.containsKey(Criterion.Type.MPLS_LABEL)) {
if (((MplsCriterion) criterionMap.get((Criterion.Type.MPLS_LABEL))).label()
.equals(((L2ModificationInstruction.ModMplsLabelInstruction) l2).label())) {
criterionMap.remove(Criterion.Type.ETH_DST);
} else {
return null;
}
} else {
break;
}
default:
break;
}
break;
}
case TABLE:
break;
case L3MODIFICATION: {
L3ModificationInstruction l3 = (L3ModificationInstruction) instruction;
switch (l3.subtype()) {
case IPV4_SRC:
if (criterionMap.containsKey(Criterion.Type.IPV4_SRC)) {
if (((IPCriterion) criterionMap.get(Criterion.Type.IPV4_SRC)).ip()
.contains(((L3ModificationInstruction.ModIPInstruction) l3).ip())) {
criterionMap.remove(Criterion.Type.IPV4_SRC);
} else {
return null;
}
} else {
break;
}
case IPV4_DST:
if (criterionMap.containsKey(Criterion.Type.IPV4_DST)) {
if (((IPCriterion) criterionMap.get(Criterion.Type.IPV4_DST)).ip()
.contains(((L3ModificationInstruction.ModIPInstruction) l3).ip())) {
criterionMap.remove(Criterion.Type.IPV4_DST);
} else {
return null;
}
} else {
break;
}
case IPV6_SRC:
if (criterionMap.containsKey(Criterion.Type.IPV6_SRC)) {
if (((IPCriterion) criterionMap.get(Criterion.Type.IPV6_SRC)).ip()
.contains(((L3ModificationInstruction.ModIPInstruction) l3).ip())) {
criterionMap.remove(Criterion.Type.IPV6_SRC);
} else {
return null;
}
} else {
break;
}
case IPV6_DST:
if (criterionMap.containsKey(Criterion.Type.IPV6_DST)) {
if (((IPCriterion) criterionMap.get(Criterion.Type.IPV6_DST)).ip()
.contains(((L3ModificationInstruction.ModIPInstruction) l3).ip())) {
criterionMap.remove(Criterion.Type.IPV6_DST);
} else {
return null;
}
} else {
break;
}
case IPV6_FLABEL:
if (criterionMap.containsKey(Criterion.Type.IPV6_FLABEL)) {
if (((IPv6FlowLabelCriterion) criterionMap.get(Criterion.Type.IPV6_FLABEL)).flowLabel()
== (((L3ModificationInstruction.ModIPv6FlowLabelInstruction) l3).flowLabel())) {
criterionMap.remove(Criterion.Type.IPV4_SRC);
} else {
return null;
}
} else {
break;
}
default:
break;
}
break;
}
case METADATA:
break;
default:
break;
}
}
for (Criterion criterion : criterionMap.values()) {
selectorBuilder.add(criterion);
}
return selectorBuilder.build();
}
//CHECKSTYLE:ON
public static Set<Criterion.Type> getTypeSet(TrafficSelector trafficSelector) {
Set<Criterion.Type> typeSet = new HashSet<>();
for (Criterion criterion : trafficSelector.criteria()) {
typeSet.add(criterion.type());
}
return typeSet;
}
public static Criterion intersectCriterion(Criterion c1, Criterion c2) {
switch (c1.type()) {
case IPV4_SRC: {
IpPrefix ipPrefix = intersectIpPrefix(((IPCriterion) c1).ip(), ((IPCriterion) c2).ip());
if (ipPrefix == null) {
return null;
} else {
return Criteria.matchIPSrc(ipPrefix);
}
}
case IPV4_DST: {
IpPrefix ipPrefix = intersectIpPrefix(((IPCriterion) c1).ip(), ((IPCriterion) c2).ip());
if (ipPrefix == null) {
return null;
} else {
return Criteria.matchIPDst(ipPrefix);
}
}
case IPV6_SRC: {
IpPrefix ipPrefix = intersectIpPrefix(((IPCriterion) c1).ip(), ((IPCriterion) c2).ip());
if (ipPrefix == null) {
return null;
} else {
return Criteria.matchIPv6Src(ipPrefix);
}
}
case IPV6_DST: {
IpPrefix ipPrefix = intersectIpPrefix(((IPCriterion) c1).ip(), ((IPCriterion) c2).ip());
if (ipPrefix == null) {
return null;
} else {
return Criteria.matchIPv6Dst(ipPrefix);
}
}
default:
if (!c1.equals(c2)) {
return null;
} else {
return c1;
}
}
}
public static IpPrefix intersectIpPrefix(IpPrefix ip1, IpPrefix ip2) {
if (ip1.contains(ip2)) {
return ip1;
} else if (ip2.contains(ip1)) {
return ip2;
} else {
return null;
}
}
public static FlowObjectiveCompositionTree parsePolicyString(String policy) {
List<FlowObjectiveCompositionTree> postfix = transformToPostfixForm(policy);
return buildPolicyTree(postfix);
}
private static List<FlowObjectiveCompositionTree> transformToPostfixForm(String policy) {
Stack<Character> stack = new Stack<>();
List<FlowObjectiveCompositionTree> postfix = new ArrayList<>();
for (int i = 0; i < policy.length(); i++) {
Character ch = policy.charAt(i);
if (Character.isDigit(ch)) {
int applicationId = ch - '0';
while (i + 1 < policy.length() && Character.isDigit(policy.charAt(i + 1))) {
i++;
applicationId = applicationId * 10 + policy.charAt(i) - '0';
}
postfix.add(new FlowObjectiveCompositionTree((short) applicationId));
} else if (ch == '(') {
stack.push(ch);
} else if (ch == ')') {
while (stack.peek() != '(') {
postfix.add(new FlowObjectiveCompositionTree(stack.pop()));
}
stack.pop();
} else {
while (!stack.isEmpty() && compareOperatorPriority(stack.peek(), ch)) {
postfix.add(new FlowObjectiveCompositionTree(stack.pop()));
}
stack.push(ch);
}
}
while (!stack.isEmpty()) {
postfix.add(new FlowObjectiveCompositionTree(stack.pop()));
}
return postfix;
}
private static boolean compareOperatorPriority(char peek, char cur) {
if (peek == '/' && (cur == '+' || cur == '>' || cur == '/')) {
return true;
} else if (peek == '>' && (cur == '+' || cur == '>')) {
return true;
} else if (peek == '+' && cur == '+') {
return true;
}
return false;
}
private static FlowObjectiveCompositionTree buildPolicyTree(List<FlowObjectiveCompositionTree> postfix) {
Stack<FlowObjectiveCompositionTree> stack = new Stack<>();
for (FlowObjectiveCompositionTree node : postfix) {
if (node.operator == FlowObjectiveCompositionManager.PolicyOperator.Application) {
stack.push(node);
} else {
node.rightChild = stack.pop();
node.leftChild = stack.pop();
stack.push(node);
}
}
return stack.pop();
}
public static Collection<ForwardingObjective> minusForwardingObjectives(Collection<ForwardingObjective> fo1,
Collection<ForwardingObjective> fo2) {
Map<Integer, ForwardingObjective> map = new HashMap<>();
for (ForwardingObjective fo : fo1) {
map.put(fo.id(), fo);
}
for (ForwardingObjective fo : fo2) {
map.remove(fo.id());
}
return map.values();
}
}