"macapp/package.json" did not exist on "e88dd25babdf8c09e0010aea8ba754df7eb2191d"
Commit edb4215d authored by Jialin Li's avatar Jialin Li
Browse files

tofino: add tofino switch p4 code; add tofino net to tcp_single and udp_single experiments

parent a8fd899d
......@@ -34,7 +34,7 @@ import simbricks.nodeconfig as node
kinds_of_host = ['qemu']
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']
experiments = []
......@@ -49,6 +49,8 @@ for n in kinds_of_net:
net_class = sim.NS3DumbbellNet
if n == 'bridge':
net_class = sim.NS3BridgeNet
if n == 'tofino':
net_class = sim.TofinoNet
# set nic sim
......@@ -56,28 +58,28 @@ for n in kinds_of_net:
net = net_class()
e = exp.Experiment('qemu-' + c + '-' + n + '-' + 'TCPs')
e.add_network(net)
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)
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)
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)
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)
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)
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)
clients[0].wait = True
clients[0].node_config.app.server_ip = servers[0].node_config.ip
......
......@@ -34,7 +34,7 @@ import simbricks.nodeconfig as node
kinds_of_host = ['qemu']
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']
rate = '200m'
......@@ -51,6 +51,8 @@ for n in kinds_of_net:
net_class = sim.NS3DumbbellNet
if n == 'bridge':
net_class = sim.NS3BridgeNet
if n == 'tofino':
net_class = sim.TofinoNet
# set nic sim
......@@ -58,28 +60,28 @@ for n in kinds_of_net:
net = net_class()
e = exp.Experiment('qemu-' + c + '-' + n + '-' + 'UDPs')
e.add_network(net)
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)
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)
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)
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)
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)
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)
clients[0].wait = True
clients[0].node_config.app.server_ip = servers[0].node_config.ip
clients[0].node_config.app.rate = rate
......
/* -*- 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;
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()
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment