diff --git a/pipelines/fabric/src/main/java/org/onosproject/pipelines/fabric/FabricConstants.java b/pipelines/fabric/src/main/java/org/onosproject/pipelines/fabric/FabricConstants.java index 333964a6ab..a71cfa4d46 100644 --- a/pipelines/fabric/src/main/java/org/onosproject/pipelines/fabric/FabricConstants.java +++ b/pipelines/fabric/src/main/java/org/onosproject/pipelines/fabric/FabricConstants.java @@ -155,4 +155,6 @@ public final class FabricConstants { // Packet Metadata IDs public static final PiControlMetadataId CTRL_META_EGRESS_PORT_ID = PiControlMetadataId.of("egress_port"); public static final PiControlMetadataId CTRL_META_INGRESS_PORT_ID = PiControlMetadataId.of("ingress_port"); + + public static final int PORT_BITWIDTH = 9; } \ No newline at end of file diff --git a/pipelines/fabric/src/main/java/org/onosproject/pipelines/fabric/FabricInterpreter.java b/pipelines/fabric/src/main/java/org/onosproject/pipelines/fabric/FabricInterpreter.java new file mode 100644 index 0000000000..65729c0ed7 --- /dev/null +++ b/pipelines/fabric/src/main/java/org/onosproject/pipelines/fabric/FabricInterpreter.java @@ -0,0 +1,269 @@ +/* + * 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; + +import com.google.common.collect.ImmutableBiMap; +import com.google.common.collect.ImmutableList; +import com.google.common.collect.ImmutableSet; +import org.onlab.packet.DeserializationException; +import org.onlab.packet.Ethernet; +import org.onlab.util.ImmutableByteSequence; +import org.onosproject.net.ConnectPoint; +import org.onosproject.net.DeviceId; +import org.onosproject.net.Port; +import org.onosproject.net.PortNumber; +import org.onosproject.net.device.DeviceService; +import org.onosproject.net.driver.AbstractHandlerBehaviour; +import org.onosproject.net.flow.TrafficTreatment; +import org.onosproject.net.flow.criteria.Criterion; +import org.onosproject.net.flow.instructions.Instructions; +import org.onosproject.net.packet.DefaultInboundPacket; +import org.onosproject.net.packet.InboundPacket; +import org.onosproject.net.packet.OutboundPacket; +import org.onosproject.net.pi.model.PiCounterId; +import org.onosproject.net.pi.model.PiMatchFieldId; +import org.onosproject.net.pi.model.PiPipelineInterpreter; +import org.onosproject.net.pi.model.PiTableId; +import org.onosproject.net.pi.runtime.PiAction; +import org.onosproject.net.pi.runtime.PiControlMetadata; +import org.onosproject.net.pi.runtime.PiPacketOperation; + +import java.nio.ByteBuffer; +import java.util.Collection; +import java.util.List; +import java.util.Optional; +import java.util.Set; + +import static java.lang.String.format; +import static java.util.stream.Collectors.toList; +import static org.onlab.util.ImmutableByteSequence.copyFrom; +import static org.onlab.util.ImmutableByteSequence.fit; +import static org.onosproject.net.PortNumber.FLOOD; +import static org.onosproject.net.flow.instructions.Instruction.Type.OUTPUT; +import static org.onosproject.net.pi.model.PiPacketOperationType.PACKET_OUT; + +/** + * Interpreter for fabric pipeline. + */ +public class FabricInterpreter extends AbstractHandlerBehaviour + implements PiPipelineInterpreter { + private static final ImmutableBiMap TABLE_ID_MAP = + ImmutableBiMap.builder() + // Filtering + .put(0, FabricConstants.TBL_INGRESS_PORT_VLAN_ID) + .put(1, FabricConstants.TBL_FWD_CLASSIFIER_ID) + // Forwarding + .put(2, FabricConstants.TBL_MPLS_ID) + .put(3, FabricConstants.TBL_UNICAST_V4_ID) + .put(4, FabricConstants.TBL_UNICAST_V6_ID) + .put(5, FabricConstants.TBL_MULTICAST_V4_ID) + .put(6, FabricConstants.TBL_MULTICAST_V6_ID) + .put(7, FabricConstants.TBL_BRIDGING_ID) + .put(8, FabricConstants.TBL_ACL_ID) + // Next + .put(9, FabricConstants.TBL_NEXT_ID_MAPPING_ID) + .put(10, FabricConstants.TBL_SIMPLE_ID) + .put(11, FabricConstants.TBL_HASHED_ID) + .put(12, FabricConstants.TBL_BROADCAST_ID) + .build(); + + private static final Set FILTERING_CTRL_TBLS = ImmutableSet.of(FabricConstants.TBL_INGRESS_PORT_VLAN_ID, + FabricConstants.TBL_FWD_CLASSIFIER_ID); + private static final Set FORWARDING_CTRL_TBLS = ImmutableSet.of(FabricConstants.TBL_MPLS_ID, + FabricConstants.TBL_UNICAST_V4_ID, + FabricConstants.TBL_UNICAST_V6_ID, + FabricConstants.TBL_MULTICAST_V4_ID, + FabricConstants.TBL_MULTICAST_V6_ID, + FabricConstants.TBL_BRIDGING_ID, + FabricConstants.TBL_ACL_ID); + private static final Set NEXT_CTRL_TBLS = ImmutableSet.of(FabricConstants.TBL_NEXT_ID_MAPPING_ID, + FabricConstants.TBL_SIMPLE_ID, + FabricConstants.TBL_HASHED_ID, + FabricConstants.TBL_BROADCAST_ID); + + private static final ImmutableBiMap TABLE_COUNTER_MAP = + ImmutableBiMap.builder() + .put(FabricConstants.TBL_MULTICAST_V4_ID, FabricConstants.CNT_MULTICAST_V4_COUNTER_ID) + .put(FabricConstants.TBL_MULTICAST_V6_ID, FabricConstants.CNT_MULTICAST_V6_COUNTER_ID) + .put(FabricConstants.TBL_FWD_CLASSIFIER_ID, FabricConstants.CNT_FWD_CLASSIFIER_COUNTER_ID) + .put(FabricConstants.TBL_ACL_ID, FabricConstants.CNT_ACL_COUNTER_ID) + .put(FabricConstants.TBL_BROADCAST_ID, FabricConstants.CNT_BROADCAST_COUNTER_ID) + .put(FabricConstants.TBL_HASHED_ID, FabricConstants.CNT_HASHED_COUNTER_ID) + .put(FabricConstants.TBL_INGRESS_PORT_VLAN_ID, FabricConstants.CNT_INGRESS_PORT_VLAN_COUNTER_ID) + .put(FabricConstants.TBL_NEXT_ID_MAPPING_ID, FabricConstants.CNT_NEXT_ID_MAPPING_COUNTER_ID) + .put(FabricConstants.TBL_UNICAST_V6_ID, FabricConstants.CNT_UNICAST_V6_COUNTER_ID) + .put(FabricConstants.TBL_SIMPLE_ID, FabricConstants.CNT_SIMPLE_COUNTER_ID) + .put(FabricConstants.TBL_BRIDGING_ID, FabricConstants.CNT_BRIDGING_COUNTER_ID) + .put(FabricConstants.TBL_UNICAST_V4_ID, FabricConstants.CNT_UNICAST_V4_COUNTER_ID) + .put(FabricConstants.TBL_MPLS_ID, FabricConstants.CNT_MPLS_COUNTER_ID) + .build(); + private static final ImmutableBiMap CRITERION_MAP = + ImmutableBiMap.builder() + .put(Criterion.Type.IN_PORT, FabricConstants.HF_STANDARD_METADATA_INGRESS_PORT_ID) + .put(Criterion.Type.ETH_DST, FabricConstants.HF_ETHERNET_DST_ADDR_ID) + .put(Criterion.Type.ETH_SRC, FabricConstants.HF_ETHERNET_SRC_ADDR_ID) + .put(Criterion.Type.ETH_TYPE, FabricConstants.HF_ETHERNET_ETHER_TYPE_ID) + .put(Criterion.Type.MPLS_BOS, FabricConstants.HF_MPLS_BOS_ID) + .put(Criterion.Type.MPLS_LABEL, FabricConstants.HF_MPLS_LABEL_ID) + .put(Criterion.Type.MPLS_TC, FabricConstants.HF_MPLS_TC_ID) + .put(Criterion.Type.VLAN_VID, FabricConstants.HF_VLAN_TAG_VLAN_ID_ID) + .put(Criterion.Type.VLAN_PCP, FabricConstants.HF_VLAN_TAG_PRI_ID) + .put(Criterion.Type.IPV4_DST, FabricConstants.HF_IPV4_DST_ADDR_ID) + .put(Criterion.Type.IPV4_SRC, FabricConstants.HF_IPV4_SRC_ADDR_ID) + .put(Criterion.Type.IPV6_DST, FabricConstants.HF_IPV6_DST_ADDR_ID) + .put(Criterion.Type.IPV6_SRC, FabricConstants.HF_IPV6_SRC_ADDR_ID) + .put(Criterion.Type.TCP_SRC, FabricConstants.HF_TCP_SRC_PORT_ID) + .put(Criterion.Type.TCP_DST, FabricConstants.HF_TCP_DST_PORT_ID) + .put(Criterion.Type.UDP_SRC, FabricConstants.HF_UDP_SRC_PORT_ID) + .put(Criterion.Type.UDP_DST, FabricConstants.HF_UDP_DST_PORT_ID) + .put(Criterion.Type.IP_PROTO, FabricConstants.HF_FABRIC_METADATA_IP_PROTO_ID) + .put(Criterion.Type.ICMPV6_TYPE, FabricConstants.HF_ICMP_ICMP_TYPE_ID) + .put(Criterion.Type.ICMPV6_CODE, FabricConstants.HF_ICMP_ICMP_CODE_ID) + .build(); + + @Override + public Optional mapCriterionType(Criterion.Type type) { + return Optional.ofNullable(CRITERION_MAP.get(type)); + } + + @Override + public Optional mapPiMatchFieldId(PiMatchFieldId fieldId) { + return Optional.ofNullable(CRITERION_MAP.inverse().get(fieldId)); + } + + @Override + public Optional mapFlowRuleTableId(int flowRuleTableId) { + return Optional.ofNullable(TABLE_ID_MAP.get(flowRuleTableId)); + } + + @Override + public Optional mapPiTableId(PiTableId piTableId) { + return Optional.ofNullable(TABLE_ID_MAP.inverse().get(piTableId)); + } + + @Override + public PiAction mapTreatment(TrafficTreatment treatment, PiTableId piTableId) + throws PiInterpreterException { + + if (FILTERING_CTRL_TBLS.contains(piTableId)) { + return FabricTreatmentInterpreter.mapFilteringTreatment(treatment); + } else if (FORWARDING_CTRL_TBLS.contains(piTableId)) { + return FabricTreatmentInterpreter.mapForwardingTreatment(treatment); + } else if (NEXT_CTRL_TBLS.contains(piTableId)) { + return FabricTreatmentInterpreter.mapNextTreatment(treatment); + } else { + throw new PiInterpreterException(String.format("Table %s unsupported", piTableId)); + } + } + + @Override + public Optional mapTableCounter(PiTableId piTableId) { + return Optional.ofNullable(TABLE_COUNTER_MAP.get(piTableId)); + } + + private PiPacketOperation createPiPacketOperation(DeviceId deviceId, ByteBuffer data, long portNumber) + throws PiInterpreterException { + PiControlMetadata metadata = createPacketMetadata(portNumber); + return PiPacketOperation.builder() + .forDevice(deviceId) + .withType(PACKET_OUT) + .withData(copyFrom(data)) + .withMetadatas(ImmutableList.of(metadata)) + .build(); + } + + private PiControlMetadata createPacketMetadata(long portNumber) throws PiInterpreterException { + try { + return PiControlMetadata.builder() + .withId(FabricConstants.CTRL_META_EGRESS_PORT_ID) + .withValue(fit(copyFrom(portNumber), FabricConstants.PORT_BITWIDTH)) + .build(); + } catch (ImmutableByteSequence.ByteSequenceTrimException e) { + throw new PiInterpreterException(format( + "Port number %d too big, %s", portNumber, e.getMessage())); + } + } + + @Override + public Collection mapOutboundPacket(OutboundPacket packet) + throws PiInterpreterException { + DeviceId deviceId = packet.sendThrough(); + TrafficTreatment treatment = packet.treatment(); + + // fabric.p4 supports only OUTPUT instructions. + List outInstructions = treatment + .allInstructions() + .stream() + .filter(i -> i.type().equals(OUTPUT)) + .map(i -> (Instructions.OutputInstruction) i) + .collect(toList()); + + if (treatment.allInstructions().size() != outInstructions.size()) { + // There are other instructions that are not of type OUTPUT. + throw new PiInterpreterException("Treatment not supported: " + treatment); + } + + ImmutableList.Builder builder = ImmutableList.builder(); + for (Instructions.OutputInstruction outInst : outInstructions) { + if (outInst.port().isLogical() && !outInst.port().equals(FLOOD)) { + throw new PiInterpreterException(format( + "Output on logical port '%s' not supported", outInst.port())); + } else if (outInst.port().equals(FLOOD)) { + // Since fabric.p4 does not support flooding, we create a packet + // operation for each switch port. + final DeviceService deviceService = handler().get(DeviceService.class); + for (Port port : deviceService.getPorts(packet.sendThrough())) { + builder.add(createPiPacketOperation(deviceId, packet.data(), port.number().toLong())); + } + } else { + builder.add(createPiPacketOperation(deviceId, packet.data(), outInst.port().toLong())); + } + } + return builder.build(); + } + + @Override + public InboundPacket mapInboundPacket(PiPacketOperation packetIn) throws PiInterpreterException { + // Assuming that the packet is ethernet, which is fine since fabric.p4 + // can deparse only ethernet packets. + DeviceId deviceId = packetIn.deviceId(); + Ethernet ethPkt; + try { + ethPkt = Ethernet.deserializer().deserialize(packetIn.data().asArray(), 0, + packetIn.data().size()); + } catch (DeserializationException dex) { + throw new PiInterpreterException(dex.getMessage()); + } + + // Returns the ingress port packet metadata. + Optional packetMetadata = packetIn.metadatas() + .stream().filter(m -> m.id().equals(FabricConstants.CTRL_META_INGRESS_PORT_ID)) + .findFirst(); + + if (packetMetadata.isPresent()) { + ImmutableByteSequence portByteSequence = packetMetadata.get().value(); + short s = portByteSequence.asReadOnlyBuffer().getShort(); + ConnectPoint receivedFrom = new ConnectPoint(deviceId, PortNumber.portNumber(s)); + ByteBuffer rawData = ByteBuffer.wrap(packetIn.data().asArray()); + return new DefaultInboundPacket(receivedFrom, ethPkt, rawData); + } else { + throw new PiInterpreterException(format( + "Missing metadata '%s' in packet-in received from '%s': %s", + FabricConstants.CTRL_META_INGRESS_PORT_ID, deviceId, packetIn)); + } + } +} diff --git a/pipelines/fabric/src/main/java/org/onosproject/pipelines/fabric/FabricTreatmentInterpreter.java b/pipelines/fabric/src/main/java/org/onosproject/pipelines/fabric/FabricTreatmentInterpreter.java new file mode 100644 index 0000000000..a2e4974e8f --- /dev/null +++ b/pipelines/fabric/src/main/java/org/onosproject/pipelines/fabric/FabricTreatmentInterpreter.java @@ -0,0 +1,237 @@ +/* + * 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; + +import com.google.common.collect.ImmutableList; +import org.onlab.packet.MacAddress; +import org.onlab.packet.VlanId; +import org.onlab.util.ImmutableByteSequence; +import org.onosproject.net.PortNumber; +import org.onosproject.net.flow.TrafficTreatment; +import org.onosproject.net.flow.instructions.Instruction; +import org.onosproject.net.flow.instructions.Instructions; +import org.onosproject.net.flow.instructions.Instructions.OutputInstruction; +import org.onosproject.net.flow.instructions.L2ModificationInstruction; +import org.onosproject.net.flow.instructions.L2ModificationInstruction.ModEtherInstruction; +import org.onosproject.net.flow.instructions.L2ModificationInstruction.ModVlanIdInstruction; +import org.onosproject.net.pi.model.PiActionId; +import org.onosproject.net.pi.model.PiPipelineInterpreter.PiInterpreterException; +import org.onosproject.net.pi.runtime.PiAction; +import org.onosproject.net.pi.runtime.PiActionParam; + +import java.util.List; + +import static java.lang.String.format; +import static org.onosproject.net.flow.instructions.Instruction.Type.L2MODIFICATION; +import static org.onosproject.net.flow.instructions.Instruction.Type.OUTPUT; +import static org.onosproject.net.flow.instructions.L2ModificationInstruction.L2SubType.ETH_DST; +import static org.onosproject.net.flow.instructions.L2ModificationInstruction.L2SubType.ETH_SRC; +import static org.onosproject.net.flow.instructions.L2ModificationInstruction.L2SubType.VLAN_ID; +import static org.onosproject.net.flow.instructions.L2ModificationInstruction.L2SubType.VLAN_PUSH; + + +final class FabricTreatmentInterpreter { + private static final String INVALID_TREATMENT = "Invalid treatment for %s block: %s"; + + // Hide default constructor + protected FabricTreatmentInterpreter() { + } + + /* + * In Filtering block, we need to implement these actions: + * + * push_internal_vlan + * set_vlan + * nop + * + * Unsupported, using PiAction directly: + * set_forwarding_type + */ + + static PiAction mapFilteringTreatment(TrafficTreatment treatment) + throws PiInterpreterException { + List instructions = treatment.allInstructions(); + Instruction noActInst = Instructions.createNoAction(); + if (instructions.isEmpty() || instructions.contains(noActInst)) { + // nop + return PiAction.builder() + .withId(FabricConstants.ACT_NOP_ID) + .build(); + } + + L2ModificationInstruction.ModVlanHeaderInstruction pushVlanInst = null; + ModVlanIdInstruction setVlanInst = null; + + for (Instruction inst : instructions) { + if (inst.type() == L2MODIFICATION) { + L2ModificationInstruction l2Inst = (L2ModificationInstruction) inst; + + if (l2Inst.subtype() == VLAN_PUSH) { + pushVlanInst = (L2ModificationInstruction.ModVlanHeaderInstruction) l2Inst; + + } else if (l2Inst.subtype() == VLAN_ID) { + setVlanInst = (ModVlanIdInstruction) l2Inst; + } + } + } + + if (setVlanInst == null) { + throw new PiInterpreterException(format(INVALID_TREATMENT, "filtering", treatment)); + } + + VlanId vlanId = setVlanInst.vlanId(); + PiActionParam param = new PiActionParam(FabricConstants.ACT_PRM_NEW_VLAN_ID_ID, + ImmutableByteSequence.copyFrom(vlanId.toShort())); + PiActionId actionId; + if (pushVlanInst != null) { + // push_internal_vlan + actionId = FabricConstants.ACT_PUSH_INTERNAL_VLAN_ID; + } else { + actionId = FabricConstants.ACT_SET_VLAN_ID; + } + + // set_vlan + return PiAction.builder() + .withId(actionId) + .withParameter(param) + .build(); + } + + /* + * In forwarding block, we need to implement these actions: + * duplicate_to_controller + * + * Unsupported, using PiAction directly: + * set_next_id + * push_mpls_and_next_v4 + * push_mpls_and_next_v6 + */ + + public static PiAction mapForwardingTreatment(TrafficTreatment treatment) + throws PiInterpreterException { + List insts = treatment.allInstructions(); + OutputInstruction outInst = insts.stream() + .filter(inst -> inst.type() == OUTPUT) + .map(inst -> (OutputInstruction) inst) + .findFirst() + .orElse(null); + + if (outInst == null) { + throw new PiInterpreterException(format(INVALID_TREATMENT, "forwarding", treatment)); + } + + PortNumber portNumber = outInst.port(); + if (!portNumber.equals(PortNumber.CONTROLLER)) { + throw new PiInterpreterException(format("Unsupported port number %s," + + "supports punt action only", + portNumber)); + } + + return PiAction.builder() + .withId(FabricConstants.ACT_DUPLICATE_TO_CONTROLLER_ID) + .build(); + } + + /* + * In Next block, we need to implement these actions: + * output + * output_ecmp + * set_vlan_output + * + * Unsupported, using PiAction directly: + * set_mcast_group + */ + + public static PiAction mapNextTreatment(TrafficTreatment treatment) + throws PiInterpreterException { + List insts = treatment.allInstructions(); + OutputInstruction outInst = null; + ModEtherInstruction modEthDstInst = null; + ModEtherInstruction modEthSrcInst = null; + ModVlanIdInstruction modVlanIdInst = null; + + // TODO: use matrix to store the combination of instruction + for (Instruction inst : insts) { + switch (inst.type()) { + case L2MODIFICATION: + L2ModificationInstruction l2Inst = (L2ModificationInstruction) inst; + + if (l2Inst.subtype() == ETH_SRC) { + modEthSrcInst = (ModEtherInstruction) l2Inst; + } + + if (l2Inst.subtype() == ETH_DST) { + modEthDstInst = (ModEtherInstruction) l2Inst; + } + + if (l2Inst.subtype() == VLAN_ID) { + modVlanIdInst = (ModVlanIdInstruction) l2Inst; + } + break; + case OUTPUT: + outInst = (OutputInstruction) inst; + break; + default: + break; + } + } + + if (outInst == null) { + throw new PiInterpreterException(format(INVALID_TREATMENT, "next", treatment)); + } + short portNum = (short) outInst.port().toLong(); + PiActionParam portNumParam = new PiActionParam(FabricConstants.ACT_PRM_PORT_NUM_ID, + ImmutableByteSequence.copyFrom(portNum)); + if (modEthDstInst == null && modEthSrcInst == null) { + if (modVlanIdInst != null) { + VlanId vlanId = modVlanIdInst.vlanId(); + PiActionParam vlanParam = + new PiActionParam(FabricConstants.ACT_PRM_NEW_VLAN_ID_ID, + ImmutableByteSequence.copyFrom(vlanId.toShort())); + // set_vlan_output + return PiAction.builder() + .withId(FabricConstants.ACT_SET_VLAN_OUTPUT_ID) + .withParameters(ImmutableList.of(portNumParam, vlanParam)) + .build(); + } else { + // output + return PiAction.builder() + .withId(FabricConstants.ACT_OUTPUT_ID) + .withParameter(portNumParam) + .build(); + } + } + + if (modEthDstInst != null && modEthSrcInst != null) { + // output and rewrite src/dst mac + MacAddress srcMac = modEthSrcInst.mac(); + MacAddress dstMac = modEthDstInst.mac(); + PiActionParam srcMacParam = new PiActionParam(FabricConstants.ACT_PRM_SMAC_ID, + ImmutableByteSequence.copyFrom(srcMac.toBytes())); + PiActionParam dstMacParam = new PiActionParam(FabricConstants.ACT_PRM_DMAC_ID, + ImmutableByteSequence.copyFrom(dstMac.toBytes())); + return PiAction.builder() + .withId(FabricConstants.ACT_L3_ROUTING_ID) + .withParameters(ImmutableList.of(portNumParam, + srcMacParam, + dstMacParam)) + .build(); + } + + throw new PiInterpreterException(format(INVALID_TREATMENT, "next", treatment)); + } +} diff --git a/pipelines/fabric/src/main/java/org/onosproject/pipelines/fabric/PipeconfLoader.java b/pipelines/fabric/src/main/java/org/onosproject/pipelines/fabric/PipeconfLoader.java index f7fc45ff10..2eec587723 100644 --- a/pipelines/fabric/src/main/java/org/onosproject/pipelines/fabric/PipeconfLoader.java +++ b/pipelines/fabric/src/main/java/org/onosproject/pipelines/fabric/PipeconfLoader.java @@ -26,6 +26,7 @@ import org.onosproject.net.device.PortStatisticsDiscovery; import org.onosproject.net.pi.model.DefaultPiPipeconf; import org.onosproject.net.pi.model.PiPipeconf; import org.onosproject.net.pi.model.PiPipeconfId; +import org.onosproject.net.pi.model.PiPipelineInterpreter; import org.onosproject.net.pi.model.PiPipelineModel; import org.onosproject.net.pi.service.PiPipeconfService; import org.onosproject.p4runtime.model.P4InfoParser; @@ -76,6 +77,7 @@ public class PipeconfLoader { return DefaultPiPipeconf.builder() .withId(FABRIC_PIPECONF_ID) .withPipelineModel(model) + .addBehaviour(PiPipelineInterpreter.class, FabricInterpreter.class) .addBehaviour(PortStatisticsDiscovery.class, FabricPortStatisticsDiscovery.class) .addExtension(P4_INFO_TEXT, p4InfoUrl) .addExtension(BMV2_JSON, jsonUrl) diff --git a/pipelines/fabric/src/test/java/org/onosproject/pipelines/fabric/FabricInterpreterTest.java b/pipelines/fabric/src/test/java/org/onosproject/pipelines/fabric/FabricInterpreterTest.java new file mode 100644 index 0000000000..76bd030816 --- /dev/null +++ b/pipelines/fabric/src/test/java/org/onosproject/pipelines/fabric/FabricInterpreterTest.java @@ -0,0 +1,201 @@ +/* + * 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; + +import com.google.common.collect.ImmutableList; +import org.junit.Before; +import org.junit.Test; +import org.onlab.packet.MacAddress; +import org.onlab.packet.VlanId; +import org.onlab.util.ImmutableByteSequence; +import org.onosproject.net.PortNumber; +import org.onosproject.net.flow.DefaultTrafficTreatment; +import org.onosproject.net.flow.TrafficTreatment; +import org.onosproject.net.pi.runtime.PiAction; +import org.onosproject.net.pi.runtime.PiActionParam; + +import static org.junit.Assert.assertEquals; + +/** + * Test for fabric interpreter. + */ +public class FabricInterpreterTest { + private static final VlanId VLAN_100 = VlanId.vlanId("100"); + private static final PortNumber PORT_1 = PortNumber.portNumber(1); + private static final MacAddress SRC_MAC = MacAddress.valueOf("00:00:00:00:00:01"); + private static final MacAddress DST_MAC = MacAddress.valueOf("00:00:00:00:00:02"); + + private FabricInterpreter interpreter; + + @Before + public void setup() { + interpreter = new FabricInterpreter(); + } + + /* Filtering control block */ + + /** + * Map treatment to push_internal_vlan action. + */ + @Test + public void testFilteringTreatment1() throws Exception { + TrafficTreatment treatment = DefaultTrafficTreatment.builder() + .pushVlan() + .setVlanId(VLAN_100) + .build(); + PiAction mappedAction = interpreter.mapTreatment(treatment, + FabricConstants.TBL_INGRESS_PORT_VLAN_ID); + PiActionParam param = new PiActionParam(FabricConstants.ACT_PRM_NEW_VLAN_ID_ID, + ImmutableByteSequence.copyFrom(VLAN_100.toShort())); + PiAction expectedAction = PiAction.builder() + .withId(FabricConstants.ACT_PUSH_INTERNAL_VLAN_ID) + .withParameter(param) + .build(); + + assertEquals(expectedAction, mappedAction); + } + + /** + * Map treatment to set_vlan action. + */ + @Test + public void testFilteringTreatment2() throws Exception { + TrafficTreatment treatment = DefaultTrafficTreatment.builder() + .setVlanId(VLAN_100) + .build(); + PiAction mappedAction = interpreter.mapTreatment(treatment, + FabricConstants.TBL_INGRESS_PORT_VLAN_ID); + PiActionParam param = new PiActionParam(FabricConstants.ACT_PRM_NEW_VLAN_ID_ID, + ImmutableByteSequence.copyFrom(VLAN_100.toShort())); + PiAction expectedAction = PiAction.builder() + .withId(FabricConstants.ACT_SET_VLAN_ID) + .withParameter(param) + .build(); + + assertEquals(expectedAction, mappedAction); + } + + /** + * Map treatment to nop action. + */ + @Test + public void testFilteringTreatment3() throws Exception { + TrafficTreatment treatment = DefaultTrafficTreatment.emptyTreatment(); + PiAction mappedAction = interpreter.mapTreatment(treatment, + FabricConstants.TBL_INGRESS_PORT_VLAN_ID); + PiAction expectedAction = PiAction.builder() + .withId(FabricConstants.ACT_NOP_ID) + .build(); + + assertEquals(expectedAction, mappedAction); + } + + /* Forwarding control block */ + + /** + * Map treatment to duplicate_to_controller action. + */ + @Test + public void testForwardingTreatment1() throws Exception { + TrafficTreatment treatment = DefaultTrafficTreatment.builder() + .punt() + .build(); + PiAction mappedAction = interpreter.mapTreatment(treatment, + FabricConstants.TBL_ACL_ID); + PiAction expectedAction = PiAction.builder() + .withId(FabricConstants.ACT_DUPLICATE_TO_CONTROLLER_ID) + .build(); + + assertEquals(expectedAction, mappedAction); + } + + /* Next control block */ + + /** + * Map treatment to output action. + */ + @Test + public void testNextTreatment1() throws Exception { + TrafficTreatment treatment = DefaultTrafficTreatment.builder() + .setOutput(PORT_1) + .build(); + PiAction mappedAction = interpreter.mapTreatment(treatment, + FabricConstants.TBL_SIMPLE_ID); + short portNumVal = (short) PORT_1.toLong(); + PiActionParam param = new PiActionParam(FabricConstants.ACT_PRM_PORT_NUM_ID, + ImmutableByteSequence.copyFrom(portNumVal)); + PiAction expectedAction = PiAction.builder() + .withId(FabricConstants.ACT_OUTPUT_ID) + .withParameter(param) + .build(); + + assertEquals(expectedAction, mappedAction); + } + + /** + * Map treatment to output_ecmp action. + */ + @Test + public void testNextTreatment2() throws Exception { + TrafficTreatment treatment = DefaultTrafficTreatment.builder() + .setEthSrc(SRC_MAC) + .setEthDst(DST_MAC) + .setOutput(PORT_1) + .build(); + PiAction mappedAction = interpreter.mapTreatment(treatment, + FabricConstants.TBL_HASHED_ID); + short portNumVal = (short) PORT_1.toLong(); + PiActionParam ethSrcParam = new PiActionParam(FabricConstants.ACT_PRM_SMAC_ID, + ImmutableByteSequence.copyFrom(SRC_MAC.toBytes())); + PiActionParam ethDstParam = new PiActionParam(FabricConstants.ACT_PRM_DMAC_ID, + ImmutableByteSequence.copyFrom(DST_MAC.toBytes())); + PiActionParam portParam = new PiActionParam(FabricConstants.ACT_PRM_PORT_NUM_ID, + ImmutableByteSequence.copyFrom(portNumVal)); + PiAction expectedAction = PiAction.builder() + .withId(FabricConstants.ACT_L3_ROUTING_ID) + .withParameters(ImmutableList.of(ethSrcParam, ethDstParam, portParam)) + .build(); + + assertEquals(expectedAction, mappedAction); + } + + /** + * Map treatment to set_vlan_output action. + */ + @Test + public void testNextTreatment3() throws Exception { + TrafficTreatment treatment = DefaultTrafficTreatment.builder() + .setVlanId(VLAN_100) + .setOutput(PORT_1) + .build(); + PiAction mappedAction = interpreter.mapTreatment(treatment, + FabricConstants.TBL_SIMPLE_ID); + short portNumVal = (short) PORT_1.toLong(); + PiActionParam portParam = new PiActionParam(FabricConstants.ACT_PRM_PORT_NUM_ID, + ImmutableByteSequence.copyFrom(portNumVal)); + short vlanVal = VLAN_100.toShort(); + PiActionParam vlanParam = new PiActionParam(FabricConstants.ACT_PRM_NEW_VLAN_ID_ID, + ImmutableByteSequence.copyFrom(vlanVal)); + + PiAction expectedAction = PiAction.builder() + .withId(FabricConstants.ACT_SET_VLAN_OUTPUT_ID) + .withParameters(ImmutableList.of(vlanParam, portParam)) + .build(); + + assertEquals(expectedAction, mappedAction); + } +}