MyTunnel P4 tutorial app and pipeconf

Change-Id: I0549276fc7f6c8d0d244d6c52b1b9e85b9c3e13c
This commit is contained in:
Carmelo Cascone 2018-04-11 12:02:16 -07:00
parent 181f3f46cc
commit 700648c993
15 changed files with 2397 additions and 1519 deletions

View File

@ -1,25 +0,0 @@
COMPILE_DEPS = [
'//lib:CORE_DEPS',
'//apps/p4-tutorial/pipeconf:onos-apps-p4-tutorial-pipeconf',
]
osgi_jar (
deps = COMPILE_DEPS,
)
BUNDLES = [
'//apps/p4-tutorial/icmpdropper:onos-apps-p4-tutorial-icmpdropper',
]
onos_app (
app_name = 'org.onosproject.p4tutorial.icmpdropper',
title = 'ICMP Dropper (P4 Tutorial)',
category = 'Security',
url = 'http://onosproject.org',
description = 'Inhibits forwarding of ICMP packets for PI-enabled devices using the ' +
'p4-tutorial-pipeconf.',
included_bundles = BUNDLES,
required_apps = [
'org.onosproject.p4tutorial.pipeconf',
]
)

View File

@ -1,167 +0,0 @@
/*
* 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.p4tutorial.icmpdropper;
import org.apache.felix.scr.annotations.Activate;
import org.apache.felix.scr.annotations.Component;
import org.apache.felix.scr.annotations.Deactivate;
import org.apache.felix.scr.annotations.Reference;
import org.apache.felix.scr.annotations.ReferenceCardinality;
import org.onosproject.app.ApplicationAdminService;
import org.onosproject.core.ApplicationId;
import org.onosproject.core.CoreService;
import org.onosproject.net.Device;
import org.onosproject.net.DeviceId;
import org.onosproject.net.device.DeviceEvent;
import org.onosproject.net.device.DeviceListener;
import org.onosproject.net.device.DeviceService;
import org.onosproject.net.flow.DefaultFlowRule;
import org.onosproject.net.flow.DefaultTrafficSelector;
import org.onosproject.net.flow.DefaultTrafficTreatment;
import org.onosproject.net.flow.FlowRule;
import org.onosproject.net.flow.FlowRuleService;
import org.onosproject.net.flow.criteria.PiCriterion;
import org.onosproject.net.behaviour.PiPipelineProgrammable;
import org.onosproject.net.pi.runtime.PiAction;
import org.onosproject.net.pi.model.PiActionId;
import org.onosproject.net.pi.model.PiMatchFieldId;
import org.onosproject.net.pi.service.PiPipeconfService;
import org.onosproject.net.pi.model.PiTableId;
import org.onosproject.p4tutorial.pipeconf.PipeconfFactory;
import org.slf4j.Logger;
import static org.onosproject.net.device.DeviceEvent.Type.DEVICE_ADDED;
import static org.slf4j.LoggerFactory.getLogger;
/**
* Simple application that drops all ICMP packets.
*/
@Component(immediate = true)
public class IcmpDropper {
private static final Logger log = getLogger(IcmpDropper.class);
private static final String APP_NAME = "org.onosproject.p4tutorial.icmpdropper";
@Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
private DeviceService deviceService;
@Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
private FlowRuleService flowRuleService;
@Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
private ApplicationAdminService appService;
@Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
private CoreService coreService;
@Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
private PiPipeconfService piPipeconfService;
private final DeviceListener deviceListener = new InternalDeviceListener();
private ApplicationId appId;
@Activate
public void activate() {
log.info("Starting...");
appId = coreService.registerApplication(APP_NAME);
// Register listener for handling new devices.
deviceService.addListener(deviceListener);
// Install rules to existing devices.
deviceService.getDevices()
.forEach(device -> installDropRule(device.id()));
log.info("STARTED", appId.id());
}
@Deactivate
public void deactivate() {
log.info("Stopping...");
deviceService.removeListener(deviceListener);
flowRuleService.removeFlowRulesById(appId);
log.info("STOPPED");
}
private boolean checkPipeconf(Device device) {
if (!device.is(PiPipelineProgrammable.class)) {
// Device is not PI-pipeline programmable. Ignore.
return false;
}
if (!piPipeconfService.ofDevice(device.id()).isPresent() ||
!piPipeconfService.ofDevice(device.id()).get().equals(PipeconfFactory.PIPECONF_ID)) {
log.warn("Device {} has pipeconf {} instead of {}, can't install flow rule for this device",
device.id(), piPipeconfService.ofDevice(device.id()).get(), PipeconfFactory.PIPECONF_ID);
return false;
}
return true;
}
private void installDropRule(DeviceId deviceId) {
PiMatchFieldId ipv4ProtoFieldId = PiMatchFieldId.of("hdr.ipv4.protocol");
PiActionId dropActionId = PiActionId.of("_drop");
PiCriterion piCriterion = PiCriterion.builder()
.matchExact(ipv4ProtoFieldId, (byte) 0x01)
.build();
PiAction dropAction = PiAction.builder()
.withId(dropActionId)
.build();
FlowRule flowRule = DefaultFlowRule.builder()
.forDevice(deviceId)
.forTable(PiTableId.of("ip_proto_filter_table"))
.fromApp(appId)
.makePermanent()
.withPriority(1000)
.withSelector(DefaultTrafficSelector.builder()
.matchPi(piCriterion)
.build())
.withTreatment(
DefaultTrafficTreatment.builder()
.piTableAction(dropAction)
.build())
.build();
log.warn("Installing ICMP drop rule to {}", deviceId);
flowRuleService.applyFlowRules(flowRule);
}
/**
* A listener of device events that installs a rule to drop packet for each new device.
*/
private class InternalDeviceListener implements DeviceListener {
@Override
public void event(DeviceEvent event) {
Device device = event.subject();
if (checkPipeconf(device)) {
installDropRule(device.id());
}
}
@Override
public boolean isRelevant(DeviceEvent event) {
// Reacts only to new devices.
return event.type() == DEVICE_ADDED;
}
}
}

View File

@ -0,0 +1,24 @@
COMPILE_DEPS = [
'//lib:CORE_DEPS',
'//apps/p4-tutorial/pipeconf:onos-apps-p4-tutorial-pipeconf',
]
osgi_jar (
deps = COMPILE_DEPS,
)
BUNDLES = [
'//apps/p4-tutorial/mytunnel:onos-apps-p4-tutorial-mytunnel',
]
onos_app (
app_name = 'org.onosproject.p4tutorial.mytunnel',
title = 'MyTunnel Demo App',
category = 'Steering',
url = 'http://onosproject.org',
description = 'Provides forwarding between each pair of hosts via MyTunnel protocol',
included_bundles = BUNDLES,
required_apps = [
'org.onosproject.p4tutorial.pipeconf',
]
)

View File

