├── README.md ├── libs └── mgr.py ├── prog.p4 ├── prog.py └── tofino_8_19_20.yaml /README.md: -------------------------------------------------------------------------------- 1 | # tofino_p4_simple_example 2 | 3 | This is a simple P4 program for the Tofino meant as a "hello world" type example. This readme has basic instructions for compiling and running it. 4 | 5 | 6 | ### Contents 7 | prog.p4 -- a p4 program that selects an output port for each packet based on its input port. By default, the program reflects the packet out of the same port that it came in on. 8 | prog.py -- a python control script that uses grpc and thrift to configure the switch. Runs on the switch cpu. 9 | 10 | ### Setup 11 | 12 | 1. Install the SDE. You can use the configuration file in tofino_8_19_20.yaml. Put ``tofino_8_19_20.yaml`` into ``p4studio_build/profiles`` and run: 13 | 14 | ``` 15 | ./p4studio_build.py -up profiles/tofino_8_19_20.yaml 16 | ``` 17 | 18 | **Note: This was tested with SDE 9.2.0, but should be the same up to 9.5.1. I am not sure after that.** 19 | 20 | 2. Set the $SDE and $SDE_INSTALL variables in your p4-studio SDE installation. 21 | 22 | 3. Make sure that the tofino driver kernel module is loaded. 23 | 24 | ``` 25 | jsonch@localhost:~$ lsmod | grep bf_kpkt 26 | bf_kpkt 16338944 0 27 | ``` 28 | If you don't see any output, run this command to load the kernel module: 29 | 30 | ``` 31 | sudo $SDE/install/bin/bf_kpkt_mod_load $SDE/install 32 | ``` 33 | 34 | ### Usage 35 | 36 | 1. **compiling the p4** 37 | 38 | run ``$SDE/p4-build.sh prog.p4`` to compile the program to $SDE/build/p4-build/prog. 39 | 40 | ``` 41 | jsonch@localhost:~/projects/reflector$ $SDE/p4_build.sh prog.p4 42 | Using SDE /home/jsonch/bf_sde/bf-sde-9.2.0 43 | Using SDE_INSTALL /home/jsonch/bf_sde/bf-sde-9.2.0/install 44 | Your PATH contains $SDE_INSTALL/bin. Good 45 | Found bf-sde-9.2.0 in $SDE 46 | OS Name: "Open Network Linux OS ONL-HEAD, 2020-03-16.19 47 | This system has 8GB of RAM and 8 CPU(s) 48 | Parallelization: Recommended: -j4 Actual: -j4 49 | Compiling for p4_16/tna 50 | P4 compiler: /home/jsonch/bf_sde/bf-sde-9.2.0/install/bin/p4c 51 | P4 compiler version: 9.2.0 (SHA: 639d9ec) (p4c-based) 52 | Using Build Directory /home/jsonch/bf_sde/bf-sde-9.2.0/build/p4-build/prog 53 | Building prog in `build_dir prog` CLEAR CONFIGURE MAKE INSTALL ... DONE 54 | ``` 55 | 56 | 2. **starting the data plane** 57 | 58 | Run ``jsonch@localhost:~/projects/reflector$ $SDE/run_switchd.sh -p prog`` to start the program from its build directory. This will load the program into the ASIC and eventually bring you to the bfshell cli: 59 | 60 | ``` 61 | jsonch@localhost:~/projects/reflector$ $SDE/run_switchd.sh -p prog 62 | Using SDE /home/jsonch/bf_sde/bf-sde-9.2.0 63 | Using SDE_INSTALL /home/jsonch/bf_sde/bf-sde-9.2.0/install 64 | Setting up DMA Memory Pool 65 | Using TARGET_CONFIG_FILE /home/jsonch/bf_sde/bf-sde-9.2.0/install/share/p4/targets/tofino/prog.conf 66 | Using PATH /home/jsonch/bf_sde/bf-sde-9.2.0/install/bin:/home/jsonch/bf_sde/bf-sde-9.2.0/install/bin:/usr/local/bin:/usr/bin:/bin:/usr/local/games:/usr/games:/lib/platform-config/current/onl/bin:/lib/platform-config/current/onl/sbin:/lib/platform-config/current/onl/lib/bin:/lib/platform-config/current/onl/lib/sbin 67 | Using LD_LIBRARY_PATH /usr/local/lib:/home/jsonch/bf_sde/bf-sde-9.2.0/install/lib: 68 | bf_sysfs_fname /sys/class/bf/bf0/device/dev_add 69 | kernel mode packet driver present, forcing kernel_pkt option! 70 | Install dir: /home/jsonch/bf_sde/bf-sde-9.2.0/install (0x55f13d552560) 71 | bf_switchd: system services initialized 72 | bf_switchd: loading conf_file /home/jsonch/bf_sde/bf-sde-9.2.0/install/share/p4/targets/tofino/prog.conf... 73 | bf_switchd: processing device configuration... 74 | Configuration for dev_id 0 75 | Family : tofino 76 | pci_sysfs_str : /sys/devices/pci0000:00/0000:00:03.0/0000:05:00.0 77 | pci_domain : 0 78 | pci_bus : 5 79 | pci_fn : 0 80 | pci_dev : 0 81 | pci_int_mode : 1 82 | sbus_master_fw: /home/jsonch/bf_sde/bf-sde-9.2.0/install/ 83 | pcie_fw : /home/jsonch/bf_sde/bf-sde-9.2.0/install/ 84 | serdes_fw : /home/jsonch/bf_sde/bf-sde-9.2.0/install/ 85 | sds_fw_path : /home/jsonch/bf_sde/bf-sde-9.2.0/install/ 86 | microp_fw_path: 87 | bf_switchd: processing P4 configuration... 88 | P4 profile for dev_id 0 89 | num P4 programs 1 90 | p4_name: prog 91 | p4_pipeline_name: pipe 92 | libpd: 93 | libpdthrift: 94 | context: /home/jsonch/bf_sde/bf-sde-9.2.0/install/share/tofinopd/prog/pipe/context.json 95 | config: /home/jsonch/bf_sde/bf-sde-9.2.0/install/share/tofinopd/prog/pipe/tofino.bin 96 | Pipes in scope [0 1 2 3 ] 97 | diag: 98 | accton diag: 99 | Agent[0]: /home/jsonch/bf_sde/bf-sde-9.2.0/install/lib/libpltfm_mgr.so 100 | non_default_port_ppgs: 0 101 | SAI default initialize: 1 102 | bf_switchd: library /home/jsonch/bf_sde/bf-sde-9.2.0/install/lib/libpltfm_mgr.so loaded 103 | bf_switchd: agent[0] initialized 104 | Tcl server started.. 105 | Tcl server: listen socket created 106 | Tcl server: bind done on port 8008, listening... 107 | Tcl server: waiting for incoming connections... 108 | Health monitor started 109 | Operational mode set to ASIC 110 | Initialized the device types using platforms infra API 111 | ASIC detected at PCI /sys/class/bf/bf0/device 112 | ASIC pci device id is 16 113 | Skipped pkt-mgr init 114 | Starting PD-API RPC server on port 9090 115 | bf_switchd: drivers initialized 116 | detecting.. IOMMU not enabled on the platform 117 | - 118 | bf_switchd: dev_id 0 initialized 119 | 120 | bf_switchd: initialized 1 devices 121 | bf_switchd: Credo python server thread initialized..credo_python_intf: listen socket created 122 | Adding Thrift service for bf-platforms to server 123 | credo_python_intf: bind done on port 9001, listening... 124 | credo_python_intf: listening for incoming connections... 125 | bf_switchd: thrift initialized for agent : 0 126 | bf_switchd: spawning cli server thread 127 | bf_switchd: spawning driver shell 128 | bf_switchd: server started - listening on port 9999 129 | bfruntime gRPC server started on 0.0.0.0:50052 130 | 131 | ******************************************** 132 | * WARNING: Authorised Access Only * 133 | ******************************************** 134 | 135 | bfshell> 136 | ``` 137 | 138 | 3. **Bringing up ports and testing the program** 139 | 140 | The first thing you probably want to do is bring up some ports. This can be done in the control script, but you can also do it manually from bfshell > ucli > pm: 141 | 142 | 143 | ``` 144 | bfshell> ucli 145 | Starting UCLI from bf-shell 146 | Cannot read termcap database; 147 | using dumb terminal settings. 148 | bf-sde> pm 149 | bf-sde.pm> ? 150 | # ... list of commands (not shown) 151 | ``` 152 | pm has a bunch of commands. The most important ones are: 153 | 154 | ``show [-a]`` -- show the currently configured ports. If run with -a, show all ports. 155 | 156 | ``` 157 | bf-sde.pm> show -a 158 | -----+----+---+----+-------+----+---+---+---+--------+----------------+----------------+- 159 | PORT |MAC |D_P|P/PT|SPEED |FEC |RDY|ADM|OPR|LPBK |FRAMES RX |FRAMES TX |E 160 | -----+----+---+----+-------+----+---+---+---+--------+----------------+----------------+- 161 | 1/0 |23/0|128|3/ 0|-------|----|YES|---|---|--------|----------------|----------------|- 162 | 1/1 |23/1|129|3/ 1|-------|----|YES|---|---|--------|----------------|----------------|- 163 | ``` 164 | Important columns are: 165 | PORT -- the front panel port name. 166 | D_P -- the port's id from inside of P4. 167 | RDY -- is a cable detected? 168 | ADM -- is the port configured? 169 | OPR -- is the port down (DWN) or up (UP)? 170 | Most of the other columns are self explanatory. LPBK is whether the port is configured as a loopback port. 171 | 172 | To configure a port, use port-add. To bring a configured port up, use port-enb. 173 | 174 | `` port-add `` 175 | 176 | ```` is the port's front panel id. 100G ports can be split into 4 separate 25G or 10G ports, so each port id has two components: the physical port name and the id of the port's channel: ``/`` 177 | 178 | So, for example, `1/0` and `2/0` are the first two ports in 100G mode. If you configure port 1 in 4x25G mode, you can also use ports `1/1`, `1/2`, and `1/3`. 179 | 180 | You configure each port individually. So, to bring up 1/0 - 1/3 in 10G mode with no FEC, use: 181 | 182 | ``` 183 | bf-sde.pm> port-add 1/0 10G NONE 184 | bf-sde.pm> port-add 1/1 10G NONE 185 | bf-sde.pm> port-add 1/2 10G NONE 186 | bf-sde.pm> port-add 1/3 10G NONE 187 | bf-sde.pm> show 188 | -----+----+---+----+-------+----+---+---+---+--------+----------------+----------------+- 189 | PORT |MAC |D_P|P/PT|SPEED |FEC |RDY|ADM|OPR|LPBK |FRAMES RX |FRAMES TX |E 190 | -----+----+---+----+-------+----+---+---+---+--------+----------------+----------------+- 191 | 1/0 |23/0|128|3/ 0|10G |NONE|YES|DIS|DWN| NONE | 0| 0| 192 | 1/1 |23/1|129|3/ 1|10G |NONE|YES|DIS|DWN| NONE | 0| 0| 193 | 1/2 |23/2|130|3/ 2|10G |NONE|YES|DIS|DWN| NONE | 0| 0| 194 | 1/3 |23/3|131|3/ 3|10G |NONE|YES|DIS|DWN| NONE | 0| 0| 195 | ``` 196 | 197 | Next, to bring up a configured port, say ``1/0``, use ``port-enb``: 198 | 199 | ``` 200 | bf-sde.pm> port-enb 1/0 201 | bf-sde.pm> show 202 | -----+----+---+----+-------+----+---+---+---+--------+----------------+----------------+- 203 | PORT |MAC |D_P|P/PT|SPEED |FEC |RDY|ADM|OPR|LPBK |FRAMES RX |FRAMES TX |E 204 | -----+----+---+----+-------+----+---+---+---+--------+----------------+----------------+- 205 | 1/0 |23/0|128|3/ 0|10G |NONE|YES|ENB|DWN| NONE | 0| 0| 206 | 1/1 |23/1|129|3/ 1|10G |NONE|YES|DIS|DWN| NONE | 0| 0| 207 | ``` 208 | 209 | At this point, once the ethernet autonegotiation completes the OPR column for an enabled port should change to "UP". 210 | 211 | Once you have a server connected to a port that's UP, you can test the program: send a packet into the switch from the server, the P4 program should send a copy of the exact same packet back. You should see the frames RX and frames TX counters increase in the CLI. 212 | 213 | 214 | 3. **A simple python control script** 215 | 216 | Instead of configuring the switch manually, you can run the control script ``prog.py`` to configure the switch via grpc and thrift interfaces. These interfaces can also be used to add rules to tables, poll counters, and configure most of the other switch setings. 217 | 218 | After starting the data plane, in a separate window, run: 219 | ``python2 prog.py`` 220 | ``` 221 | 222 | jsonch@localhost:~/projects/reflector$ python2 prog.py 223 | adding path: /home/jsonch/bf_sde/bf-sde-9.2.0/install/lib/python2.7/site-packages/tofino 224 | adding path: /home/jsonch/bf_sde/bf-sde-9.2.0/install/lib/python2.7/site-packages 225 | adding path: /home/jsonch/bf_sde/bf-sde-9.2.0/install/lib/python2.7/site-packages/p4testutils 226 | adding path: /home/jsonch/bf_sde/bf-sde-9.2.0/install/lib/python2.7/site-packages/bf-ptf 227 | WARNING:root:VXLAN support not found in Scapy 228 | WARNING:root:ERSPAN support not found in Scapy 229 | WARNING:root:GENEVE support not found in Scapy 230 | WARNING:root:NVGRE support not found in Scapy 231 | controller running (idle) -- press ctrl+c to exit 232 | ``` 233 | 234 | The default ``prog.py`` just sets up connections to the switch's drivers and then idles until ctrl+c. It uses ``libs/mgr.py`` to make the connections. ``mgr.py`` also has some helper functions, like port_up and addExactEntry: 235 | 236 | ``` 237 | def example_ports_up(): 238 | # bring up port with dpid 128 at 100G. 239 | m.port_up(128, pal_port_speed_t.BF_SPEED_10G, pal_fec_type_t.BF_FEC_TYP_NONE) 240 | 241 | def example_table_add(): 242 | m.addExactEntry("tiWire", ["ig_intr_md.ingress_port"], [128], "aiOut", {"out_port":128}) 243 | ``` 244 | 245 | ``mgr.py`` doesn't interact with the grpc interfaces directly, it uses the python libraries included with the SDE for testing tofino programs. The python libraries are in $SDE_INSTALL/lib/python2.7/site-packages. You can get a better sense of how the grpc interface works by digging into those libraries. 246 | 247 | 248 | 249 | -------------------------------------------------------------------------------- /libs/mgr.py: -------------------------------------------------------------------------------- 1 | # =================================================================== 2 | # = Helper functions for simple python control. = 3 | # =================================================================== 4 | 5 | import time, sys, os, re, importlib, binascii, json, logging 6 | 7 | 8 | # paths to tofino python libs 9 | pylib_paths = ["/lib/python2.7/site-packages/tofino", "/lib/python2.7/site-packages", "/lib/python2.7/site-packages/p4testutils", "/lib/python2.7/site-packages/bf-ptf"] 10 | pylib_paths = [os.getenv('SDE_INSTALL')+p for p in pylib_paths] 11 | for p in pylib_paths: 12 | print ("adding path: %s"%str(p)) 13 | sys.path.append(p) 14 | 15 | from thrift.transport import TSocket, TTransport 16 | from thrift.protocol import TBinaryProtocol, TMultiplexedProtocol 17 | from res_pd_rpc.ttypes import * # DevTarget_t 18 | from ptf.thriftutils import * # hex_to_i16 19 | from mirror_pd_rpc.ttypes import * 20 | from devport_mgr_pd_rpc.ttypes import * 21 | from pal_rpc.ttypes import * # pal_port_speed_t, pal_fec_type_t 22 | 23 | import pal_rpc.pal as pal_i 24 | import conn_mgr_pd_rpc.conn_mgr as conn_mgr_client_module 25 | import mc_pd_rpc.mc as mc_client_module 26 | 27 | from bfruntime_client_base_tests import BfRuntimeTest 28 | import bfrt_grpc.client as gc 29 | # disable logging from gc 30 | gc.logger.setLevel(logging.CRITICAL) 31 | 32 | class Manager(object): 33 | 34 | def __init__(self, p4Name=None): 35 | self.p4Name = p4Name 36 | self.connect() 37 | 38 | def connect(self): 39 | self.grpc_connect() 40 | self.fixed_function_connect() 41 | 42 | def grpc_connect(self): 43 | if (self.p4Name != None): 44 | grpc_addr = 'localhost:50052' 45 | client_id = 0 46 | print ("setting up gRPC client interface...") 47 | self.interface = gc.ClientInterface(grpc_addr, 48 | client_id = client_id, device_id=0, 49 | notifications=None) 50 | self.interface.bind_pipeline_config(self.p4Name) 51 | 52 | def fixed_function_connect(self): 53 | # master connection 54 | self.transport = TTransport.TBufferedTransport(TSocket.TSocket('localhost', 9090)) 55 | self.transport.open() 56 | bprotocol = TBinaryProtocol.TBinaryProtocol(self.transport) 57 | 58 | # module connections 59 | self.pal = pal_i.Client(TMultiplexedProtocol.TMultiplexedProtocol(bprotocol, "pal")) 60 | self.conn_mgr = conn_mgr_client_module.Client(TMultiplexedProtocol.TMultiplexedProtocol(bprotocol, "conn_mgr")) 61 | self.mc = mc_client_module.Client(TMultiplexedProtocol.TMultiplexedProtocol(bprotocol, "mc")) 62 | 63 | # session and device handlers 64 | self.sess_hdl = self.conn_mgr.client_init() 65 | self.mc_sess_hdl = self.mc.mc_create_session() 66 | self.dev_tgt = DevTarget_t(0, hex_to_i16(0xFFFF)) 67 | 68 | def disconnect(self): 69 | # grpc 70 | if(self.p4Name != None): 71 | self.interface._tear_down_stream() 72 | 73 | # fixed function 74 | self.mc.mc_destroy_session(self.mc_sess_hdl) 75 | self.conn_mgr.complete_operations(self.sess_hdl) 76 | self.conn_mgr.client_cleanup(self.sess_hdl) 77 | self.transport.close() 78 | print ("mgr.py disconnect complete.") 79 | 80 | def port_up(self, dpid, rate, fec_type): 81 | self.pal.pal_port_add(0, dpid, rate, fec_type) 82 | self.pal.pal_port_enable(0, dpid) 83 | 84 | def add_mc_group(self, mc_gid, dpids): 85 | lag_map = set_port_or_lag_bitmap(256, []) 86 | flood_ports = [int(p) for p in dpids] 87 | print("mc gid: %s; ports: %s"%(mc_gid, str(dpids))) 88 | port_map = set_port_or_lag_bitmap(288, list(flood_ports)) 89 | mc_grp_hdl = self.mc.mc_mgrp_create(self.mc_sess_hdl, self.dev_tgt.dev_id, hex_to_i16(mc_gid)) 90 | mc_node_hdl = self.mc.mc_node_create(self.mc_sess_hdl, self.dev_tgt.dev_id, 0, port_map, lag_map) 91 | self.mc.mc_associate_node(self.mc_sess_hdl, self.dev_tgt.dev_id, mc_grp_hdl, mc_node_hdl, 0, 0) 92 | # mc.mc_mgrp_destroy(mc_sess_hdl, dev_tgt.dev_id, hex_to_i32(mc_grp_hdl)) 93 | self.conn_mgr.complete_operations(self.sess_hdl) 94 | self.mc.mc_complete_operations(self.mc_sess_hdl) 95 | return (mc_node_hdl, mc_grp_hdl) 96 | 97 | def add_multinode_mc_group(self, mc_gid, dpids_and_rids): 98 | # create the mc group 99 | mc_grp_hdl = self.mc.mc_mgrp_create(self.mc_sess_hdl, self.dev_tgt.dev_id, hex_to_i16(mc_gid)) 100 | # add one node for each (dpid, rid) pair 101 | lag_map = set_port_or_lag_bitmap(256, []) 102 | mc_node_hdls = [] 103 | for (dpid, rid) in dpids_and_rids: 104 | port_map = set_port_or_lag_bitmap(288, [dpid]) 105 | mc_node_hdl = self.mc.mc_node_create(self.mc_sess_hdl, self.dev_tgt.dev_id, rid, port_map, lag_map) 106 | self.mc.mc_associate_node(self.mc_sess_hdl, self.dev_tgt.dev_id, mc_grp_hdl, mc_node_hdl, 0, 0) 107 | mc_node_hdls.append(mc_node_hdl) 108 | return mc_node_hdls, mc_grp_hdl 109 | 110 | 111 | def delete_mc_group(self, mc_grp_hdl): 112 | self.mc.mc_mgrp_destroy(self.mc_sess_hdl, self.dev_tgt.dev_id, hex_to_i32(mc_grp_hdl)) 113 | 114 | def addExactEntry(self, tableName, fieldNames, fieldVals, actionName, actionArgs={}): 115 | print ("adding exact entry: {0}[{1}=={2}] --> {3}({4})".format(tableName, str(fieldNames), str(fieldVals), actionName, actionArgs)) 116 | bfrt_info = self.interface.bfrt_info_get(self.p4Name) 117 | target = gc.Target(device_id=0, pipe_id=0xffff) 118 | table = bfrt_info.table_get(tableName) 119 | key_list = [table.make_key([gc.KeyTuple(fieldName, fieldVal)]) for (fieldName, fieldVal) in zip(fieldNames, fieldVals)] 120 | data_list = [] 121 | for argName, argVal in actionArgs.items(): 122 | data_list.append(table.make_data([gc.DataTuple(argName, argVal)], actionName)) 123 | table.entry_add(target,key_list, data_list) 124 | 125 | 126 | # helpers 127 | def port_to_pipe(port): 128 | return port >> 7 129 | def port_to_pipe_local_id(port): 130 | return port & 0x7F 131 | def port_to_bit_idx(port): 132 | pipe = port_to_pipe(port) 133 | index = port_to_pipe_local_id(port) 134 | return 72 * pipe + index 135 | def set_port_or_lag_bitmap(bit_map_size, indicies): 136 | bit_map = [0] * ((bit_map_size+7)/8) 137 | for i in indicies: 138 | index = port_to_bit_idx(i) 139 | bit_map[index/8] = (bit_map[index/8] | (1 << (index%8))) & 0xFF 140 | return bytes_to_string(bit_map) -------------------------------------------------------------------------------- /prog.p4: -------------------------------------------------------------------------------- 1 | /* This is a simple wire example that forwards packets 2 | out of the same port that they came in on. */ 3 | 4 | #include 5 | #include 6 | 7 | 8 | /*============================================= 9 | = Headers and metadata. = 10 | =============================================*/ 11 | typedef bit<48> mac_addr_t; 12 | header ethernet_h { 13 | mac_addr_t dst_addr; 14 | mac_addr_t src_addr; 15 | bit<16> ether_type; 16 | } 17 | 18 | typedef bit<32> ipv4_addr_t; 19 | header ipv4_h { 20 | bit<4> version; 21 | bit<4> ihl; 22 | bit<8> tos; 23 | bit<16> total_len; 24 | bit<16> identification; 25 | bit<3> flags; 26 | bit<13> frag_offset; 27 | bit<8> ttl; 28 | bit<8> protocol; 29 | bit<16> hdr_checksum; 30 | ipv4_addr_t src_addr; 31 | ipv4_addr_t dst_addr; 32 | } 33 | 34 | // Global headers and metadata 35 | struct header_t { 36 | ethernet_h ethernet; 37 | ipv4_h ip; 38 | } 39 | struct metadata_t { 40 | // Nothing here. 41 | } 42 | 43 | /*=============================== 44 | = Parsing = 45 | ===============================*/ 46 | // Parser for tofino-specific metadata. 47 | parser TofinoIngressParser( 48 | packet_in pkt, 49 | out ingress_intrinsic_metadata_t ig_intr_md, 50 | out header_t hdr, 51 | out metadata_t md) { 52 | state start { 53 | pkt.extract(ig_intr_md); 54 | transition select(ig_intr_md.resubmit_flag) { 55 | 1 : parse_resubmit; 56 | 0 : parse_port_metadata; 57 | } 58 | } 59 | state parse_resubmit { 60 | // Parse resubmitted packet here. 61 | transition reject; 62 | } 63 | state parse_port_metadata { 64 | pkt.advance(64); // skip this. 65 | transition accept; 66 | } 67 | } 68 | 69 | 70 | const bit<16> ETHERTYPE_IPV4 = 16w0x0800; 71 | parser EthIpParser(packet_in pkt, out header_t hdr, out metadata_t md){ 72 | state start { 73 | pkt.extract(hdr.ethernet); 74 | transition select(hdr.ethernet.ether_type) { 75 | ETHERTYPE_IPV4 : parse_ip; 76 | default : accept; 77 | } 78 | } 79 | state parse_ip { 80 | pkt.extract(hdr.ip); 81 | transition accept; 82 | } 83 | } 84 | 85 | 86 | parser TofinoEgressParser( 87 | packet_in pkt, 88 | out egress_intrinsic_metadata_t eg_intr_md) { 89 | state start { 90 | pkt.extract(eg_intr_md); 91 | transition accept; 92 | } 93 | } 94 | 95 | /*======================================== 96 | = Ingress parsing = 97 | ========================================*/ 98 | 99 | parser IngressParser( 100 | packet_in pkt, 101 | out header_t hdr, 102 | out metadata_t md, 103 | out ingress_intrinsic_metadata_t ig_intr_md) 104 | { 105 | state start { 106 | TofinoIngressParser.apply(pkt, ig_intr_md, hdr, md); 107 | EthIpParser.apply(pkt, hdr, md); 108 | transition accept; 109 | } 110 | } 111 | 112 | 113 | control CiL2Fwd( 114 | in ingress_intrinsic_metadata_t ig_intr_md, 115 | inout ingress_intrinsic_metadata_for_tm_t ig_tm_md) { 116 | action aiOut(bit<9> out_port) { 117 | ig_tm_md.ucast_egress_port = out_port; 118 | } 119 | action aiNoop() {} 120 | action aiReflect() { 121 | ig_tm_md.ucast_egress_port = ig_intr_md.ingress_port; 122 | } 123 | /* This table just maps each packet to an output 124 | port based on its input port. */ 125 | table tiWire { 126 | key = { 127 | ig_intr_md.ingress_port : exact; 128 | } 129 | actions = { 130 | aiOut; 131 | aiNoop; 132 | aiReflect; 133 | } 134 | const default_action = aiReflect(); 135 | } 136 | apply { 137 | tiWire.apply(); 138 | } 139 | } 140 | 141 | /*=========================================== 142 | = ingress match-action = 143 | ===========================================*/ 144 | control Ingress( 145 | inout header_t hdr, 146 | inout metadata_t md, 147 | in ingress_intrinsic_metadata_t ig_intr_md, 148 | in ingress_intrinsic_metadata_from_parser_t ig_prsr_md, 149 | inout ingress_intrinsic_metadata_for_deparser_t ig_dprsr_md, 150 | inout ingress_intrinsic_metadata_for_tm_t ig_tm_md) { 151 | 152 | CiL2Fwd() ciL2Fwd; 153 | 154 | apply { 155 | ciL2Fwd.apply(ig_intr_md, ig_tm_md); 156 | } 157 | } 158 | 159 | control IngressDeparser( 160 | packet_out pkt, 161 | inout header_t hdr, 162 | in metadata_t md, 163 | in ingress_intrinsic_metadata_for_deparser_t ig_dprsr_md) { 164 | apply { 165 | pkt.emit(hdr); 166 | } 167 | } 168 | 169 | /*====================================== 170 | = Egress parsing = 171 | ======================================*/ 172 | parser EgressParser( 173 | packet_in pkt, 174 | out header_t hdr, 175 | out metadata_t eg_md, 176 | out egress_intrinsic_metadata_t eg_intr_md) { 177 | TofinoEgressParser() tofino_parser; 178 | EthIpParser() eth_ip_parser; 179 | state start { 180 | tofino_parser.apply(pkt, eg_intr_md); 181 | transition parse_packet; 182 | } 183 | state parse_packet { 184 | eth_ip_parser.apply(pkt, hdr, eg_md); 185 | transition accept; 186 | } 187 | } 188 | 189 | /*========================================= 190 | = Egress match-action = 191 | =========================================*/ 192 | control Egress( 193 | inout header_t hdr, 194 | inout metadata_t eg_mg, 195 | in egress_intrinsic_metadata_t eg_intr_md, 196 | in egress_intrinsic_metadata_from_parser_t eg_prsr_md, 197 | inout egress_intrinsic_metadata_for_deparser_t eg_dprsr_md, 198 | inout egress_intrinsic_metadata_for_output_port_t eg_oport_md){ 199 | 200 | apply { 201 | } 202 | } 203 | 204 | 205 | control EgressDeparser( 206 | packet_out pkt, 207 | inout header_t hdr, 208 | in metadata_t eg_md, 209 | in egress_intrinsic_metadata_for_deparser_t eg_dprsr_md) { 210 | apply { 211 | pkt.emit(hdr); 212 | } 213 | } 214 | /*============================================== 215 | = The switch's pipeline = 216 | ==============================================*/ 217 | Pipeline( 218 | IngressParser(), Ingress(), IngressDeparser(), 219 | EgressParser(), Egress(), EgressDeparser()) pipe; 220 | 221 | Switch(pipe) main; -------------------------------------------------------------------------------- /prog.py: -------------------------------------------------------------------------------- 1 | 2 | import sys, os, time 3 | from signal import signal, SIGINT 4 | from sys import exit 5 | sys.path.append(os.path.dirname(os.path.realpath(__file__))+"/libs") 6 | from mgr import * 7 | from pal_rpc.ttypes import * # pal_port_speed_t, pal_fec_type_t 8 | 9 | PROGRAM_NAME="prog" 10 | m = Manager(PROGRAM_NAME) 11 | # m.add_multinode_mc_group(1066, [(196, 1)]) 12 | 13 | def main(): 14 | signal(SIGINT, handler) 15 | print ("controller running (idle) -- press ctrl+c to exit") 16 | example_port_up() 17 | example_table_add() 18 | while True: 19 | time.sleep(1) 20 | 21 | def example_port_up(): 22 | # bring up port with dpid 128 at 100G. 23 | # constants are defined in pal_rpc.ttypes 24 | m.port_up(128, pal_port_speed_t.BF_SPEED_10G, pal_fec_type_t.BF_FEC_TYP_NONE) 25 | 26 | def example_table_add(): 27 | m.addExactEntry("tiWire", ["ig_intr_md.ingress_port"], [128], "aiOut", {"out_port":128}) 28 | 29 | 30 | def handler(signal_received, frame): 31 | # Handle any cleanup here 32 | print('Exiting..') 33 | m.disconnect() 34 | exit(0) 35 | 36 | if __name__ == '__main__': 37 | main() 38 | -------------------------------------------------------------------------------- /tofino_8_19_20.yaml: -------------------------------------------------------------------------------- 1 | # SDE_VERSION : 9.2.0 2 | tofino_8_19_20: 3 | global_configure_options: '' 4 | package_dependencies: 5 | - thrift 6 | - grpc 7 | packages: 8 | - bf-syslibs: 9 | - bf_syslibs_configure_options: '' 10 | - bf-utils: 11 | - bf_utils_configure_options: '' 12 | - bf-drivers: 13 | - bf_drivers_configure_options: '' 14 | - bf-runtime 15 | - p4-runtime 16 | - pi 17 | - bf-diags: 18 | - bf_diags_configure_options: '' 19 | - bf-platforms: 20 | - bsp_path: /home/jsonch/bf_sde/bf-reference-bsp-9.2.0/ 21 | - bf_platforms_configure_options: '' 22 | - ptf-modules 23 | tofino_architecture: tofino 24 | --------------------------------------------------------------------------------