/* * Copyright 2014-present 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.flow; import java.util.List; import java.util.Objects; import org.onlab.packet.EthType; import org.onlab.packet.IpAddress; import org.onlab.packet.MacAddress; import org.onlab.packet.MplsLabel; import org.onlab.packet.TpPort; import org.onlab.packet.VlanId; import org.onosproject.core.GroupId; import org.onosproject.net.DeviceId; import org.onosproject.net.PortNumber; import org.onosproject.net.flow.instructions.ExtensionTreatment; import org.onosproject.net.flow.instructions.Instruction; import org.onosproject.net.flow.instructions.Instructions; import org.onosproject.net.meter.MeterId; import com.google.common.base.MoreObjects; import com.google.common.collect.ImmutableList; import com.google.common.collect.Lists; import static com.google.common.base.Preconditions.checkNotNull; /** * Default traffic treatment implementation. */ public final class DefaultTrafficTreatment implements TrafficTreatment { private final List<Instruction> immediate; private final List<Instruction> deferred; private final List<Instruction> all; private final Instructions.TableTypeTransition table; private final Instructions.MetadataInstruction meta; private final boolean hasClear; private static final DefaultTrafficTreatment EMPTY = new DefaultTrafficTreatment(ImmutableList.of(Instructions.createNoAction())); private final Instructions.MeterInstruction meter; /** * Creates a new traffic treatment from the specified list of instructions. * * @param immediate immediate instructions */ private DefaultTrafficTreatment(List<Instruction> immediate) { this.immediate = ImmutableList.copyOf(checkNotNull(immediate)); this.deferred = ImmutableList.of(); this.all = this.immediate; this.hasClear = false; this.table = null; this.meta = null; this.meter = null; } /** * Creates a new traffic treatment from the specified list of instructions. * * @param deferred deferred instructions * @param immediate immediate instructions * @param table table transition instruction * @param clear instruction to clear the deferred actions list */ private DefaultTrafficTreatment(List<Instruction> deferred, List<Instruction> immediate, Instructions.TableTypeTransition table, boolean clear, Instructions.MetadataInstruction meta, Instructions.MeterInstruction meter) { this.immediate = ImmutableList.copyOf(checkNotNull(immediate)); this.deferred = ImmutableList.copyOf(checkNotNull(deferred)); this.all = new ImmutableList.Builder<Instruction>() .addAll(immediate) .addAll(deferred) .build(); this.table = table; this.meta = meta; this.hasClear = clear; this.meter = meter; } @Override public List<Instruction> deferred() { return deferred; } @Override public List<Instruction> immediate() { return immediate; } @Override public List<Instruction> allInstructions() { return all; } @Override public Instructions.TableTypeTransition tableTransition() { return table; } @Override public boolean clearedDeferred() { return hasClear; } @Override public Instructions.MetadataInstruction writeMetadata() { return meta; } @Override public Instructions.MeterInstruction metered() { return meter; } /** * Returns a new traffic treatment builder. * * @return traffic treatment builder */ public static TrafficTreatment.Builder builder() { return new Builder(); } /** * Returns an empty traffic treatment. * * @return empty traffic treatment */ public static TrafficTreatment emptyTreatment() { return EMPTY; } /** * Returns a new traffic treatment builder primed to produce entities * patterned after the supplied treatment. * * @param treatment base treatment * @return traffic treatment builder */ public static TrafficTreatment.Builder builder(TrafficTreatment treatment) { return new Builder(treatment); } //FIXME: Order of instructions may affect hashcode @Override public int hashCode() { return Objects.hash(immediate, deferred, table, meta); } @Override public boolean equals(Object obj) { if (this == obj) { return true; } if (obj instanceof DefaultTrafficTreatment) { DefaultTrafficTreatment that = (DefaultTrafficTreatment) obj; return Objects.equals(immediate, that.immediate) && Objects.equals(deferred, that.deferred) && Objects.equals(table, that.table) && Objects.equals(meta, that.meta); } return false; } @Override public String toString() { return MoreObjects.toStringHelper(getClass()) .add("immediate", immediate) .add("deferred", deferred) .add("transition", table == null ? "None" : table.toString()) .add("meter", meter == null ? "None" : meter.toString()) .add("cleared", hasClear) .add("metadata", meta) .toString(); } /** * Builds a list of treatments following the following order. * Modifications -> Group -> Output (including drop) */ public static final class Builder implements TrafficTreatment.Builder { boolean clear = false; Instructions.TableTypeTransition table; Instructions.MetadataInstruction meta; Instructions.MeterInstruction meter; List<Instruction> deferred = Lists.newLinkedList(); List<Instruction> immediate = Lists.newLinkedList(); List<Instruction> current = immediate; // Creates a new builder private Builder() { } // Creates a new builder based off an existing treatment private Builder(TrafficTreatment treatment) { deferred(); treatment.deferred().forEach(i -> add(i)); immediate(); treatment.immediate().stream() // NOACTION will get re-added if there are no other actions .filter(i -> i.type() != Instruction.Type.NOACTION) .forEach(i -> add(i)); clear = treatment.clearedDeferred(); } @Override public Builder add(Instruction instruction) { switch (instruction.type()) { case NOACTION: case OUTPUT: case GROUP: case QUEUE: case L0MODIFICATION: case L1MODIFICATION: case L2MODIFICATION: case L3MODIFICATION: case L4MODIFICATION: case EXTENSION: current.add(instruction); break; case TABLE: table = (Instructions.TableTypeTransition) instruction; break; case METADATA: meta = (Instructions.MetadataInstruction) instruction; break; case METER: meter = (Instructions.MeterInstruction) instruction; break; default: throw new IllegalArgumentException("Unknown instruction type: " + instruction.type()); } return this; } /** * Add a NOACTION when DROP instruction is explicitly specified. * * @return the traffic treatment builder */ @Override public Builder drop() { return add(Instructions.createNoAction()); } /** * Add a NOACTION when no instruction is specified. * * @return the traffic treatment builder */ private Builder noAction() { return add(Instructions.createNoAction()); } @Override public Builder punt() { return add(Instructions.createOutput(PortNumber.CONTROLLER)); } @Override public Builder setOutput(PortNumber number) { return add(Instructions.createOutput(number)); } @Override public Builder setEthSrc(MacAddress addr) { return add(Instructions.modL2Src(addr)); } @Override public Builder setEthDst(MacAddress addr) { return add(Instructions.modL2Dst(addr)); } @Override public Builder setVlanId(VlanId id) { return add(Instructions.modVlanId(id)); } @Override public Builder setVlanPcp(Byte pcp) { return add(Instructions.modVlanPcp(pcp)); } @Override public Builder setIpSrc(IpAddress addr) { return add(Instructions.modL3Src(addr)); } @Override public Builder setIpDst(IpAddress addr) { return add(Instructions.modL3Dst(addr)); } @Override public Builder decNwTtl() { return add(Instructions.decNwTtl()); } @Override public Builder copyTtlIn() { return add(Instructions.copyTtlIn()); } @Override public Builder copyTtlOut() { return add(Instructions.copyTtlOut()); } @Override public Builder pushMpls() { return add(Instructions.pushMpls()); } @Override public Builder popMpls() { return add(Instructions.popMpls()); } @Override public Builder popMpls(EthType etherType) { return add(Instructions.popMpls(etherType)); } @Override public Builder setMpls(MplsLabel mplsLabel) { return add(Instructions.modMplsLabel(mplsLabel)); } @Override public Builder setMplsBos(boolean mplsBos) { return add(Instructions.modMplsBos(mplsBos)); } @Override public Builder decMplsTtl() { return add(Instructions.decMplsTtl()); } @Override public Builder group(GroupId groupId) { return add(Instructions.createGroup(groupId)); } @Override public Builder setQueue(long queueId) { return add(Instructions.setQueue(queueId, null)); } @Override public Builder setQueue(long queueId, PortNumber port) { return add(Instructions.setQueue(queueId, port)); } @Override public TrafficTreatment.Builder meter(MeterId meterId) { return add(Instructions.meterTraffic(meterId)); } @Override public Builder popVlan() { return add(Instructions.popVlan()); } @Override public Builder pushVlan() { return add(Instructions.pushVlan()); } @Override public Builder pushVlan(EthType ethType) { return add(Instructions.pushVlan(ethType)); } @Override public Builder transition(Integer tableId) { return add(Instructions.transition(tableId)); } @Override public Builder immediate() { current = immediate; return this; } @Override public Builder deferred() { current = deferred; return this; } @Override public Builder wipeDeferred() { clear = true; return this; } @Override public Builder writeMetadata(long metadata, long metadataMask) { return add(Instructions.writeMetadata(metadata, metadataMask)); } @Override public Builder setTunnelId(long tunnelId) { return add(Instructions.modTunnelId(tunnelId)); } @Override public TrafficTreatment.Builder setTcpSrc(TpPort port) { return add(Instructions.modTcpSrc(port)); } @Override public TrafficTreatment.Builder setTcpDst(TpPort port) { return add(Instructions.modTcpDst(port)); } @Override public TrafficTreatment.Builder setUdpSrc(TpPort port) { return add(Instructions.modUdpSrc(port)); } @Override public TrafficTreatment.Builder setUdpDst(TpPort port) { return add(Instructions.modUdpDst(port)); } @Override public Builder setArpSpa(IpAddress addr) { return add(Instructions.modArpSpa(addr)); } @Override public Builder setArpSha(MacAddress addr) { return add(Instructions.modArpSha(addr)); } @Override public Builder setArpOp(short op) { return add(Instructions.modL3ArpOp(op)); } @Override public TrafficTreatment.Builder extension(ExtensionTreatment extension, DeviceId deviceId) { return add(Instructions.extension(extension, deviceId)); } @Override public TrafficTreatment.Builder addTreatment(TrafficTreatment treatment) { List<Instruction> previous = current; deferred(); treatment.deferred().forEach(i -> add(i)); immediate(); treatment.immediate().stream() // NOACTION will get re-added if there are no other actions .filter(i -> i.type() != Instruction.Type.NOACTION) .forEach(i -> add(i)); clear = treatment.clearedDeferred(); current = previous; return this; } @Override public TrafficTreatment build() { if (deferred.isEmpty() && immediate.isEmpty() && table == null && !clear) { immediate(); noAction(); } return new DefaultTrafficTreatment(deferred, immediate, table, clear, meta, meter); } } }