@ -0,0 +1,323 @@
/*
* 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.p4tutorial.mytunnel;
import com.google.common.collect.Lists;
import org.apache.felix.scr.annotations.Activate;
import org.apache.felix.scr.annotations.Component;
import org.apache.felix.scr.annotations.Deactivate;
import org.apache.felix.scr.annotations.Reference;
import org.apache.felix.scr.annotations.ReferenceCardinality;
import org.onlab.packet.IpAddress;
import org.onosproject.core.ApplicationId;
import org.onosproject.core.CoreService;
import org.onosproject.net.DeviceId;
import org.onosproject.net.Host;
import org.onosproject.net.Link;
import org.onosproject.net.Path;
import org.onosproject.net.PortNumber;
import org.onosproject.net.flow.DefaultFlowRule;
import org.onosproject.net.flow.DefaultTrafficSelector;
import org.onosproject.net.flow.DefaultTrafficTreatment;
import org.onosproject.net.flow.FlowRule;
import org.onosproject.net.flow.FlowRuleService;
import org.onosproject.net.flow.criteria.PiCriterion;
import org.onosproject.net.host.HostEvent;
import org.onosproject.net.host.HostListener;
import org.onosproject.net.host.HostService;
import org.onosproject.net.pi.model.PiActionId;
import org.onosproject.net.pi.model.PiActionParamId;
import org.onosproject.net.pi.model.PiMatchFieldId;
import org.onosproject.net.pi.model.PiTableId;
import org.onosproject.net.pi.runtime.PiAction;
import org.onosproject.net.pi.runtime.PiActionParam;
import org.onosproject.net.topology.Topology;
import org.onosproject.net.topology.TopologyService;
import org.slf4j.Logger;
import java.util.Collections;
import java.util.List;
import java.util.Random;
import java.util.Set;
import java.util.concurrent.atomic.AtomicInteger;
import static org.onlab.util.ImmutableByteSequence.copyFrom;
import static org.slf4j.LoggerFactory.getLogger;
/**
* MyTunnel application which provides forwarding between each pair of hosts via
* MyTunnel protocol as defined in mytunnel.p4.
* <p>
* The app works by listening for host events. Each time a new host is
* discovered, it provisions a tunnel between that host and all the others.
*/
@Component(immediate = true)
public class MyTunnelApp {
private static final String APP_NAME = "org.onosproject.p4tutorial.mytunnel";
// Default priority used for flow rules installed by this app.
private static final int FLOW_RULE_PRIORITY = 100;
private final HostListener hostListener = new InternalHostListener();
private ApplicationId appId;
private AtomicInteger nextTunnelId = new AtomicInteger();
private static final Logger log = getLogger(MyTunnelApp.class);
//--------------------------------------------------------------------------
// P4 program entity names. Values derived from mytunnel.p4info
//--------------------------------------------------------------------------
// Match field IDs.
private static final PiMatchFieldId MF_ID_MY_TUNNEL_DST_ID =
PiMatchFieldId.of("hdr.my_tunnel.tun_id");
private static final PiMatchFieldId MF_ID_IP_DST =
PiMatchFieldId.of("hdr.ipv4.dst_addr");
// Table IDs.
private static final PiTableId TBL_ID_TUNNEL_FWD =
PiTableId.of("c_ingress.t_tunnel_fwd");
private static final PiTableId TBL_ID_TUNNEL_INGRESS =
PiTableId.of("c_ingress.t_tunnel_ingress");
// Action IDs.
private static final PiActionId ACT_ID_MY_TUNNEL_INGRESS =
PiActionId.of("c_ingress.my_tunnel_ingress");
private static final PiActionId ACT_ID_SET_OUT_PORT =
PiActionId.of("c_ingress.set_out_port");
private static final PiActionId ACT_ID_MY_TUNNEL_EGRESS =
PiActionId.of("c_ingress.my_tunnel_egress");
// Action params ID.
private static final PiActionParamId ACT_PARAM_ID_TUN_ID =
PiActionParamId.of("tun_id");
private static final PiActionParamId ACT_PARAM_ID_PORT =
PiActionParamId.of("port");
//--------------------------------------------------------------------------
// ONOS services needed by this application.
//--------------------------------------------------------------------------
@Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
private FlowRuleService flowRuleService;
@Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
private CoreService coreService;
@Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
private TopologyService topologyService;
@Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
private HostService hostService;
//--------------------------------------------------------------------------
//--------------------------------------------------------------------------
@Activate
public void activate() {
// Register app and event listeners.
log.info("Starting...");
appId = coreService.registerApplication(APP_NAME);
hostService.addListener(hostListener);
log.info("STARTED", appId.id());
}
@Deactivate
public void deactivate() {
// Remove listeners and clean-up flow rules.
log.info("Stopping...");
hostService.removeListener(hostListener);
flowRuleService.removeFlowRulesById(appId);
log.info("STOPPED");
}
/**
* Provisions a tunnel between the given source and destination host with
* the given tunnel ID. The tunnel is established using a randomly picked
* shortest path based on the given topology snapshot.
*
* @param tunId tunnel ID
* @param srcHost tunnel source host
* @param dstHost tunnel destination host
* @param topo topology snapshot
*/
private void provisionTunnel(int tunId, Host srcHost, Host dstHost, Topology topo) {
// Get all shortest paths between switches connected to source and
// destination hosts.
DeviceId srcSwitch = srcHost.location().deviceId();
DeviceId dstSwitch = dstHost.location().deviceId();
List<Link> pathLinks;
if (srcSwitch.equals(dstSwitch)) {
// Source and dest hosts are connected to the same switch.
pathLinks = Collections.emptyList();
} else {
// Compute shortest path.
Set<Path> allPaths = topologyService.getPaths(topo, srcSwitch, dstSwitch);
if (allPaths.size() == 0) {
log.warn("No paths between {} and {}", srcHost.id(), dstHost.id());
return;
}
// If many shortest paths are available, pick a random one.
pathLinks = pickRandomPath(allPaths).links();
}
// Tunnel ingress rules.
for (IpAddress dstIpAddr : dstHost.ipAddresses()) {
// In ONOS discovered hosts can have multiple IP addresses.
// Insert tunnel ingress rule for each IP address.
// Next switches will forward based only on tunnel ID.
insertIngressRule(srcSwitch, dstIpAddr, tunId);
}
// Insert tunnel forward rules on all switches in the path, excluded the
// last one.
for (Link link : pathLinks) {
DeviceId sw = link.src().deviceId();
PortNumber port = link.src().port();
insertForwardRule(sw, port, tunId, false);
}
// Tunnel egress rule.
PortNumber egressSwitchPort = dstHost.location().port();
insertForwardRule(dstSwitch, egressSwitchPort, tunId, true);
log.info("** Completed provisioning of tunnel {} (srcHost={} dstHost={})",
tunId, srcHost.id(), dstHost.id());
}
/**
* Generates and insert a flow rule to perform the tunnel INGRESS function
* for the given switch, destination IP address and tunnel ID.
*
* @param switchId switch ID
* @param dstIpAddr IP address to forward inside the tunnel
* @param tunId tunnel ID
*/
private void insertIngressRule(DeviceId switchId,
IpAddress dstIpAddr,
int tunId) {
log.info("Inserting INGRESS rule: switchId={}, dstIpAddr={}, tunId={}",
switchId, dstIpAddr, tunId);
PiCriterion match = PiCriterion.builder()
.matchLpm(MF_ID_IP_DST, dstIpAddr.toOctets(), 32)
.build();
PiAction action = PiAction.builder()
.withId(ACT_ID_MY_TUNNEL_INGRESS)
.withParameter(new PiActionParam(
ACT_PARAM_ID_TUN_ID, copyFrom(tunId)))
.build();
insertPiFlowRule(switchId, TBL_ID_TUNNEL_INGRESS, match, action);
}
/**
* Generates and insert a flow rule to perform the tunnel FORWARD/EGRESS
* function for the given switch, output port address and tunnel ID.
*
* @param switchId switch ID
* @param outPort output port where to forward tunneled packets
* @param tunId tunnel ID
* @param isEgress if true, perform tunnel egress action, otherwise forward
* packet as is to port
*/
private void insertForwardRule(DeviceId switchId,
PortNumber outPort,
int tunId,
boolean isEgress) {
log.info("Inserting {} rule: switchId={}, outPort={}, tunId={}",
isEgress ? "EGRESS" : "FORWARD", switchId, outPort, tunId);
PiCriterion match = PiCriterion.builder()
.matchExact(MF_ID_MY_TUNNEL_DST_ID, tunId)
.build();
PiActionId actionId = isEgress ? ACT_ID_MY_TUNNEL_EGRESS : ACT_ID_SET_OUT_PORT;
PiAction action = PiAction.builder()
.withId(actionId)
.withParameter(new PiActionParam(
ACT_PARAM_ID_PORT, copyFrom((short) outPort.toLong())))
.build();
insertPiFlowRule(switchId, TBL_ID_TUNNEL_FWD, match, action);
}
/**
* Inserts a flow rule in the system that using a PI criterion and action.
*
* @param switchId switch ID
* @param tableId table ID
* @param piCriterion PI criterion
* @param piAction PI action
*/
private void insertPiFlowRule(DeviceId switchId, PiTableId tableId,
PiCriterion piCriterion, PiAction piAction) {
FlowRule rule = DefaultFlowRule.builder()
.forDevice(switchId)
.forTable(tableId)
.fromApp(appId)
.withPriority(FLOW_RULE_PRIORITY)
.makePermanent()
.withSelector(DefaultTrafficSelector.builder()
.matchPi(piCriterion).build())
.withTreatment(DefaultTrafficTreatment.builder()
.piTableAction(piAction).build())
.build();
flowRuleService.applyFlowRules(rule);
}
private int getNewTunnelId() {
return nextTunnelId.incrementAndGet();
}
private Path pickRandomPath(Set<Path> paths) {
int item = new Random().nextInt(paths.size());
List<Path> pathList = Lists.newArrayList(paths);
return pathList.get(item);
}
/**
* A listener of host events that provisions two tunnels for each pair of
* hosts when a new host is discovered.
*/
private class InternalHostListener implements HostListener {
@Override
public void event(HostEvent event) {
if (event.type() != HostEvent.Type.HOST_ADDED) {
// Ignore other host events.
return;
}
synchronized (this) {
// Synchronizing here is an overkill, but safer for demo purposes.
Host host = event.subject();
Topology topo = topologyService.currentTopology();
for (Host otherHost : hostService.getHosts()) {
if (!host.equals(otherHost)) {
provisionTunnel(getNewTunnelId(), host, otherHost, topo);
provisionTunnel(getNewTunnelId(), otherHost, host, topo);
}
}
}
}
}
}

View File

@ -16,4 +16,4 @@
/**
* P4 tutorial application classes.
*/
package org.onosproject.p4tutorial.icmpdropper;
package org.onosproject.p4tutorial.mytunnel;

