Skip to content

Commit

Permalink
PacketOut based on L2 header (#40)
Browse files Browse the repository at this point in the history
Makes the P4 program Tofino chip model independent by removing the dependency on the cpu port at compile time.
Instead this information is inserted at runtime by the controller into the switch_info table, and stored in the metadata for usage in the egress stages. To remove the dependency in the ingress parser (to detect PacketOuts from dataplane packets), we now use a special Ethertype in the serialized metadata header of PacketOuts.
  • Loading branch information
pudelkoM authored and bocon13 committed Dec 7, 2020
1 parent 1407a31 commit 1772f66
Show file tree
Hide file tree
Showing 13 changed files with 107 additions and 26 deletions.
1 change: 0 additions & 1 deletion p4src/build.sh
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,6 @@ function do_p4c() {
cpu_port=$2
echo "*** Compiling profile '${PROFILE}' for ${pltf} platform..."
echo "*** Output in ${P4C_OUT}/${pltf}"
pp_flags="-DCPU_PORT=${cpu_port}"
p4c_flags="--auto-init-metadata"
mkdir -p ${P4C_OUT}/${pltf}
(
Expand Down
3 changes: 2 additions & 1 deletion p4src/include/control/acl.p4
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,8 @@ control Acl (inout parsed_headers_t hdr,

// Send immendiatelly to CPU - skip the rest of ingress.
action punt_to_cpu() {
ig_intr_md_for_tm.ucast_egress_port = CPU_PORT;
ig_intr_md_for_tm.copy_to_cpu = 1;
ig_intr_md_for_dprsr.drop_ctl = 1;
fabric_md.skip_next = true;
acl_counter.count();
}
Expand Down
17 changes: 15 additions & 2 deletions p4src/include/control/packetio.p4
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,8 @@ control PacketIoIngress(inout parsed_headers_t hdr,
inout ingress_intrinsic_metadata_for_deparser_t ig_intr_md_for_dprsr) {
@hidden
action do_packet_out() {
hdr.packet_out.setInvalid();
ig_intr_md_for_tm.ucast_egress_port = hdr.packet_out.egress_port;
hdr.packet_out.setInvalid();
// Straight to output port.
fabric_md.bridged.setInvalid();
ig_intr_md_for_tm.bypass_egress = 1;
Expand Down Expand Up @@ -65,8 +65,21 @@ control PacketIoEgress(inout parsed_headers_t hdr,
inout fabric_egress_metadata_t fabric_md,
in egress_intrinsic_metadata_t eg_intr_md) {

action set_cpu_port(PortId_t cpu_port) {
fabric_md.cpu_port = cpu_port;
}

table switch_info {
actions = {
set_cpu_port;
@defaultonly nop;
}
}

apply {
if (eg_intr_md.egress_port == CPU_PORT) {
switch_info.apply();
// Check if this is a clone of a copy_to_cpu packet.
if (eg_intr_md.egress_port == fabric_md.cpu_port) {
hdr.packet_in.setValid();
hdr.packet_in.ingress_port = fabric_md.bridged.ig_port;
hdr.fake_ethernet.setInvalid();
Expand Down
5 changes: 1 addition & 4 deletions p4src/include/define.p4
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,6 @@

#define MAX_PORTS 511

#ifndef CPU_PORT
#deinfe CPU_PORT 192
#endif

#if ! defined(WITH_SIMPLE_NEXT)
#define WITH_HASHED_NEXT
#endif
Expand Down Expand Up @@ -93,6 +89,7 @@ const bit<16> ETHERTYPE_IPV6 = 0x86dd;
const bit<16> ETHERTYPE_ARP = 0x0806;
const bit<16> ETHERTYPE_PPPOED = 0x8863;
const bit<16> ETHERTYPE_PPPOES = 0x8864;
const bit<16> ETHERTYPE_PACKET_OUT = 0xBF01;

// Fake ether types used to distinguish regular packets from those used for
// CPU-based loopback testing.
Expand Down
6 changes: 5 additions & 1 deletion p4src/include/header.p4
Original file line number Diff line number Diff line change
Expand Up @@ -16,11 +16,14 @@ header packet_in_header_t {
bit<7> _pad0;
}

// This header must have a pseudo ethertype at offset 12, to be parseable as an
// Ethernet frame in the ingress parser.
@controller_header("packet_out")
header packet_out_header_t {
PortId_t egress_port;
CpuLoopbackMode_t cpu_loopback_mode;
bit<5> pad0;
@padding bit<85> pad0;
bit<16> ether_type;
}

header ethernet_t {
Expand Down Expand Up @@ -195,6 +198,7 @@ struct fabric_ingress_metadata_t {
@flexible
struct fabric_egress_metadata_t {
bridged_metadata_t bridged;
PortId_t cpu_port;
#ifdef WITH_SPGW
bool inner_ipv4_checksum_err;
#endif // WITH_SPGW
Expand Down
7 changes: 2 additions & 5 deletions p4src/include/int/int.p4
Original file line number Diff line number Diff line change
Expand Up @@ -189,11 +189,8 @@ control IntEgress (
// Reports don't need to go through the rest of the egress pipe.
exit;
} else {
if (fabric_md.bridged.ig_port != CPU_PORT &&
eg_intr_md.egress_port != CPU_PORT) {
if (watchlist.apply().hit) {
mirror_session_id.apply();
}
if (watchlist.apply().hit) {
mirror_session_id.apply();
}
}
}
Expand Down
15 changes: 7 additions & 8 deletions p4src/include/parser.p4
Original file line number Diff line number Diff line change
Expand Up @@ -25,14 +25,6 @@ parser FabricIngressParser (packet_in packet,
fabric_md.bridged.bridged_md_type = BridgedMdType_t.I2E;
fabric_md.bridged.ig_port = ig_intr_md.ingress_port;
fabric_md.bridged.ig_tstamp = ig_intr_md.ingress_mac_tstamp;
transition select(ig_intr_md.ingress_port) {
CPU_PORT: parse_packet_out;
default: check_ethernet;
}
}

state parse_packet_out {
packet.extract(hdr.packet_out);
transition check_ethernet;
}

Expand All @@ -43,6 +35,7 @@ parser FabricIngressParser (packet_in packet,
transition select(tmp.ether_type) {
ETHERTYPE_CPU_LOOPBACK_INGRESS: parse_fake_ethernet;
ETHERTYPE_CPU_LOOPBACK_EGRESS: parse_fake_ethernet_and_accept;
ETHERTYPE_PACKET_OUT: parse_packet_out;
default: parse_ethernet;
}
}
Expand All @@ -58,6 +51,11 @@ parser FabricIngressParser (packet_in packet,
transition accept;
}

state parse_packet_out {
packet.extract(hdr.packet_out);
transition parse_ethernet;
}

state parse_ethernet {
packet.extract(hdr.ethernet);
transition select(packet.lookahead<bit<16>>()) {
Expand Down Expand Up @@ -273,6 +271,7 @@ parser FabricEgressParser (packet_in packet,

state start {
packet.extract(eg_intr_md);
fabric_md.cpu_port = 0;
BridgedMdType_t bridged_md_type = packet.lookahead<BridgedMdType_t>();
transition select(bridged_md_type) {
BridgedMdType_t.I2E: parse_bridged_md;
Expand Down
4 changes: 0 additions & 4 deletions ptf/tests/ptf/fabric.ptf/test.py
Original file line number Diff line number Diff line change
Expand Up @@ -1128,8 +1128,6 @@ def doRunTest(self, pkt, tagged):
self.verify_packet_in(pkt, port)
self.verify_no_other_packets()

@tvsetup
@autocleanup
def runTest(self):
print ""
for pkt_type in ["tcp", "udp", "icmp", "arp"]:
Expand Down Expand Up @@ -1158,8 +1156,6 @@ def doRunTest(self, pkt):
self.verify_packet_in(pkt, port)
self.verify_no_other_packets()

@tvsetup
@autocleanup
def runTest(self):
print ""
for pkt_type in ["tcp", "udp", "icmp", "arp"]:
Expand Down
22 changes: 22 additions & 0 deletions ptf/tests/ptf/fabric_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@
ETH_TYPE_PPPOE = 0x8864
ETH_TYPE_MPLS_UNICAST = 0x8847

ETH_TYPE_PACKET_OUT = 0xBF01
ETH_TYPE_CPU_LOOPBACK_INGRESS = 0xBF02
ETH_TYPE_CPU_LOOPBACK_EGRESS = 0xBF03

Expand Down Expand Up @@ -289,6 +290,10 @@ def build_packet_out(self, pkt, port, cpu_loopback_mode=CPU_LOOPBACK_MODE_DISABL
pad0_md = packet_out.metadata.add()
pad0_md.metadata_id = 3
pad0_md.value = stringify(0, 1)
# ether type
ether_type_md = packet_out.metadata.add()
ether_type_md.metadata_id = 4
ether_type_md.value = stringify(ETH_TYPE_PACKET_OUT, 2)
return packet_out

def setup_int(self):
Expand Down Expand Up @@ -332,6 +337,22 @@ def setup_port(self, port_id, vlan_id, tagged=False, double_tagged=False, inner_
vlan_valid=False, internal_vlan_id=vlan_id)
self.set_egress_vlan_pop(egress_port=port_id, vlan_id=vlan_id)

def setup_cpu_port(self):
req = self.get_new_write_request()
self.push_update_add_entry_to_action(req,
"FabricEgress.pkt_io_egress.switch_info", None,
"FabricEgress.pkt_io_egress.set_cpu_port",
[("cpu_port", stringify(self.cpu_port, 2))])
return req, self.write_request(req, store=False)

def reset_cpu_port(self):
req = self.get_new_write_request()
self.push_update_add_entry_to_action(req,
"FabricEgress.pkt_io_egress.switch_info", None,
"nop",
[])
return req, self.write_request(req)

def set_ingress_port_vlan(self, ingress_port,
vlan_valid=False,
vlan_id=0,
Expand Down Expand Up @@ -1220,6 +1241,7 @@ def runPacketOutTest(self, pkt):

class PacketInTest(FabricTest):
def runPacketInTest(self, pkt, eth_type, tagged=False, vlan_id=10):
self.setup_cpu_port()
self.add_forwarding_acl_punt_to_cpu(eth_type=eth_type)
for port in [self.port1, self.port2]:
if tagged:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@ public class FabricInterpreter extends AbstractFabricHandlerBehavior
private static final int CPU_LOOPBACK_MODE_DISABLED = 0;
private static final int CPU_LOOPBACK_MODE_DIRECT = 1;
private static final int CPU_LOOPBACK_MODE_INGRESS = 2;
private static final int ETHER_TYPE_PACKET_OUT = 0xBF01;

// Group tables by control block.
private static final Set<PiTableId> FILTERING_CTRL_TBLS = ImmutableSet.of(
Expand Down Expand Up @@ -199,6 +200,10 @@ private Collection<PiPacketMetadata> createPacketMetadata(long portNumber)
.withId(P4InfoConstants.PAD0)
.withValue(copyFrom(0))
.build());
builder.add(PiPacketMetadata.builder()
.withId(P4InfoConstants.ETHER_TYPE)
.withValue(copyFrom(ETHER_TYPE_PACKET_OUT))
.build());
return builder.build();
} catch (ImmutableByteSequence.ByteSequenceTrimException e) {
throw new PiInterpreterException(format(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,8 @@ private P4InfoConstants() {
PiTableId.of("FabricEgress.int_egress.report");
public static final PiTableId FABRIC_EGRESS_INT_EGRESS_WATCHLIST =
PiTableId.of("FabricEgress.int_egress.watchlist");
public static final PiTableId FABRIC_EGRESS_PKT_IO_EGRESS_SWITCH_INFO =
PiTableId.of("FabricEgress.pkt_io_egress.switch_info");
public static final PiTableId FABRIC_INGRESS_ACL_ACL =
PiTableId.of("FabricIngress.acl.acl");
public static final PiTableId FABRIC_INGRESS_FILTERING_FWD_CLASSIFIER =
Expand Down Expand Up @@ -153,6 +155,8 @@ private P4InfoConstants() {
PiActionId.of("FabricEgress.int_egress.do_report_encap");
public static final PiActionId FABRIC_EGRESS_INT_EGRESS_INIT_METADATA =
PiActionId.of("FabricEgress.int_egress.init_metadata");
public static final PiActionId FABRIC_EGRESS_PKT_IO_EGRESS_SET_CPU_PORT =
PiActionId.of("FabricEgress.pkt_io_egress.set_cpu_port");
public static final PiActionId FABRIC_INGRESS_ACL_COPY_TO_CPU =
PiActionId.of("FabricIngress.acl.copy_to_cpu");
public static final PiActionId FABRIC_INGRESS_ACL_DROP =
Expand Down Expand Up @@ -210,6 +214,8 @@ private P4InfoConstants() {
public static final PiActionId NO_ACTION = PiActionId.of("NoAction");
public static final PiActionId NOP = PiActionId.of("nop");
// Action Param IDs
public static final PiActionParamId CPU_PORT =
PiActionParamId.of("cpu_port");
public static final PiActionParamId CTR_ID = PiActionParamId.of("ctr_id");
public static final PiActionParamId DIRECTION =
PiActionParamId.of("direction");
Expand Down Expand Up @@ -257,6 +263,8 @@ private P4InfoConstants() {
PiPacketMetadataId.of("cpu_loopback_mode");
public static final PiPacketMetadataId EGRESS_PORT =
PiPacketMetadataId.of("egress_port");
public static final PiPacketMetadataId ETHER_TYPE =
PiPacketMetadataId.of("ether_type");
public static final PiPacketMetadataId INGRESS_PORT =
PiPacketMetadataId.of("ingress_port");
public static final PiPacketMetadataId PAD0 = PiPacketMetadataId.of("pad0");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,14 +5,19 @@

import com.google.common.collect.ImmutableList;
import org.onlab.util.SharedExecutors;
import org.onosproject.core.ApplicationId;
import org.onosproject.core.CoreService;
import org.onosproject.net.DeviceId;
import org.onosproject.net.PortNumber;
import org.onosproject.net.behaviour.NextGroup;
import org.onosproject.net.behaviour.Pipeliner;
import org.onosproject.net.behaviour.PipelinerContext;
import org.onosproject.net.flow.DefaultFlowRule;
import org.onosproject.net.flow.DefaultTrafficTreatment;
import org.onosproject.net.flow.FlowRule;
import org.onosproject.net.flow.FlowRuleOperations;
import org.onosproject.net.flow.FlowRuleService;
import org.onosproject.net.flow.TrafficTreatment;
import org.onosproject.net.flowobjective.FilteringObjective;
import org.onosproject.net.flowobjective.FlowObjectiveStore;
import org.onosproject.net.flowobjective.ForwardingObjective;
Expand All @@ -23,9 +28,13 @@
import org.onosproject.net.flowobjective.ObjectiveError;
import org.onosproject.net.group.GroupDescription;
import org.onosproject.net.group.GroupService;
import org.onosproject.net.pi.runtime.PiAction;
import org.onosproject.net.pi.runtime.PiActionParam;
import org.slf4j.Logger;
import org.stratumproject.fabric.tna.behaviour.AbstractFabricHandlerBehavior;
import org.stratumproject.fabric.tna.behaviour.FabricCapabilities;
import org.stratumproject.fabric.tna.behaviour.P4InfoConstants;
import org.stratumproject.fabric.tna.PipeconfLoader;

import java.util.Collection;
import java.util.List;
Expand All @@ -48,11 +57,14 @@ public class FabricPipeliner extends AbstractFabricHandlerBehavior
implements Pipeliner {

private static final Logger log = getLogger(FabricPipeliner.class);
private static final int DEFAULT_FLOW_PRIORITY = 100;

protected DeviceId deviceId;
protected ApplicationId appId;
protected FlowRuleService flowRuleService;
protected GroupService groupService;
protected FlowObjectiveStore flowObjectiveStore;
protected CoreService coreService;

private FilteringObjectiveTranslator filteringTranslator;
private ForwardingObjectiveTranslator forwardingTranslator;
Expand Down Expand Up @@ -86,6 +98,10 @@ public void init(DeviceId deviceId, PipelinerContext context) {
this.filteringTranslator = new FilteringObjectiveTranslator(deviceId, capabilities);
this.forwardingTranslator = new ForwardingObjectiveTranslator(deviceId, capabilities);
this.nextTranslator = new NextObjectiveTranslator(deviceId, capabilities);
this.coreService = context.directory().get(CoreService.class);
this.appId = coreService.getAppId(PipeconfLoader.APP_NAME);

initializePipeline();
}

@Override
Expand Down Expand Up @@ -128,6 +144,26 @@ public List<String> getNextMappings(NextGroup nextGroup) {
.collect(Collectors.toList());
}

protected void initializePipeline() {
final PiActionParam param = new PiActionParam(P4InfoConstants.CPU_PORT, capabilities.cpuPort().get());
final PiAction action = PiAction.builder()
.withId(P4InfoConstants.FABRIC_EGRESS_PKT_IO_EGRESS_SET_CPU_PORT)
.withParameter(param)
.build();
final TrafficTreatment treatment = DefaultTrafficTreatment.builder()
.piTableAction(action)
.build();
FlowRule rule = DefaultFlowRule.builder()
.forDevice(deviceId)
.withTreatment(treatment)
.withPriority(DEFAULT_FLOW_PRIORITY)
.fromApp(appId)
.makePermanent()
.forTable(P4InfoConstants.FABRIC_EGRESS_PKT_IO_EGRESS_SWITCH_INFO)
.build();
flowRuleService.applyFlowRules(rule);
}

private void handleResult(Objective obj, ObjectiveTranslation result) {
if (result.error().isPresent()) {
fail(obj, result.error().get());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -235,6 +235,10 @@ public void testMapOutboundPacket() throws PiPipelineInterpreter.PiInterpreterEx
.withId(P4InfoConstants.CPU_LOOPBACK_MODE)
.withValue(ImmutableByteSequence.copyFrom(0))
.build());
builder.add(PiPacketMetadata.builder()
.withId(P4InfoConstants.ETHER_TYPE)
.withValue(ImmutableByteSequence.copyFrom(0xBF01))
.build());
builder.add(PiPacketMetadata.builder()
.withId(P4InfoConstants.PAD0)
.withValue(ImmutableByteSequence.copyFrom(0))
Expand Down

0 comments on commit 1772f66

Please sign in to comment.