ONOS-7058 Refactored default pipeconfs in new pipelines directory

- Minimal refactoring of P4 programs
- Removed symlinks to BMv2 JSON/P4Info
- Bumped p4c commit (which fixes known parser bug)
- Renamed "default" pipeconf to "basic" (ONOS-6818)

Change-Id: I319f8b142ab22dba9b15457e28cd62d17f78a423
This commit is contained in:
Carmelo Cascone 2017-10-27 14:16:59 -07:00
parent b211b87829
commit ca94bcf5ea
65 changed files with 3701 additions and 13455 deletions

View File

@ -9,6 +9,7 @@ COMPILE_DEPS = [
'//protocols/p4runtime/ctl:onos-protocols-p4runtime-ctl',
'//protocols/p4runtime/proto:onos-protocols-p4runtime-proto',
'//drivers/bmv2:onos-drivers-bmv2',
'//pipelines/basic:onos-pipelines-basic',
'//incubator/grpc-dependencies:grpc-core-repkg-' + GRPC_VER,
'//lib:grpc-stub-' + GRPC_VER,
'//lib:protobuf-java-' + PROTOBUF_VER,

View File

@ -23,7 +23,6 @@ import org.junit.Before;
import org.junit.Ignore;
import org.junit.Test;
import org.onlab.util.ImmutableByteSequence;
import org.onosproject.drivers.bmv2.Bmv2DefaultPipeconfFactory;
import org.onosproject.grpc.ctl.GrpcControllerImpl;
import org.onosproject.net.DeviceId;
import org.onosproject.net.pi.model.PiPipeconf;
@ -48,6 +47,7 @@ import org.onosproject.net.pi.runtime.PiTernaryFieldMatch;
import org.onosproject.p4runtime.api.P4RuntimeClient;
import org.onosproject.p4runtime.ctl.P4RuntimeClientImpl;
import org.onosproject.p4runtime.ctl.P4RuntimeControllerImpl;
import org.onosproject.pipelines.basic.PipeconfLoader;
import org.slf4j.Logger;
import p4.P4RuntimeGrpc;
import p4.P4RuntimeOuterClass;
@ -57,7 +57,9 @@ import java.util.Collection;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException;
import static org.onlab.util.ImmutableByteSequence.*;
import static org.onlab.util.ImmutableByteSequence.copyFrom;
import static org.onlab.util.ImmutableByteSequence.fit;
import static org.onlab.util.ImmutableByteSequence.ofZeros;
import static org.onosproject.net.pi.model.PiPipeconf.ExtensionType.BMV2_JSON;
import static org.onosproject.net.pi.runtime.PiPacketOperation.Type.PACKET_OUT;
import static org.slf4j.LoggerFactory.getLogger;
@ -84,10 +86,10 @@ public class P4RuntimeTest {
private static final String ETHER_TYPE = "etherType";
private final URL p4InfoUrl = this.getClass().getResource("/bmv2/default.p4info");
private final URL jsonUrl = this.getClass().getResource("/bmv2/default.json");
private final URL p4InfoUrl = this.getClass().getResource("/p4c-out/bmv2/basic.p4info");
private final URL jsonUrl = this.getClass().getResource("/p4c-out/bmv2/basic.json");
private final PiPipeconf bmv2DefaultPipeconf = Bmv2DefaultPipeconfFactory.get();
private final PiPipeconf bmv2DefaultPipeconf = PipeconfLoader.BASIC_PIPECONF;
private final P4RuntimeControllerImpl controller = new P4RuntimeControllerImpl();
private final GrpcControllerImpl grpcController = new GrpcControllerImpl();
private final DeviceId deviceId = DeviceId.deviceId("dummy:1");

View File

@ -1 +0,0 @@
../../../../../../tools/test/p4src/p4-16/p4c-out/default.json

View File

@ -1 +0,0 @@
../../../../../../tools/test/p4src/p4-16/p4c-out/default.p4info

View File

@ -75,7 +75,9 @@ import static java.lang.String.format;
import static java.util.stream.Collectors.toSet;
import static java.util.stream.Stream.concat;
import static org.onlab.util.Tools.groupedThreads;
import static org.onosproject.net.device.DeviceEvent.Type.*;
import static org.onosproject.net.device.DeviceEvent.Type.DEVICE_ADDED;
import static org.onosproject.net.device.DeviceEvent.Type.DEVICE_AVAILABILITY_CHANGED;
import static org.onosproject.net.device.DeviceEvent.Type.DEVICE_UPDATED;
import static org.slf4j.LoggerFactory.getLogger;
/**
@ -181,7 +183,6 @@ public abstract class AbstractUpgradableFabricApp {
appId = coreService.registerApplication(appName);
deviceService.addListener(deviceListener);
appPipeconfs.forEach(piPipeconfService::register);
init();
@ -201,9 +202,6 @@ public abstract class AbstractUpgradableFabricApp {
scheduledExecutorService.shutdown();
deviceService.removeListener(deviceListener);
flowRuleService.removeFlowRulesById(appId);
appPipeconfs.stream()
.map(PiPipeconf::id)
.forEach(piPipeconfService::remove);
appActive = false;
APP_HANDLES.remove(appName);
@ -454,10 +452,10 @@ public abstract class AbstractUpgradableFabricApp {
* Returns a new, pre-configured flow rule builder.
*
* @param did a device id
* @param tableName a table name
* @param tableId a table id
* @return a new flow rule builder
*/
protected FlowRule.Builder flowRuleBuilder(DeviceId did, String tableName) throws FlowRuleGeneratorException {
protected FlowRule.Builder flowRuleBuilder(DeviceId did, PiTableId tableId) throws FlowRuleGeneratorException {
final Device device = deviceService.getDevice(did);
if (!device.is(PiPipelineInterpreter.class)) {
@ -465,10 +463,10 @@ public abstract class AbstractUpgradableFabricApp {
}
final PiPipelineInterpreter interpreter = device.as(PiPipelineInterpreter.class);
final int flowRuleTableId;
if (interpreter.mapPiTableId(PiTableId.of(tableName)).isPresent()) {
flowRuleTableId = interpreter.mapPiTableId(PiTableId.of(tableName)).get();
if (interpreter.mapPiTableId(tableId).isPresent()) {
flowRuleTableId = interpreter.mapPiTableId(tableId).get();
} else {
throw new FlowRuleGeneratorException(format("Unknown table %s in interpreter", tableName));
throw new FlowRuleGeneratorException(format("Unknown table '%s' in interpreter", tableId));
}
return DefaultFlowRule.builder()

View File

@ -1,10 +1,8 @@
COMPILE_DEPS = [
'//lib:CORE_DEPS',
'//lib:minimal-json',
'//incubator/bmv2/model:onos-incubator-bmv2-model',
'//apps/pi-demo/common:onos-apps-pi-demo-common',
'//drivers/default:onos-drivers-default',
'//drivers/p4runtime:onos-drivers-p4runtime',
'//pipelines/basic:onos-pipelines-basic',
]
osgi_jar (
@ -12,25 +10,18 @@ osgi_jar (
)
BUNDLES = [
'//apps/pi-demo/ecmp:onos-apps-pi-demo-ecmp',
'//apps/pi-demo/common:onos-apps-pi-demo-common',
'//drivers/default:onos-drivers-default',
'//incubator/bmv2/model:onos-incubator-bmv2-model',
'//apps/pi-demo/ecmp:onos-apps-pi-demo-ecmp',
]
onos_app (
app_name = 'org.onosproject.pi-ecmp-fabric',
app_name = 'org.onosproject.pi-ecmp',
title = 'PI Demo ECMP Fabric',
category = 'Traffic Steering',
url = 'http://onosproject.org',
description = 'Provides ECMP support for a 2-stage clos fabric topology of PI-enabled devices',
included_bundles = BUNDLES,
required_apps = [
# FIXME: there should be no dependendcy on a driver here.
# However, we depend on the DefaultP4Interpreter that currently lives in the p4runtime
# driver. Bringing up the whole app avoids to specify all transitive runtime dependencies
# as bundles. DefaultP4Interpreter and other pipeconf-related stuff should leave in a
# separate location, outside the drivers.
'org.onosproject.drivers.p4runtime'
'org.onosproject.pipelines.basic'
]
)

View File

@ -34,15 +34,13 @@ import org.onosproject.net.flow.TrafficTreatment;
import org.onosproject.net.flow.criteria.Criterion;
import org.onosproject.net.flow.criteria.PiCriterion;
import org.onosproject.net.pi.runtime.PiAction;
import org.onosproject.net.pi.runtime.PiActionId;
import org.onosproject.net.pi.runtime.PiActionParam;
import org.onosproject.net.pi.runtime.PiActionParamId;
import org.onosproject.net.pi.runtime.PiHeaderFieldId;
import org.onosproject.net.pi.runtime.PiTableAction;
import org.onosproject.net.topology.DefaultTopologyVertex;
import org.onosproject.net.topology.Topology;
import org.onosproject.net.topology.TopologyGraph;
import org.onosproject.pi.demo.app.common.AbstractUpgradableFabricApp;
import org.onosproject.pipelines.basic.PipeconfLoader;
import java.util.Collection;
import java.util.Iterator;
@ -52,23 +50,29 @@ import java.util.Set;
import java.util.stream.Collectors;
import static java.lang.String.format;
import static java.util.Collections.singleton;
import static java.util.stream.Collectors.toSet;
import static org.onlab.packet.EthType.EtherType.IPV4;
import static org.onosproject.pi.demo.app.ecmp.EcmpInterpreter.*;
import static org.onosproject.pipelines.basic.BasicConstants.ACT_PRM_NEXT_HOP_ID;
import static org.onosproject.pipelines.basic.BasicConstants.ACT_SET_NEXT_HOP_ID;
import static org.onosproject.pipelines.basic.BasicConstants.HDR_NEXT_HOP_ID;
import static org.onosproject.pipelines.basic.BasicConstants.HDR_SELECTOR_ID;
import static org.onosproject.pipelines.basic.BasicConstants.TBL_TABLE0_ID;
import static org.onosproject.pipelines.basic.EcmpConstants.TBL_ECMP_TABLE_ID;
/**
* Implementation of an upgradable fabric app for the ECMP configuration.
* Implementation of an upgradable fabric app for the ECMP pipeconf.
*/
@Component(immediate = true)
public class EcmpFabricApp extends AbstractUpgradableFabricApp {
private static final String APP_NAME = "org.onosproject.pi-ecmp-fabric";
private static final String APP_NAME = "org.onosproject.pi-ecmp";
private static final Map<DeviceId, Map<Set<PortNumber>, Short>> DEVICE_GROUP_ID_MAP = Maps.newHashMap();
public EcmpFabricApp() {
super(APP_NAME, EcmpPipeconfFactory.getAll());
super(APP_NAME, singleton(PipeconfLoader.ECMP_PIPECONF));
}
@Override
@ -120,7 +124,7 @@ public class EcmpFabricApp extends AbstractUpgradableFabricApp {
// From srHost to dstHosts.
for (Host dstHost : dstHosts) {
FlowRule rule = flowRuleBuilder(leaf, EcmpInterpreter.TABLE0)
FlowRule rule = flowRuleBuilder(leaf, TBL_TABLE0_ID)
.withSelector(
DefaultTrafficSelector.builder()
.matchInPort(hostPort)
@ -135,7 +139,7 @@ public class EcmpFabricApp extends AbstractUpgradableFabricApp {
// From fabric ports to this leaf host.
for (PortNumber port : fabricPorts) {
FlowRule rule = flowRuleBuilder(leaf, EcmpInterpreter.TABLE0)
FlowRule rule = flowRuleBuilder(leaf, TBL_TABLE0_ID)
.withSelector(
DefaultTrafficSelector.builder()
.matchInPort(port)
@ -183,7 +187,7 @@ public class EcmpFabricApp extends AbstractUpgradableFabricApp {
treatment = DefaultTrafficTreatment.builder().piTableAction(result.getLeft()).build();
}
FlowRule rule = flowRuleBuilder(deviceId, EcmpInterpreter.TABLE0)
FlowRule rule = flowRuleBuilder(deviceId, TBL_TABLE0_ID)
.withSelector(
DefaultTrafficSelector.builder()
.matchEthType(IPV4.ethType().toShort())
@ -212,7 +216,7 @@ public class EcmpFabricApp extends AbstractUpgradableFabricApp {
Iterator<PortNumber> portIterator = fabricPorts.iterator();
List<FlowRule> rules = Lists.newArrayList();
for (short i = 0; i < HASHED_LINKS; i++) {
FlowRule rule = flowRuleBuilder(deviceId, EcmpInterpreter.ECMP_GROUP_TABLE)
FlowRule rule = flowRuleBuilder(deviceId, TBL_ECMP_TABLE_ID)
.withSelector(
buildEcmpTrafficSelector(groupId, i))
.withTreatment(
@ -231,16 +235,16 @@ public class EcmpFabricApp extends AbstractUpgradableFabricApp {
private PiTableAction buildEcmpPiTableAction(int groupId) {
return PiAction.builder()
.withId(PiActionId.of(ECMP_GROUP_ACTION_NAME))
.withParameter(new PiActionParam(PiActionParamId.of(GROUP_ID),
.withId(ACT_SET_NEXT_HOP_ID)
.withParameter(new PiActionParam(ACT_PRM_NEXT_HOP_ID,
ImmutableByteSequence.copyFrom(groupId)))
.build();
}
private TrafficSelector buildEcmpTrafficSelector(int groupId, int selector) {
Criterion ecmpCriterion = PiCriterion.builder()
.matchExact(PiHeaderFieldId.of(ECMP_METADATA_HEADER_NAME, GROUP_ID), groupId)
.matchExact(PiHeaderFieldId.of(ECMP_METADATA_HEADER_NAME, SELECTOR), selector)
.matchExact(HDR_NEXT_HOP_ID, groupId)
.matchExact(HDR_SELECTOR_ID, selector)
.build();
return DefaultTrafficSelector.builder()
@ -248,7 +252,7 @@ public class EcmpFabricApp extends AbstractUpgradableFabricApp {
.build();
}
public int groupIdOf(DeviceId deviceId, Set<PortNumber> ports) {
private int groupIdOf(DeviceId deviceId, Set<PortNumber> ports) {
DEVICE_GROUP_ID_MAP.putIfAbsent(deviceId, Maps.newHashMap());
// Counts the number of unique portNumber sets for each deviceId.
// Each distinct set of portNumbers will have a unique ID.

View File

@ -1,63 +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.pi.demo.app.ecmp;
import org.onosproject.bmv2.model.Bmv2PipelineModelParser;
import org.onosproject.driver.pipeline.DefaultSingleTablePipeline;
import org.onosproject.drivers.p4runtime.DefaultP4PortStatisticsDiscovery;
import org.onosproject.net.behaviour.Pipeliner;
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 java.net.URL;
import java.util.Collection;
import java.util.Collections;
import static org.onosproject.net.pi.model.PiPipeconf.ExtensionType.BMV2_JSON;
import static org.onosproject.net.pi.model.PiPipeconf.ExtensionType.P4_INFO_TEXT;
final class EcmpPipeconfFactory {
private static final String BMV2_PIPECONF_ID = "pi-demo-ecmp";
private static final URL BMV2_P4INFO_URL = EcmpFabricApp.class.getResource("/ecmp.p4info");
private static final URL BMV2_JSON_URL = EcmpFabricApp.class.getResource("/ecmp.json");
private static final PiPipeconf BMV2_PIPECONF = buildBmv2Pipeconf();
private EcmpPipeconfFactory() {
// Hides constructor.
}
static Collection<PiPipeconf> getAll() {
return Collections.singleton(BMV2_PIPECONF);
}
private static PiPipeconf buildBmv2Pipeconf() {
return DefaultPiPipeconf.builder()
.withId(new PiPipeconfId(BMV2_PIPECONF_ID))
.withPipelineModel(Bmv2PipelineModelParser.parse(BMV2_JSON_URL))
.addBehaviour(PiPipelineInterpreter.class, EcmpInterpreter.class)
.addBehaviour(Pipeliner.class, DefaultSingleTablePipeline.class)
.addBehaviour(PortStatisticsDiscovery.class, DefaultP4PortStatisticsDiscovery.class)
.addExtension(P4_INFO_TEXT, BMV2_P4INFO_URL)
.addExtension(BMV2_JSON, BMV2_JSON_URL)
.build();
}
}

View File

@ -1 +0,0 @@
../../../../../../tools/test/p4src/p4-16/p4c-out/ecmp.json

View File

@ -1 +0,0 @@
../../../../../../tools/test/p4src/p4-16/p4c-out/ecmp.p4info

View File

@ -17,7 +17,7 @@ TEST_DEPS = [
'//core/store/dist:onos-core-dist',
'//core/store/dist:onos-core-dist-tests',
'//utils/osgi:onlab-osgi-tests',
'//incubator/bmv2/model:onos-incubator-bmv2-model',
'//pipelines/basic:onos-pipelines-basic',
'//lib:minimal-json',
]

View File

@ -286,6 +286,15 @@ final class PiFlowRuleTranslator {
fieldModel.field().type().name(),
fieldModel.field().header().index());
// FIXME: workaround until ONOS-7066 is resolved
if (fieldId.id().startsWith("scalars")) {
String newFieldId = fieldId.id()
.replace("scalars.", "")
.replace("_t.", ".");
String[] piecies = newFieldId.split("\\.");
fieldId = PiHeaderFieldId.of(piecies[0], piecies[1]);
}
int bitWidth = fieldModel.field().type().bitWidth();
int fieldByteWidth = (int) Math.ceil((double) bitWidth / 8);

View File

@ -1,150 +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.net.pi.impl;
import com.google.common.collect.ImmutableBiMap;
import com.google.common.collect.ImmutableList;
import org.onlab.util.ImmutableByteSequence;
import org.onosproject.net.DeviceId;
import org.onosproject.net.PortNumber;
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.packet.InboundPacket;
import org.onosproject.net.packet.OutboundPacket;
import org.onosproject.net.pi.model.PiPipelineInterpreter;
import org.onosproject.net.pi.runtime.PiAction;
import org.onosproject.net.pi.runtime.PiActionId;
import org.onosproject.net.pi.runtime.PiActionParam;
import org.onosproject.net.pi.runtime.PiActionParamId;
import org.onosproject.net.pi.runtime.PiCounterId;
import org.onosproject.net.pi.runtime.PiHeaderFieldId;
import org.onosproject.net.pi.runtime.PiPacketOperation;
import org.onosproject.net.pi.runtime.PiTableId;
import java.util.Collection;
import java.util.Optional;
import static org.onosproject.net.PortNumber.CONTROLLER;
import static org.onosproject.net.flow.instructions.Instructions.OutputInstruction;
/**
* Mock interpreter implementation.
*/
public class MockInterpreter extends AbstractHandlerBehaviour implements PiPipelineInterpreter {
static final String TABLE0 = "table0";
static final String SEND_TO_CPU = "send_to_cpu";
static final String PORT = "port";
static final String DROP = "_drop";
static final String SET_EGRESS_PORT = "set_egress_port";
static final PiHeaderFieldId IN_PORT_ID = PiHeaderFieldId.of("standard_metadata", "ingress_port");
static final PiHeaderFieldId ETH_DST_ID = PiHeaderFieldId.of("ethernet", "dstAddr");
static final PiHeaderFieldId ETH_SRC_ID = PiHeaderFieldId.of("ethernet", "srcAddr");
static final PiHeaderFieldId ETH_TYPE_ID = PiHeaderFieldId.of("ethernet", "etherType");
private static final ImmutableBiMap<Criterion.Type, PiHeaderFieldId> CRITERION_MAP = ImmutableBiMap.of(
Criterion.Type.IN_PORT, IN_PORT_ID,
Criterion.Type.ETH_DST, ETH_DST_ID,
Criterion.Type.ETH_SRC, ETH_SRC_ID,
Criterion.Type.ETH_TYPE, ETH_TYPE_ID);
private static final ImmutableBiMap<Integer, PiTableId> TABLE_MAP = ImmutableBiMap.of(
0, PiTableId.of(TABLE0));
@Override
public PiAction mapTreatment(TrafficTreatment treatment, PiTableId piTableId) throws PiInterpreterException {
if (treatment.allInstructions().size() == 0) {
// No instructions means drop for us.
return actionWithName(DROP);
} else if (treatment.allInstructions().size() > 1) {
// Otherwise, we understand treatments with only 1 instruction.
throw new PiPipelineInterpreter.PiInterpreterException("Treatment has multiple instructions");
}
Instruction instruction = treatment.allInstructions().get(0);
switch (instruction.type()) {
case OUTPUT:
OutputInstruction outInstruction = (OutputInstruction) instruction;
PortNumber port = outInstruction.port();
if (!port.isLogical()) {
PiAction.builder()
.withId(PiActionId.of(SET_EGRESS_PORT))
.withParameter(new PiActionParam(PiActionParamId.of(PORT),
ImmutableByteSequence.copyFrom(port.toLong())))
.build();
} else if (port.equals(CONTROLLER)) {
return actionWithName(SEND_TO_CPU);
} else {
throw new PiInterpreterException("Egress on logical port not supported: " + port);
}
case NOACTION:
return actionWithName(DROP);
default:
throw new PiInterpreterException("Instruction type not supported: " + instruction.type().name());
}
}
@Override
public Optional<PiCounterId> mapTableCounter(PiTableId piTableId) {
return Optional.empty();
}
@Override
public Collection<PiPacketOperation> mapOutboundPacket(OutboundPacket packet)
throws PiInterpreterException {
return ImmutableList.of();
}
@Override
public InboundPacket mapInboundPacket(DeviceId deviceId, PiPacketOperation packetInOperation)
throws PiInterpreterException {
return null;
}
/**
* Returns an action instance with no runtime parameters.
*/
private PiAction actionWithName(String name) {
return PiAction.builder().withId(PiActionId.of(name)).build();
}
@Override
public Optional<PiHeaderFieldId> mapCriterionType(Criterion.Type type) {
return Optional.ofNullable(CRITERION_MAP.get(type));
}
@Override
public Optional<Criterion.Type> mapPiHeaderFieldId(PiHeaderFieldId headerFieldId) {
return Optional.ofNullable(CRITERION_MAP.inverse().get(headerFieldId));
}
@Override
public Optional<PiTableId> mapFlowRuleTableId(int flowRuleTableId) {
return Optional.ofNullable(TABLE_MAP.get(flowRuleTableId));
}
@Override
public Optional<Integer> mapPiTableId(PiTableId piTableId) {
return Optional.ofNullable(TABLE_MAP.inverse().get(piTableId));
}
}

View File

@ -23,10 +23,8 @@ import com.google.common.collect.ImmutableSet;
import org.junit.Before;
import org.junit.Test;
import org.onlab.util.ItemNotFoundException;
import org.onosproject.bmv2.model.Bmv2PipelineModelParser;
import org.onosproject.net.DeviceId;
import org.onosproject.net.behaviour.Pipeliner;
import org.onosproject.net.behaviour.PipelinerAdapter;
import org.onosproject.net.config.Config;
import org.onosproject.net.config.ConfigApplyDelegate;
import org.onosproject.net.config.ConfigFactory;
@ -46,22 +44,23 @@ import org.onosproject.net.driver.DriverAdminServiceAdapter;
import org.onosproject.net.driver.DriverProvider;
import org.onosproject.net.driver.DriverService;
import org.onosproject.net.driver.DriverServiceAdapter;
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.runtime.PiPipeconfConfig;
import org.onosproject.pipelines.basic.PipeconfLoader;
import java.io.IOException;
import java.io.InputStream;
import java.net.URL;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import static org.junit.Assert.*;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
/**
@ -69,11 +68,6 @@ import static org.junit.Assert.*;
*/
public class PiPipeconfManagerTest {
private static final String PIPECONF_ID = "org.project.pipeconf.default";
private static final String BMV2_JSON_PATH = "/org/onosproject/net/pi/impl/default.json";
private final URL bmv2JsonConfigUrl = this.getClass().getResource(BMV2_JSON_PATH);
private static final DeviceId DEVICE_ID = DeviceId.deviceId("test:test");
private static final String BASE_DRIVER = "baseDriver";
private static final Set<Class<? extends Behaviour>> EXPECTED_BEHAVIOURS =
@ -105,11 +99,7 @@ public class PiPipeconfManagerTest {
@Before
public void setUp() throws IOException {
piPipeconfService = new PiPipeconfManager();
piPipeconf = DefaultPiPipeconf.builder()
.withId(new PiPipeconfId(PIPECONF_ID))
.withPipelineModel(Bmv2PipelineModelParser.parse(bmv2JsonConfigUrl))
.addBehaviour(Pipeliner.class, PipelinerAdapter.class)
.build();
piPipeconf = PipeconfLoader.BASIC_PIPECONF;
completeDriverName = BASE_DRIVER + ":" + piPipeconf.id();
piPipeconfService.cfgService = cfgService;
piPipeconfService.driverService = driverService;

View File

@ -24,7 +24,6 @@ import org.junit.Test;
import org.onlab.packet.MacAddress;
import org.onlab.util.ImmutableByteSequence;
import org.onosproject.TestApplicationId;
import org.onosproject.bmv2.model.Bmv2PipelineModelParser;
import org.onosproject.core.ApplicationId;
import org.onosproject.core.DefaultApplicationId;
import org.onosproject.core.GroupId;
@ -44,23 +43,17 @@ import org.onosproject.net.group.Group;
import org.onosproject.net.group.GroupBucket;
import org.onosproject.net.group.GroupBuckets;
import org.onosproject.net.group.GroupDescription;
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.runtime.PiAction;
import org.onosproject.net.pi.runtime.PiActionGroup;
import org.onosproject.net.pi.runtime.PiActionGroupMember;
import org.onosproject.net.pi.runtime.PiActionGroupMemberId;
import org.onosproject.net.pi.runtime.PiActionId;
import org.onosproject.net.pi.runtime.PiActionParam;
import org.onosproject.net.pi.runtime.PiActionParamId;
import org.onosproject.net.pi.runtime.PiActionProfileId;
import org.onosproject.net.pi.runtime.PiGroupKey;
import org.onosproject.net.pi.runtime.PiTableAction;
import org.onosproject.net.pi.runtime.PiTableEntry;
import org.onosproject.net.pi.runtime.PiTableId;
import org.onosproject.net.pi.runtime.PiTernaryFieldMatch;
import org.onosproject.pipelines.basic.PipeconfLoader;
import java.util.Collection;
import java.util.List;
@ -73,8 +66,17 @@ import static org.hamcrest.MatcherAssert.assertThat;
import static org.onlab.util.ImmutableByteSequence.copyFrom;
import static org.onlab.util.ImmutableByteSequence.fit;
import static org.onosproject.net.group.GroupDescription.Type.SELECT;
import static org.onosproject.net.pi.impl.MockInterpreter.*;
import static org.onosproject.net.pi.impl.PiFlowRuleTranslator.MAX_PI_PRIORITY;
import static org.onosproject.pipelines.basic.BasicConstants.ACT_PRF_WCMP_SELECTOR_ID;
import static org.onosproject.pipelines.basic.BasicConstants.ACT_PRM_PORT_ID;
import static org.onosproject.pipelines.basic.BasicConstants.ACT_SET_EGRESS_PORT_ID;
import static org.onosproject.pipelines.basic.BasicConstants.HDR_ETH_DST_ID;
import static org.onosproject.pipelines.basic.BasicConstants.HDR_ETH_SRC_ID;
import static org.onosproject.pipelines.basic.BasicConstants.HDR_ETH_TYPE_ID;
import static org.onosproject.pipelines.basic.BasicConstants.HDR_IN_PORT_ID;
import static org.onosproject.pipelines.basic.BasicConstants.PORT_BITWIDTH;
import static org.onosproject.pipelines.basic.BasicConstants.TBL_TABLE0_ID;
import static org.onosproject.pipelines.basic.BasicConstants.TBL_WCMP_TABLE_ID;
/**
* Tests for {@link PiFlowRuleTranslator}.
@ -82,22 +84,17 @@ import static org.onosproject.net.pi.impl.PiFlowRuleTranslator.MAX_PI_PRIORITY;
@SuppressWarnings("ConstantConditions")
public class PiTranslatorServiceTest {
private static final String BMV2_JSON_PATH = "/org/onosproject/net/pi/impl/default.json";
private static final short IN_PORT_MASK = 0x01ff; // 9-bit mask
private static final short ETH_TYPE_MASK = (short) 0xffff;
private static final DeviceId DEVICE_ID = DeviceId.deviceId("device:dummy:1");
private static final ApplicationId APP_ID = TestApplicationId.create("dummy");
private static final PiTableId ECMP_TABLE_ID = PiTableId.of("ecmp");
private static final PiActionProfileId ACT_PROF_ID = PiActionProfileId.of("ecmp_selector");
private static final GroupId GROUP_ID = GroupId.valueOf(1);
private static final PiActionId EGRESS_PORT_ACTION_ID = PiActionId.of("set_egress_port");
private static final int PORT_BITWIDTH = 9;
private static final PiActionParamId PORT_PARAM_ID = PiActionParamId.of("port");
private static final List<GroupBucket> BUCKET_LIST = ImmutableList.of(outputBucket(1),
outputBucket(2),
outputBucket(3)
);
private static final PiGroupKey GROUP_KEY = new PiGroupKey(ECMP_TABLE_ID, ACT_PROF_ID, GROUP_ID.id());
private static final PiGroupKey GROUP_KEY = new PiGroupKey(TBL_WCMP_TABLE_ID, ACT_PRF_WCMP_SELECTOR_ID,
GROUP_ID.id());
private static final GroupBuckets BUCKETS = new GroupBuckets(BUCKET_LIST);
private static final GroupDescription GROUP_DESC =
new DefaultGroupDescription(DEVICE_ID, SELECT, BUCKETS, GROUP_KEY, GROUP_ID.id(), APP_ID);
@ -111,12 +108,7 @@ public class PiTranslatorServiceTest {
@Before
public void setUp() throws Exception {
pipeconf = DefaultPiPipeconf.builder()
.withId(new PiPipeconfId("mock-pipeconf"))
.withPipelineModel(Bmv2PipelineModelParser.parse(this.getClass().getResource(BMV2_JSON_PATH)))
.addBehaviour(PiPipelineInterpreter.class, MockInterpreter.class)
.build();
pipeconf = PipeconfLoader.BASIC_PIPECONF;
expectedMembers = ImmutableSet.of(outputMember(1),
outputMember(2),
outputMember(3));
@ -177,13 +169,13 @@ public class PiTranslatorServiceTest {
.addEqualityGroup(entry1, entry2)
.testEquals();
int numMatchParams = pipeconf.pipelineModel().table(TABLE0).get().matchFields().size();
int numMatchParams = pipeconf.pipelineModel().table(TBL_TABLE0_ID.id()).get().matchFields().size();
// parse values stored in entry1
PiTernaryFieldMatch inPortParam = (PiTernaryFieldMatch) entry1.matchKey().fieldMatch(IN_PORT_ID).get();
PiTernaryFieldMatch ethDstParam = (PiTernaryFieldMatch) entry1.matchKey().fieldMatch(ETH_DST_ID).get();
PiTernaryFieldMatch ethSrcParam = (PiTernaryFieldMatch) entry1.matchKey().fieldMatch(ETH_SRC_ID).get();
PiTernaryFieldMatch ethTypeParam = (PiTernaryFieldMatch) entry1.matchKey().fieldMatch(ETH_TYPE_ID).get();
Optional<Double> expectedTimeout = pipeconf.pipelineModel().table(TABLE0).get().supportsAging()
PiTernaryFieldMatch inPortParam = (PiTernaryFieldMatch) entry1.matchKey().fieldMatch(HDR_IN_PORT_ID).get();
PiTernaryFieldMatch ethDstParam = (PiTernaryFieldMatch) entry1.matchKey().fieldMatch(HDR_ETH_DST_ID).get();
PiTernaryFieldMatch ethSrcParam = (PiTernaryFieldMatch) entry1.matchKey().fieldMatch(HDR_ETH_SRC_ID).get();
PiTernaryFieldMatch ethTypeParam = (PiTernaryFieldMatch) entry1.matchKey().fieldMatch(HDR_ETH_TYPE_ID).get();
Optional<Double> expectedTimeout = pipeconf.pipelineModel().table(TBL_TABLE0_ID.id()).get().supportsAging()
? Optional.of((double) rule1.timeout()) : Optional.empty();
// check that the number of parameters in the entry is the same as the number of table keys
@ -216,8 +208,8 @@ public class PiTranslatorServiceTest {
private static GroupBucket outputBucket(int portNum) {
ImmutableByteSequence paramVal = copyFrom(portNum);
PiActionParam param = new PiActionParam(PiActionParamId.of(PORT_PARAM_ID.name()), paramVal);
PiTableAction action = PiAction.builder().withId(EGRESS_PORT_ACTION_ID).withParameter(param).build();
PiActionParam param = new PiActionParam(ACT_PRM_PORT_ID, paramVal);
PiTableAction action = PiAction.builder().withId(ACT_SET_EGRESS_PORT_ID).withParameter(param).build();
TrafficTreatment treatment = DefaultTrafficTreatment.builder()
.add(Instructions.piTableAction(action))
.build();
@ -226,9 +218,9 @@ public class PiTranslatorServiceTest {
private static PiActionGroupMember outputMember(int portNum)
throws ImmutableByteSequence.ByteSequenceTrimException {
PiActionParam param = new PiActionParam(PORT_PARAM_ID, fit(copyFrom(portNum), PORT_BITWIDTH));
PiActionParam param = new PiActionParam(ACT_PRM_PORT_ID, fit(copyFrom(portNum), PORT_BITWIDTH));
PiAction piAction = PiAction.builder()
.withId(EGRESS_PORT_ACTION_ID)
.withId(ACT_SET_EGRESS_PORT_ID)
.withParameter(param).build();
return PiActionGroupMember.builder()
.withAction(piAction)
@ -255,7 +247,7 @@ public class PiTranslatorServiceTest {
assertThat("Group type must be SELECT",
piGroup1.type(), is(equalTo(PiActionGroup.Type.SELECT)));
assertThat("Action profile ID must be equal",
piGroup1.actionProfileId(), is(equalTo(ACT_PROF_ID)));
piGroup1.actionProfileId(), is(equalTo(ACT_PRF_WCMP_SELECTOR_ID)));
// members installed
Collection<PiActionGroupMember> members = piGroup1.members();

View File

@ -1 +0,0 @@
../../../../../../../../../../tools/test/p4src/p4-16/p4c-out/default.json

View File

@ -1,3 +1,3 @@
{
"piPipeconfId": "org.project.pipeconf.default"
"piPipeconfId": "org.onosproject.pipelines.basic"
}

View File

@ -9,6 +9,7 @@ COMPILE_DEPS = [
'//drivers/p4runtime:onos-drivers-p4runtime',
'//incubator/grpc-dependencies:grpc-core-repkg-' + GRPC_VER,
'//lib:grpc-netty-' + GRPC_VER,
'//pipelines/basic:onos-pipelines-basic',
]
BUNDLES = [
@ -29,5 +30,6 @@ onos_app (
included_bundles = BUNDLES,
required_apps = [
'org.onosproject.drivers.p4runtime',
'org.onosproject.pipelines.basic',
],
)

View File

@ -1,68 +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.drivers.bmv2;
import org.onosproject.bmv2.model.Bmv2PipelineModelParser;
import org.onosproject.driver.pipeline.DefaultSingleTablePipeline;
import org.onosproject.drivers.p4runtime.DefaultP4Interpreter;
import org.onosproject.drivers.p4runtime.DefaultP4PortStatisticsDiscovery;
import org.onosproject.net.behaviour.Pipeliner;
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 java.net.URL;
import static org.onosproject.net.pi.model.PiPipeconf.ExtensionType.BMV2_JSON;
import static org.onosproject.net.pi.model.PiPipeconf.ExtensionType.P4_INFO_TEXT;
/**
* Factory of pipeconf implementation for the default.p4 program on BMv2.
*/
public final class Bmv2DefaultPipeconfFactory {
private static final String PIPECONF_ID = "bmv2-default-pipeconf";
private static final String JSON_PATH = "/default.json";
private static final String P4INFO_PATH = "/default.p4info";
private static final PiPipeconf PIPECONF = buildPipeconf();
private Bmv2DefaultPipeconfFactory() {
// Hides constructor.
}
public static PiPipeconf get() {
return PIPECONF;
}
private static PiPipeconf buildPipeconf() {
final URL jsonUrl = Bmv2DefaultPipeconfFactory.class.getResource(JSON_PATH);
final URL p4InfoUrl = Bmv2DefaultPipeconfFactory.class.getResource(P4INFO_PATH);
return DefaultPiPipeconf.builder()
.withId(new PiPipeconfId(PIPECONF_ID))
.withPipelineModel(Bmv2PipelineModelParser.parse(jsonUrl))
.addBehaviour(PiPipelineInterpreter.class, DefaultP4Interpreter.class)
.addBehaviour(Pipeliner.class, DefaultSingleTablePipeline.class)
.addBehaviour(PortStatisticsDiscovery.class, DefaultP4PortStatisticsDiscovery.class)
.addExtension(P4_INFO_TEXT, p4InfoUrl)
.addExtension(BMV2_JSON, jsonUrl)
.build();
}
}

View File

@ -17,10 +17,7 @@
package org.onosproject.drivers.bmv2;
import org.apache.felix.scr.annotations.Component;
import org.apache.felix.scr.annotations.Reference;
import org.apache.felix.scr.annotations.ReferenceCardinality;
import org.onosproject.net.driver.AbstractDriverLoader;
import org.onosproject.net.pi.runtime.PiPipeconfService;
/**
* Loader for P4Runtime device drivers.
@ -28,22 +25,7 @@ import org.onosproject.net.pi.runtime.PiPipeconfService;
@Component(immediate = true)
public class Bmv2DriversLoader extends AbstractDriverLoader {
@Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
protected PiPipeconfService pipeconfService;
public Bmv2DriversLoader() {
super("/bmv2-drivers.xml");
}
@Override
public void activate() {
pipeconfService.register(Bmv2DefaultPipeconfFactory.get());
super.activate();
}
@Override
public void deactivate() {
pipeconfService.remove(Bmv2DefaultPipeconfFactory.get().id());
super.deactivate();
}
}

View File

@ -23,6 +23,7 @@ import org.onosproject.net.pi.model.PiPipeconf;
import org.onosproject.net.pi.model.PiPipelineProgrammable;
import org.onosproject.p4runtime.api.P4RuntimeClient;
import org.onosproject.p4runtime.api.P4RuntimeController;
import org.onosproject.pipelines.basic.PipeconfLoader;
import org.slf4j.Logger;
import java.util.Optional;
@ -37,8 +38,6 @@ import static org.slf4j.LoggerFactory.getLogger;
*/
public class Bmv2PipelineProgrammable extends AbstractHandlerBehaviour implements PiPipelineProgrammable {
private static final PiPipeconf DEFAULT_PIPECONF = Bmv2DefaultPipeconfFactory.get();
private final Logger log = getLogger(getClass());
@Override
@ -87,6 +86,6 @@ public class Bmv2PipelineProgrammable extends AbstractHandlerBehaviour implement
@Override
public Optional<PiPipeconf> getDefaultPipeconf() {
return Optional.of(DEFAULT_PIPECONF);
return Optional.of(PipeconfLoader.BASIC_PIPECONF);
}
}

View File

@ -1 +0,0 @@
../../../../../tools/test/p4src/p4-16/p4c-out/default.json

View File

@ -1 +0,0 @@
../../../../../tools/test/p4src/p4-16/p4c-out/default.p4info

View File

@ -1,331 +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.drivers.p4runtime;
import com.google.common.collect.ImmutableBiMap;
import com.google.common.collect.ImmutableList;
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.Instruction;
import org.onosproject.net.flow.instructions.Instructions;
import org.onosproject.net.flow.instructions.PiInstruction;
import org.onosproject.net.packet.DefaultInboundPacket;
import org.onosproject.net.packet.InboundPacket;
import org.onosproject.net.packet.OutboundPacket;
import org.onosproject.net.pi.model.PiHeaderFieldModel;
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.model.PiTableModel;
import org.onosproject.net.pi.runtime.PiAction;
import org.onosproject.net.pi.runtime.PiActionId;
import org.onosproject.net.pi.runtime.PiActionParam;
import org.onosproject.net.pi.runtime.PiActionParamId;
import org.onosproject.net.pi.runtime.PiCounterId;
import org.onosproject.net.pi.runtime.PiCounterType;
import org.onosproject.net.pi.runtime.PiHeaderFieldId;
import org.onosproject.net.pi.runtime.PiPacketMetadata;
import org.onosproject.net.pi.runtime.PiPacketMetadataId;
import org.onosproject.net.pi.runtime.PiPacketOperation;
import org.onosproject.net.pi.runtime.PiPipeconfService;
import org.onosproject.net.pi.runtime.PiTableId;
import java.nio.ByteBuffer;
import java.util.Collection;
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.onlab.util.ImmutableByteSequence.fit;
import static org.onosproject.net.PortNumber.CONTROLLER;
import static org.onosproject.net.PortNumber.FLOOD;
import static org.onosproject.net.flow.instructions.Instruction.Type.OUTPUT;
import static org.onosproject.net.pi.runtime.PiPacketOperation.Type.PACKET_OUT;
/**
* Implementation of an interpreter that can be used for any P4 program based on default.p4 (i.e. those under
* onos/tools/test/p4src).
*/
public class DefaultP4Interpreter extends AbstractHandlerBehaviour implements PiPipelineInterpreter {
// FIXME: Should move this class out of the p4runtime drivers.
// e.g. in a dedicated onos/pipeconf directory, along with any related P4 source code.
public static final String TABLE0 = "table0";
public static final String TABLE0_COUNTER = "table0_counter";
public static final String ECMP = "ecmp";
public static final String SEND_TO_CPU = "send_to_cpu";
public static final String PORT = "port";
public static final String DROP = "_drop";
public static final String SET_EGRESS_PORT = "set_egress_port";
public static final String EGRESS_PORT = "egress_port";
public static final String INGRESS_PORT = "ingress_port";
private static final PiTableId TABLE0_ID = PiTableId.of(TABLE0);
private static final PiTableId ECMP_ID = PiTableId.of(ECMP);
protected static final PiHeaderFieldId ETH_DST_ID = PiHeaderFieldId.of("ethernet", "dstAddr");
protected static final PiHeaderFieldId ETH_SRC_ID = PiHeaderFieldId.of("ethernet", "srcAddr");
protected static final PiHeaderFieldId ETH_TYPE_ID = PiHeaderFieldId.of("ethernet", "etherType");
private static final ImmutableBiMap<Integer, PiTableId> TABLE_MAP = ImmutableBiMap.of(
0, TABLE0_ID,
1, ECMP_ID);
private static final ImmutableBiMap<PiTableId, PiCounterId> TABLE_COUNTER_MAP = ImmutableBiMap.of(
TABLE0_ID, PiCounterId.of(TABLE0_COUNTER, PiCounterType.DIRECT));
private boolean targetAttributesInitialized = false;
/*
The following attributes are target-specific, i.e. they might change from one target to another.
*/
private ImmutableBiMap<Criterion.Type, PiHeaderFieldId> criterionMap;
private int portFieldBitWidth;
/**
* Populates target-specific attributes based on this device's pipeline model.
*/
private synchronized void initTargetSpecificAttributes() {
if (targetAttributesInitialized) {
return;
}
DeviceId deviceId = this.handler().data().deviceId();
PiPipeconfService pipeconfService = this.handler().get(PiPipeconfService.class);
PiPipeconfId pipeconfId = pipeconfService.ofDevice(deviceId)
.orElseThrow(() -> new RuntimeException(format(
"Unable to get current pipeconf for device %s", this.data().deviceId())));
PiPipeconf pipeconf = pipeconfService.getPipeconf(pipeconfId)
.orElseThrow(() -> new RuntimeException(format(
"Pipeconf %s is not registered", pipeconfId)));
PiPipelineModel model = pipeconf.pipelineModel();
this.portFieldBitWidth = extractPortFieldBitWidth(model);
this.criterionMap = new ImmutableBiMap.Builder<Criterion.Type, PiHeaderFieldId>()
.put(Criterion.Type.IN_PORT, extractInPortFieldId(model))
.put(Criterion.Type.ETH_DST, ETH_DST_ID)
.put(Criterion.Type.ETH_SRC, ETH_SRC_ID)
.put(Criterion.Type.ETH_TYPE, ETH_TYPE_ID)
.build();
this.targetAttributesInitialized = true;
}
private static PiHeaderFieldId extractInPortFieldId(PiPipelineModel model) {
/*
For the targets we currently support, the field name is "ingress_port", but we miss the header name, which is
target-specific. We know table0 defines that field as a match key, we look for it and we get the header name.
*/
PiTableModel tableModel = model.table(TABLE0).orElseThrow(() -> new RuntimeException(format(
"No such table '%s' in pipeline model", TABLE0)));
PiHeaderFieldModel fieldModel = tableModel.matchFields().stream()
.filter(m -> m.field().type().name().equals(INGRESS_PORT))
.findFirst()
.orElseThrow(() -> new RuntimeException(format(
"No such match field in table '%s' with name '%s'", TABLE0, INGRESS_PORT)))
.field();
return PiHeaderFieldId.of(fieldModel.header().name(), INGRESS_PORT);
}
private static int extractPortFieldBitWidth(PiPipelineModel model) {
/*
Get it form the set_egress_port action parameters.
*/
return model
.action(SET_EGRESS_PORT).orElseThrow(() -> new RuntimeException(format(
"No such action '%s' in pipeline model", SET_EGRESS_PORT)))
.param(PORT).orElseThrow(() -> new RuntimeException(format(
"No such parameter '%s' of action '%s' in pipeline model", PORT, SET_EGRESS_PORT)))
.bitWidth();
}
@Override
public PiAction mapTreatment(TrafficTreatment treatment, PiTableId piTableId) throws PiInterpreterException {
initTargetSpecificAttributes();
if (treatment.allInstructions().size() == 0) {
// No instructions means drop for us.
return actionWithName(DROP);
} else if (treatment.allInstructions().size() > 1) {
// Otherwise, we understand treatments with only 1 instruction.
throw new PiPipelineInterpreter.PiInterpreterException("Treatment has multiple instructions");
}
Instruction instruction = treatment.allInstructions().get(0);
switch (instruction.type()) {
case OUTPUT:
Instructions.OutputInstruction outInstruction = (Instructions.OutputInstruction) instruction;
return outputPiAction(outInstruction);
case PROTOCOL_INDEPENDENT:
PiInstruction piInstruction = (PiInstruction) instruction;
return (PiAction) piInstruction.action();
case NOACTION:
return actionWithName(DROP);
default:
throw new PiInterpreterException(format("Instruction type '%s' not supported", instruction.type()));
}
}
private PiAction outputPiAction(Instructions.OutputInstruction outInstruction) throws PiInterpreterException {
PortNumber port = outInstruction.port();
if (!port.isLogical()) {
try {
return PiAction.builder()
.withId(PiActionId.of(SET_EGRESS_PORT))
.withParameter(new PiActionParam(PiActionParamId.of(PORT),
fit(copyFrom(port.toLong()), portFieldBitWidth)))
.build();
} catch (ImmutableByteSequence.ByteSequenceTrimException e) {
throw new PiInterpreterException(e.getMessage());
}
} else if (port.equals(CONTROLLER)) {
return actionWithName(SEND_TO_CPU);
} else {
throw new PiInterpreterException(format("Egress on logical port '%s' not supported", port));
}
}
@Override
public Optional<PiCounterId> mapTableCounter(PiTableId piTableId) {
return Optional.ofNullable(TABLE_COUNTER_MAP.get(piTableId));
}
@Override
public Collection<PiPacketOperation> mapOutboundPacket(OutboundPacket packet)
throws PiInterpreterException {
TrafficTreatment treatment = packet.treatment();
// default.p4 supports only 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 default.p4 does not support flooding, we create a packet operation for each switch port.
for (Port port : handler().get(DeviceService.class).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(DeviceId deviceId, PiPacketOperation packetIn)
throws PiInterpreterException {
// Assuming 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<PiPacketMetadata> packetMetadata = packetIn.metadatas()
.stream().filter(metadata -> metadata.id().name().equals(INGRESS_PORT))
.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", INGRESS_PORT, deviceId, packetIn));
}
}
private PiPacketOperation createPiPacketOperation(ByteBuffer data, long portNumber) throws PiInterpreterException {
PiPacketMetadata metadata = createPacketMetadata(portNumber);
return PiPacketOperation.builder()
.withType(PACKET_OUT)
.withData(copyFrom(data))
.withMetadatas(ImmutableList.of(metadata))
.build();
}
private PiPacketMetadata createPacketMetadata(long portNumber) throws PiInterpreterException {
initTargetSpecificAttributes();
try {
return PiPacketMetadata.builder()
.withId(PiPacketMetadataId.of(EGRESS_PORT))
.withValue(fit(copyFrom(portNumber), portFieldBitWidth))
.build();
} catch (ImmutableByteSequence.ByteSequenceTrimException e) {
throw new PiInterpreterException(format("Port number %d too big, %s", portNumber, e.getMessage()));
}
}
/**
* Returns an action instance with no runtime parameters.
*/
private PiAction actionWithName(String name) {
return PiAction.builder().withId(PiActionId.of(name)).build();
}
@Override
public Optional<PiHeaderFieldId> mapCriterionType(Criterion.Type type) {
initTargetSpecificAttributes();
return Optional.ofNullable(criterionMap.get(type));
}
@Override
public Optional<Criterion.Type> mapPiHeaderFieldId(PiHeaderFieldId headerFieldId) {
initTargetSpecificAttributes();
return Optional.ofNullable(criterionMap.inverse().get(headerFieldId));
}
@Override
public Optional<PiTableId> mapFlowRuleTableId(int flowRuleTableId) {
return Optional.ofNullable(TABLE_MAP.get(flowRuleTableId));
}
@Override
public Optional<Integer> mapPiTableId(PiTableId piTableId) {
return Optional.ofNullable(TABLE_MAP.inverse().get(piTableId));
}
}

View File

@ -243,6 +243,10 @@ MODELS = [
'//models/polatis:onos-models-polatis-oar',
]
PIPELINES = [
'//pipelines/basic:onos-pipelines-basic-oar',
]
APP_JARS = [
'//apps/cpman/api:onos-apps-cpman-api',
'//apps/routing-api:onos-apps-routing-api',
@ -259,5 +263,6 @@ APP_JARS = [
# '//apps/p4runtime-test:onos-apps-p4runtime-test',
]
APPS = ONOS_DRIVERS + ONOS_PROVIDERS + ONOS_APPS + MODELS + PROTOCOL_APPS
APPS = ONOS_DRIVERS + ONOS_PROVIDERS + ONOS_APPS + MODELS + PIPELINES \
+ PROTOCOL_APPS

30
pipelines/basic/BUCK Normal file
View File

@ -0,0 +1,30 @@
COMPILE_DEPS = [
'//lib:CORE_DEPS',
'//lib:minimal-json',
'//incubator/bmv2/model:onos-incubator-bmv2-model',
'//drivers/default:onos-drivers-default',
'//protocols/p4runtime/api:onos-protocols-p4runtime-api',
]
BUNDLES = [
'//pipelines/basic:onos-pipelines-basic',
'//drivers/default:onos-drivers-default',
'//incubator/bmv2/model:onos-incubator-bmv2-model',
]
osgi_jar(
deps = COMPILE_DEPS,
)
onos_app(
app_name = 'org.onosproject.pipelines.basic',
title = 'Basic Pipelines',
category = 'Pipeline',
url = 'http://onosproject.org',
description = 'Provides pipelines with basic L2/L3 forwarding capabilities and packet-in/out '
+ 'support.',
included_bundles = BUNDLES,
required_apps = [
'org.onosproject.drivers.p4runtime',
]
)

View File

@ -0,0 +1,73 @@
/*
* 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.basic;
import org.onosproject.net.pi.runtime.PiActionId;
import org.onosproject.net.pi.runtime.PiActionParamId;
import org.onosproject.net.pi.runtime.PiActionProfileId;
import org.onosproject.net.pi.runtime.PiCounterId;
import org.onosproject.net.pi.runtime.PiCounterType;
import org.onosproject.net.pi.runtime.PiHeaderFieldId;
import org.onosproject.net.pi.runtime.PiPacketMetadataId;
import org.onosproject.net.pi.runtime.PiTableId;
/**
* Constants for the basic.p4 program.
*/
public final class BasicConstants {
// TODO: constants could be auto-generated starting from the P4info.
// Header field IDs
public static final String ETHERNET = "ethernet";
public static final String LOCAL_METADATA = "local_metadata";
public static final String STANDARD_METADATA = "standard_metadata";
public static final PiHeaderFieldId HDR_IN_PORT_ID = PiHeaderFieldId.of(STANDARD_METADATA, "ingress_port");
public static final PiHeaderFieldId HDR_ETH_DST_ID = PiHeaderFieldId.of(ETHERNET, "dst_addr");
public static final PiHeaderFieldId HDR_ETH_SRC_ID = PiHeaderFieldId.of(ETHERNET, "src_addr");
public static final PiHeaderFieldId HDR_ETH_TYPE_ID = PiHeaderFieldId.of(ETHERNET, "ether_type");
public static final PiHeaderFieldId HDR_NEXT_HOP_ID = PiHeaderFieldId.of(LOCAL_METADATA, "next_hop_id");
public static final PiHeaderFieldId HDR_SELECTOR_ID = PiHeaderFieldId.of(LOCAL_METADATA, "selector");
// Table IDs
public static final PiTableId TBL_TABLE0_ID = PiTableId.of("table0_control.table0");
public static final PiTableId TBL_WCMP_TABLE_ID = PiTableId.of("wcmp_control.wcmp_table");
// Counter IDs
public static final PiCounterId CNT_TABLE0_ID = PiCounterId.of("table0_control.table0_counter",
PiCounterType.DIRECT);
public static final PiCounterId CNT_WCMP_TABLE_ID = PiCounterId.of("wcmp_control.wcmp_table_counter",
PiCounterType.DIRECT);
// Action IDs
public static final PiActionId ACT_NOACTION_ID = PiActionId.of("NoAction");
public static final PiActionId ACT_DROP_ID = PiActionId.of("_drop");
public static final PiActionId ACT_SET_EGRESS_PORT_ID = PiActionId.of("set_egress_port");
public static final PiActionId ACT_SET_NEXT_HOP_ID = PiActionId.of("table0_control.set_next_hop_id");
public static final PiActionId ACT_SEND_TO_CPU_ID = PiActionId.of("send_to_cpu");
// Action Param IDs
public static final PiActionParamId ACT_PRM_PORT_ID = PiActionParamId.of("port");
public static final PiActionParamId ACT_PRM_NEXT_HOP_ID = PiActionParamId.of("next_hop_id");
// Action Profile IDs
public static final PiActionProfileId ACT_PRF_WCMP_SELECTOR_ID = PiActionProfileId.of("wcmp_selector");
// Packet Metadata IDs
public static final PiPacketMetadataId PKT_META_EGRESS_PORT_ID = PiPacketMetadataId.of("egress_port");
public static final PiPacketMetadataId PKT_META_INGRESS_PORT_ID = PiPacketMetadataId.of("ingress_port");
// Bitwidths
public static final int PORT_BITWIDTH = 9;
private BasicConstants() {
// Hides constructor.
}
}

View File

@ -0,0 +1,257 @@
/*
* 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.basic;
import com.google.common.collect.ImmutableBiMap;
import com.google.common.collect.ImmutableList;
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.Instruction;
import org.onosproject.net.packet.DefaultInboundPacket;
import org.onosproject.net.packet.InboundPacket;
import org.onosproject.net.packet.OutboundPacket;
import org.onosproject.net.pi.model.PiPipelineInterpreter;
import org.onosproject.net.pi.runtime.PiAction;
import org.onosproject.net.pi.runtime.PiActionParam;
import org.onosproject.net.pi.runtime.PiCounterId;
import org.onosproject.net.pi.runtime.PiHeaderFieldId;
import org.onosproject.net.pi.runtime.PiPacketMetadata;
import org.onosproject.net.pi.runtime.PiPacketOperation;
import org.onosproject.net.pi.runtime.PiTableId;
import java.nio.ByteBuffer;
import java.util.Collection;
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.onlab.util.ImmutableByteSequence.fit;
import static org.onosproject.net.PortNumber.CONTROLLER;
import static org.onosproject.net.PortNumber.FLOOD;
import static org.onosproject.net.flow.instructions.Instruction.Type.OUTPUT;
import static org.onosproject.net.flow.instructions.Instructions.OutputInstruction;
import static org.onosproject.net.pi.runtime.PiPacketOperation.Type.PACKET_OUT;
import static org.onosproject.pipelines.basic.BasicConstants.ACT_DROP_ID;
import static org.onosproject.pipelines.basic.BasicConstants.ACT_NOACTION_ID;
import static org.onosproject.pipelines.basic.BasicConstants.ACT_PRM_PORT_ID;
import static org.onosproject.pipelines.basic.BasicConstants.ACT_SEND_TO_CPU_ID;
import static org.onosproject.pipelines.basic.BasicConstants.ACT_SET_EGRESS_PORT_ID;
import static org.onosproject.pipelines.basic.BasicConstants.CNT_TABLE0_ID;
import static org.onosproject.pipelines.basic.BasicConstants.CNT_WCMP_TABLE_ID;
import static org.onosproject.pipelines.basic.BasicConstants.HDR_ETH_DST_ID;
import static org.onosproject.pipelines.basic.BasicConstants.HDR_ETH_SRC_ID;
import static org.onosproject.pipelines.basic.BasicConstants.HDR_ETH_TYPE_ID;
import static org.onosproject.pipelines.basic.BasicConstants.HDR_IN_PORT_ID;
import static org.onosproject.pipelines.basic.BasicConstants.PKT_META_EGRESS_PORT_ID;
import static org.onosproject.pipelines.basic.BasicConstants.PKT_META_INGRESS_PORT_ID;
import static org.onosproject.pipelines.basic.BasicConstants.PORT_BITWIDTH;
import static org.onosproject.pipelines.basic.BasicConstants.TBL_TABLE0_ID;
import static org.onosproject.pipelines.basic.BasicConstants.TBL_WCMP_TABLE_ID;
/**
* Interpreter implementation for basic.p4.
*/
public class BasicInterpreterImpl extends AbstractHandlerBehaviour
implements PiPipelineInterpreter {
private static final ImmutableBiMap<Integer, PiTableId> TABLE_MAP =
new ImmutableBiMap.Builder<Integer, PiTableId>()
.put(0, TBL_TABLE0_ID)
.build();
private static final ImmutableBiMap<PiTableId, PiCounterId> TABLE_COUNTER_MAP =
new ImmutableBiMap.Builder<PiTableId, PiCounterId>()
.put(TBL_TABLE0_ID, CNT_TABLE0_ID)
.put(TBL_WCMP_TABLE_ID, CNT_WCMP_TABLE_ID)
.build();
private static final ImmutableBiMap<Criterion.Type, PiHeaderFieldId> CRITERION_MAP =
new ImmutableBiMap.Builder<Criterion.Type, PiHeaderFieldId>()
.put(Criterion.Type.IN_PORT, HDR_IN_PORT_ID)
.put(Criterion.Type.ETH_DST, HDR_ETH_DST_ID)
.put(Criterion.Type.ETH_SRC, HDR_ETH_SRC_ID)
.put(Criterion.Type.ETH_TYPE, HDR_ETH_TYPE_ID)
.build();
@Override
public PiAction mapTreatment(TrafficTreatment treatment, PiTableId piTableId)
throws PiInterpreterException {
if (treatment.allInstructions().size() == 0) {
// No actions means drop.
return PiAction.builder().withId(ACT_DROP_ID).build();
} else if (treatment.allInstructions().size() > 1) {
// We understand treatments with only 1 instruction.
throw new PiInterpreterException("Treatment has multiple instructions");
}
Instruction instruction = treatment.allInstructions().get(0);
switch (instruction.type()) {
case OUTPUT:
return outputPiAction((OutputInstruction) instruction);
case NOACTION:
return PiAction.builder().withId(ACT_NOACTION_ID).build();
default:
throw new PiInterpreterException(format(
"Instruction type '%s' not supported", instruction.type()));
}
}
private PiAction outputPiAction(OutputInstruction outInstruction)
throws PiInterpreterException {
PortNumber port = outInstruction.port();
if (!port.isLogical()) {
try {
return PiAction.builder()
.withId(ACT_SET_EGRESS_PORT_ID)
.withParameter(new PiActionParam(ACT_PRM_PORT_ID,
fit(copyFrom(port.toLong()), PORT_BITWIDTH)))
.build();
} catch (ImmutableByteSequence.ByteSequenceTrimException e) {
throw new PiInterpreterException(e.getMessage());
}
} else if (port.equals(CONTROLLER)) {
return PiAction.builder().withId(ACT_SEND_TO_CPU_ID).build();
} else {
throw new PiInterpreterException(format(
"Egress on logical port '%s' not supported", port));
}
}
@Override
public Optional<PiCounterId> mapTableCounter(PiTableId piTableId) {
return Optional.ofNullable(TABLE_COUNTER_MAP.get(piTableId));
}
@Override
public Collection<PiPacketOperation> mapOutboundPacket(OutboundPacket packet)
throws PiInterpreterException {
TrafficTreatment treatment = packet.treatment();
// basic.p4 supports only OUTPUT instructions.
List<OutputInstruction> outInstructions = treatment
.allInstructions()
.stream()
.filter(i -> i.type().equals(OUTPUT))
.map(i -> (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 (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 basic.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(packet.data(), port.number().toLong()));
}
} else {
builder.add(createPiPacketOperation(packet.data(), outInst.port().toLong()));
}
}
return builder.build();
}
@Override
public InboundPacket mapInboundPacket(DeviceId deviceId, PiPacketOperation packetIn)
throws PiInterpreterException {
// Assuming that the packet is ethernet, which is fine since basic.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<PiPacketMetadata> packetMetadata = packetIn.metadatas()
.stream().filter(m -> m.id().equals(PKT_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",
PKT_META_INGRESS_PORT_ID, deviceId, packetIn));
}
}
private PiPacketOperation createPiPacketOperation(ByteBuffer data, long portNumber)
throws PiInterpreterException {
PiPacketMetadata metadata = createPacketMetadata(portNumber);
return PiPacketOperation.builder()
.withType(PACKET_OUT)
.withData(copyFrom(data))
.withMetadatas(ImmutableList.of(metadata))
.build();
}
private PiPacketMetadata createPacketMetadata(long portNumber) throws PiInterpreterException {
try {
return PiPacketMetadata.builder()
.withId(PKT_META_EGRESS_PORT_ID)
.withValue(fit(copyFrom(portNumber), PORT_BITWIDTH))
.build();
} catch (ImmutableByteSequence.ByteSequenceTrimException e) {
throw new PiInterpreterException(format(
"Port number %d too big, %s", portNumber, e.getMessage()));
}
}
@Override
public Optional<PiHeaderFieldId> mapCriterionType(Criterion.Type type) {
return Optional.ofNullable(CRITERION_MAP.get(type));
}
@Override
public Optional<Criterion.Type> mapPiHeaderFieldId(PiHeaderFieldId headerFieldId) {
return Optional.ofNullable(CRITERION_MAP.inverse().get(headerFieldId));
}
@Override
public Optional<PiTableId> mapFlowRuleTableId(int flowRuleTableId) {
return Optional.ofNullable(TABLE_MAP.get(flowRuleTableId));
}
@Override
public Optional<Integer> mapPiTableId(PiTableId piTableId) {
return Optional.ofNullable(TABLE_MAP.inverse().get(piTableId));
}
}

View File

@ -0,0 +1,31 @@
/*
* 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.basic;
import org.onosproject.net.pi.runtime.PiTableId;
/**
* Constants for the ecmp.p4 program.
*/
public final class EcmpConstants {
public static final PiTableId TBL_ECMP_TABLE_ID = PiTableId.of("ecmp_table");
private EcmpConstants() {
// Hides constructor.
}
}

View File

@ -14,28 +14,24 @@
* limitations under the License.
*/
package org.onosproject.pi.demo.app.ecmp;
package org.onosproject.pipelines.basic;
import com.google.common.collect.ImmutableBiMap;
import org.onosproject.drivers.p4runtime.DefaultP4Interpreter;
import org.onosproject.net.pi.runtime.PiTableId;
import java.util.Optional;
/**
* Implementation of a PiPipeline interpreter for the ecmp.json configuration.
*/
public class EcmpInterpreter extends DefaultP4Interpreter {
import static org.onosproject.pipelines.basic.BasicConstants.TBL_TABLE0_ID;
import static org.onosproject.pipelines.basic.EcmpConstants.TBL_ECMP_TABLE_ID;
protected static final String ECMP_METADATA_HEADER_NAME = "ecmp_metadata";
protected static final String ECMP_GROUP_ACTION_NAME = "ecmp_group";
protected static final String GROUP_ID = "group_id";
protected static final String SELECTOR = "selector";
protected static final String ECMP_GROUP_TABLE = "ecmp_group_table";
/**
* Interpreter implementation for ecmp.p4.
*/
public class EcmpInterpreterImpl extends BasicInterpreterImpl {
private static final ImmutableBiMap<Integer, PiTableId> TABLE_MAP = new ImmutableBiMap.Builder<Integer, PiTableId>()
.put(0, PiTableId.of(TABLE0))
.put(1, PiTableId.of(ECMP_GROUP_TABLE))
.put(0, TBL_TABLE0_ID)
.put(1, TBL_ECMP_TABLE_ID)
.build();
@Override

View File

@ -0,0 +1,106 @@
/*
* 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.basic;
import com.google.common.collect.ImmutableList;
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.bmv2.model.Bmv2PipelineModelParser;
import org.onosproject.driver.pipeline.DefaultSingleTablePipeline;
import org.onosproject.net.behaviour.Pipeliner;
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.runtime.PiPipeconfService;
import java.net.URL;
import java.util.Collection;
import static org.onosproject.net.pi.model.PiPipeconf.ExtensionType.BMV2_JSON;
import static org.onosproject.net.pi.model.PiPipeconf.ExtensionType.P4_INFO_TEXT;
/**
* Component that produces and registers the basic pipeconfs when loaded.
*/
@Component(immediate = true)
public final class PipeconfLoader {
private static final PiPipeconfId BASIC_PIPECONF_ID = new PiPipeconfId("org.onosproject.pipelines.basic");
private static final String BASIC_JSON_PATH = "/p4c-out/bmv2/basic.json";
private static final String BASIC_P4INFO = "/p4c-out/bmv2/basic.p4info";
private static final PiPipeconfId ECMP_PIPECONF_ID = new PiPipeconfId("org.onosproject.pipelines.ecmp");
private static final String ECMP_JSON_PATH = "/p4c-out/bmv2/ecmp.json";
private static final String ECMP_P4INFO = "/p4c-out/bmv2/ecmp.p4info";
public static final PiPipeconf BASIC_PIPECONF = buildBasicPipeconf();
public static final PiPipeconf ECMP_PIPECONF = buildEcmpPipeconf();
private static final Collection<PiPipeconf> ALL_PIPECONFS = ImmutableList.of(BASIC_PIPECONF, ECMP_PIPECONF);
@Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
private PiPipeconfService piPipeconfService;
@Activate
public void activate() {
// Registers all pipeconf at component activation.
ALL_PIPECONFS.forEach(piPipeconfService::register);
}
@Deactivate
public void deactivate() {
ALL_PIPECONFS.stream().map(PiPipeconf::id).forEach(piPipeconfService::remove);
}
private static PiPipeconf buildBasicPipeconf() {
final URL jsonUrl = PipeconfLoader.class.getResource(BASIC_JSON_PATH);
final URL p4InfoUrl = PipeconfLoader.class.getResource(BASIC_P4INFO);
final PiPipelineModel model = Bmv2PipelineModelParser.parse(jsonUrl);
return DefaultPiPipeconf.builder()
.withId(BASIC_PIPECONF_ID)
.withPipelineModel(model)
.addBehaviour(PiPipelineInterpreter.class, BasicInterpreterImpl.class)
.addBehaviour(Pipeliner.class, DefaultSingleTablePipeline.class)
.addBehaviour(PortStatisticsDiscovery.class, PortStatisticsDiscoveryImpl.class)
.addExtension(P4_INFO_TEXT, p4InfoUrl)
.addExtension(BMV2_JSON, jsonUrl)
// Put here other target-specific extensions,
// e.g. Tofino's bin and context.json.
.build();
}
private static PiPipeconf buildEcmpPipeconf() {
final URL jsonUrl = PipeconfLoader.class.getResource(ECMP_JSON_PATH);
final URL p4InfoUrl = PipeconfLoader.class.getResource(ECMP_P4INFO);
final PiPipelineModel model = Bmv2PipelineModelParser.parse(jsonUrl);
return DefaultPiPipeconf.builder()
.withId(ECMP_PIPECONF_ID)
.withPipelineModel(model)
.addBehaviour(PiPipelineInterpreter.class, EcmpInterpreterImpl.class)
.addBehaviour(Pipeliner.class, DefaultSingleTablePipeline.class)
.addBehaviour(PortStatisticsDiscovery.class, PortStatisticsDiscoveryImpl.class)
.addExtension(P4_INFO_TEXT, p4InfoUrl)
.addExtension(BMV2_JSON, jsonUrl)
.build();
}
}

View File

@ -14,17 +14,26 @@
* limitations under the License.
*/
package org.onosproject.drivers.p4runtime;
package org.onosproject.pipelines.basic;
import com.google.common.collect.Maps;
import com.google.common.collect.Sets;
import org.onosproject.net.DeviceId;
import org.onosproject.net.device.DefaultPortStatistics;
import org.onosproject.net.device.DeviceService;
import org.onosproject.net.device.PortStatistics;
import org.onosproject.net.device.PortStatisticsDiscovery;
import org.onosproject.net.driver.AbstractHandlerBehaviour;
import org.onosproject.net.pi.model.PiPipeconf;
import org.onosproject.net.pi.runtime.PiCounterCellData;
import org.onosproject.net.pi.runtime.PiCounterCellId;
import org.onosproject.net.pi.runtime.PiCounterId;
import org.onosproject.net.pi.runtime.PiIndirectCounterCellId;
import org.onosproject.net.pi.runtime.PiPipeconfService;
import org.onosproject.p4runtime.api.P4RuntimeClient;
import org.onosproject.p4runtime.api.P4RuntimeController;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.Collection;
import java.util.Collections;
@ -36,22 +45,17 @@ import java.util.stream.Collectors;
import static org.onosproject.net.pi.runtime.PiCounterType.INDIRECT;
/**
* Implementation of a PortStatisticsBehaviour that can be used for any P4 program based on default.p4 (i.e. those
* under onos/tools/test/p4src).
* Implementation of the PortStatisticsBehaviour for basic.p4.
*/
public class DefaultP4PortStatisticsDiscovery extends AbstractP4RuntimeHandlerBehaviour
implements PortStatisticsDiscovery {
public class PortStatisticsDiscoveryImpl extends AbstractHandlerBehaviour implements PortStatisticsDiscovery {
// FIXME: hard-coding the scope here will break support for the P4_14 version of the program.
// With P4_14, counter names in the generated P4Info won't have any scope.
// A solution could be that of dynamically building counter IDs based on the P4Info (as in DefaultP4Interpreter).
private static final String DEFAULT_SCOPE = "port_counters_control";
private static final PiCounterId DEFAULT_INGRESS_COUNTER_ID = PiCounterId.of(DEFAULT_SCOPE,
"ingress_port_counter",
INDIRECT);
private static final PiCounterId DEFAULT_EGRESS_COUNTER_ID = PiCounterId.of(DEFAULT_SCOPE,
"egress_port_counter",
INDIRECT);
protected final Logger log = LoggerFactory.getLogger(getClass());
private static final String SCOPE = "port_counters_control";
private static final PiCounterId INGRESS_COUNTER_ID = PiCounterId.of("port_counters_ingress",
"ingress_port_counter", INDIRECT);
private static final PiCounterId EGRESS_COUNTER_ID = PiCounterId.of("port_counters_egress",
"egress_port_counter", INDIRECT);
/**
* Returns the ID of the ingress port counter.
@ -59,7 +63,7 @@ public class DefaultP4PortStatisticsDiscovery extends AbstractP4RuntimeHandlerBe
* @return counter ID
*/
public PiCounterId ingressCounterId() {
return DEFAULT_INGRESS_COUNTER_ID;
return INGRESS_COUNTER_ID;
}
/**
@ -68,18 +72,31 @@ public class DefaultP4PortStatisticsDiscovery extends AbstractP4RuntimeHandlerBe
* @return counter ID
*/
public PiCounterId egressCounterId() {
return DEFAULT_EGRESS_COUNTER_ID;
return EGRESS_COUNTER_ID;
}
@Override
public Collection<PortStatistics> discoverPortStatistics() {
if (!super.setupBehaviour()) {
DeviceService deviceService = this.handler().get(DeviceService.class);
DeviceId deviceId = this.data().deviceId();
PiPipeconfService piPipeconfService = handler().get(PiPipeconfService.class);
if (!piPipeconfService.ofDevice(deviceId).isPresent() ||
!piPipeconfService.getPipeconf(piPipeconfService.ofDevice(deviceId).get()).isPresent()) {
log.warn("Unable to get the pipeconf of {}, aborting operation", deviceId);
return Collections.emptyList();
}
PiPipeconf pipeconf = piPipeconfService.getPipeconf(piPipeconfService.ofDevice(deviceId).get()).get();
P4RuntimeController controller = handler().get(P4RuntimeController.class);
if (!controller.hasClient(deviceId)) {
log.warn("Unable to find client for {}, aborting operation", deviceId);
return Collections.emptyList();
}
P4RuntimeClient client = controller.getClient(deviceId);
Map<Long, DefaultPortStatistics.Builder> portStatBuilders = Maps.newHashMap();
deviceService.getPorts(deviceId)
.forEach(p -> portStatBuilders.put(p.number().toLong(),
DefaultPortStatistics.builder()

View File

@ -14,20 +14,8 @@
* limitations under the License.
*/
#ifndef CHECKSUMS
#define CHECKSUMS
#include "headers.p4"
control verifyChecksum(in headers_t hdr, inout metadata_t meta) {
apply {
// Nothing to do
}
}
control computeChecksum(inout headers_t hdr, inout metadata_t meta) {
apply {
// Nothing to do
}
}
#endif
/**
* ONOS default pipelines that provide basic L2/L3 forwarding capabilities
* and packet-in/out support.
*/
package org.onosproject.pipelines.basic;

View File

@ -0,0 +1,14 @@
all: basic ecmp
basic: basic.p4
p4c-bm2-ss -o p4c-out/bmv2/basic.json \
--p4runtime-file p4c-out/bmv2/basic.p4info \
--p4runtime-format text basic.p4
ecmp: ecmp.p4
p4c-bm2-ss -o p4c-out/bmv2/ecmp.json \
--p4runtime-file p4c-out/bmv2/ecmp.p4info \
--p4runtime-format text ecmp.p4
clean:
rm -rf p4c-out/bmv2/*.json
rm -rf p4c-out/bmv2/*.p4info

View File

@ -0,0 +1,69 @@
/*
* 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.
*/
#include <core.p4>
#include <v1model.p4>
#include "include/headers.p4"
#include "include/defines.p4"
#include "include/parsers.p4"
#include "include/actions.p4"
#include "include/port_counters.p4"
#include "include/checksums.p4"
#include "include/packet_io.p4"
#include "include/table0.p4"
#include "include/wcmp.p4"
//------------------------------------------------------------------------------
// INGRESS PIPELINE
//------------------------------------------------------------------------------
control ingress(inout headers_t hdr,
inout local_metadata_t local_metadata,
inout standard_metadata_t standard_metadata) {
apply {
port_counters_ingress.apply(hdr, standard_metadata);
packetio_ingress.apply(hdr, standard_metadata);
table0_control.apply(hdr, local_metadata, standard_metadata);
wcmp_control.apply(hdr, local_metadata, standard_metadata);
}
}
//------------------------------------------------------------------------------
// EGRESS PIPELINE
//------------------------------------------------------------------------------
control egress(inout headers_t hdr,
inout local_metadata_t local_metadata,
inout standard_metadata_t standard_metadata) {
apply {
port_counters_egress.apply(hdr, standard_metadata);
packetio_egress.apply(hdr, standard_metadata);
}
}
//------------------------------------------------------------------------------
// SWITCH INSTANTIATION
//------------------------------------------------------------------------------
V1Switch(parser_impl(),
verify_checksum_control(),
ingress(),
egress(),
compute_checksum_control(),
deparser()) main;

View File

@ -0,0 +1,105 @@
/*
* 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.
*/
#include <core.p4>
#include <v1model.p4>
#include "include/headers.p4"
#include "include/defines.p4"
#include "include/parsers.p4"
#include "include/actions.p4"
#include "include/port_counters.p4"
#include "include/checksums.p4"
#include "include/packet_io.p4"
#include "include/table0.p4"
// FIXME: this program is obsolete and should be removed.
// The PI ECMP demo app should be refactored to use the WCMP capability of default.p4
// Expected number of ports of an ECMP group.
// This value is fixed, .i.e. we do not support ECMP over port groups of different
// size. Due to hardware limitations, this value must be constant and a power of 2.
#define ECMP_GROUP_SIZE 128w2
//------------------------------------------------------------------------------
// INGRESS PIPELINE
//------------------------------------------------------------------------------
control ingress(inout headers_t hdr,
inout local_metadata_t local_metadata,
inout standard_metadata_t standard_metadata) {
direct_counter(CounterType.packets_and_bytes) ecmp_table_counter;
table ecmp_table {
key = {
local_metadata.next_hop_id : exact;
local_metadata.selector : exact;
}
actions = {
set_egress_port(standard_metadata);
}
counters = ecmp_table_counter;
}
action set_ecmp_selector() {
hash(local_metadata.selector, HashAlgorithm.crc16, (bit<64>) 0,
{
hdr.ipv4.src_addr,
hdr.ipv4.dst_addr,
hdr.ipv4.protocol,
local_metadata.l4_src_port,
local_metadata.l4_dst_port
},
ECMP_GROUP_SIZE);
}
apply {
port_counters_ingress.apply(hdr, standard_metadata);
packetio_ingress.apply(hdr, standard_metadata);
table0_control.apply(hdr, local_metadata, standard_metadata);
if (local_metadata.next_hop_id > 0) {
set_ecmp_selector();
ecmp_table.apply();
}
}
}
//------------------------------------------------------------------------------
// EGRESS PIPELINE
//------------------------------------------------------------------------------
control egress(inout headers_t hdr,
inout local_metadata_t local_metadata,
inout standard_metadata_t standard_metadata) {
apply {
port_counters_egress.apply(hdr, standard_metadata);
packetio_egress.apply(hdr, standard_metadata);
}
}
//------------------------------------------------------------------------------
// SWITCH INSTANTIATION
//------------------------------------------------------------------------------
V1Switch(parser_impl(),
verify_checksum_control(),
ingress(),
egress(),
compute_checksum_control(),
deparser()) main;

View File

@ -14,10 +14,11 @@
* limitations under the License.
*/
#ifndef ACTIONS
#define ACTIONS
#include "defines.p4"
#ifndef __ACTIONS__
#define __ACTIONS__
#include "headers.p4"
#include "defines.p4"
action send_to_cpu(inout standard_metadata_t standard_metadata) {
standard_metadata.egress_spec = CPU_PORT;
@ -27,8 +28,10 @@ action set_egress_port(inout standard_metadata_t standard_metadata, port_t port)
standard_metadata.egress_spec = port;
}
action _drop(inout standard_metadata_t standard_metadata) {
standard_metadata.egress_spec = DROP_PORT;
action _drop() {
mark_to_drop();
}
#endif

View File

@ -0,0 +1,36 @@
/*
* 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.
*/
#ifndef __CHECKSUMS__
#define __CHECKSUMS__
#include "headers.p4"
control verify_checksum_control(inout headers_t hdr,
inout local_metadata_t local_metadata) {
apply {
// Assume checksum is always correct.
}
}
control compute_checksum_control(inout headers_t hdr,
inout local_metadata_t local_metadata) {
apply {
// No need to recompute.
}
}
#endif

View File

@ -14,19 +14,17 @@
* limitations under the License.
*/
#ifndef DEFINES
#define DEFINES
#ifndef __DEFINES__
#define __DEFINES__
#define MAX_PORTS 255
#define ETH_TYPE_IPV4 16w0x800
#define IP_TYPE_TCP 8w6
#define IP_TYPE_UDP 8w17
#define ETH_TYPE_IPV4 0x0800
#define IP_PROTO_TCP 8w6
#define IP_PROTO_UDP 8w17
#define MAX_PORTS 511
typedef bit<9> port_t;
typedef bit<8> ecmp_group_id_t;
typedef bit<16> next_hop_id_t;
const port_t CPU_PORT = 255;
const port_t DROP_PORT = 511;
#endif

View File

@ -14,8 +14,10 @@
* limitations under the License.
*/
#ifndef HEADERS
#define HEADERS
#ifndef __HEADERS__
#define __HEADERS__
#include "defines.p4"
@controller_header("packet_in")
header packet_in_header_t {
@ -27,51 +29,44 @@ header packet_out_header_t {
bit<9> egress_port;
}
struct intrinsic_metadata_t {
bit<32> ingress_global_timestamp;
bit<32> lf_field_list;
bit<16> mcast_grp;
bit<16> egress_rid;
}
header ethernet_t {
bit<48> dstAddr;
bit<48> srcAddr;
bit<16> etherType;
bit<48> dst_addr;
bit<48> src_addr;
bit<16> ether_type;
}
header ipv4_t {
bit<4> version;
bit<4> ihl;
bit<8> diffserv;
bit<16> totalLen;
bit<16> len;
bit<16> identification;
bit<3> flags;
bit<13> fragOffset;
bit<13> frag_offset;
bit<8> ttl;
bit<8> protocol;
bit<16> hdrChecksum;
bit<32> srcAddr;
bit<32> dstAddr;
bit<16> hdr_checksum;
bit<32> src_addr;
bit<32> dst_addr;
}
header tcp_t {
bit<16> srcPort;
bit<16> dstPort;
bit<32> seqNo;
bit<32> ackNo;
bit<4> dataOffset;
bit<16> src_port;
bit<16> dst_port;
bit<32> seq_no;
bit<32> ack_no;
bit<4> data_offset;
bit<3> res;
bit<3> ecn;
bit<6> ctrl;
bit<16> window;
bit<16> checksum;
bit<16> urgentPtr;
bit<16> urgent_ptr;
}
header udp_t {
bit<16> srcPort;
bit<16> dstPort;
bit<16> src_port;
bit<16> dst_port;
bit<16> length_;
bit<16> checksum;
}
@ -84,4 +79,12 @@ struct headers_t {
packet_out_header_t packet_out;
packet_in_header_t packet_in;
}
struct local_metadata_t {
bit<16> l4_src_port;
bit<16> l4_dst_port;
next_hop_id_t next_hop_id;
bit<16> selector;
}
#endif

View File

@ -14,20 +14,26 @@
* limitations under the License.
*/
#ifndef PACKET_IO
#define PACKET_IO
#ifndef __PACKET_IO__
#define __PACKET_IO__
control PacketIoIngressControl(inout headers_t hdr, inout standard_metadata_t standard_metadata) {
#include "headers.p4"
#include "defines.p4"
control packetio_ingress(inout headers_t hdr,
inout standard_metadata_t standard_metadata) {
apply {
if (hdr.packet_out.isValid()) {
if (standard_metadata.ingress_port == CPU_PORT) {
standard_metadata.egress_spec = hdr.packet_out.egress_port;
hdr.packet_out.setInvalid();
exit;
}
}
}
control PacketIoEgressControl(inout headers_t hdr, inout standard_metadata_t standard_metadata) {
control packetio_egress(inout headers_t hdr,
inout standard_metadata_t standard_metadata) {
apply {
hdr.packet_out.setInvalid();
if (standard_metadata.egress_port == CPU_PORT) {
hdr.packet_in.setValid();
hdr.packet_in.ingress_port = standard_metadata.ingress_port;

View File

@ -14,13 +14,24 @@
* limitations under the License.
*/
#ifndef PARSERS
#define PARSERS
#include "headers.p4"
#ifndef __PARSERS__
#define __PARSERS__
parser ParserImpl(packet_in packet, out headers_t hdr, inout metadata_t meta,
#include "headers.p4"
#include "defines.p4"
parser parser_impl(packet_in packet,
out headers_t hdr,
inout local_metadata_t local_metadata,
inout standard_metadata_t standard_metadata) {
state start {
transition select(standard_metadata.ingress_port) {
CPU_PORT: parse_packet_out;
default: parse_ethernet;
}
}
state parse_packet_out {
packet.extract(hdr.packet_out);
transition parse_ethernet;
@ -28,7 +39,7 @@ parser ParserImpl(packet_in packet, out headers_t hdr, inout metadata_t meta,
state parse_ethernet {
packet.extract(hdr.ethernet);
transition select(hdr.ethernet.etherType) {
transition select(hdr.ethernet.ether_type) {
ETH_TYPE_IPV4: parse_ipv4;
default: accept;
}
@ -37,37 +48,35 @@ parser ParserImpl(packet_in packet, out headers_t hdr, inout metadata_t meta,
state parse_ipv4 {
packet.extract(hdr.ipv4);
transition select(hdr.ipv4.protocol) {
IP_TYPE_TCP: parse_tcp;
IP_TYPE_UDP: parse_udp;
IP_PROTO_TCP: parse_tcp;
IP_PROTO_UDP: parse_udp;
default: accept;
}
}
state parse_tcp {
packet.extract(hdr.tcp);
local_metadata.l4_src_port = hdr.tcp.src_port;
local_metadata.l4_dst_port = hdr.tcp.dst_port;
transition accept;
}
state parse_udp {
packet.extract(hdr.udp);
local_metadata.l4_src_port = hdr.udp.src_port;
local_metadata.l4_dst_port = hdr.udp.dst_port;
transition accept;
}
state start {
transition select(standard_metadata.ingress_port) {
CPU_PORT: parse_packet_out;
default: parse_ethernet;
}
}
}
control DeparserImpl(packet_out packet, in headers_t hdr) {
control deparser(packet_out packet, in headers_t hdr) {
apply {
packet.emit(hdr.packet_in);
packet.emit(hdr.ethernet);
packet.emit(hdr.ipv4);
packet.emit(hdr.udp);
packet.emit(hdr.tcp);
packet.emit(hdr.udp);
}
}
#endif

View File

@ -14,21 +14,30 @@
* limitations under the License.
*/
#ifndef PORT_COUNTERS
#define PORT_COUNTERS
#ifndef __PORT_COUNTERS__
#define __PORT_COUNTERS__
#include "headers.p4"
#include "defines.p4"
control PortCountersControl(inout headers_t hdr, inout metadata_t meta, inout standard_metadata_t standard_metadata) {
counter(MAX_PORTS, CounterType.packets) egress_port_counter;
control port_counters_ingress(inout headers_t hdr,
inout standard_metadata_t standard_metadata) {
counter(MAX_PORTS, CounterType.packets) ingress_port_counter;
apply {
if (standard_metadata.egress_spec < MAX_PORTS) {
egress_port_counter.count((bit<32>)standard_metadata.egress_spec);
}
if (standard_metadata.ingress_port < MAX_PORTS) {
ingress_port_counter.count((bit<32>) standard_metadata.ingress_port);
}
}
control port_counters_egress(inout headers_t hdr,
inout standard_metadata_t standard_metadata) {
counter(MAX_PORTS, CounterType.packets) egress_port_counter;
apply {
egress_port_counter.count((bit<32>) standard_metadata.egress_port);
}
}
#endif

View File

@ -0,0 +1,60 @@
/*
* 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.
*/
#ifndef __TABLE0__
#define __TABLE0__
#include "headers.p4"
#include "defines.p4"
control table0_control(inout headers_t hdr,
inout local_metadata_t local_metadata,
inout standard_metadata_t standard_metadata) {
direct_counter(CounterType.packets_and_bytes) table0_counter;
action set_next_hop_id(next_hop_id_t next_hop_id) {
local_metadata.next_hop_id = next_hop_id;
}
table table0 {
key = {
standard_metadata.ingress_port : ternary;
hdr.ethernet.src_addr : ternary;
hdr.ethernet.dst_addr : ternary;
hdr.ethernet.ether_type : ternary;
hdr.ipv4.src_addr : ternary;
hdr.ipv4.dst_addr : ternary;
hdr.ipv4.protocol : ternary;
local_metadata.l4_src_port : ternary;
local_metadata.l4_dst_port : ternary;
}
actions = {
set_egress_port(standard_metadata);
send_to_cpu(standard_metadata);
set_next_hop_id();
_drop();
}
const default_action = _drop();
counters = table0_counter;
}
apply {
table0.apply();
}
}
#endif

View File

@ -0,0 +1,54 @@
/*
* 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.
*/
#ifndef __WCMP__
#define __WCMP__
#include "headers.p4"
#include "defines.p4"
control wcmp_control(inout headers_t hdr,
inout local_metadata_t local_metadata,
inout standard_metadata_t standard_metadata) {
direct_counter(CounterType.packets_and_bytes) wcmp_table_counter;
action_selector(HashAlgorithm.crc16, 32w64, 32w16) wcmp_selector;
table wcmp_table {
support_timeout = false;
key = {
local_metadata.next_hop_id : exact;
hdr.ipv4.src_addr : selector;
hdr.ipv4.dst_addr : selector;
hdr.ipv4.protocol : selector;
local_metadata.l4_src_port : selector;
local_metadata.l4_dst_port : selector;
}
actions = {
set_egress_port(standard_metadata);
}
implementation = wcmp_selector;
counters = wcmp_table_counter;
}
apply {
if (local_metadata.next_hop_id != 0) {
wcmp_table.apply();
}
}
}
#endif

File diff suppressed because it is too large Load Diff

View File

@ -1,7 +1,7 @@
tables {
preamble {
id: 33617813
name: "table0"
id: 33571508
name: "table0_control.table0"
alias: "table0"
}
match_fields {
@ -12,19 +12,49 @@ tables {
}
match_fields {
id: 2
name: "hdr.ethernet.dstAddr"
name: "hdr.ethernet.src_addr"
bitwidth: 48
match_type: TERNARY
}
match_fields {
id: 3
name: "hdr.ethernet.srcAddr"
name: "hdr.ethernet.dst_addr"
bitwidth: 48
match_type: TERNARY
}
match_fields {
id: 4
name: "hdr.ethernet.etherType"
name: "hdr.ethernet.ether_type"
bitwidth: 16
match_type: TERNARY
}
match_fields {
id: 5
name: "hdr.ipv4.src_addr"
bitwidth: 32
match_type: TERNARY
}
match_fields {
id: 6
name: "hdr.ipv4.dst_addr"
bitwidth: 32
match_type: TERNARY
}
match_fields {
id: 7
name: "hdr.ipv4.protocol"
bitwidth: 8
match_type: TERNARY
}
match_fields {
id: 8
name: "local_metadata.l4_src_port"
bitwidth: 16
match_type: TERNARY
}
match_fields {
id: 9
name: "local_metadata.l4_dst_port"
bitwidth: 16
match_type: TERNARY
}
@ -32,36 +62,30 @@ tables {
id: 16794308
}
action_refs {
id: 16791212
id: 16829080
}
action_refs {
id: 16829080
id: 16802895
}
action_refs {
id: 16784184
}
direct_resource_ids: 301990488
const_default_action_id: 16784184
direct_resource_ids: 302046050
size: 1024
with_entry_timeout: true
}
tables {
preamble {
id: 33596222
name: "wcmp_group_table"
alias: "wcmp_group_table"
id: 33592597
name: "wcmp_control.wcmp_table"
alias: "wcmp_table"
}
match_fields {
id: 1
name: "meta.wcmp_metadata.group_id"
name: "local_metadata.next_hop_id"
bitwidth: 16
match_type: EXACT
}
match_fields {
id: 2
name: "meta.wcmp_metadata.selector"
bitwidth: 64
match_type: LPM
}
action_refs {
id: 16794308
}
@ -69,7 +93,8 @@ tables {
id: 16800567
annotations: "@defaultonly()"
}
direct_resource_ids: 302006421
implementation_id: 285259294
direct_resource_ids: 302001091
size: 1024
}
actions {
@ -107,66 +132,69 @@ actions {
}
actions {
preamble {
id: 16791212
name: "wcmp_group"
alias: "wcmp_group"
id: 16802895
name: "table0_control.set_next_hop_id"
alias: "set_next_hop_id"
}
params {
id: 1
name: "group_id"
name: "next_hop_id"
bitwidth: 16
}
}
actions {
action_profiles {
preamble {
id: 16819919
name: "wcmp_set_selector"
alias: "wcmp_set_selector"
id: 285259294
name: "wcmp_control.wcmp_selector"
alias: "wcmp_selector"
}
table_ids: 33592597
with_selector: true
size: 64
}
counters {
preamble {
id: 302025528
name: "port_counters_control.egress_port_counter"
alias: "egress_port_counter"
}
spec {
unit: PACKETS
}
size: 255
}
counters {
preamble {
id: 301999025
name: "port_counters_control.ingress_port_counter"
id: 302012579
name: "port_counters_ingress.ingress_port_counter"
alias: "ingress_port_counter"
}
spec {
unit: PACKETS
}
size: 255
size: 511
}
counters {
preamble {
id: 302012501
name: "port_counters_egress.egress_port_counter"
alias: "egress_port_counter"
}
spec {
unit: PACKETS
}
size: 511
}
direct_counters {
preamble {
id: 301990488
name: "table0_counter"
id: 302046050
name: "table0_control.table0_counter"
alias: "table0_counter"
}
spec {
unit: PACKETS
unit: BOTH
}
direct_table_id: 33617813
direct_table_id: 33571508
}
direct_counters {
preamble {
id: 302006421
name: "wcmp_group_table_counter"
alias: "wcmp_group_table_counter"
id: 302001091
name: "wcmp_control.wcmp_table_counter"
alias: "wcmp_table_counter"
}
spec {
unit: PACKETS
unit: BOTH
}
direct_table_id: 33596222
direct_table_id: 33592597
}
controller_packet_metadata {
preamble {

File diff suppressed because it is too large Load Diff

View File

@ -1,35 +1,7 @@
tables {
preamble {
id: 33612022
name: "ecmp_group_table"
alias: "ecmp_group_table"
}
match_fields {
id: 1
name: "meta.ecmp_metadata.group_id"
bitwidth: 16
match_type: EXACT
}
match_fields {
id: 2
name: "meta.ecmp_metadata.selector"
bitwidth: 16
match_type: EXACT
}
action_refs {
id: 16794308
}
action_refs {
id: 16800567
annotations: "@defaultonly()"
}
direct_resource_ids: 302009688
size: 1024
}
tables {
preamble {
id: 33617813
name: "table0"
id: 33571508
name: "table0_control.table0"
alias: "table0"
}
match_fields {
@ -40,24 +12,51 @@ tables {
}
match_fields {
id: 2
name: "hdr.ethernet.dstAddr"
name: "hdr.ethernet.src_addr"
bitwidth: 48
match_type: TERNARY
}
match_fields {
id: 3
name: "hdr.ethernet.srcAddr"
name: "hdr.ethernet.dst_addr"
bitwidth: 48
match_type: TERNARY
}
match_fields {
id: 4
name: "hdr.ethernet.etherType"
name: "hdr.ethernet.ether_type"
bitwidth: 16
match_type: TERNARY
}
action_refs {
id: 16830055
match_fields {
id: 5
name: "hdr.ipv4.src_addr"
bitwidth: 32
match_type: TERNARY
}
match_fields {
id: 6
name: "hdr.ipv4.dst_addr"
bitwidth: 32
match_type: TERNARY
}
match_fields {
id: 7
name: "hdr.ipv4.protocol"
bitwidth: 8
match_type: TERNARY
}
match_fields {
id: 8
name: "local_metadata.l4_src_port"
bitwidth: 16
match_type: TERNARY
}
match_fields {
id: 9
name: "local_metadata.l4_dst_port"
bitwidth: 16
match_type: TERNARY
}
action_refs {
id: 16794308
@ -65,12 +64,43 @@ tables {
action_refs {
id: 16829080
}
action_refs {
id: 16802895
}
action_refs {
id: 16784184
}
direct_resource_ids: 301990488
const_default_action_id: 16784184
direct_resource_ids: 302046050
size: 1024
}
tables {
preamble {
id: 33601431
name: "ecmp_table"
alias: "ecmp_table"
}
match_fields {
id: 1
name: "local_metadata.next_hop_id"
bitwidth: 16
match_type: EXACT
}
match_fields {
id: 2
name: "local_metadata.selector"
bitwidth: 16
match_type: EXACT
}
action_refs {
id: 16794308
}
action_refs {
id: 16800567
annotations: "@defaultonly()"
}
direct_resource_ids: 302010883
size: 1024
with_entry_timeout: true
}
actions {
preamble {
@ -84,13 +114,6 @@ actions {
bitwidth: 9
}
}
actions {
preamble {
id: 16800567
name: "NoAction"
alias: "NoAction"
}
}
actions {
preamble {
id: 16829080
@ -107,59 +130,73 @@ actions {
}
actions {
preamble {
id: 16830055
name: "ecmp_group"
alias: "ecmp_group"
id: 16800567
name: "NoAction"
alias: "NoAction"
}
}
actions {
preamble {
id: 16802895
name: "table0_control.set_next_hop_id"
alias: "set_next_hop_id"
}
params {
id: 1
name: "group_id"
name: "next_hop_id"
bitwidth: 16
}
}
counters {
actions {
preamble {
id: 302025528
name: "port_counters_control.egress_port_counter"
alias: "egress_port_counter"
id: 16789898
name: "set_ecmp_selector"
alias: "set_ecmp_selector"
}
spec {
unit: PACKETS
}
size: 255
}
counters {
preamble {
id: 301999025
name: "port_counters_control.ingress_port_counter"
id: 302012579
name: "port_counters_ingress.ingress_port_counter"
alias: "ingress_port_counter"
}
spec {
unit: PACKETS
}
size: 255
size: 511
}
direct_counters {
counters {
preamble {
id: 302009688
name: "ecmp_group_table_counter"
alias: "ecmp_group_table_counter"
id: 302012501
name: "port_counters_egress.egress_port_counter"
alias: "egress_port_counter"
}
spec {
unit: PACKETS
}
direct_table_id: 33612022
size: 511
}
direct_counters {
preamble {
id: 301990488
name: "table0_counter"
id: 302046050
name: "table0_control.table0_counter"
alias: "table0_counter"
}
spec {
unit: PACKETS
unit: BOTH
}
direct_table_id: 33617813
direct_table_id: 33571508
}
direct_counters {
preamble {
id: 302010883
name: "ecmp_table_counter"
alias: "ecmp_table_counter"
}
spec {
unit: BOTH
}
direct_table_id: 33601431
}
controller_packet_metadata {
preamble {

View File

@ -60,7 +60,9 @@ import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;
import static org.easymock.EasyMock.niceMock;
import static org.junit.Assert.*;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;
import static org.onosproject.net.pi.model.PiPipeconf.ExtensionType.P4_INFO_TEXT;
import static org.onosproject.net.pi.runtime.PiActionGroup.Type.SELECT;
import static org.onosproject.p4runtime.api.P4RuntimeClient.WriteOperationType.INSERT;
@ -72,7 +74,7 @@ import static p4.P4RuntimeOuterClass.ReadResponse;
*/
public class P4RuntimeGroupTest {
private static final String PIPECONF_ID = "p4runtime-mock-pipeconf";
private static final String P4INFO_PATH = "/default.p4info";
private static final String P4INFO_PATH = "/test.p4info";
private static final PiPipeconf PIPECONF = buildPipeconf();
private static final int P4_INFO_ACT_PROF_ID = 285227860;
private static final PiActionProfileId ACT_PROF_ID = PiActionProfileId.of("ecmp_selector");

View File

@ -44,7 +44,9 @@ import java.util.Random;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.hasSize;
import static org.hamcrest.Matchers.is;
import static org.onlab.util.ImmutableByteSequence.*;
import static org.onlab.util.ImmutableByteSequence.copyFrom;
import static org.onlab.util.ImmutableByteSequence.fit;
import static org.onlab.util.ImmutableByteSequence.ofOnes;
import static org.onosproject.net.pi.model.PiPipeconf.ExtensionType.P4_INFO_TEXT;
import static org.onosproject.p4runtime.ctl.TableEntryEncoder.decode;
import static org.onosproject.p4runtime.ctl.TableEntryEncoder.encode;
@ -64,7 +66,7 @@ public class TableEntryEncoderTest {
private static final String ETHER_TYPE = "etherType";
private final Random rand = new Random();
private final URL p4InfoUrl = this.getClass().getResource("/default.p4info");
private final URL p4InfoUrl = this.getClass().getResource("/test.p4info");
private final PiPipeconf defaultPipeconf = DefaultPiPipeconf.builder()
.withId(new PiPipeconfId("mock"))

View File

@ -1 +0,0 @@
../../../../../../tools/test/p4src/p4-16/p4c-out/default.p4info

View File

@ -17,7 +17,7 @@ set -e
BUILD_DIR=~/p4tools
BMV2_COMMIT="4eeb8dad8e8f062636f9d0d8296aa7f288c6f6dd"
PI_COMMIT="9fc50cd0a0187eb1346272524d4b8bafb51bb513"
P4C_COMMIT="55067fd0e5f9e25fef06e58e49033da3493f796d"
P4C_COMMIT="040b931fbfcb7912e3a14cd05df950fbdd49b038"
PROTOBUF_COMMIT="tags/v3.0.2"
GRPC_COMMIT="tags/v1.3.0"

1
tools/test/p4src/FIXME Normal file
View File

@ -0,0 +1 @@
This directory should be removed and all P4 source code moved to the respective pipeline module (under /pipelines)

View File

@ -1,25 +0,0 @@
all: default.json empty.json ecmp.json wcmp.json
default.json: default.p4
p4c-bm2-ss -o p4c-out/default.json \
--p4runtime-file p4c-out/default.p4info --p4runtime-format text \
default.p4
empty.json: empty.p4
p4c-bm2-ss -o p4c-out/empty.json \
--p4runtime-file p4c-out/empty.p4info --p4runtime-format text \
empty.p4
ecmp.json: ecmp.p4
p4c-bm2-ss -o p4c-out/ecmp.json \
--p4runtime-file p4c-out/ecmp.p4info --p4runtime-format text \
ecmp.p4
wcmp.json: wcmp.p4
p4c-bm2-ss -o p4c-out/wcmp.json \
--p4runtime-file p4c-out/wcmp.p4info --p4runtime-format text \
wcmp.p4
clean:
rm -rf p4c-out/*.json
rm -rf p4c-out/*.p4info

View File

@ -1,114 +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.
*/
#include <core.p4>
#include <v1model.p4>
#include "include/defines.p4"
#include "include/headers.p4"
struct ecmp_metadata_t {
ecmp_group_id_t ecmp_group_id;
}
struct metadata_t {
intrinsic_metadata_t intrinsic_metadata;
ecmp_metadata_t ecmp_metadata;
}
#include "include/parsers.p4"
#include "include/port_counters.p4"
#include "include/checksums.p4"
#include "include/actions.p4"
#include "include/packet_io.p4"
control ingress(inout headers_t hdr, inout metadata_t meta, inout standard_metadata_t standard_metadata) {
direct_counter(CounterType.packets) table0_counter;
direct_counter(CounterType.packets) ecmp_counter;
action do_ecmp(ecmp_group_id_t ecmp_group_id) {
meta.ecmp_metadata.ecmp_group_id = ecmp_group_id;
}
table table0 {
/*
Disabling timeout here as P4runtime doesn't allow setting timeouts.
This way the FlowRuleTranslator will produce instances of PiTableEntry without timeout.
*/
support_timeout = false;
key = {
standard_metadata.ingress_port : ternary;
hdr.ethernet.dstAddr : ternary;
hdr.ethernet.srcAddr : ternary;
hdr.ethernet.etherType : ternary;
}
actions = {
set_egress_port(standard_metadata);
send_to_cpu(standard_metadata);
do_ecmp();
_drop(standard_metadata);
}
counters = table0_counter;
default_action = _drop(standard_metadata);
}
action_selector(HashAlgorithm.crc16, 32w64, 32w16) ecmp_selector;
table ecmp {
support_timeout = false;
key = {
meta.ecmp_metadata.ecmp_group_id : exact;
// Not for matching.
// Inputs to the hash function of the action selector.
hdr.ipv4.srcAddr : selector;
hdr.ipv4.dstAddr : selector;
hdr.ipv4.protocol : selector;
hdr.tcp.srcPort : selector;
hdr.tcp.dstPort : selector;
hdr.udp.srcPort : selector;
hdr.udp.dstPort : selector;
}
actions = {
set_egress_port(standard_metadata);
}
implementation = ecmp_selector;
counters = ecmp_counter;
}
PacketIoIngressControl() packet_io_ingress_control;
PortCountersControl() port_counters_control;
apply {
packet_io_ingress_control.apply(hdr, standard_metadata);
if (!hdr.packet_out.isValid()) {
switch(table0.apply().action_run) {
do_ecmp: {
ecmp.apply();
}
}
}
port_counters_control.apply(hdr, meta, standard_metadata);
}
}
control egress(inout headers_t hdr, inout metadata_t meta, inout standard_metadata_t standard_metadata) {
PacketIoEgressControl() packet_io_egress_control;
apply {
packet_io_egress_control.apply(hdr, standard_metadata);
}
}
V1Switch(ParserImpl(), verifyChecksum(), ingress(), egress(), computeChecksum(), DeparserImpl()) main;

View File

@ -1,113 +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.
*/
#include <core.p4>
#include <v1model.p4>
#include "include/defines.p4"
#include "include/headers.p4"
typedef bit<16> group_id_t;
/*
Expected number of ports of an ECMP group.
This value is fixed, .i.e. we do not support ECMP over port groups of different size.
Due to hardware limitations, this value must be constant and a power of 2.
*/
#define ECMP_GROUP_SIZE 128w2
struct ecmp_metadata_t {
group_id_t group_id;
bit<16> selector;
}
struct metadata_t {
ecmp_metadata_t ecmp_metadata;
intrinsic_metadata_t intrinsic_metadata;
}
#include "include/parsers.p4"
#include "include/port_counters.p4"
#include "include/checksums.p4"
#include "include/actions.p4"
#include "include/packet_io.p4"
control ingress(inout headers_t hdr, inout metadata_t meta, inout standard_metadata_t standard_metadata) {
direct_counter(CounterType.packets) ecmp_group_table_counter;
direct_counter(CounterType.packets) table0_counter;
action ecmp_group(group_id_t group_id) {
meta.ecmp_metadata.group_id = group_id;
hash(meta.ecmp_metadata.selector, HashAlgorithm.crc32, (bit<64>)0,
{ hdr.ipv4.srcAddr, hdr.ipv4.dstAddr, hdr.ipv4.protocol, hdr.tcp.srcPort, hdr.tcp.dstPort, hdr.udp.srcPort,
hdr.udp.dstPort }, ECMP_GROUP_SIZE);
}
table ecmp_group_table {
actions = {
set_egress_port(standard_metadata);
}
key = {
meta.ecmp_metadata.group_id : exact;
meta.ecmp_metadata.selector: exact;
}
counters = ecmp_group_table_counter;
}
table table0 {
support_timeout = true;
actions = {
ecmp_group;
set_egress_port(standard_metadata);
send_to_cpu(standard_metadata);
_drop(standard_metadata);
}
key = {
standard_metadata.ingress_port: ternary;
hdr.ethernet.dstAddr : ternary;
hdr.ethernet.srcAddr : ternary;
hdr.ethernet.etherType : ternary;
}
counters = table0_counter;
default_action = _drop(standard_metadata);
}
PortCountersControl() port_counters_control;
PacketIoIngressControl() packet_io_ingress_control;
apply {
packet_io_ingress_control.apply(hdr, standard_metadata);
if (!hdr.packet_out.isValid()) {
switch (table0.apply().action_run) {
ecmp_group: {
ecmp_group_table.apply();
}
}
}
port_counters_control.apply(hdr, meta, standard_metadata);
}
}
control egress(inout headers_t hdr, inout metadata_t meta, inout standard_metadata_t standard_metadata) {
PacketIoEgressControl() packet_io_egress_control;
apply {
packet_io_egress_control.apply(hdr, standard_metadata);
}
}
V1Switch(ParserImpl(), verifyChecksum(), ingress(), egress(), computeChecksum(), DeparserImpl()) main;

View File

@ -1,78 +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.
*/
#include <core.p4>
#include <v1model.p4>
struct dummy_t {
bit<8> dummyField;
}
struct metadata_t {
dummy_t dummy_metadata;
}
struct headers_t {
}
parser ParserImpl(packet_in packet, out headers_t hdr, inout metadata_t meta, inout standard_metadata_t standard_metadata) {
state start {
transition accept;
}
}
control ingress(inout headers_t hdr, inout metadata_t meta, inout standard_metadata_t standard_metadata) {
action dummy_action() {
meta.dummy_metadata.dummyField = 8w1;
}
table table0 {
actions = {
dummy_action;
}
key = {
meta.dummy_metadata.dummyField: exact;
}
}
apply {
table0.apply();
}
}
control egress(inout headers_t hdr, inout metadata_t meta, inout standard_metadata_t standard_metadata) {
apply {
// Nothing to do
}
}
control DeparserImpl(packet_out packet, in headers_t hdr) {
apply {
// Nothing to do
}
}
control verifyChecksum(in headers_t hdr, inout metadata_t meta) {
apply {
// Nothing to do
}
}
control computeChecksum(inout headers_t hdr, inout metadata_t meta) {
apply {
// Nothing to do
}
}
V1Switch(ParserImpl(), verifyChecksum(), ingress(), egress(), computeChecksum(), DeparserImpl()) main;

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -1,264 +0,0 @@
{
"program" : "empty.p4",
"__meta__" : {
"version" : [2, 7],
"compiler" : "https://github.com/p4lang/p4c"
},
"header_types" : [
{
"name" : "scalars_0",
"id" : 0,
"fields" : []
},
{
"name" : "dummy_t",
"id" : 1,
"fields" : [
["dummyField", 8, false]
]
},
{
"name" : "standard_metadata",
"id" : 2,
"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", 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" : "dummy_metadata",
"id" : 2,
"header_type" : "dummy_t",
"metadata" : true,
"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" : "default",
"mask" : null,
"next_state" : null
}
],
"transition_key" : []
}
]
}
],
"deparsers" : [
{
"name" : "deparser",
"id" : 0,
"source_info" : {
"filename" : "empty.p4",
"line" : 60,
"column" : 8,
"source_fragment" : "DeparserImpl"
},
"order" : []
}
],
"meter_arrays" : [],
"counter_arrays" : [],
"register_arrays" : [],
"calculations" : [],
"learn_lists" : [],
"actions" : [
{
"name" : "NoAction",
"id" : 0,
"runtime_data" : [],
"primitives" : []
},
{
"name" : "dummy_action",
"id" : 1,
"runtime_data" : [],
"primitives" : [
{
"op" : "assign",
"parameters" : [
{
"type" : "field",
"value" : ["dummy_metadata", "dummyField"]
},
{
"type" : "hexstr",
"value" : "0x01"
}
],
"source_info" : {
"filename" : "empty.p4",
"line" : 39,
"column" : 8,
"source_fragment" : "meta.dummy_metadata.dummyField = 8w1"
}
}
]
}
],
"pipelines" : [
{
"name" : "ingress",
"id" : 0,
"source_info" : {
"filename" : "empty.p4",
"line" : 37,
"column" : 8,
"source_fragment" : "ingress"
},
"init_table" : "table0",
"tables" : [
{
"name" : "table0",
"id" : 0,
"source_info" : {
"filename" : "empty.p4",
"line" : 41,
"column" : 10,
"source_fragment" : "table0"
},
"key" : [
{
"match_type" : "exact",
"target" : ["dummy_metadata", "dummyField"],
"mask" : null
}
],
"match_type" : "exact",
"type" : "simple",
"max_size" : 1024,
"with_counters" : false,
"support_timeout" : false,
"direct_meters" : null,
"action_ids" : [1, 0],
"actions" : ["dummy_action", "NoAction"],
"base_default_next" : null,
"next_tables" : {
"dummy_action" : null,
"NoAction" : null
},
"default_entry" : {
"action_id" : 0,
"action_const" : false,
"action_data" : [],
"action_entry_const" : false
}
}
],
"action_profiles" : [],
"conditionals" : []
},
{
"name" : "egress",
"id" : 1,
"source_info" : {
"filename" : "empty.p4",
"line" : 54,
"column" : 8,
"source_fragment" : "egress"
},
"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,35 +0,0 @@
tables {
preamble {
id: 33617813
name: "table0"
alias: "table0"
}
match_fields {
id: 1
name: "meta.dummy_metadata.dummyField"
bitwidth: 8
match_type: EXACT
}
action_refs {
id: 16836827
}
action_refs {
id: 16800567
annotations: "@defaultonly()"
}
size: 1024
}
actions {
preamble {
id: 16800567
name: "NoAction"
alias: "NoAction"
}
}
actions {
preamble {
id: 16836827
name: "dummy_action"
alias: "dummy_action"
}
}

File diff suppressed because it is too large Load Diff

View File

@ -1,115 +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.
*/
#include <core.p4>
#include <v1model.p4>
#include "include/defines.p4"
#include "include/headers.p4"
#define SELECTOR_WIDTH 64
const bit<SELECTOR_WIDTH> ONE = 64w1;
typedef bit<16> group_id_t;
struct wcmp_metadata_t {
group_id_t group_id;
bit<8> numBits;
bit<SELECTOR_WIDTH> selector;
}
struct metadata_t {
wcmp_metadata_t wcmp_metadata;
intrinsic_metadata_t intrinsic_metadata;
}
#include "include/parsers.p4"
#include "include/port_counters.p4"
#include "include/checksums.p4"
#include "include/actions.p4"
#include "include/packet_io.p4"
control ingress(inout headers_t hdr, inout metadata_t meta, inout standard_metadata_t standard_metadata) {
direct_counter(CounterType.packets) table0_counter;
direct_counter(CounterType.packets) wcmp_group_table_counter;
action wcmp_group(group_id_t group_id) {
meta.wcmp_metadata.group_id = group_id;
hash(meta.wcmp_metadata.numBits, HashAlgorithm.crc16, (bit<64>)2,
{ hdr.ipv4.srcAddr, hdr.ipv4.dstAddr, hdr.ipv4.protocol, hdr.tcp.srcPort, hdr.tcp.dstPort, hdr.udp.srcPort,
hdr.udp.dstPort },
(bit<128>)62);
}
action wcmp_set_selector() {
meta.wcmp_metadata.selector = ((ONE << meta.wcmp_metadata.numBits) - ONE) << (SELECTOR_WIDTH - meta.wcmp_metadata.numBits);
}
table table0 {
support_timeout = true;
actions = {
set_egress_port(standard_metadata);
wcmp_group;
send_to_cpu(standard_metadata);
_drop(standard_metadata);
}
key = {
standard_metadata.ingress_port: ternary;
hdr.ethernet.dstAddr : ternary;
hdr.ethernet.srcAddr : ternary;
hdr.ethernet.etherType : ternary;
}
counters = table0_counter;
default_action = _drop(standard_metadata);
}
table wcmp_group_table {
actions = {
set_egress_port(standard_metadata);
}
key = {
meta.wcmp_metadata.group_id : exact;
meta.wcmp_metadata.selector: lpm;
}
counters = wcmp_group_table_counter;
}
PortCountersControl() port_counters_control;
PacketIoIngressControl() packet_io_ingress_control;
apply {
packet_io_ingress_control.apply(hdr, standard_metadata);
if (!hdr.packet_out.isValid()) {
switch (table0.apply().action_run) {
wcmp_group: {
wcmp_set_selector();
wcmp_group_table.apply();
}
}
}
port_counters_control.apply(hdr, meta, standard_metadata);
}
}
control egress(inout headers_t hdr, inout metadata_t meta, inout standard_metadata_t standard_metadata) {
PacketIoEgressControl() packet_io_egress_control;
apply {
packet_io_egress_control.apply(hdr, standard_metadata);
}
}
V1Switch(ParserImpl(), verifyChecksum(), ingress(), egress(), computeChecksum(), DeparserImpl()) main;