View File

@ -45,8 +45,8 @@ import static org.onosproject.net.pi.model.PiPipeconf.ExtensionType.P4_INFO_TEXT
public final class PipeconfFactory {
public static final PiPipeconfId PIPECONF_ID = new PiPipeconfId("p4-tutorial-pipeconf");
private static final URL P4INFO_URL = PipeconfFactory.class.getResource("/main.p4info");
private static final URL BMV2_JSON_URL = PipeconfFactory.class.getResource("/main.json");
private static final URL P4INFO_URL = PipeconfFactory.class.getResource("/mytunnel.p4info");
private static final URL BMV2_JSON_URL = PipeconfFactory.class.getResource("/mytunnel.json");
@Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
private PiPipeconfService piPipeconfService;
@ -75,7 +75,7 @@ public final class PipeconfFactory {
.withPipelineModel(pipelineModel)
.addBehaviour(PiPipelineInterpreter.class, PipelineInterpreterImpl.class)
.addBehaviour(PortStatisticsDiscovery.class, PortStatisticsDiscoveryImpl.class)
// Since main.p4 defines only 1 table, we re-use the existing single-table pipeliner.
// Since mytunnel.p4 defines only 1 table, we re-use the existing single-table pipeliner.
.addBehaviour(Pipeliner.class, DefaultSingleTablePipeline.class)
.addExtension(P4_INFO_TEXT, P4INFO_URL)
.addExtension(BMV2_JSON, BMV2_JSON_URL)

View File

@ -19,10 +19,12 @@ package org.onosproject.p4tutorial.pipeconf;
import com.google.common.collect.BiMap;
import com.google.common.collect.ImmutableBiMap;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.Lists;
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;
@ -30,7 +32,7 @@ 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.Instruction;
import org.onosproject.net.flow.instructions.Instructions;
import org.onosproject.net.flow.instructions.Instructions.OutputInstruction;
import org.onosproject.net.packet.DefaultInboundPacket;
import org.onosproject.net.packet.InboundPacket;
import org.onosproject.net.packet.OutboundPacket;
@ -51,7 +53,6 @@ import java.util.List;
import java.util.Optional;
import static java.lang.String.format;
import static java.util.stream.Collectors.toList;
import static org.onlab.util.ImmutableByteSequence.copyFrom;
import static org.onosproject.net.PortNumber.CONTROLLER;
import static org.onosproject.net.PortNumber.FLOOD;
@ -59,34 +60,48 @@ import static org.onosproject.net.flow.instructions.Instruction.Type.OUTPUT;
import static org.onosproject.net.pi.model.PiPacketOperationType.PACKET_OUT;
/**
* Implementation of a PI interpreter for the main.p4 program.
* Implementation of a pipeline interpreter for the mytunnel.p4 program.
*/
public final class PipelineInterpreterImpl extends AbstractHandlerBehaviour implements PiPipelineInterpreter {
public final class PipelineInterpreterImpl
extends AbstractHandlerBehaviour
implements PiPipelineInterpreter {
private static final String DOT = ".";
private static final String DOT = ".";
private static final String HDR = "hdr";
private static final String TABLE0 = "table0";
private static final String IP_PROTO_FILTER_TABLE = "ip_proto_filter_table";
private static final String SEND_TO_CPU = "send_to_cpu";
private static final String PORT = "port";
private static final String DROP = "_drop";
private static final String SET_EGRESS_PORT = "set_egress_port";
private static final String C_INGRESS = "c_ingress";
private static final String T_L2_FWD = "t_l2_fwd";
private static final String EGRESS_PORT = "egress_port";
private static final String INGRESS_PORT = "ingress_port";
private static final String ETHERNET = "ethernet";
private static final String STANDARD_METADATA = "standard_metadata";
private static final int PORT_FIELD_BITWIDTH = 9;
private static final PiMatchFieldId INGRESS_PORT_ID = PiMatchFieldId.of(STANDARD_METADATA + DOT + "ingress_port");
private static final PiMatchFieldId ETH_DST_ID = PiMatchFieldId.of(HDR + DOT + ETHERNET + DOT + "dst_addr");
private static final PiMatchFieldId ETH_SRC_ID = PiMatchFieldId.of(HDR + DOT + ETHERNET + DOT + "src_addr");
private static final PiMatchFieldId ETH_TYPE_ID = PiMatchFieldId.of(HDR + DOT + ETHERNET + DOT + "ether_type");
private static final PiTableId TABLE0_ID = PiTableId.of(TABLE0);
private static final PiTableId IP_PROTO_FILTER_TABLE_ID = PiTableId.of(IP_PROTO_FILTER_TABLE);
private static final PiMatchFieldId INGRESS_PORT_ID =
PiMatchFieldId.of(STANDARD_METADATA + DOT + "ingress_port");
private static final PiMatchFieldId ETH_DST_ID =
PiMatchFieldId.of(HDR + DOT + ETHERNET + DOT + "dst_addr");
private static final PiMatchFieldId ETH_SRC_ID =
PiMatchFieldId.of(HDR + DOT + ETHERNET + DOT + "src_addr");
private static final PiMatchFieldId ETH_TYPE_ID =
PiMatchFieldId.of(HDR + DOT + ETHERNET + DOT + "ether_type");
private static final BiMap<Integer, PiTableId> TABLE_MAP = ImmutableBiMap.of(
0, TABLE0_ID,
1, IP_PROTO_FILTER_TABLE_ID);
private static final PiTableId TABLE_L2_FWD_ID =
PiTableId.of(C_INGRESS + DOT + T_L2_FWD);
private static final PiActionId ACT_ID_NOP =
PiActionId.of("NoAction");
private static final PiActionId ACT_ID_SEND_TO_CPU =
PiActionId.of(C_INGRESS + DOT + "send_to_cpu");
private static final PiActionId ACT_ID_SET_EGRESS_PORT =
PiActionId.of(C_INGRESS + DOT + "set_egress_port");
private static final PiActionParamId ACT_PARAM_ID_PORT =
PiActionParamId.of("port");
private static final BiMap<Integer, PiTableId> TABLE_MAP =
new ImmutableBiMap.Builder<Integer, PiTableId>()
.put(0, TABLE_L2_FWD_ID)
.build();
private static final BiMap<Criterion.Type, PiMatchFieldId> CRITERION_MAP =
new ImmutableBiMap.Builder<Criterion.Type, PiMatchFieldId>()
@ -96,129 +111,6 @@ public final class PipelineInterpreterImpl extends AbstractHandlerBehaviour impl
.put(Criterion.Type.ETH_TYPE, ETH_TYPE_ID)
.build();
@Override
public PiAction mapTreatment(TrafficTreatment treatment, PiTableId piTableId)
throws PiInterpreterException {
if (treatment.allInstructions().size() == 0) {
// No instructions means drop for us.
return PiAction.builder()
.withId(PiActionId.of(DROP))
.build();
} else if (treatment.allInstructions().size() > 1) {
// We understand treatments with only 1 instruction.
throw new PiInterpreterException("Treatment has multiple instructions");
}
// Get the first and only instruction.
Instruction instruction = treatment.allInstructions().get(0);
switch (instruction.type()) {
case OUTPUT:
// We understand only instructions of type OUTPUT.
Instructions.OutputInstruction outInstruction = (Instructions.OutputInstruction) instruction;
PortNumber port = outInstruction.port();
if (!port.isLogical()) {
return PiAction.builder()
.withId(PiActionId.of(SET_EGRESS_PORT))
.withParameter(new PiActionParam(PiActionParamId.of(PORT), copyFrom(port.toLong())))
.build();
} else if (port.equals(CONTROLLER)) {
return PiAction.builder()
.withId(PiActionId.of(SEND_TO_CPU))
.build();
} else {
throw new PiInterpreterException(format("Output on logical port '%s' not supported", port));
}
default:
throw new PiInterpreterException(format("Instruction of type '%s' not supported", instruction.type()));
}
}
@Override
public Collection<PiPacketOperation> mapOutboundPacket(OutboundPacket packet)
throws PiInterpreterException {
TrafficTreatment treatment = packet.treatment();
// We support only packet-out with OUTPUT instructions.
List<Instructions.OutputInstruction> 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<PiPacketOperation> 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 main.p4 does not support flooding, we create a packet operation for each switch port.
DeviceService deviceService = handler().get(DeviceService.class);
for (Port port : deviceService.getPorts(packet.sendThrough())) {
builder.add(createPiPacketOperation(packet.data(), port.number().toLong()));
}
} else {
builder.add(createPiPacketOperation(packet.data(), outInst.port().toLong()));
}
}
return builder.build();
}
@Override
public InboundPacket mapInboundPacket(PiPacketOperation packetIn)
throws PiInterpreterException {
// We assume that the packet is ethernet, which is fine since default.p4 can deparse only ethernet packets.
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<PiControlMetadata> packetMetadata = packetIn.metadatas().stream()
.filter(metadata -> metadata.id().toString().equals(INGRESS_PORT))
.findFirst();
if (packetMetadata.isPresent()) {
ImmutableByteSequence portByteSequence = packetMetadata.get().value();
short s = portByteSequence.asReadOnlyBuffer().getShort();
ConnectPoint receivedFrom = new ConnectPoint(packetIn.deviceId(), PortNumber.portNumber(s));
return new DefaultInboundPacket(receivedFrom, ethPkt, packetIn.data().asReadOnlyBuffer());
} else {
throw new PiInterpreterException(format(
"Missing metadata '%s' in packet-in received from '%s': %s",
INGRESS_PORT, packetIn.deviceId(), packetIn));
}
}
private PiPacketOperation createPiPacketOperation(ByteBuffer data, long portNumber) throws PiInterpreterException {
PiControlMetadata metadata = createControlMetadata(portNumber);
return PiPacketOperation.builder()
.forDevice(this.data().deviceId())
.withType(PACKET_OUT)
.withData(copyFrom(data))
.withMetadatas(ImmutableList.of(metadata))
.build();
}
private PiControlMetadata createControlMetadata(long portNumber) throws PiInterpreterException {
try {
return PiControlMetadata.builder()
.withId(PiControlMetadataId.of(EGRESS_PORT))
.withValue(copyFrom(portNumber).fit(PORT_FIELD_BITWIDTH))
.build();
} catch (ImmutableByteSequence.ByteSequenceTrimException e) {
throw new PiInterpreterException(format("Port number %d too big, %s", portNumber, e.getMessage()));
}
}
@Override
public Optional<PiMatchFieldId> mapCriterionType(Criterion.Type type) {
return Optional.ofNullable(CRITERION_MAP.get(type));
@ -238,4 +130,139 @@ public final class PipelineInterpreterImpl extends AbstractHandlerBehaviour impl
public Optional<Integer> mapPiTableId(PiTableId piTableId) {
return Optional.ofNullable(TABLE_MAP.inverse().get(piTableId));
}
@Override
public PiAction mapTreatment(TrafficTreatment treatment, PiTableId piTableId)
throws PiInterpreterException {
if (piTableId != TABLE_L2_FWD_ID) {
throw new PiInterpreterException(
"Can map treatments only for 't_l2_fwd' table");
}
if (treatment.allInstructions().size() == 0) {
// 0 instructions means "NoAction"
return PiAction.builder().withId(ACT_ID_NOP).build();
} else if (treatment.allInstructions().size() > 1) {
// We understand treatments with only 1 instruction.
throw new PiInterpreterException("Treatment has multiple instructions");
}
// Get the first and only instruction.
Instruction instruction = treatment.allInstructions().get(0);
if (instruction.type() != OUTPUT) {
// We can map only instructions of type OUTPUT.
throw new PiInterpreterException(format(
"Instruction of type '%s' not supported", instruction.type()));
}
OutputInstruction outInstruction = (OutputInstruction) instruction;
PortNumber port = outInstruction.port();
if (!port.isLogical()) {
return PiAction.builder()
.withId(ACT_ID_SET_EGRESS_PORT)
.withParameter(new PiActionParam(
ACT_PARAM_ID_PORT, copyFrom(port.toLong())))
.build();
} else if (port.equals(CONTROLLER)) {
return PiAction.builder()
.withId(ACT_ID_SEND_TO_CPU)
.build();
} else {
throw new PiInterpreterException(format(
"Output on logical port '%s' not supported", port));
}
}
@Override
public Collection<PiPacketOperation> mapOutboundPacket(OutboundPacket packet)
throws PiInterpreterException {
TrafficTreatment treatment = packet.treatment();
// We support only packet-out with OUTPUT instructions.
if (treatment.allInstructions().size() != 1 &&
treatment.allInstructions().get(0).type() != OUTPUT) {
throw new PiInterpreterException(
"Treatment not supported: " + treatment.toString());
}
Instruction instruction = treatment.allInstructions().get(0);
PortNumber port = ((OutputInstruction) instruction).port();
List<PiPacketOperation> piPacketOps = Lists.newArrayList();
if (!port.isLogical()) {
piPacketOps.add(createPiPacketOp(packet.data(), port.toLong()));
} else if (port.equals(FLOOD)) {
// Since mytunnel.p4 does not support flooding, we create a packet
// operation for each switch port.
DeviceService deviceService = handler().get(DeviceService.class);
DeviceId deviceId = packet.sendThrough();
for (Port p : deviceService.getPorts(deviceId)) {
piPacketOps.add(createPiPacketOp(packet.data(), p.number().toLong()));
}
} else {
throw new PiInterpreterException(format(
"Output on logical port '%s' not supported", port));
}
return piPacketOps;
}
@Override
public InboundPacket mapInboundPacket(PiPacketOperation packetIn)
throws PiInterpreterException {
// We assume that the packet is ethernet, which is fine since mytunnel.p4
// can deparse only ethernet packets.
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<PiControlMetadata> packetMetadata = packetIn.metadatas().stream()
.filter(metadata -> metadata.id().toString().equals(INGRESS_PORT))
.findFirst();
if (packetMetadata.isPresent()) {
short s = packetMetadata.get().value().asReadOnlyBuffer().getShort();
ConnectPoint receivedFrom = new ConnectPoint(
packetIn.deviceId(), PortNumber.portNumber(s));
return new DefaultInboundPacket(
receivedFrom, ethPkt, packetIn.data().asReadOnlyBuffer());
} else {
throw new PiInterpreterException(format(
"Missing metadata '%s' in packet-in received from '%s': %s",
INGRESS_PORT, packetIn.deviceId(), packetIn));
}
}
private PiPacketOperation createPiPacketOp(ByteBuffer data, long portNumber)
throws PiInterpreterException {
PiControlMetadata metadata = createControlMetadata(portNumber);
return PiPacketOperation.builder()
.forDevice(this.data().deviceId())
.withType(PACKET_OUT)
.withData(copyFrom(data))
.withMetadatas(ImmutableList.of(metadata))
.build();
}
private PiControlMetadata createControlMetadata(long portNumber)
throws PiInterpreterException {
try {
return PiControlMetadata.builder()
.withId(PiControlMetadataId.of(EGRESS_PORT))
.withValue(copyFrom(portNumber).fit(PORT_FIELD_BITWIDTH))
.build();
} catch (ImmutableByteSequence.ByteSequenceTrimException e) {
throw new PiInterpreterException(format(
"Port number %d too big, %s", portNumber, e.getMessage()));
}
}
}

View File

@ -44,15 +44,15 @@ import java.util.stream.Collectors;
import static org.onosproject.net.pi.model.PiCounterType.INDIRECT;
/**
* Implementation of the PortStatisticsDiscovery behaviour for the main.p4 program. This behaviour works by using a
* Implementation of the PortStatisticsDiscovery behaviour for the mytunnel.p4 program. This behaviour works by using a
* P4Runtime client to read the values of the ingress/egress port counters defined in the P4 program.
*/
public final class PortStatisticsDiscoveryImpl extends AbstractHandlerBehaviour implements PortStatisticsDiscovery {
private static final Logger log = LoggerFactory.getLogger(PortStatisticsDiscoveryImpl.class);
private static final PiCounterId INGRESS_COUNTER_ID = PiCounterId.of("igr_port_counter");
private static final PiCounterId EGRESS_COUNTER_ID = PiCounterId.of("egr_port_counter");
private static final PiCounterId INGRESS_COUNTER_ID = PiCounterId.of("c_ingress.rx_port_counter");
private static final PiCounterId EGRESS_COUNTER_ID = PiCounterId.of("c_ingress.tx_port_counter");
@Override
public Collection<PortStatistics> discoverPortStatistics() {

View File

@ -1,6 +1,4 @@
all: main
all: mytunnel
main: main.p4
p4c-bm2-ss -o main.json --p4runtime-file main.p4info --p4runtime-format text main.p4
# Fix for BMv2/p4c bug...
sed -i -e 's/"value" : "0xff"/"value" : "0x00ff"/g' main.json
mytunnel: mytunnel.p4
p4c-bm2-ss -o mytunnel.json --p4runtime-file mytunnel.p4info --p4runtime-format text mytunnel.p4

View File

@ -1,931 +0,0 @@
{
"program" : "main.p4",
"__meta__" : {
"version" : [2, 7],
"compiler" : "https://github.com/p4lang/p4c"
},
"header_types" : [
{
"name" : "scalars_0",
"id" : 0,
"fields" : [
["tmp", 32, false],
["tmp_0", 32, false]
]
},
{
"name" : "ethernet_t",
"id" : 1,
"fields" : [
["dst_addr", 48, false],
["src_addr", 48, false],
["ether_type", 16, false]
]
},
{
"name" : "ipv4_t",
"id" : 2,
"fields" : [
["version", 4, false],
["ihl", 4, false],
["diffserv", 8, false],
["len", 16, false],
["identification", 16, false],
["flags", 3, false],
["frag_offset", 13, false],
["ttl", 8, false],
["protocol", 8, false],
["hdr_checksum", 16, false],
["src_addr", 32, false],
["dst_addr", 32, false]
]
},
{
"name" : "packet_out_header_t",
"id" : 3,
"fields" : [
["egress_port", 9, false],
["_padding", 7, false]
]
},
{
"name" : "packet_in_header_t",
"id" : 4,
"fields" : [
["ingress_port", 9, false],
["_padding_0", 7, false]
]
},
{
"name" : "standard_metadata",
"id" : 5,
"fields" : [
["ingress_port", 9, false],
["egress_spec", 9, false],
["egress_port", 9, false],
["clone_spec", 32, false],
["instance_type", 32, false],
["drop", 1, false],
["recirculate_port", 16, false],
["packet_length", 32, false],
["enq_timestamp", 32, false],
["enq_qdepth", 19, false],
["deq_timedelta", 32, false],
["deq_qdepth", 19, false],
["ingress_global_timestamp", 48, false],
["lf_field_list", 32, false],
["mcast_grp", 16, false],
["resubmit_flag", 1, false],
["egress_rid", 16, false],
["_padding_1", 5, false]
]
}
],
"headers" : [
{
"name" : "scalars",
"id" : 0,
"header_type" : "scalars_0",
"metadata" : true,
"pi_omit" : true
},
{
"name" : "standard_metadata",
"id" : 1,
"header_type" : "standard_metadata",
"metadata" : true,
"pi_omit" : true
},
{
"name" : "ethernet",
"id" : 2,
"header_type" : "ethernet_t",
"metadata" : false,
"pi_omit" : true
},
{
"name" : "ipv4",
"id" : 3,
"header_type" : "ipv4_t",
"metadata" : false,
"pi_omit" : true
},
{
"name" : "packet_out",
"id" : 4,
"header_type" : "packet_out_header_t",
"metadata" : false,
"pi_omit" : true
},
{
"name" : "packet_in",
"id" : 5,
"header_type" : "packet_in_header_t",
"metadata" : false,
"pi_omit" : true
}
],
"header_stacks" : [],
"header_union_types" : [],
"header_unions" : [],
"header_union_stacks" : [],
"field_lists" : [],
"errors" : [
["NoError", 1],
["PacketTooShort", 2],
["NoMatch", 3],
["StackOutOfBounds", 4],
["HeaderTooShort", 5],
["ParserTimeout", 6]
],
"enums" : [],
"parsers" : [
{
"name" : "parser",
"id" : 0,
"init_state" : "start",
"parse_states" : [
{
"name" : "start",
"id" : 0,
"parser_ops" : [],
"transitions" : [
{
"value" : "0x00ff",
"mask" : null,
"next_state" : "parse_packet_out"
},
{
"value" : "default",
"mask" : null,
"next_state" : "parse_ethernet"
}
],
"transition_key" : [
{
"type" : "field",
"value" : ["standard_metadata", "ingress_port"]
}
]
},
{
"name" : "parse_packet_out",
"id" : 1,
"parser_ops" : [
{
"parameters" : [
{
"type" : "regular",
"value" : "packet_out"
}
],
"op" : "extract"
}
],
"transitions" : [
{
"value" : "default",
"mask" : null,
"next_state" : "parse_ethernet"
}
],
"transition_key" : []
},
{
"name" : "parse_ethernet",
"id" : 2,
"parser_ops" : [
{
"parameters" : [
{
"type" : "regular",
"value" : "ethernet"
}
],
"op" : "extract"
}
],
"transitions" : [
{
"value" : "0x0800",
"mask" : null,
"next_state" : "parse_ipv4"
},
{
"value" : "default",
"mask" : null,
"next_state" : null
}
],
"transition_key" : [
{
"type" : "field",
"value" : ["ethernet", "ether_type"]
}
]
},
{
"name" : "parse_ipv4",
"id" : 3,
"parser_ops" : [
{
"parameters" : [
{
"type" : "regular",
"value" : "ipv4"
}
],
"op" : "extract"
}
],
"transitions" : [
{
"value" : "default",
"mask" : null,
"next_state" : null
}
],
"transition_key" : []
}
]
}
],
"deparsers" : [
{
"name" : "deparser",
"id" : 0,
"source_info" : {
"filename" : "main.p4",
"line" : 264,
"column" : 8,
"source_fragment" : "DeparserImpl"
},
"order" : ["packet_in", "ethernet", "ipv4"]
}
],
"meter_arrays" : [],
"counter_arrays" : [
{
"name" : "egr_port_counter",
"id" : 0,
"source_info" : {
"filename" : "main.p4",
"line" : 181,
"column" : 38,
"source_fragment" : "egr_port_counter"
},
"size" : 511,
"is_direct" : false
},
{
"name" : "igr_port_counter",
"id" : 1,
"source_info" : {
"filename" : "main.p4",
"line" : 182,
"column" : 38,
"source_fragment" : "igr_port_counter"
},
"size" : 511,
"is_direct" : false
}
],
"register_arrays" : [],
"calculations" : [],
"learn_lists" : [],
"actions" : [
{
"name" : "NoAction",
"id" : 0,
"runtime_data" : [],
"primitives" : []
},
{
"name" : "send_to_cpu",
"id" : 1,
"runtime_data" : [],
"primitives" : [
{
"op" : "assign",
"parameters" : [
{
"type" : "field",
"value" : ["standard_metadata", "egress_spec"]
},
{
"type" : "hexstr",
"value" : "0x00ff"
}
],
"source_info" : {
"filename" : "main.p4",
"line" : 24,
"column" : 24,
"source_fragment" : "255; ..."
}
},
{
"op" : "add_header",
"parameters" : [
{
"type" : "header",
"value" : "packet_in"
}
],
"source_info" : {
"filename" : "main.p4",
"line" : 137,
"column" : 8,
"source_fragment" : "hdr.packet_in.setValid()"
}
},
{
"op" : "assign",
"parameters" : [
{
"type" : "field",
"value" : ["packet_in", "ingress_port"]
},
{
"type" : "field",
"value" : ["standard_metadata", "ingress_port"]
}
],
"source_info" : {
"filename" : "main.p4",
"line" : 138,
"column" : 8,
"source_fragment" : "hdr.packet_in.ingress_port = standard_metadata.ingress_port"
}
}
]
},
{
"name" : "set_egress_port",
"id" : 2,
"runtime_data" : [
{
"name" : "port",
"bitwidth" : 9
}
],
"primitives" : [
{
"op" : "assign",
"parameters" : [
{
"type" : "field",
"value" : ["standard_metadata", "egress_spec"]
},
{
"type" : "runtime_data",
"value" : 0
}
],
"source_info" : {
"filename" : "main.p4",
"line" : 142,
"column" : 8,
"source_fragment" : "standard_metadata.egress_spec = port"
}
}
]
},
{
"name" : "_drop",
"id" : 3,
"runtime_data" : [],
"primitives" : [
{
"op" : "assign",
"parameters" : [
{
"type" : "field",
"value" : ["standard_metadata", "egress_spec"]
},
{
"type" : "hexstr",
"value" : "0x01ff"
}
],
"source_info" : {
"filename" : "main.p4",
"line" : 25,
"column" : 25,
"source_fragment" : "511; ..."
}
}
]
},
{
"name" : "_drop",
"id" : 4,
"runtime_data" : [],
"primitives" : [
{
"op" : "assign",
"parameters" : [
{
"type" : "field",
"value" : ["standard_metadata", "egress_spec"]
},
{
"type" : "hexstr",
"value" : "0x01ff"
}
],
"source_info" : {
"filename" : "main.p4",
"line" : 25,
"column" : 25,
"source_fragment" : "511; ..."
}
}
]
},
{
"name" : "act",
"id" : 5,
"runtime_data" : [],
"primitives" : [
{
"op" : "assign",
"parameters" : [
{
"type" : "field",
"value" : ["standard_metadata", "egress_spec"]
},
{
"type" : "field",
"value" : ["packet_out", "egress_port"]
}
],
"source_info" : {
"filename" : "main.p4",
"line" : 195,
"column" : 12,
"source_fragment" : "standard_metadata.egress_spec = hdr.packet_out.egress_port"
}
},
{
"op" : "remove_header",
"parameters" : [
{
"type" : "header",
"value" : "packet_out"
}
],
"source_info" : {
"filename" : "main.p4",
"line" : 196,
"column" : 12,
"source_fragment" : "hdr.packet_out.setInvalid()"
}
}
]
},
{
"name" : "act_0",
"id" : 6,
"runtime_data" : [],
"primitives" : [
{
"op" : "assign",
"parameters" : [
{
"type" : "field",
"value" : ["scalars", "tmp"]
},
{
"type" : "expression",
"value" : {
"type" : "expression",
"value" : {
"op" : "&",
"left" : {
"type" : "field",
"value" : ["standard_metadata", "egress_spec"]
},
"right" : {
"type" : "hexstr",
"value" : "0xffffffff"
}
}
}
}
]
},
{
"op" : "count",
"parameters" : [
{
"type" : "counter_array",
"value" : "egr_port_counter"
},
{
"type" : "field",
"value" : ["scalars", "tmp"]
}
],
"source_info" : {
"filename" : "main.p4",
"line" : 218,
"column" : 12,
"source_fragment" : "egr_port_counter.count((bit<32>) standard_metadata.egress_spec)"
}
}
]
},
{
"name" : "act_1",
"id" : 7,
"runtime_data" : [],
"primitives" : [
{
"op" : "assign",
"parameters" : [
{
"type" : "field",
"value" : ["scalars", "tmp_0"]
},
{
"type" : "expression",
"value" : {
"type" : "expression",
"value" : {
"op" : "&",
"left" : {
"type" : "field",
"value" : ["standard_metadata", "ingress_port"]
},
"right" : {
"type" : "hexstr",
"value" : "0xffffffff"
}
}
}
}
]
},
{
"op" : "count",
"parameters" : [
{
"type" : "counter_array",
"value" : "igr_port_counter"
},
{
"type" : "field",
"value" : ["scalars", "tmp_0"]
}
],
"source_info" : {
"filename" : "main.p4",
"line" : 221,
"column" : 12,
"source_fragment" : "igr_port_counter.count((bit<32>) standard_metadata.ingress_port)"
}
}
]
}
],
"pipelines" : [
{
"name" : "ingress",
"id" : 0,
"source_info" : {
"filename" : "main.p4",
"line" : 126,
"column" : 8,
"source_fragment" : "IngressImpl"
},
"init_table" : "node_2",
"tables" : [
{
"name" : "tbl_act",
"id" : 0,
"key" : [],
"match_type" : "exact",
"type" : "simple",
"max_size" : 1024,
"with_counters" : false,
"support_timeout" : false,
"direct_meters" : null,
"action_ids" : [5],
"actions" : ["act"],
"base_default_next" : "node_7",
"next_tables" : {
"act" : "node_7"
},
"default_entry" : {
"action_id" : 5,
"action_const" : true,
"action_data" : [],
"action_entry_const" : true
}
},
{
"name" : "table0",
"id" : 1,
"source_info" : {
"filename" : "main.p4",
"line" : 149,
"column" : 10,
"source_fragment" : "table0"
},
"key" : [
{
"match_type" : "ternary",
"target" : ["standard_metadata", "ingress_port"],
"mask" : null
},
{
"match_type" : "ternary",
"target" : ["ethernet", "dst_addr"],
"mask" : null
},
{
"match_type" : "ternary",
"target" : ["ethernet", "src_addr"],
"mask" : null
},
{
"match_type" : "ternary",
"target" : ["ethernet", "ether_type"],
"mask" : null
}
],
"match_type" : "ternary",
"type" : "simple",
"max_size" : 1024,
"with_counters" : false,
"support_timeout" : false,
"direct_meters" : null,
"action_ids" : [2, 1, 3],
"actions" : ["set_egress_port", "send_to_cpu", "_drop"],
"base_default_next" : "node_7",
"next_tables" : {
"set_egress_port" : "node_5",
"send_to_cpu" : "node_7",
"_drop" : "node_7"
},
"default_entry" : {
"action_id" : 3,
"action_const" : false,
"action_data" : [],
"action_entry_const" : false
}
},
{
"name" : "ip_proto_filter_table",
"id" : 2,
"source_info" : {
"filename" : "main.p4",
"line" : 164,
"column" : 10,
"source_fragment" : "ip_proto_filter_table"
},
"key" : [
{
"match_type" : "ternary",
"target" : ["ipv4", "src_addr"],
"mask" : null
},
{
"match_type" : "exact",
"target" : ["ipv4", "protocol"],
"mask" : null
}
],
"match_type" : "ternary",
"type" : "simple",
"max_size" : 1024,
"with_counters" : false,
"support_timeout" : false,
"direct_meters" : null,
"action_ids" : [4, 0],
"actions" : ["_drop", "NoAction"],
"base_default_next" : "node_7",
"next_tables" : {
"_drop" : "node_7",
"NoAction" : "node_7"
},
"default_entry" : {
"action_id" : 0,
"action_const" : false,
"action_data" : [],
"action_entry_const" : false
}
},
{
"name" : "tbl_act_0",
"id" : 3,
"key" : [],
"match_type" : "exact",
"type" : "simple",
"max_size" : 1024,
"with_counters" : false,
"support_timeout" : false,
"direct_meters" : null,
"action_ids" : [6],
"actions" : ["act_0"],
"base_default_next" : "node_9",
"next_tables" : {
"act_0" : "node_9"
},
"default_entry" : {
"action_id" : 6,
"action_const" : true,
"action_data" : [],
"action_entry_const" : true
}
},
{
"name" : "tbl_act_1",
"id" : 4,
"key" : [],
"match_type" : "exact",
"type" : "simple",
"max_size" : 1024,
"with_counters" : false,
"support_timeout" : false,
"direct_meters" : null,
"action_ids" : [7],
"actions" : ["act_1"],
"base_default_next" : null,
"next_tables" : {
"act_1" : null
},
"default_entry" : {
"action_id" : 7,
"action_const" : true,
"action_data" : [],
"action_entry_const" : true
}
}
],
"action_profiles" : [],
"conditionals" : [
{
"name" : "node_2",
"id" : 0,
"source_info" : {
"filename" : "main.p4",
"line" : 188,
"column" : 12,
"source_fragment" : "standard_metadata.ingress_port == CPU_PORT"
},
"expression" : {
"type" : "expression",
"value" : {
"op" : "==",
"left" : {
"type" : "field",
"value" : ["standard_metadata", "ingress_port"]
},
"right" : {
"type" : "hexstr",
"value" : "0x00ff"
}
}
},
"true_next" : "tbl_act",
"false_next" : "table0"
},
{
"name" : "node_5",
"id" : 1,
"source_info" : {
"filename" : "main.p4",
"line" : 205,
"column" : 24,
"source_fragment" : "hdr.ipv4.isValid()"
},
"expression" : {
"type" : "expression",
"value" : {
"op" : "==",
"left" : {
"type" : "field",
"value" : ["ipv4", "$valid$"]
},
"right" : {
"type" : "hexstr",
"value" : "0x01"
}
}
},
"true_next" : "ip_proto_filter_table",
"false_next" : "node_7"
},
{
"name" : "node_7",
"id" : 2,
"source_info" : {
"filename" : "main.p4",
"line" : 217,
"column" : 12,
"source_fragment" : "standard_metadata.egress_spec < 511"
},
"expression" : {
"type" : "expression",
"value" : {
"op" : "<",
"left" : {
"type" : "field",
"value" : ["standard_metadata", "egress_spec"]
},
"right" : {
"type" : "hexstr",
"value" : "0x01ff"
}
}
},
"true_next" : "tbl_act_0",
"false_next" : "node_9"
},
{
"name" : "node_9",
"id" : 3,
"source_info" : {
"filename" : "main.p4",
"line" : 220,
"column" : 12,
"source_fragment" : "standard_metadata.ingress_port < 511"
},
"expression" : {
"type" : "expression",
"value" : {
"op" : "<",
"left" : {
"type" : "field",
"value" : ["standard_metadata", "ingress_port"]
},
"right" : {
"type" : "hexstr",
"value" : "0x01ff"
}
}
},
"false_next" : null,
"true_next" : "tbl_act_1"
}
]
},
{
"name" : "egress",
"id" : 1,
"source_info" : {
"filename" : "main.p4",
"line" : 230,
"column" : 8,
"source_fragment" : "EgressImpl"
},
"init_table" : null,
"tables" : [],
"action_profiles" : [],
"conditionals" : []
}
],
"checksums" : [],
"force_arith" : [],
"extern_instances" : [],
"field_aliases" : [
[
"queueing_metadata.enq_timestamp",
["standard_metadata", "enq_timestamp"]
],
[
"queueing_metadata.enq_qdepth",
["standard_metadata", "enq_qdepth"]
],
[
"queueing_metadata.deq_timedelta",
["standard_metadata", "deq_timedelta"]
],
[
"queueing_metadata.deq_qdepth",
["standard_metadata", "deq_qdepth"]
],
[
"intrinsic_metadata.ingress_global_timestamp",
["standard_metadata", "ingress_global_timestamp"]
],
[
"intrinsic_metadata.lf_field_list",
["standard_metadata", "lf_field_list"]
],
[
"intrinsic_metadata.mcast_grp",
["standard_metadata", "mcast_grp"]
],
[
"intrinsic_metadata.resubmit_flag",
["standard_metadata", "resubmit_flag"]
],
[
"intrinsic_metadata.egress_rid",
["standard_metadata", "egress_rid"]
]
]
}

View File

@ -1,147 +0,0 @@
tables {
preamble {
id: 33617813
name: "table0"
alias: "table0"
}
match_fields {
id: 1
name: "standard_metadata.ingress_port"
bitwidth: 9
match_type: TERNARY
}
match_fields {
id: 2
name: "hdr.ethernet.dst_addr"
bitwidth: 48
match_type: TERNARY
}
match_fields {
id: 3
name: "hdr.ethernet.src_addr"
bitwidth: 48
match_type: TERNARY
}
match_fields {
id: 4
name: "hdr.ethernet.ether_type"
bitwidth: 16
match_type: TERNARY
}
action_refs {
id: 16794308
}
action_refs {
id: 16829080
}
action_refs {
id: 16784184
}
size: 1024
}
tables {
preamble {
id: 33573361
name: "ip_proto_filter_table"
alias: "ip_proto_filter_table"
}
match_fields {
id: 1
name: "hdr.ipv4.src_addr"
bitwidth: 32
match_type: TERNARY
}
match_fields {
id: 2
name: "hdr.ipv4.protocol"
bitwidth: 8
match_type: EXACT
}
action_refs {
id: 16784184
}
action_refs {
id: 16800567
annotations: "@defaultonly()"
}
size: 1024
}
actions {
preamble {
id: 16800567
name: "NoAction"
alias: "NoAction"
}
}
actions {
preamble {
id: 16829080
name: "send_to_cpu"
alias: "send_to_cpu"
}
}
actions {
preamble {
id: 16794308
name: "set_egress_port"
alias: "set_egress_port"
}
params {
id: 1
name: "port"
bitwidth: 9
}
}
actions {
preamble {
id: 16784184
name: "_drop"
alias: "_drop"
}
}
counters {
preamble {
id: 302012419
name: "egr_port_counter"
alias: "egr_port_counter"
}
spec {
unit: PACKETS
}
size: 511
}
counters {
preamble {
id: 302054463
name: "igr_port_counter"
alias: "igr_port_counter"
}
spec {
unit: PACKETS
}
size: 511
}
controller_packet_metadata {
preamble {
id: 2868941301
name: "packet_in"
annotations: "@controller_header(\"packet_in\")"
}
metadata {
id: 1
name: "ingress_port"
bitwidth: 9
}
}
controller_packet_metadata {
preamble {
id: 2868916615
name: "packet_out"
annotations: "@controller_header(\"packet_out\")"
}
metadata {
id: 1
name: "egress_port"
bitwidth: 9
}
}

File diff suppressed because it is too large Load Diff

View File

@ -17,12 +17,13 @@
#include <core.p4>
#include <v1model.p4>
#define ETH_TYPE_IPV4 0x0800
#define MAX_PORTS 511
#define MAX_PORTS 255
const bit<16> ETH_TYPE_MYTUNNEL = 0x1212;
const bit<16> ETH_TYPE_IPV4 = 0x800;
typedef bit<9> port_t;
const port_t CPU_PORT = 255;
const port_t DROP_PORT = 511;
//------------------------------------------------------------------------------
// HEADERS
@ -34,6 +35,11 @@ header ethernet_t {
bit<16> ether_type;
}
header my_tunnel_t {
bit<16> proto_id;
bit<32> tun_id;
}
header ipv4_t {
bit<4> version;
bit<4> ihl;
@ -49,46 +55,39 @@ header ipv4_t {
bit<32> dst_addr;
}
/*
Packet-in header. Prepended to packets sent to the controller and used to carry
the original ingress port where the packet was received.
*/
// Packet-in header. Prepended to packets sent to the controller and used to
// carry the original ingress port where the packet was received.
@controller_header("packet_in")
header packet_in_header_t {
bit<9> ingress_port;
}
/*
Packet-out header. Prepended to packets received by the controller and used to
tell the switch on which physical port this packet should be forwarded.
*/
// Packet-out header. Prepended to packets received by the controller and used
// to tell the switch on which port this packet should be forwarded.
@controller_header("packet_out")
header packet_out_header_t {
bit<9> egress_port;
}
/*
For convenience we collect all headers under the same struct.
*/
// For convenience we collect all headers under the same struct.
struct headers_t {
ethernet_t ethernet;
my_tunnel_t my_tunnel;
ipv4_t ipv4;
packet_out_header_t packet_out;
packet_in_header_t packet_in;
}
/*
Metadata can be used to carry information from one table to another.
*/
// Metadata can be used to carry information from one table to another.
struct metadata_t {
/* Empty. We don't use it in this program. */
// Empty. We don't use it in this program.
}
//------------------------------------------------------------------------------
// PARSER
//------------------------------------------------------------------------------
parser ParserImpl(packet_in packet,
parser c_parser(packet_in packet,
out headers_t hdr,
inout metadata_t meta,
inout standard_metadata_t standard_metadata) {
@ -108,6 +107,15 @@ parser ParserImpl(packet_in packet,
state parse_ethernet {
packet.extract(hdr.ethernet);
transition select(hdr.ethernet.ether_type) {
ETH_TYPE_MYTUNNEL: parse_my_tunnel;
ETH_TYPE_IPV4: parse_ipv4;
default: accept;
}
}
state parse_my_tunnel {
packet.extract(hdr.my_tunnel);
transition select(hdr.my_tunnel.proto_id) {
ETH_TYPE_IPV4: parse_ipv4;
default: accept;
}
@ -123,30 +131,48 @@ parser ParserImpl(packet_in packet,
// INGRESS PIPELINE
//------------------------------------------------------------------------------
control IngressImpl(inout headers_t hdr,
control c_ingress(inout headers_t hdr,
inout metadata_t meta,
inout standard_metadata_t standard_metadata) {
// We use these counters to count packets/bytes received/sent on each port.
// For each counter we instantiate a number of cells equal to MAX_PORTS.
counter(MAX_PORTS, CounterType.packets_and_bytes) tx_port_counter;
counter(MAX_PORTS, CounterType.packets_and_bytes) rx_port_counter;
action send_to_cpu() {
standard_metadata.egress_spec = CPU_PORT;
/*
Packets sent to the controller needs to be prepended with the packet-in
header. By setting it valid we make sure it will be deparsed before the
ethernet header (see DeparserImpl).
*/
// Packets sent to the controller needs to be prepended with the
// packet-in header. By setting it valid we make sure it will be
// deparsed on the wire (see c_deparser).
hdr.packet_in.setValid();
hdr.packet_in.ingress_port = standard_metadata.ingress_port;
}
action set_egress_port(port_t port) {
action set_out_port(port_t port) {
standard_metadata.egress_spec = port;
}
action _drop() {
standard_metadata.egress_spec = DROP_PORT;
mark_to_drop();
}
table table0 {
action my_tunnel_ingress(bit<32> tun_id) {
hdr.my_tunnel.setValid();
hdr.my_tunnel.tun_id = tun_id;
hdr.my_tunnel.proto_id = hdr.ethernet.ether_type;
hdr.ethernet.ether_type = ETH_TYPE_MYTUNNEL;
}
action my_tunnel_egress(bit<9> port) {
standard_metadata.egress_spec = port;
hdr.ethernet.ether_type = hdr.my_tunnel.proto_id;
hdr.my_tunnel.setInvalid();
}
direct_counter(CounterType.packets_and_bytes) l2_fwd_counter;
table t_l2_fwd {
key = {
standard_metadata.ingress_port : ternary;
hdr.ethernet.dst_addr : ternary;
@ -154,71 +180,73 @@ control IngressImpl(inout headers_t hdr,
hdr.ethernet.ether_type : ternary;
}
actions = {
set_egress_port();
set_out_port();
send_to_cpu();
_drop();
NoAction;
}
default_action = NoAction();
counters = l2_fwd_counter;
}
table t_tunnel_ingress {
key = {
hdr.ipv4.dst_addr: lpm;
}
actions = {
my_tunnel_ingress;
_drop();
}
default_action = _drop();
}
table ip_proto_filter_table {
table t_tunnel_fwd {
key = {
hdr.ipv4.src_addr : ternary;
hdr.ipv4.protocol : exact;
hdr.my_tunnel.tun_id: exact;
}
actions = {
set_out_port;
my_tunnel_egress;
_drop();
}
default_action = _drop();
}
/*
Port counters.
We use these counter instances to count packets/bytes received/sent on each
port. BMv2 always counts both packets and bytes, even if the counter is
instantiated as "packets". For each counter we instantiate a number of cells
equal to MAX_PORTS.
*/
counter(MAX_PORTS, CounterType.packets) egr_port_counter;
counter(MAX_PORTS, CounterType.packets) igr_port_counter;
/*
We define here the processing to be executed by this ingress pipeline.
*/
// Define processing applied by this control block.
apply {
if (standard_metadata.ingress_port == CPU_PORT) {
/*
Packet received from CPU_PORT, this is a packet-out sent by the
controller. Skip pipeline processing, set the egress port as
requested by the controller (packet_out header) and remove the
packet_out header.
*/
// Packet received from CPU_PORT, this is a packet-out sent by the
// controller. Skip table processing, set the egress port as
// requested by the controller (packet_out header) and remove the
// packet_out header.
standard_metadata.egress_spec = hdr.packet_out.egress_port;
hdr.packet_out.setInvalid();
} else {
/*
Packet received from switch port. Apply table0, if action is
set_egress_port and packet is IPv4, then apply
ip_proto_filter_table.
*/
switch(table0.apply().action_run) {
set_egress_port: {
if (hdr.ipv4.isValid()) {
ip_proto_filter_table.apply();
}
}
// Packet received from data plane port.
if (t_l2_fwd.apply().hit) {
// Packet hit an entry in t_l2_fwd table. A forwarding action
// has already been taken. No need to apply other tables, exit
// this control block.
return;
}
if (hdr.ipv4.isValid() && !hdr.my_tunnel.isValid()) {
// Process only non-tunneled IPv4 packets.
t_tunnel_ingress.apply();
}
if (hdr.my_tunnel.isValid()) {
// Process all tunneled packets.
t_tunnel_fwd.apply();
}
}
/*
For each port counter, we update the cell at index = ingress/egress
port. We avoid counting packets sent/received on CPU_PORT or dropped
(DROP_PORT).
*/
// Update port counters at index = ingress or egress port.
if (standard_metadata.egress_spec < MAX_PORTS) {
egr_port_counter.count((bit<32>) standard_metadata.egress_spec);
tx_port_counter.count((bit<32>) standard_metadata.egress_spec);
}
if (standard_metadata.ingress_port < MAX_PORTS) {
igr_port_counter.count((bit<32>) standard_metadata.ingress_port);
rx_port_counter.count((bit<32>) standard_metadata.ingress_port);
}
}
}
@ -227,13 +255,11 @@ control IngressImpl(inout headers_t hdr,
// EGRESS PIPELINE
//------------------------------------------------------------------------------
control EgressImpl(inout headers_t hdr,
inout metadata_t meta,
inout standard_metadata_t standard_metadata) {
control c_egress(inout headers_t hdr,
inout metadata_t meta,
inout standard_metadata_t standard_metadata) {
apply {
/*
Nothing to do on the egress pipeline.
*/
// Nothing to do on the egress pipeline.
}
}
@ -241,19 +267,15 @@ control EgressImpl(inout headers_t hdr,
// CHECKSUM HANDLING
//------------------------------------------------------------------------------
control VerifyChecksumImpl(in headers_t hdr, inout metadata_t meta) {
control c_verify_checksum(inout headers_t hdr, inout metadata_t meta) {
apply {
/*
Nothing to do here, we assume checksum is always correct.
*/
// Nothing to do here, we assume checksum is always correct.
}
}
control ComputeChecksumImpl(inout headers_t hdr, inout metadata_t meta) {
control c_compute_checksum(inout headers_t hdr, inout metadata_t meta) {
apply {
/*
Nothing to do here, as we do not modify packet headers.
*/
// No need to compute checksum as we do not modify packet headers.
}
}
@ -261,13 +283,13 @@ control ComputeChecksumImpl(inout headers_t hdr, inout metadata_t meta) {
// DEPARSER
//------------------------------------------------------------------------------
control DeparserImpl(packet_out packet, in headers_t hdr) {
control c_deparser(packet_out packet, in headers_t hdr) {
apply {
/*
Deparse headers in order. Only valid headers are emitted.
*/
// Emit headers on the wire in the following order.
// Only valid headers are emitted.
packet.emit(hdr.packet_in);
packet.emit(hdr.ethernet);
packet.emit(hdr.my_tunnel);
packet.emit(hdr.ipv4);
}
}
@ -276,9 +298,9 @@ control DeparserImpl(packet_out packet, in headers_t hdr) {
// SWITCH INSTANTIATION
//------------------------------------------------------------------------------
V1Switch(ParserImpl(),
VerifyChecksumImpl(),
IngressImpl(),
EgressImpl(),
ComputeChecksumImpl(),
DeparserImpl()) main;
V1Switch(c_parser(),
c_verify_checksum(),
c_ingress(),
c_egress(),
c_compute_checksum(),
c_deparser()) main;

View File

@ -0,0 +1,202 @@
tables {
preamble {
id: 33606914
name: "c_ingress.t_l2_fwd"
alias: "t_l2_fwd"
}
match_fields {
id: 1
name: "standard_metadata.ingress_port"
bitwidth: 9
match_type: TERNARY
}
match_fields {
id: 2
name: "hdr.ethernet.dst_addr"
bitwidth: 48
match_type: TERNARY
}
match_fields {
id: 3
name: "hdr.ethernet.src_addr"
bitwidth: 48
match_type: TERNARY
}
match_fields {
id: 4
name: "hdr.ethernet.ether_type"
bitwidth: 16
match_type: TERNARY
}
action_refs {
id: 16831479
}
action_refs {
id: 16822540
}
action_refs {
id: 16808599
}
action_refs {
id: 16800567
}
direct_resource_ids: 302001589
size: 1024
}
tables {
preamble {
id: 33565612
name: "c_ingress.t_tunnel_ingress"
alias: "t_tunnel_ingress"
}
match_fields {
id: 1
name: "hdr.ipv4.dst_addr"
bitwidth: 32
match_type: LPM
}
action_refs {
id: 16835665
}
action_refs {
id: 16808599
}
size: 1024
}
tables {
preamble {
id: 33556067
name: "c_ingress.t_tunnel_fwd"
alias: "t_tunnel_fwd"
}
match_fields {
id: 1
name: "hdr.my_tunnel.tun_id"
bitwidth: 32
match_type: EXACT
}
action_refs {
id: 16831479
}
action_refs {
id: 16800149
}
action_refs {
id: 16808599
}
size: 1024
}
actions {
preamble {
id: 16800567
name: "NoAction"
alias: "NoAction"
}
}
actions {
preamble {
id: 16822540
name: "c_ingress.send_to_cpu"
alias: "send_to_cpu"
}
}
actions {
preamble {
id: 16831479
name: "c_ingress.set_out_port"
alias: "set_out_port"
}
params {
id: 1
name: "port"
bitwidth: 9
}
}
actions {
preamble {
id: 16808599
name: "c_ingress._drop"
alias: "_drop"
}
}
actions {
preamble {
id: 16835665
name: "c_ingress.my_tunnel_ingress"
alias: "my_tunnel_ingress"
}
params {
id: 1
name: "tun_id"
bitwidth: 32
}
}
actions {
preamble {
id: 16800149
name: "c_ingress.my_tunnel_egress"
alias: "my_tunnel_egress"
}
params {
id: 1
name: "port"
bitwidth: 9
}
}
counters {
preamble {
id: 302003196
name: "c_ingress.tx_port_counter"
alias: "tx_port_counter"
}
spec {
unit: BOTH
}
size: 255
}
counters {
preamble {
id: 302045227
name: "c_ingress.rx_port_counter"
alias: "rx_port_counter"
}
spec {
unit: BOTH
}
size: 255
}
direct_counters {
preamble {
id: 302001589
name: "c_ingress.l2_fwd_counter"
alias: "l2_fwd_counter"
}
spec {
unit: BOTH
}
direct_table_id: 33606914
}
controller_packet_metadata {
preamble {
id: 2868941301
name: "packet_in"
annotations: "@controller_header(\"packet_in\")"
}
metadata {
id: 1
name: "ingress_port"
bitwidth: 9
}
}
controller_packet_metadata {
preamble {
id: 2868916615
name: "packet_out"
annotations: "@controller_header(\"packet_out\")"
}
metadata {
id: 1
name: "egress_port"
bitwidth: 9
}
}

View File

@ -233,7 +233,7 @@ ONOS_APPS = [
'//incubator/protobuf/services/nb:onos-incubator-protobuf-services-nb-oar',
'//apps/openstacknetworkingui:onos-apps-openstacknetworkingui-oar',
'//apps/p4-tutorial/pipeconf:onos-apps-p4-tutorial-pipeconf-oar',
'//apps/p4-tutorial/icmpdropper:onos-apps-p4-tutorial-icmpdropper-oar',
'//apps/p4-tutorial/mytunnel:onos-apps-p4-tutorial-mytunnel-oar',
'//apps/cfm:onos-apps-cfm-oar',
'//apps/routeradvertisement:onos-apps-routeradvertisement-oar',
'//apps/powermanagement:onos-apps-powermanagement-oar',