Skip to content
GitLab
Menu
Projects
Groups
Snippets
Loading...
Help
Help
Support
Community forum
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
Menu
Open sidebar
ycai
simbricks
Commits
edb4215d
Commit
edb4215d
authored
Aug 17, 2021
by
Jialin Li
Browse files
tofino: add tofino switch p4 code; add tofino net to tcp_single and udp_single experiments
parent
a8fd899d
Changes
4
Hide whitespace changes
Inline
Side-by-side
Showing
4 changed files
with
1101 additions
and
24 deletions
+1101
-24
experiments/pyexps/qemu_tcp_single.py
experiments/pyexps/qemu_tcp_single.py
+14
-12
experiments/pyexps/qemu_udp_single.py
experiments/pyexps/qemu_udp_single.py
+14
-12
sims/tofino/p4/l2_switch.p4
sims/tofino/p4/l2_switch.p4
+503
-0
sims/tofino/p4/switch_setup.py
sims/tofino/p4/switch_setup.py
+570
-0
No files found.
experiments/pyexps/qemu_tcp_single.py
View file @
edb4215d
...
@@ -34,7 +34,7 @@ import simbricks.nodeconfig as node
...
@@ -34,7 +34,7 @@ import simbricks.nodeconfig as node
kinds_of_host
=
[
'qemu'
]
kinds_of_host
=
[
'qemu'
]
kinds_of_nic
=
[
'cv'
,
'cb'
,
'ib'
]
kinds_of_nic
=
[
'cv'
,
'cb'
,
'ib'
]
kinds_of_net
=
[
'wire'
,
'switch'
,
'dumbbell'
,
'bridge'
]
kinds_of_net
=
[
'wire'
,
'switch'
,
'dumbbell'
,
'bridge'
,
'tofino'
]
kinds_of_app
=
[
'TCPs'
]
kinds_of_app
=
[
'TCPs'
]
experiments
=
[]
experiments
=
[]
...
@@ -49,6 +49,8 @@ for n in kinds_of_net:
...
@@ -49,6 +49,8 @@ for n in kinds_of_net:
net_class
=
sim
.
NS3DumbbellNet
net_class
=
sim
.
NS3DumbbellNet
if
n
==
'bridge'
:
if
n
==
'bridge'
:
net_class
=
sim
.
NS3BridgeNet
net_class
=
sim
.
NS3BridgeNet
if
n
==
'tofino'
:
net_class
=
sim
.
TofinoNet
# set nic sim
# set nic sim
...
@@ -56,28 +58,28 @@ for n in kinds_of_net:
...
@@ -56,28 +58,28 @@ for n in kinds_of_net:
net
=
net_class
()
net
=
net_class
()
e
=
exp
.
Experiment
(
'qemu-'
+
c
+
'-'
+
n
+
'-'
+
'TCPs'
)
e
=
exp
.
Experiment
(
'qemu-'
+
c
+
'-'
+
n
+
'-'
+
'TCPs'
)
e
.
add_network
(
net
)
e
.
add_network
(
net
)
if
c
==
'cv'
:
if
c
==
'cv'
:
servers
=
sim
.
create_basic_hosts
(
e
,
1
,
'server'
,
net
,
sim
.
CorundumVerilatorNIC
,
sim
.
QemuHost
,
servers
=
sim
.
create_basic_hosts
(
e
,
1
,
'server'
,
net
,
sim
.
CorundumVerilatorNIC
,
sim
.
QemuHost
,
node
.
CorundumLinuxNode
,
node
.
IperfTCPServer
)
node
.
CorundumLinuxNode
,
node
.
IperfTCPServer
)
clients
=
sim
.
create_basic_hosts
(
e
,
1
,
'client'
,
net
,
sim
.
CorundumVerilatorNIC
,
sim
.
QemuHost
,
clients
=
sim
.
create_basic_hosts
(
e
,
1
,
'client'
,
net
,
sim
.
CorundumVerilatorNIC
,
sim
.
QemuHost
,
node
.
CorundumLinuxNode
,
node
.
IperfTCPClient
,
ip_start
=
2
)
node
.
CorundumLinuxNode
,
node
.
IperfTCPClient
,
ip_start
=
2
)
if
c
==
'cb'
:
if
c
==
'cb'
:
servers
=
sim
.
create_basic_hosts
(
e
,
1
,
'server'
,
net
,
sim
.
CorundumBMNIC
,
sim
.
QemuHost
,
servers
=
sim
.
create_basic_hosts
(
e
,
1
,
'server'
,
net
,
sim
.
CorundumBMNIC
,
sim
.
QemuHost
,
node
.
CorundumLinuxNode
,
node
.
IperfTCPServer
)
node
.
CorundumLinuxNode
,
node
.
IperfTCPServer
)
clients
=
sim
.
create_basic_hosts
(
e
,
1
,
'client'
,
net
,
sim
.
CorundumBMNIC
,
sim
.
QemuHost
,
clients
=
sim
.
create_basic_hosts
(
e
,
1
,
'client'
,
net
,
sim
.
CorundumBMNIC
,
sim
.
QemuHost
,
node
.
CorundumLinuxNode
,
node
.
IperfTCPClient
,
ip_start
=
2
)
node
.
CorundumLinuxNode
,
node
.
IperfTCPClient
,
ip_start
=
2
)
if
c
==
'ib'
:
if
c
==
'ib'
:
servers
=
sim
.
create_basic_hosts
(
e
,
1
,
'server'
,
net
,
sim
.
I40eNIC
,
sim
.
QemuHost
,
servers
=
sim
.
create_basic_hosts
(
e
,
1
,
'server'
,
net
,
sim
.
I40eNIC
,
sim
.
QemuHost
,
node
.
I40eLinuxNode
,
node
.
IperfTCPServer
)
node
.
I40eLinuxNode
,
node
.
IperfTCPServer
)
clients
=
sim
.
create_basic_hosts
(
e
,
1
,
'client'
,
net
,
sim
.
I40eNIC
,
sim
.
QemuHost
,
clients
=
sim
.
create_basic_hosts
(
e
,
1
,
'client'
,
net
,
sim
.
I40eNIC
,
sim
.
QemuHost
,
node
.
I40eLinuxNode
,
node
.
IperfTCPClient
,
ip_start
=
2
)
node
.
I40eLinuxNode
,
node
.
IperfTCPClient
,
ip_start
=
2
)
clients
[
0
].
wait
=
True
clients
[
0
].
wait
=
True
clients
[
0
].
node_config
.
app
.
server_ip
=
servers
[
0
].
node_config
.
ip
clients
[
0
].
node_config
.
app
.
server_ip
=
servers
[
0
].
node_config
.
ip
...
...
experiments/pyexps/qemu_udp_single.py
View file @
edb4215d
...
@@ -34,7 +34,7 @@ import simbricks.nodeconfig as node
...
@@ -34,7 +34,7 @@ import simbricks.nodeconfig as node
kinds_of_host
=
[
'qemu'
]
kinds_of_host
=
[
'qemu'
]
kinds_of_nic
=
[
'cv'
,
'cb'
,
'ib'
]
kinds_of_nic
=
[
'cv'
,
'cb'
,
'ib'
]
kinds_of_net
=
[
'wire'
,
'switch'
,
'dumbbell'
,
'bridge'
]
kinds_of_net
=
[
'wire'
,
'switch'
,
'dumbbell'
,
'bridge'
,
'tofino'
]
kinds_of_app
=
[
'UDPs'
]
kinds_of_app
=
[
'UDPs'
]
rate
=
'200m'
rate
=
'200m'
...
@@ -51,6 +51,8 @@ for n in kinds_of_net:
...
@@ -51,6 +51,8 @@ for n in kinds_of_net:
net_class
=
sim
.
NS3DumbbellNet
net_class
=
sim
.
NS3DumbbellNet
if
n
==
'bridge'
:
if
n
==
'bridge'
:
net_class
=
sim
.
NS3BridgeNet
net_class
=
sim
.
NS3BridgeNet
if
n
==
'tofino'
:
net_class
=
sim
.
TofinoNet
# set nic sim
# set nic sim
...
@@ -58,28 +60,28 @@ for n in kinds_of_net:
...
@@ -58,28 +60,28 @@ for n in kinds_of_net:
net
=
net_class
()
net
=
net_class
()
e
=
exp
.
Experiment
(
'qemu-'
+
c
+
'-'
+
n
+
'-'
+
'UDPs'
)
e
=
exp
.
Experiment
(
'qemu-'
+
c
+
'-'
+
n
+
'-'
+
'UDPs'
)
e
.
add_network
(
net
)
e
.
add_network
(
net
)
if
c
==
'cv'
:
if
c
==
'cv'
:
servers
=
sim
.
create_basic_hosts
(
e
,
1
,
'server'
,
net
,
sim
.
CorundumVerilatorNIC
,
sim
.
QemuHost
,
servers
=
sim
.
create_basic_hosts
(
e
,
1
,
'server'
,
net
,
sim
.
CorundumVerilatorNIC
,
sim
.
QemuHost
,
node
.
CorundumLinuxNode
,
node
.
IperfUDPServer
)
node
.
CorundumLinuxNode
,
node
.
IperfUDPServer
)
clients
=
sim
.
create_basic_hosts
(
e
,
1
,
'client'
,
net
,
sim
.
CorundumVerilatorNIC
,
sim
.
QemuHost
,
clients
=
sim
.
create_basic_hosts
(
e
,
1
,
'client'
,
net
,
sim
.
CorundumVerilatorNIC
,
sim
.
QemuHost
,
node
.
CorundumLinuxNode
,
node
.
IperfUDPClient
,
ip_start
=
2
)
node
.
CorundumLinuxNode
,
node
.
IperfUDPClient
,
ip_start
=
2
)
if
c
==
'cb'
:
if
c
==
'cb'
:
servers
=
sim
.
create_basic_hosts
(
e
,
1
,
'server'
,
net
,
sim
.
CorundumBMNIC
,
sim
.
QemuHost
,
servers
=
sim
.
create_basic_hosts
(
e
,
1
,
'server'
,
net
,
sim
.
CorundumBMNIC
,
sim
.
QemuHost
,
node
.
CorundumLinuxNode
,
node
.
IperfUDPServer
)
node
.
CorundumLinuxNode
,
node
.
IperfUDPServer
)
clients
=
sim
.
create_basic_hosts
(
e
,
1
,
'client'
,
net
,
sim
.
CorundumBMNIC
,
sim
.
QemuHost
,
clients
=
sim
.
create_basic_hosts
(
e
,
1
,
'client'
,
net
,
sim
.
CorundumBMNIC
,
sim
.
QemuHost
,
node
.
CorundumLinuxNode
,
node
.
IperfUDPClient
,
ip_start
=
2
)
node
.
CorundumLinuxNode
,
node
.
IperfUDPClient
,
ip_start
=
2
)
if
c
==
'ib'
:
if
c
==
'ib'
:
servers
=
sim
.
create_basic_hosts
(
e
,
1
,
'server'
,
net
,
sim
.
I40eNIC
,
sim
.
QemuHost
,
servers
=
sim
.
create_basic_hosts
(
e
,
1
,
'server'
,
net
,
sim
.
I40eNIC
,
sim
.
QemuHost
,
node
.
I40eLinuxNode
,
node
.
IperfUDPServer
)
node
.
I40eLinuxNode
,
node
.
IperfUDPServer
)
clients
=
sim
.
create_basic_hosts
(
e
,
1
,
'client'
,
net
,
sim
.
I40eNIC
,
sim
.
QemuHost
,
clients
=
sim
.
create_basic_hosts
(
e
,
1
,
'client'
,
net
,
sim
.
I40eNIC
,
sim
.
QemuHost
,
node
.
I40eLinuxNode
,
node
.
IperfUDPClient
,
ip_start
=
2
)
node
.
I40eLinuxNode
,
node
.
IperfUDPClient
,
ip_start
=
2
)
clients
[
0
].
wait
=
True
clients
[
0
].
wait
=
True
clients
[
0
].
node_config
.
app
.
server_ip
=
servers
[
0
].
node_config
.
ip
clients
[
0
].
node_config
.
app
.
server_ip
=
servers
[
0
].
node_config
.
ip
clients
[
0
].
node_config
.
app
.
rate
=
rate
clients
[
0
].
node_config
.
app
.
rate
=
rate
...
...
sims/tofino/p4/l2_switch.p4
0 → 100644
View file @
edb4215d
/* -*- P4_16 -*- */
#include <core.p4>
#include <tna.p4>
/*************************************************************************
************* C O N S T A N T S A N D T Y P E S *******************
**************************************************************************/
enum bit<16> ether_type_t {
IPV4 = 0x0800,
ARP = 0x0806,
TPID = 0x8100,
TPID2 = 0x9100,
IPV6 = 0x86DD
}
const ReplicationId_t L2_MCAST_RID = 0xFFFF;
const int MAC_TABLE_SIZE = 65536;
const int VLAN_PORT_TABLE_SIZE = 1 << (7 + 12);
/* We can use up to 8 different digest types */
const bit<3> L2_LEARN_DIGEST = 1;
/*************************************************************************
*********************** H E A D E R S *********************************
*************************************************************************/
/* Define all the headers the program will recognize */
/* The actual sets of headers processed by each gress can differ */
/* Standard ethernet header */
header ethernet_h {
bit<48> dst_addr;
bit<48> src_addr;
}
header vlan_tag_h {
bit<16> tpid;
bit<3> pcp;
bit<1> dei;
bit<12> vid;
}
header etherII_h {
ether_type_t ether_type;
}
/*** Internal Headers ***/
typedef bit<4> header_type_t; /* These fields can be coombined into one */
typedef bit<4> header_info_t; /* 8-bit field as well. Exercise for the brave */
const header_type_t HEADER_TYPE_BRIDGE = 0xB;
const header_type_t HEADER_TYPE_MIRROR_INGRESS = 0xC;
const header_type_t HEADER_TYPE_MIRROR_EGRESS = 0xD;
const header_type_t HEADER_TYPE_RESUBMIT = 0xA;
/*
* This is a common "preamble" header that must be present in all internal
* headers. The only time you do not need it is when you know that you are
* not going to have more than one internal header type ever
*/
#define INTERNAL_HEADER \
header_type_t header_type; \
header_info_t header_info
header inthdr_h {
INTERNAL_HEADER;
}
/* Bridged metadata */
header bridge_h {
INTERNAL_HEADER;
bit<7> pad0;
PortId_t ingress_port;
bit<3> pcp;
bit<1> dei;
bit<12> vid;
}
/*************************************************************************
************** I N G R E S S P R O C E S S I N G *******************
*************************************************************************/
/*********************** H E A D E R S ************************/
struct my_ingress_headers_t {
bridge_h bridge;
ethernet_h ethernet;
vlan_tag_h vlan_tag;
etherII_h etherII;
}
/****** G L O B A L I N G R E S S M E T A D A T A *********/
struct port_metadata_t {
bit<3> port_pcp;
bit<12> port_vid;
bit<9> l2_xid;
}
struct my_ingress_metadata_t {
port_metadata_t port_properties;
bit<3> pcp;
bit<1> dei;
bit<12> vid;
bit<9> mac_move; /* Should have the same size as PortId_t */
bit<1> is_static;
bit<1> smac_hit;
PortId_t ingress_port;
}
/*********************** P A R S E R **************************/
parser IngressParser(packet_in pkt,
/* User */
out my_ingress_headers_t hdr,
out my_ingress_metadata_t meta,
/* Intrinsic */
out ingress_intrinsic_metadata_t ig_intr_md)
{
/* This is a mandatory state, required by Tofino Architecture */
state start {
pkt.extract(ig_intr_md);
meta.port_properties = port_metadata_unpack<port_metadata_t>(pkt);
transition meta_init;
}
state meta_init {
meta.pcp = 0;
meta.dei = 0;
meta.vid = 0;
meta.mac_move = 0;
meta.is_static = 0;
meta.smac_hit = 0;
meta.ingress_port = ig_intr_md.ingress_port;
transition parse_ethernet;
}
state parse_ethernet {
pkt.extract(hdr.ethernet);
transition select(pkt.lookahead<bit<16>>()) {
(bit<16>)ether_type_t.TPID &&& 0xEFFF: parse_vlan_tag;
default: parse_etherII;
}
}
state parse_vlan_tag {
pkt.extract(hdr.vlan_tag);
meta.pcp = hdr.vlan_tag.pcp;
meta.dei = hdr.vlan_tag.dei;
meta.vid = hdr.vlan_tag.vid;
transition parse_etherII;
}
state parse_etherII {
pkt.extract(hdr.etherII);
transition accept;
}
}
/***************** M A T C H - A C T I O N *********************/
control Ingress(
/* User */
inout my_ingress_headers_t hdr,
inout my_ingress_metadata_t meta,
/* Intrinsic */
in ingress_intrinsic_metadata_t ig_intr_md,
in ingress_intrinsic_metadata_from_parser_t ig_prsr_md,
inout ingress_intrinsic_metadata_for_deparser_t ig_dprsr_md,
inout ingress_intrinsic_metadata_for_tm_t ig_tm_md)
{
action drop() {
ig_dprsr_md.drop_ctl = 1;
}
action send(PortId_t port) {
ig_tm_md.ucast_egress_port = port;
}
action smac_hit(PortId_t port, bit<1> is_static) {
meta.mac_move = ig_intr_md.ingress_port ^ port;
meta.smac_hit = 1;
meta.is_static = is_static;
}
action smac_miss() { }
action smac_drop() {
drop(); exit;
}
@idletime_precision(3)
table smac {
key = {
meta.vid : exact;
hdr.ethernet.src_addr : exact;
}
actions = {
smac_hit; smac_miss; smac_drop;
}
size = MAC_TABLE_SIZE;
const default_action = smac_miss();
idle_timeout = true;
}
action mac_learn_notify() {
ig_dprsr_md.digest_type = L2_LEARN_DIGEST;
}
table smac_results {
key = {
meta.mac_move : ternary;
meta.is_static : ternary;
meta.smac_hit : ternary;
}
actions = {
mac_learn_notify; NoAction; smac_drop;
}
const entries = {
( _, _, 0) : mac_learn_notify();
( 0, _, 1) : NoAction();
( _, 0, 1) : mac_learn_notify();
( _, 1, 1) : smac_drop();
}
}
action dmac_unicast(PortId_t port) {
send(port);
}
action dmac_miss() {
ig_tm_md.mcast_grp_a = (MulticastGroupId_t)meta.vid;
ig_tm_md.rid = L2_MCAST_RID;
/* Set the exclusion id here, since parser can't acces ig_tm_md */
ig_tm_md.level2_exclusion_id = meta.port_properties.l2_xid;
}
action dmac_multicast(MulticastGroupId_t mcast_grp) {
ig_tm_md.mcast_grp_a = mcast_grp;
ig_tm_md.rid = L2_MCAST_RID;
/* Set the exclusion id here, since parser can't acces ig_tm_md */
ig_tm_md.level2_exclusion_id = meta.port_properties.l2_xid;
}
action dmac_drop() {
drop();
exit;
}
table dmac {
key = {
meta.vid : exact;
hdr.ethernet.dst_addr : exact;
}
actions = {
dmac_unicast; dmac_miss; dmac_multicast; dmac_drop;
}
size = MAC_TABLE_SIZE;
default_action = dmac_miss();
}
apply {
/* Assign Port-based VLAN to untagged/priority-tagged packets */
if (meta.vid == 0) {
meta.vid = meta.port_properties.port_vid;
}
if (!hdr.vlan_tag.isValid()) {
meta.pcp = meta.port_properties.port_pcp;
}
smac.apply();
smac_results.apply();
switch (dmac.apply().action_run) {
dmac_unicast: { /* Unicast source pruning */
if (ig_intr_md.ingress_port ==
ig_tm_md.ucast_egress_port) {
drop();
}
}
}
/* Bridge metadata to the egress pipeline */
hdr.bridge.setValid();
hdr.bridge.header_type = HEADER_TYPE_BRIDGE;
hdr.bridge.header_info = 0; /* Ignore */
hdr.bridge.ingress_port = ig_intr_md.ingress_port;
hdr.bridge.pcp = meta.pcp;
hdr.bridge.dei = meta.dei;
hdr.bridge.vid = meta.vid;
}
}
/********************* D E P A R S E R ************************/
/* This struct is needed for proper digest receive API generation */
struct l2_digest_t {
bit<12> vid;
bit<48> src_mac;
bit<9> ingress_port;
bit<9> mac_move;
bit<1> is_static;
bit<1> smac_hit;
}
control IngressDeparser(packet_out pkt,
/* User */
inout my_ingress_headers_t hdr,
in my_ingress_metadata_t meta,
/* Intrinsic */
in ingress_intrinsic_metadata_for_deparser_t ig_dprsr_md)
{
Digest <l2_digest_t>() l2_digest;
apply {
if (ig_dprsr_md.digest_type == L2_LEARN_DIGEST) {
l2_digest.pack({
meta.vid,
hdr.ethernet.src_addr,
meta.ingress_port,
meta.mac_move,
meta.is_static,
meta.smac_hit });
}
pkt.emit(hdr);
}
}
/*************************************************************************
**************** E G R E S S P R O C E S S I N G *******************
*************************************************************************/
/*********************** H E A D E R S ************************/
struct my_egress_headers_t {
ethernet_h ethernet;
vlan_tag_h vlan_tag;
etherII_h etherII;
}
/******** G L O B A L E G R E S S M E T A D A T A *********/
struct my_egress_metadata_t {
bridge_h bridge;
PortId_t ingress_port;
bit<3> pcp;
bit<1> dei;
bit<12> vid;
}
/*********************** P A R S E R **************************/
parser EgressParser(packet_in pkt,
/* User */
out my_egress_headers_t hdr,
out my_egress_metadata_t meta,
/* Intrinsic */
out egress_intrinsic_metadata_t eg_intr_md)
{
inthdr_h inthdr;
/* This is a mandatory state, required by Tofino Architecture */
state start {
pkt.extract(eg_intr_md);
inthdr = pkt.lookahead<inthdr_h>();
transition select(inthdr.header_type, inthdr.header_info) {
( HEADER_TYPE_BRIDGE, _ ) : parse_bridge;
default : reject;
}
}
state parse_bridge {
pkt.extract(meta.bridge);
meta.ingress_port = meta.bridge.ingress_port;
meta.pcp = meta.bridge.pcp;
meta.dei = meta.bridge.dei;
meta.vid = meta.bridge.vid;
transition parse_ethernet;
}
state parse_ethernet {
pkt.extract(hdr.ethernet);
transition select(pkt.lookahead<bit<16>>()) {
(bit<16>)ether_type_t.TPID &&& 0xEFFF: parse_vlan_tag;
default: parse_etherII;
}
}
state parse_vlan_tag {
pkt.extract(hdr.vlan_tag);
transition parse_etherII;
}
state parse_etherII {
pkt.extract(hdr.etherII);
transition accept;
}
}
/***************** M A T C H - A C T I O N *********************/
control Egress(
/* User */
inout my_egress_headers_t hdr,
inout my_egress_metadata_t meta,
/* Intrinsic */
in egress_intrinsic_metadata_t eg_intr_md,
in egress_intrinsic_metadata_from_parser_t eg_prsr_md,
inout egress_intrinsic_metadata_for_deparser_t eg_dprsr_md,
inout egress_intrinsic_metadata_for_output_port_t eg_oport_md)
{
action drop() {
eg_dprsr_md.drop_ctl = eg_dprsr_md.drop_ctl | 1;
}
action send_tagged() {
hdr.vlan_tag.setValid();
hdr.vlan_tag.tpid = (bit<16>)ether_type_t.TPID;
#ifdef P4C_1719_FIXED
hdr.vlan_tag.pcp = meta.pcp;
hdr.vlan_tag.dei = meta.dei;
#else
hdr.vlan_tag.pcp = 0;
hdr.vlan_tag.dei = 0;
#endif
hdr.vlan_tag.vid = meta.vid;
}
action send_untagged() {
hdr.vlan_tag.setInvalid();
}
action not_a_member() {
drop();
}
table egr_vlan_port {
key = {
meta.vid : exact;
#ifdef USE_MASK
eg_intr_md.egress_port & 0x7F : exact @name("egress_port");
#else
eg_intr_md.egress_port[6:0] : exact @name("egress_port");
#endif
}
actions = {
send_tagged;
send_untagged;
not_a_member;
}
default_action = not_a_member();
size = VLAN_PORT_TABLE_SIZE;
}
apply {
#ifdef P4_SOURCE_PRUNING
if (meta.ingress_port == eg_intr_md.egress_port) {
drop();
} else {
#endif
egr_vlan_port.apply();
#ifdef P4_SOURCE_PRUNING
}
#endif
}
}
/********************* D E P A R S E R ************************/
control EgressDeparser(packet_out pkt,
/* User */
inout my_egress_headers_t hdr,
in my_egress_metadata_t meta,
/* Intrinsic */
in egress_intrinsic_metadata_for_deparser_t eg_dprsr_md)
{
apply {
pkt.emit(hdr);
}
}
/************ F I N A L P A C K A G E ******************************/
Pipeline(
IngressParser(),
Ingress(),
IngressDeparser(),
EgressParser(),
Egress(),
EgressDeparser()
) pipe;
Switch(pipe) main;
sims/tofino/p4/switch_setup.py
0 → 100644
View file @
edb4215d
from
bfrtcli
import
*
#from netaddr import *
class
l2_switch
():
#
# Helper Functions to deal with ports
#
def
devport
(
self
,
pipe
,
port
):
return
((
pipe
&
3
)
<<
7
)
|
(
port
&
0x7F
)
def
pipeport
(
self
,
dp
):
return
((
dp
&
0x180
)
>>
7
,
(
dp
&
0x7F
))
def
mcport
(
self
,
pipe
,
port
):
return
pipe
*
72
+
port
def
devport_to_mcport
(
self
,
dp
):
return
self
.
mcport
(
*
self
.
pipeport
(
dp
))
# This is a useful bfrt_python function that should potentially allow one
# to quickly clear all the logical tables (including the fixed ones) in
# their data plane program.
#
# This function can clear all P4 tables and later other fixed objects
# (once proper BfRt support is added). As of SDE-9.2.0 the support is mixed.
# As a result the function contains some workarounds.
def
clear_all
(
self
,
verbose
=
True
,
batching
=
True
,
clear_ports
=
False
):
table_list
=
bfrt
.
info
(
return_info
=
True
,
print_info
=
False
)
# Remove port tables from the list
port_types
=
[
'PORT_CFG'
,
'PORT_FRONT_PANEL_IDX_INFO'
,
'PORT_HDL_INFO'
,
'PORT_STR_INFO'
]
if
not
clear_ports
:
for
table
in
list
(
table_list
):
if
table
[
'type'
]
in
port_types
:
table_list
.
remove
(
table
)
# The order is important. We do want to clear from the top,
# i.e. delete objects that use other objects. For example,
# table entries use selector groups and selector groups
# use action profile members.
#
# Same is true for the fixed tables. However, the list of
# table types grows, so we will first clean the tables we
# know and then clear the rest
for
table_types
in
([
'MATCH_DIRECT'
,
'MATCH_INDIRECT_SELECTOR'
],
[
'SELECTOR'
],
[
'ACTION_PROFILE'
],
[
'PRE_MGID'
],
[
'PRE_ECMP'
],
[
'PRE_NODE'
],
[]):
# This is catch-all
for
table
in
list
(
table_list
):
if
table
[
'type'
]
in
table_types
or
len
(
table_types
)
==
0
:
try
:
if
verbose
:
print
(
"Clearing table {:<40} ... "
.
format
(
table
[
'full_name'
]),
end
=
''
,
flush
=
True
)
table
[
'node'
].
clear
(
batch
=
batching
)
table_list
.
remove
(
table
)
if
verbose
:
print
(
'Done'
)
use_entry_list
=
False
except
:
use_entry_list
=
True
# Some tables do not support clear(). Thus we'll try
# to get a list of entries and clear them one-by-one
if
use_entry_list
:
try
:
if
batching
:
bfrt
.
batch_begin
()
# This line can result in an exception,
# since # not all tables support get()
entry_list
=
table
[
'node'
].
get
(
regex
=
True
,
return_ents
=
True
,
print_ents
=
False
)
# Not every table supports delete() method.
# For those tables we'll try to push in an
# entry with everything being zeroed out
has_delete
=
hasattr
(
table
[
'node'
],
'delete'
)
if
entry_list
!=
-
1
:
if
has_delete
:
for
entry
in
entry_list
:
entry
.
remove
()
else
:
clear_entry
=
table
[
'node'
].
entry
()
for
entry
in
entry_list
:
entry
.
data
=
clear_entry
.
data
# We can still have an exception
# here, since not all tables
# support add()/mod()
entry
.
push
()
if
verbose
:
print
(
'Done'
)
else
:
print
(
'Empty'
)
table_list
.
remove
(
table
)
except
BfRtTableError
as
e
:
print
(
'Empty'
)
table_list
.
remove
(
table
)
except
Exception
as
e
:
# We can have in a number of ways: no get(),
# no add() etc. Another reason is that the
# table is read-only.
if
verbose
:
print
(
"Failed"
)
finally
:
if
batching
:
bfrt
.
batch_end
()
bfrt
.
complete_operations
()
def
__init__
(
self
,
default_ttl
=
60000
):
self
.
p4
=
bfrt
.
l2_switch
.
pipe
self
.
vlan
=
{}
self
.
all_ports
=
[
port
.
key
[
b
'$DEV_PORT'
]
for
port
in
bfrt
.
port
.
port
.
get
(
regex
=
1
,
return_ents
=
True
,
print_ents
=
False
)]
self
.
l2_age_ttl
=
default_ttl
if
(
hasattr
(
self
.
p4
.
Egress
,
'port_vlan_member'
)
and
hasattr
(
self
.
p4
.
Egress
,
'port_vlan_tagged'
)):
self
.
egr_vlan_port
=
False
else
:
self
.
egr_vlan_port
=
True
def
setup
(
self
):
self
.
clear_all
()
self
.
__init__
()
# Preset port properties and the multicast pruning
for
dp
in
self
.
all_ports
:
l2_xid
=
self
.
mcport
(
*
self
.
pipeport
(
dp
))
self
.
p4
.
IngressParser
.
PORT_METADATA
.
entry
(
ingress_port
=
dp
,
port_vid
=
0
,
port_pcp
=
0
,
l2_xid
=
l2_xid
).
push
()
bfrt
.
pre
.
prune
.
entry
(
MULTICAST_L2_XID
=
l2_xid
,
DEV_PORT
=
[
dp
]).
push
()
# Ensure that egr_port_vlan (or its equivalent) is in asymmetric mode
if
self
.
egr_vlan_port
:
self
.
p4
.
Egress
.
egr_vlan_port
.
symmetric_mode_set
(
False
)
else
:
self
.
p4
.
Egress
.
port_vlan_member
.
symmetric_mode_set
(
False
)
self
.
p4
.
Egress
.
port_vlan_tagged
.
symmetric_mode_set
(
False
)
# Enable learning on SMAC
print
(
"Initializing learning on SMAC ... "
,
end
=
''
,
flush
=
True
)
try
:
self
.
p4
.
IngressDeparser
.
l2_digest
.
callback_deregister
()
except
:
pass
self
.
p4
.
IngressDeparser
.
l2_digest
.
callback_register
(
self
.
learning_cb
)
print
(
"Done"
)
# Enable aging on SMAC
print
(
"Inializing Aging on SMAC ... "
,
end
=
''
,
flush
=
True
)
self
.
p4
.
Ingress
.
smac
.
idle_table_set_notify
(
enable
=
False
,
callback
=
None
)
self
.
p4
.
Ingress
.
smac
.
idle_table_set_notify
(
enable
=
True
,
callback
=
self
.
aging_cb
,
interval
=
10000
,
min_ttl
=
10000
,
max_ttl
=
60000
)
print
(
"Done"
)
@
staticmethod
def
aging_cb
(
dev_id
,
pipe_id
,
direction
,
parser_id
,
entry
):
smac
=
bfrt
.
l2_switch
.
pipe
.
Ingress
.
smac
dmac
=
bfrt
.
l2_switch
.
pipe
.
Ingress
.
dmac
vid
=
entry
.
key
[
b
'meta.vid'
]
mac_addr
=
entry
.
key
[
b
'hdr.ethernet.src_addr'
]
print
(
"Aging out: VID: {}, MAC: {}"
.
format
(
vid
,
mac
(
mac_addr
)))
entry
.
remove
()
# from smac
try
:
dmac
.
delete
(
vid
=
vid
,
dst_addr
=
mac_addr
)
except
:
print
(
"WARNING: Could not find the matching DMAC entry"
)
@
staticmethod
def
learning_cb
(
dev_id
,
pipe_id
,
direction
,
parser_id
,
session
,
msg
):
smac
=
bfrt
.
l2_switch
.
pipe
.
Ingress
.
smac
dmac
=
bfrt
.
l2_switch
.
pipe
.
Ingress
.
dmac
for
digest
in
msg
:
vid
=
digest
[
"vid"
]
port
=
digest
[
"ingress_port"
]
mac_move
=
digest
[
"mac_move"
]
mac_addr
=
digest
[
"src_mac"
]
old_port
=
port
^
mac_move
# Because mac_move = ingress_port ^ port
print
(
"VID: {}, MAC: {}, Port={}"
.
format
(
vid
,
mac
(
mac_addr
),
port
),
end
=
""
)
if
mac_move
!=
0
:
print
(
"(Move from port={})"
.
format
(
old_port
))
else
:
print
(
"(New)"
)
# Since we do not have access to self, we have to use
# the hardcoded value for the TTL :(
smac
.
entry_with_smac_hit
(
vid
=
vid
,
src_addr
=
mac_addr
,
port
=
port
,
is_static
=
False
,
ENTRY_TTL
=
60000
).
push
()
dmac
.
entry_with_dmac_unicast
(
vid
=
vid
,
dst_addr
=
mac_addr
,
port
=
port
).
push
()
return
0
def
vlan_create
(
self
,
vid
):
if
vid
in
self
.
vlan
:
raise
KeyError
(
"Vlan {} already exists"
.
format
(
vid
))
if
not
1
<=
vid
<=
4095
:
raise
ValueError
(
"Vlan ID {} is incorrect"
.
format
(
vid
))
bfrt
.
pre
.
node
.
entry
(
MULTICAST_NODE_ID
=
vid
,
MULTICAST_RID
=
0xFFFF
,
# See P4 code
MULTICAST_LAG_ID
=
[
],
DEV_PORT
=
[
]).
push
()
bfrt
.
pre
.
mgid
.
entry
(
MGID
=
vid
,
MULTICAST_NODE_ID
=
[
vid
],
MULTICAST_NODE_L1_XID_VALID
=
[
0
],
MULTICAST_NODE_L1_XID
=
[
0
]).
push
()
self
.
vlan
[
vid
]
=
{
"ports"
:
{}
}
def
vlan_destroy
(
self
,
vid
):
if
vid
not
in
self
.
vlan
:
raise
KeyError
(
"Vlan {} doesn't exist"
.
format
(
vid
))
vlan_mgid
=
bfrt
.
pre
.
mgid
.
get
(
vid
,
print_ents
=
False
)
# Remove the multicast group
bfrt
.
pre
.
mgid
.
delete
(
MGID
=
vid
)
# Remove the corresponding (single) node
bfrt
.
pre
.
node
.
delete
(
MULTICAST_NODE_ID
=
vid
)
# Remove all entries from egr_vlan_port
bfrt
.
batch_begin
()
try
:
if
self
.
egr_vlan_port
:
for
pipe
in
range
(
0
,
3
):
# pipe=None
for
e
in
self
.
p4
.
Egress
.
egr_vlan_port
.
get
(
pipe
=
pipe
,
regex
=
1
,
print_ents
=
False
,
vid
=
'0x[0]*{X}'
.
format
(
vid
)):
e
.
remove
()
else
:
for
pipe
in
range
(
0
,
3
):
for
p
in
range
(
0
,
72
):
self
.
p4
.
Egress
.
port_vlan_member
.
mod
(
vid
<<
7
|
p
,
0
,
pipe
=
pipe
)
self
.
p4
.
Egress
.
port_vlan_tagged
.
mod
(
vid
<<
7
|
p
,
0
,
pipe
=
pipe
)
except
:
pass
finally
:
bfrt
.
batch_end
()
del
(
self
.
vlan
[
vid
])
def
vlan_show
(
self
,
vlans
=
None
):
if
vlans
is
None
:
vlans
=
self
.
vlan
.
keys
()
print
(
"""
+------+------------------------------------------
| VLAN | Ports (Tagged or Untagged)
+------+------------------------------------------"""
)
for
vid
in
sorted
(
vlans
):
print
(
'| {:>4d} | '
.
format
(
vid
),
end
=
''
)
for
p
in
sorted
(
self
.
vlan
[
vid
][
"ports"
].
keys
()):
print
(
p
,
end
=
''
)
if
self
.
vlan
[
vid
][
"ports"
][
p
]:
print
(
"(T)"
,
end
=
''
)
else
:
print
(
"(U)"
,
end
=
''
)
print
()
print
(
"+------+------------------------------------------"
)
def
vlan_port_add
(
self
,
vid
,
dp
,
tagged
=
False
):
if
vid
not
in
self
.
vlan
:
raise
KeyError
(
"Vlan %d doesn't exist"
%
vid
)
# Update the multicast Node
vlan_node
=
bfrt
.
pre
.
node
.
get
(
MULTICAST_NODE_ID
=
vid
,
return_ents
=
True
,
print_ents
=
False
)
vlan_node
.
data
[
b
'$DEV_PORT'
].
append
(
dp
)
vlan_node
.
push
()
# Update egr_vlan_port table with the proper action
# Unfortunately push(pipe=xxx) doesn't work (DRV-3667), so we'll
# explicitly delete the entry and then add it to avoid any
# issues
(
pipe
,
port
)
=
self
.
pipeport
(
dp
)
#pipe=None
try
:
if
self
.
egr_vlan_port
:
self
.
p4
.
Egress
.
egr_vlan_port
.
delete
(
vid
=
vid
,
egress_port
=
port
,
pipe
=
pipe
)
except
:
pass
if
self
.
egr_vlan_port
:
# Using the egr_port_vlan table
if
tagged
:
self
.
p4
.
Egress
.
egr_vlan_port
.
add_with_send_tagged
(
vid
=
vid
,
egress_port
=
port
,
pipe
=
pipe
)
else
:
self
.
p4
.
Egress
.
egr_vlan_port
.
add_with_send_untagged
(
vid
=
vid
,
egress_port
=
port
,
pipe
=
pipe
)
else
:
# Using register equivalents
print
(
'port_vlan_member.mod({}/{}, 1, {}) ... '
.
format
(
vid
,
port
,
pipe
),
end
=
''
,
flush
=
True
)
self
.
p4
.
Egress
.
port_vlan_member
.
mod
(
vid
<<
7
|
port
,
1
,
pipe
=
pipe
)
print
(
'Done'
,
flush
=
True
)
if
tagged
:
self
.
p4
.
Egress
.
port_vlan_tagged
.
mod
(
vid
<<
7
|
port
,
1
,
pipe
=
pipe
)
else
:
self
.
p4
.
Egress
.
port_vlan_tagged
.
mod
(
vid
<<
7
|
port
,
0
,
pipe
=
pipe
)
# Update internal state
vlan_ports
=
self
.
vlan
[
vid
][
"ports"
]
vlan_ports
[
dp
]
=
tagged
def
vlan_port_delete
(
self
,
vid
,
dp
):
if
vid
not
in
self
.
vlan
:
raise
KeyError
(
"Vlan %d doesn't exist"
%
vid
)
vlan_node
=
bfrt
.
pre
.
node_get
(
MULTICAST_NODE_ID
=
vid
,
return_ents
=
True
,
print_ents
=
False
)
try
:
vlan_node
.
data
[
b
'$DEV_PORT'
].
remove
(
dp
)
except
:
pass
vlan_node
.
push
()
try
:
(
pipe
,
port
)
=
self
.
pipeport
(
dp
)
#pipe=None
if
self
.
egr_vlan_port
:
self
.
p4
.
Egress
.
egr_vlan_port
.
delete
(
vid
=
vid
,
egress_port
=
port
,
pipe
=
pipe
)
else
:
self
.
p4
.
Egress
.
port_vlan_member
.
mod
(
vid
<<
7
|
port
,
0
,
pipe
=
pipe
)
self
.
p4
.
Egress
.
port_vlan_tagged
.
mod
(
vid
<<
7
|
port
,
0
,
pipe
=
pipe
)
except
:
pass
try
:
del
(
self
.
vlan
[
vid
][
'ports'
][
dp
])
except
:
pass
def
port_vlan_default_set
(
self
,
dp
,
vid
):
e
=
self
.
p4
.
IngressParser
.
PORT_METADATA
.
get
(
ingress_port
=
dp
,
return_ents
=
True
,
print_ents
=
False
)
e
.
data
[
b
'port_vid'
]
=
vid
e
.
push
()
def
port_vlan_default_get
(
self
,
dp
):
return
self
.
p4
.
IngressParser
.
PORT_METADATA
.
get
(
ingress_port
=
dp
,
return_ents
=
True
,
print_ents
=
False
).
data
[
b
'port_vid'
]
def
port_vlan_default_show
(
self
,
ports
=
None
,
show_all
=
False
):
if
ports
is
None
:
ports
=
self
.
all_ports
else
:
if
not
isinstance
(
ports
,
list
):
ports
=
[
ports
]
for
dp
in
sorted
(
ports
):
try
:
default_vid
=
self
.
port_vlan_default_get
(
dp
)
if
default_vid
!=
0
or
show_all
:
print
(
"Port %3d : Default VLAN is %4d"
%
(
dp
,
default_vid
))
except
:
pass
def
l2_lookup
(
self
,
vid
,
mac_addr
):
dmac_entry
=
self
.
p4
.
Ingress
.
dmac
.
get
(
vid
=
vid
,
dst_addr
=
mac_addr
,
return_ents
=
True
,
print_ents
=
False
)
if
dmac_entry
==
-
1
:
dmac_entry
=
None
smac_entry
=
self
.
p4
.
Ingress
.
smac
.
get
(
vid
=
vid
,
src_addr
=
mac_addr
,
return_ents
=
True
,
print_ents
=
False
)
if
smac_entry
==
-
1
:
smac_entry
=
None
return
(
dmac_entry
,
smac_entry
)
def
l2_add
(
self
,
vid
,
mac_addr
,
port
,
static
=
False
,
pending
=
False
,
ttl
=
None
,
dst_drop
=
False
,
src_drop
=
False
):
# mac_addr = mac(mac_addr)
# Program DMAC
if
pending
:
self
.
p4
.
Ingress
.
dmac
.
entry_with_dmac_miss
(
vid
=
vid
,
dst_addr
=
mac_addr
).
push
()
elif
dst_drop
:
self
.
p4
.
Ingress
.
dmac
.
entry_with_dmac_drop
(
vid
=
vid
,
dst_addr
=
mac_addr
).
push
()
else
:
self
.
p4
.
Ingress
.
dmac
.
entry_with_dmac_unicast
(
vid
=
vid
,
dst_addr
=
mac_addr
,
port
=
port
).
push
()
# Program SMAC
if
src_drop
:
self
.
p4
.
Ingress
.
smac
.
entry_with_smac_drop
(
vid
=
vid
,
src_addr
=
mac_addr
).
push
()
else
:
if
static
:
ttl
=
0
elif
ttl
==
None
:
ttl
=
self
.
l2_age_ttl
self
.
p4
.
Ingress
.
smac
.
entry_with_smac_hit
(
vid
=
vid
,
src_addr
=
mac_addr
,
is_static
=
static
,
ENTRY_TTL
=
ttl
).
push
()
def
l2_del
(
self
,
vid
,
mac_addr
):
mac_addr
=
mac
(
mac_addr
)
p4
.
Ingress
.
dmac
.
delete
(
vid
=
vid
,
dst_add
=
mac_addr
)
p4
.
Ingress
.
dmac
.
delete
(
vid
=
vid
,
src_add
=
mac_addr
)
def
l2_print
(
self
,
dmac_entry
,
smac_entry
):
vid
=
None
mac_addr
=
None
port
=
" "
pending
=
" "
valid
=
" "
static
=
" "
dst_drop
=
" "
src_drop
=
" "
static
=
" "
ttl
=
" "
dmac_eh_s
=
" "
smac_eh_s
=
" "
if
dmac_entry
is
not
None
:
valid
=
"Y"
vid
=
dmac_entry
.
key
[
b
'meta.vid'
]
mac_addr
=
mac
(
dmac_entry
.
key
[
b
'hdr.ethernet.dst_addr'
])
if
dmac_entry
.
action
.
endswith
(
"dmac_drop"
):
dst_drop
=
"Y"
else
:
dst_drop
=
" "
if
dmac_entry
.
action
.
endswith
(
"dmac_unicast"
):
port
=
dmac_entry
.
data
[
b
'port'
]
if
smac_entry
is
not
None
:
valid
=
"Y"
ttl
=
int
(
smac_entry
.
data
[
b
'$ENTRY_TTL'
])
if
ttl
>
1000
or
ttl
==
0
:
ttl
=
"%6d"
%
(
ttl
/
1000
)
else
:
ttl
=
" 0.%03d"
%
ttl
if
dmac_entry
is
None
:
vid
=
smac_entry
.
key
[
b
'meta.vid'
]
mac_addr
=
mac
(
smac_entry
.
key
[
b
'hdr.ethernet.src_addr'
])
if
smac_entry
.
action
.
endswith
(
"smac_hit"
):
if
(
dmac_entry
is
None
or
dmac_entry
.
action
.
endswith
(
"dmac_miss"
)):
pending
=
"Y"
port
=
smac_entry
.
data
[
b
'port'
]
if
smac_entry
.
data
[
b
'is_static'
]:
static
=
"Y"
elif
smac_entry
.
action
.
name
.
endswith
(
"smac_drop"
):
src_drop
=
"Y"
if
dmac_entry
or
smac_entry
:
print
(
"| %4d | %s | %3d | %s %s %s | %s %s | %s |"
%
(
vid
,
mac_addr
,
port
,
valid
,
pending
,
static
,
src_drop
,
dst_drop
,
ttl
))
def
l2_show
(
self
,
from_hw
=
True
):
dmac_entries
=
{}
try
:
for
e
in
self
.
p4
.
Ingress
.
dmac
.
get
(
regex
=
1
,
from_hw
=
from_hw
,
print_ents
=
False
,
return_ents
=
True
):
dmac_entries
[
(
e
.
key
[
b
'meta.vid'
],
e
.
key
[
b
'hdr.ethernet.dst_addr'
])]
=
e
except
:
pass
smac_entries
=
{}
try
:
for
e
in
self
.
p4
.
Ingress
.
smac
.
get
(
regex
=
1
,
from_hw
=
from_hw
,
print_ents
=
False
,
return_ents
=
True
):
smac_entries
[
(
e
.
key
[
b
'meta.vid'
],
e
.
key
[
b
'hdr.ethernet.src_addr'
])]
=
e
except
:
pass
print
(
"""
+------+-------------------+-----+-------+------+--------+
| VLAN | MAC ADDR |PORT | Flags | DROP | TTL(s) |
| | | | V P S | S D | |
+------+-------------------+-----+-------+------+--------+"""
)
for
(
vid
,
mac_addr
)
in
dmac_entries
:
dmac_entry
=
dmac_entries
[(
vid
,
mac_addr
)]
try
:
smac_entry
=
smac_entries
[(
vid
,
mac_addr
)]
except
:
smac_entry
=
None
self
.
l2_print
(
dmac_entry
,
smac_entry
)
if
smac_entry
is
not
None
:
del
(
smac_entries
[(
vid
,
mac_addr
)])
for
smac_entry
in
smac_entries
.
values
():
self
.
l2_print
(
None
,
smac_entry
)
print
(
"""+------+-------------------+-----+-------+------+--------+"""
)
# Sample setup
sl2
=
l2_switch
(
default_ttl
=
10000
)
sl2
.
setup
()
sl2
.
vlan_create
(
1
)
sl2
.
vlan_port_add
(
1
,
0
)
sl2
.
vlan_port_add
(
1
,
1
)
sl2
.
vlan_port_add
(
1
,
2
)
sl2
.
vlan_port_add
(
1
,
3
)
sl2
.
port_vlan_default_set
(
0
,
1
)
sl2
.
port_vlan_default_set
(
1
,
1
)
sl2
.
port_vlan_default_set
(
2
,
1
)
sl2
.
port_vlan_default_set
(
3
,
1
)
bfrt
.
complete_operations
()
sl2
.
vlan_show
()
sl2
.
port_vlan_default_show
()
sl2
.
l2_show
()
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
.
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment