mirror of
https://github.com/opennetworkinglab/onos.git
synced 2025-10-15 17:31:31 +02:00
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:
parent
b211b87829
commit
ca94bcf5ea
@ -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,
|
||||
|
@ -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");
|
||||
|
@ -1 +0,0 @@
|
||||
../../../../../../tools/test/p4src/p4-16/p4c-out/default.json
|
@ -1 +0,0 @@
|
||||
../../../../../../tools/test/p4src/p4-16/p4c-out/default.p4info
|
@ -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;
|
||||
|
||||
/**
|
||||
@ -151,7 +153,7 @@ public abstract class AbstractUpgradableFabricApp {
|
||||
/**
|
||||
* Creates a new PI fabric app.
|
||||
*
|
||||
* @param appName app name
|
||||
* @param appName app name
|
||||
* @param appPipeconfs collection of compatible pipeconfs
|
||||
*/
|
||||
protected AbstractUpgradableFabricApp(String appName, Collection<PiPipeconf> appPipeconfs) {
|
||||
@ -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);
|
||||
@ -228,7 +226,7 @@ public abstract class AbstractUpgradableFabricApp {
|
||||
one, it generates the necessary flow rules and starts the deploy process on each device.
|
||||
*/
|
||||
scheduledExecutorService.scheduleAtFixedRate(this::checkTopologyAndGenerateFlowRules,
|
||||
0, CHECK_TOPOLOGY_INTERVAL_SECONDS, TimeUnit.SECONDS);
|
||||
0, CHECK_TOPOLOGY_INTERVAL_SECONDS, TimeUnit.SECONDS);
|
||||
}
|
||||
|
||||
private void setAppFreezed(boolean appFreezed) {
|
||||
@ -453,11 +451,11 @@ public abstract class AbstractUpgradableFabricApp {
|
||||
/**
|
||||
* Returns a new, pre-configured flow rule builder.
|
||||
*
|
||||
* @param did a device id
|
||||
* @param tableName a table name
|
||||
* @param did a device id
|
||||
* @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()
|
||||
|
@ -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'
|
||||
]
|
||||
)
|
||||
|
@ -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.
|
||||
|
@ -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();
|
||||
}
|
||||
}
|
@ -1 +0,0 @@
|
||||
../../../../../../tools/test/p4src/p4-16/p4c-out/ecmp.json
|
@ -1 +0,0 @@
|
||||
../../../../../../tools/test/p4src/p4-16/p4c-out/ecmp.p4info
|
@ -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',
|
||||
]
|
||||
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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));
|
||||
}
|
||||
|
||||
}
|
@ -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;
|
||||
|
@ -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();
|
||||
|
@ -1 +0,0 @@
|
||||
../../../../../../../../../../tools/test/p4src/p4-16/p4c-out/default.json
|
@ -1,3 +1,3 @@
|
||||
{
|
||||
"piPipeconfId": "org.project.pipeconf.default"
|
||||
"piPipeconfId": "org.onosproject.pipelines.basic"
|
||||
}
|
@ -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',
|
||||
],
|
||||
)
|
@ -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();
|
||||
}
|
||||
}
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
@ -1 +0,0 @@
|
||||
../../../../../tools/test/p4src/p4-16/p4c-out/default.json
|
@ -1 +0,0 @@
|
||||
../../../../../tools/test/p4src/p4-16/p4c-out/default.p4info
|
@ -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));
|
||||
}
|
||||
}
|
@ -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
30
pipelines/basic/BUCK
Normal 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',
|
||||
]
|
||||
)
|
@ -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.
|
||||
}
|
||||
}
|
@ -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));
|
||||
}
|
||||
}
|
@ -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.
|
||||
}
|
||||
}
|
@ -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
|
||||
@ -47,4 +43,4 @@ public class EcmpInterpreter extends DefaultP4Interpreter {
|
||||
public Optional<PiTableId> mapFlowRuleTableId(int flowRuleTableId) {
|
||||
return Optional.ofNullable(TABLE_MAP.get(flowRuleTableId));
|
||||
}
|
||||
}
|
||||
}
|
@ -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();
|
||||
}
|
||||
}
|
@ -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()
|
@ -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;
|
14
pipelines/basic/src/main/resources/Makefile
Normal file
14
pipelines/basic/src/main/resources/Makefile
Normal 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
|
69
pipelines/basic/src/main/resources/basic.p4
Normal file
69
pipelines/basic/src/main/resources/basic.p4
Normal 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;
|
105
pipelines/basic/src/main/resources/ecmp.p4
Normal file
105
pipelines/basic/src/main/resources/ecmp.p4
Normal 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;
|
@ -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
|
||||
|
||||
|
||||
#endif
|
36
pipelines/basic/src/main/resources/include/checksums.p4
Normal file
36
pipelines/basic/src/main/resources/include/checksums.p4
Normal 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
|
@ -14,19 +14,17 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#ifndef DEFINES
|
||||
#define DEFINES
|
||||
#ifndef __DEFINES__
|
||||
#define __DEFINES__
|
||||
|
||||
#define MAX_PORTS 255
|
||||
#define ETH_TYPE_IPV4 0x0800
|
||||
#define IP_PROTO_TCP 8w6
|
||||
#define IP_PROTO_UDP 8w17
|
||||
#define MAX_PORTS 511
|
||||
|
||||
#define ETH_TYPE_IPV4 16w0x800
|
||||
#define IP_TYPE_TCP 8w6
|
||||
#define IP_TYPE_UDP 8w17
|
||||
|
||||
typedef bit<9> port_t;
|
||||
typedef bit<8> ecmp_group_id_t;
|
||||
typedef bit<9> port_t;
|
||||
typedef bit<16> next_hop_id_t;
|
||||
|
||||
const port_t CPU_PORT = 255;
|
||||
const port_t DROP_PORT = 511;
|
||||
|
||||
#endif
|
@ -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
|
@ -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;
|
@ -14,12 +14,23 @@
|
||||
* 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,
|
||||
inout standard_metadata_t standard_metadata) {
|
||||
#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);
|
||||
@ -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
|
@ -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);
|
||||
}
|
||||
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
|
60
pipelines/basic/src/main/resources/include/table0.p4
Normal file
60
pipelines/basic/src/main/resources/include/table0.p4
Normal 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
|
54
pipelines/basic/src/main/resources/include/wcmp.p4
Normal file
54
pipelines/basic/src/main/resources/include/wcmp.p4
Normal 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
|
1180
pipelines/basic/src/main/resources/p4c-out/bmv2/basic.json
Normal file
1180
pipelines/basic/src/main/resources/p4c-out/bmv2/basic.json
Normal file
File diff suppressed because it is too large
Load Diff
@ -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 {
|
1244
pipelines/basic/src/main/resources/p4c-out/bmv2/ecmp.json
Normal file
1244
pipelines/basic/src/main/resources/p4c-out/bmv2/ecmp.json
Normal file
File diff suppressed because it is too large
Load Diff
@ -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 {
|
@ -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");
|
||||
|
@ -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"))
|
||||
|
@ -1 +0,0 @@
|
||||
../../../../../../tools/test/p4src/p4-16/p4c-out/default.p4info
|
@ -188,4 +188,4 @@ controller_packet_metadata {
|
||||
name: "egress_port"
|
||||
bitwidth: 9
|
||||
}
|
||||
}
|
||||
}
|
@ -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
1
tools/test/p4src/FIXME
Normal file
@ -0,0 +1 @@
|
||||
This directory should be removed and all P4 source code moved to the respective pipeline module (under /pipelines)
|
@ -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
|
@ -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;
|
@ -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;
|
@ -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
@ -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"]
|
||||
]
|
||||
]
|
||||
}
|
@ -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
@ -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;
|
Loading…
x
Reference in New Issue
Block a user