Commit 86cfe709 authored by kesnar's avatar kesnar
Browse files

feat: add netx22-p4 workshop files

parent 82ba0a55
Loading
Loading
Loading
Loading
+73 −0
Original line number Diff line number Diff line
# Network X 22 Demo - P4 driver, Basic connectivity functionality

This functional test shows the P4 driver with a basic connectivity test between 2 hosts connected to a single P4 switch, using the TeraFlow Cloud-native SDN Controller.

## Functional test folder

This functional test can be found in folder `src/tests/netx22-p4/`. 

## P4 source and Mininet topology

This test is designed to operate with a mininet deployment that contains 2 hosts and a BMv2 switch, such a topology can be found in the 'src/tests/netx22-p4/mininet' folder.
Additionally the P4 source code, along with its compiled artifacts are present in the 'src/tests/netx22-p4/mininet' folder.

## Deployment and Dependencies

To run this functional test, it is assumed you have deployed a MicroK8s-based Kubernetes environment and a TeraFlowSDN
controller instance as described in the [Tutorial: Deployment Guide](./1-0-deployment.md), and you configured the Python
environment as described in
[Tutorial: Run Experiments Guide > 2.1. Configure Python Environment](./2-1-python-environment.md).
Remember to source the scenario settings appropriately, e.g., `cd ~/tfs-ctrl && source my_deploy.sh` in each terminal
you open.

Additionally mininet should be installed, we suggest using the mininet packaged in the [Next-Gen SDN Tutorial][https://github.com/opennetworkinglab/ngsdn-tutorial], as it provides an easy way to deploy mininet dockerized and comes with the BMv2Stratum software switch. 

## Test Execution

### Mininet 
To execute this functional test, first make sure that mininet is running the correct topology.
If you have used the Next-Gen SDN Tutorial for it, you may add the topology provided in the mininet folder of the ngsdn tutorial and add the following make rule to its Makefile
```
start-simple: NGSDN_TOPO_PY := topo-simple.py
start-simple: _start
```

After that run
```
make start-simple
make mn-cli
```
You will be prompted with the mininet cli. Run the following and let it run until the end of the experiment
```
client ping server
```

### Teraflow

In another terminal cd to the teraflow directory and run the following
```
src/tests/netx22-p4/setup.sh
```
This will copy the p4 artifacts to the device pod.

Then you can bootstrap the device to the Teraflow Controller
```
src/tests/netx22-p4/run_test_01_bootstrap.sh
```

Install the required rules to the p4 switch
```
src/tests/netx22-p4/run_test_02_create_service.sh
```
You should now check the mininet terminal. The two hosts should be pinging each other as intended.

You can remove the rules from the p4 switch
```
src/tests/netx22-p4/run_test_03_delete_service.sh
```
The two hosts on the mininet terminal, should stop pinging.

And remove the device from the Teraflow Controller
```
src/tests/netx22-p4/run_test_04_cleanup.sh
```
+14 −0
Original line number Diff line number Diff line
# Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/)
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#      http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
+17 −0
Original line number Diff line number Diff line
# Set the URL of your local Docker registry where the images will be uploaded to.
export TFS_REGISTRY_IMAGE="http://localhost:32000/tfs/"

# Set the list of components, separated by spaces, you want to build images for, and deploy.
export TFS_COMPONENTS="context device automation service compute monitoring webui"

# Set the tag you want to use for your images.
export TFS_IMAGE_TAG="dev"

# Set the name of the Kubernetes namespace to deploy to.
export TFS_K8S_NAMESPACE="tfs"

# Set additional manifest files to be applied after the deployment
export TFS_EXTRA_MANIFESTS="manifests/nginx_ingress_http.yaml"

# Set the neew Grafana admin password
export TFS_GRAFANA_PASSWORD="admin123+"
+96 −0
Original line number Diff line number Diff line
#!/usr/bin/python

#  Copyright 2019-present Open Networking Foundation
#
#  Licensed under the Apache License, Version 2.0 (the "License");
#  you may not use this file except in compliance with the License.
#  You may obtain a copy of the License at
#
#      http://www.apache.org/licenses/LICENSE-2.0
#
#  Unless required by applicable law or agreed to in writing, software
#  distributed under the License is distributed on an "AS IS" BASIS,
#  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
#  See the License for the specific language governing permissions and
#  limitations under the License.

import argparse

from mininet.cli import CLI
from mininet.log import setLogLevel
from mininet.net import Mininet
from mininet.node import Host
from mininet.topo import Topo
from stratum import StratumBmv2Switch

CPU_PORT = 255

class IPv4Host(Host):
    """Host that can be configured with an IPv4 gateway (default route).
    """

    def config(self, mac=None, ip=None, defaultRoute=None, lo='up', gw=None,
               **_params):
        super(IPv4Host, self).config(mac, ip, defaultRoute, lo, **_params)
        self.cmd('arp -s 192.168.1.1 11:22:33:44:55:77')
        self.cmd('ip -4 addr flush dev %s' % self.defaultIntf())
        self.cmd('ip -6 addr flush dev %s' % self.defaultIntf())
        self.cmd('ip -4 link set up %s' % self.defaultIntf())
        self.cmd('ip -4 addr add %s dev %s' % (ip, self.defaultIntf()))
        if gw:
            self.cmd('ip -4 route add default via %s' % gw)
        # Disable offload
        for attr in ["rx", "tx", "sg"]:
            cmd = "/sbin/ethtool --offload %s %s off" % (
                self.defaultIntf(), attr)
            self.cmd(cmd)

        def updateIP():
            return ip.split('/')[0]

        self.defaultIntf().updateIP = updateIP

class TutorialTopo(Topo):
    """Basic Server-Client topology with IPv4 hosts"""

    def __init__(self, *args, **kwargs):
        Topo.__init__(self, *args, **kwargs)

        # Spines
        # gRPC port 50001
        switch1 = self.addSwitch('switch1', cls=StratumBmv2Switch, cpuport=CPU_PORT)

        # IPv4 hosts attached to switch 1
        client = self.addHost('client', cls=IPv4Host, mac="aa:bb:cc:dd:ee:11",
                           ip='10.0.0.1/24', gw='10.0.0.100')
        server = self.addHost('server', cls=IPv4Host, mac="aa:bb:cc:dd:ee:22",
                           ip='10.0.0.2/24', gw='10.0.0.100')
        self.addLink(client, switch1)  # port 1
        self.addLink(server, switch1)  # port 2


def main():
    net = Mininet(topo=TutorialTopo(), controller=None)
    net.start()
    client = net.hosts[0]
    server = net.hosts[1]
    client.setARP('10.0.0.2', 'aa:bb:cc:dd:ee:22')
    server.setARP('10.0.0.1', 'aa:bb:cc:dd:ee:11')
    CLI(net)
    net.stop()
    print '#' * 80
    print 'ATTENTION: Mininet was stopped! Perhaps accidentally?'
    print 'No worries, it will restart automatically in a few seconds...'
    print 'To access again the Mininet CLI, use `make mn-cli`'
    print 'To detach from the CLI (without stopping), press Ctrl-D'
    print 'To permanently quit Mininet, use `make stop`'
    print '#' * 80


if __name__ == "__main__":
    parser = argparse.ArgumentParser(
        description='Mininet topology script for 2x2 fabric with stratum_bmv2 and IPv4 hosts')
    args = parser.parse_args()
    setLogLevel('info')

    main()
+669 −0
Original line number Diff line number Diff line
{
  "header_types" : [
    {
      "name" : "scalars_0",
      "id" : 0,
      "fields" : [
        ["tmp", 1, false],
        ["local_metadata_t.is_multicast", 1, false],
        ["_padding_0", 6, false]
      ]
    },
    {
      "name" : "standard_metadata",
      "id" : 1,
      "fields" : [
        ["ingress_port", 9, false],
        ["egress_spec", 9, false],
        ["egress_port", 9, false],
        ["clone_spec", 32, false],
        ["instance_type", 32, false],
        ["drop", 1, false],
        ["recirculate_port", 16, false],
        ["packet_length", 32, false],
        ["enq_timestamp", 32, false],
        ["enq_qdepth", 19, false],
        ["deq_timedelta", 32, false],
        ["deq_qdepth", 19, false],
        ["ingress_global_timestamp", 48, false],
        ["egress_global_timestamp", 48, false],
        ["lf_field_list", 32, false],
        ["mcast_grp", 16, false],
        ["resubmit_flag", 32, false],
        ["egress_rid", 16, false],
        ["recirculate_flag", 32, false],
        ["checksum_error", 1, false],
        ["parser_error", 32, false],
        ["priority", 3, false],
        ["_padding", 2, false]
      ]
    },
    {
      "name" : "ethernet_t",
      "id" : 2,
      "fields" : [
        ["dst_addr", 48, false],
        ["src_addr", 48, false],
        ["ether_type", 16, false]
      ]
    }
  ],
  "headers" : [
    {
      "name" : "scalars",
      "id" : 0,
      "header_type" : "scalars_0",
      "metadata" : true,
      "pi_omit" : true
    },
    {
      "name" : "standard_metadata",
      "id" : 1,
      "header_type" : "standard_metadata",
      "metadata" : true,
      "pi_omit" : true
    },
    {
      "name" : "ethernet",
      "id" : 2,
      "header_type" : "ethernet_t",
      "metadata" : false,
      "pi_omit" : true
    }
  ],
  "header_stacks" : [],
  "header_union_types" : [],
  "header_unions" : [],
  "header_union_stacks" : [],
  "field_lists" : [],
  "errors" : [
    ["NoError", 1],
    ["PacketTooShort", 2],
    ["NoMatch", 3],
    ["StackOutOfBounds", 4],
    ["HeaderTooShort", 5],
    ["ParserTimeout", 6],
    ["ParserInvalidArgument", 7]
  ],
  "enums" : [],
  "parsers" : [
    {
      "name" : "parser",
      "id" : 0,
      "init_state" : "start",
      "parse_states" : [
        {
          "name" : "start",
          "id" : 0,
          "parser_ops" : [
            {
              "parameters" : [
                {
                  "type" : "regular",
                  "value" : "ethernet"
                }
              ],
              "op" : "extract"
            }
          ],
          "transitions" : [
            {
              "value" : "default",
              "mask" : null,
              "next_state" : null
            }
          ],
          "transition_key" : []
        }
      ]
    }
  ],
  "parse_vsets" : [],
  "deparsers" : [
    {
      "name" : "deparser",
      "id" : 0,
      "source_info" : {
        "filename" : "p4src/main.p4",
        "line" : 148,
        "column" : 8,
        "source_fragment" : "DeparserImpl"
      },
      "order" : ["ethernet"]
    }
  ],
  "meter_arrays" : [],
  "counter_arrays" : [],
  "register_arrays" : [],
  "calculations" : [],
  "learn_lists" : [],
  "actions" : [
    {
      "name" : "IngressPipeImpl.drop",
      "id" : 0,
      "runtime_data" : [],
      "primitives" : [
        {
          "op" : "mark_to_drop",
          "parameters" : [
            {
              "type" : "header",
              "value" : "standard_metadata"
            }
          ],
          "source_info" : {
            "filename" : "p4src/main.p4",
            "line" : 77,
            "column" : 8,
            "source_fragment" : "mark_to_drop(standard_metadata)"
          }
        }
      ]
    },
    {
      "name" : "IngressPipeImpl.drop",
      "id" : 1,
      "runtime_data" : [],
      "primitives" : [
        {
          "op" : "mark_to_drop",
          "parameters" : [
            {
              "type" : "header",
              "value" : "standard_metadata"
            }
          ],
          "source_info" : {
            "filename" : "p4src/main.p4",
            "line" : 77,
            "column" : 8,
            "source_fragment" : "mark_to_drop(standard_metadata)"
          }
        }
      ]
    },
    {
      "name" : "IngressPipeImpl.set_egress_port",
      "id" : 2,
      "runtime_data" : [
        {
          "name" : "port_num",
          "bitwidth" : 9
        }
      ],
      "primitives" : [
        {
          "op" : "assign",
          "parameters" : [
            {
              "type" : "field",
              "value" : ["standard_metadata", "egress_spec"]
            },
            {
              "type" : "runtime_data",
              "value" : 0
            }
          ],
          "source_info" : {
            "filename" : "p4src/main.p4",
            "line" : 81,
            "column" : 8,
            "source_fragment" : "standard_metadata.egress_spec = port_num"
          }
        }
      ]
    },
    {
      "name" : "IngressPipeImpl.set_egress_port",
      "id" : 3,
      "runtime_data" : [
        {
          "name" : "port_num",
          "bitwidth" : 9
        }
      ],
      "primitives" : [
        {
          "op" : "assign",
          "parameters" : [
            {
              "type" : "field",
              "value" : ["standard_metadata", "egress_spec"]
            },
            {
              "type" : "runtime_data",
              "value" : 0
            }
          ],
          "source_info" : {
            "filename" : "p4src/main.p4",
            "line" : 81,
            "column" : 8,
            "source_fragment" : "standard_metadata.egress_spec = port_num"
          }
        }
      ]
    },
    {
      "name" : "IngressPipeImpl.set_multicast_group",
      "id" : 4,
      "runtime_data" : [
        {
          "name" : "gid",
          "bitwidth" : 16
        }
      ],
      "primitives" : [
        {
          "op" : "assign",
          "parameters" : [
            {
              "type" : "field",
              "value" : ["standard_metadata", "mcast_grp"]
            },
            {
              "type" : "runtime_data",
              "value" : 0
            }
          ],
          "source_info" : {
            "filename" : "p4src/main.p4",
            "line" : 89,
            "column" : 8,
            "source_fragment" : "standard_metadata.mcast_grp = gid"
          }
        },
        {
          "op" : "assign",
          "parameters" : [
            {
              "type" : "field",
              "value" : ["scalars", "local_metadata_t.is_multicast"]
            },
            {
              "type" : "expression",
              "value" : {
                "type" : "expression",
                "value" : {
                  "op" : "b2d",
                  "left" : null,
                  "right" : {
                    "type" : "bool",
                    "value" : true
                  }
                }
              }
            }
          ],
          "source_info" : {
            "filename" : "p4src/main.p4",
            "line" : 90,
            "column" : 8,
            "source_fragment" : "local_metadata.is_multicast = true"
          }
        }
      ]
    },
    {
      "name" : "IngressPipeImpl.set_multicast_group",
      "id" : 5,
      "runtime_data" : [
        {
          "name" : "gid",
          "bitwidth" : 16
        }
      ],
      "primitives" : [
        {
          "op" : "assign",
          "parameters" : [
            {
              "type" : "field",
              "value" : ["standard_metadata", "mcast_grp"]
            },
            {
              "type" : "runtime_data",
              "value" : 0
            }
          ],
          "source_info" : {
            "filename" : "p4src/main.p4",
            "line" : 89,
            "column" : 8,
            "source_fragment" : "standard_metadata.mcast_grp = gid"
          }
        },
        {
          "op" : "assign",
          "parameters" : [
            {
              "type" : "field",
              "value" : ["scalars", "local_metadata_t.is_multicast"]
            },
            {
              "type" : "expression",
              "value" : {
                "type" : "expression",
                "value" : {
                  "op" : "b2d",
                  "left" : null,
                  "right" : {
                    "type" : "bool",
                    "value" : true
                  }
                }
              }
            }
          ],
          "source_info" : {
            "filename" : "p4src/main.p4",
            "line" : 90,
            "column" : 8,
            "source_fragment" : "local_metadata.is_multicast = true"
          }
        }
      ]
    },
    {
      "name" : "act",
      "id" : 6,
      "runtime_data" : [],
      "primitives" : [
        {
          "op" : "assign",
          "parameters" : [
            {
              "type" : "field",
              "value" : ["scalars", "tmp"]
            },
            {
              "type" : "expression",
              "value" : {
                "type" : "expression",
                "value" : {
                  "op" : "b2d",
                  "left" : null,
                  "right" : {
                    "type" : "bool",
                    "value" : true
                  }
                }
              }
            }
          ]
        }
      ]
    },
    {
      "name" : "act_0",
      "id" : 7,
      "runtime_data" : [],
      "primitives" : [
        {
          "op" : "assign",
          "parameters" : [
            {
              "type" : "field",
              "value" : ["scalars", "tmp"]
            },
            {
              "type" : "expression",
              "value" : {
                "type" : "expression",
                "value" : {
                  "op" : "b2d",
                  "left" : null,
                  "right" : {
                    "type" : "bool",
                    "value" : false
                  }
                }
              }
            }
          ]
        }
      ]
    }
  ],
  "pipelines" : [
    {
      "name" : "ingress",
      "id" : 0,
      "source_info" : {
        "filename" : "p4src/main.p4",
        "line" : 71,
        "column" : 8,
        "source_fragment" : "IngressPipeImpl"
      },
      "init_table" : "IngressPipeImpl.l2_exact_table",
      "tables" : [
        {
          "name" : "IngressPipeImpl.l2_exact_table",
          "id" : 0,
          "source_info" : {
            "filename" : "p4src/main.p4",
            "line" : 95,
            "column" : 10,
            "source_fragment" : "l2_exact_table"
          },
          "key" : [
            {
              "match_type" : "exact",
              "name" : "hdr.ethernet.dst_addr",
              "target" : ["ethernet", "dst_addr"],
              "mask" : null
            }
          ],
          "match_type" : "exact",
          "type" : "simple",
          "max_size" : 1024,
          "with_counters" : false,
          "support_timeout" : false,
          "direct_meters" : null,
          "action_ids" : [2, 4, 0],
          "actions" : ["IngressPipeImpl.set_egress_port", "IngressPipeImpl.set_multicast_group", "IngressPipeImpl.drop"],
          "base_default_next" : null,
          "next_tables" : {
            "__HIT__" : "tbl_act",
            "__MISS__" : "tbl_act_0"
          },
          "default_entry" : {
            "action_id" : 0,
            "action_const" : true,
            "action_data" : [],
            "action_entry_const" : true
          }
        },
        {
          "name" : "tbl_act",
          "id" : 1,
          "key" : [],
          "match_type" : "exact",
          "type" : "simple",
          "max_size" : 1024,
          "with_counters" : false,
          "support_timeout" : false,
          "direct_meters" : null,
          "action_ids" : [6],
          "actions" : ["act"],
          "base_default_next" : "node_5",
          "next_tables" : {
            "act" : "node_5"
          },
          "default_entry" : {
            "action_id" : 6,
            "action_const" : true,
            "action_data" : [],
            "action_entry_const" : true
          }
        },
        {
          "name" : "tbl_act_0",
          "id" : 2,
          "key" : [],
          "match_type" : "exact",
          "type" : "simple",
          "max_size" : 1024,
          "with_counters" : false,
          "support_timeout" : false,
          "direct_meters" : null,
          "action_ids" : [7],
          "actions" : ["act_0"],
          "base_default_next" : "node_5",
          "next_tables" : {
            "act_0" : "node_5"
          },
          "default_entry" : {
            "action_id" : 7,
            "action_const" : true,
            "action_data" : [],
            "action_entry_const" : true
          }
        },
        {
          "name" : "IngressPipeImpl.l2_ternary_table",
          "id" : 3,
          "source_info" : {
            "filename" : "p4src/main.p4",
            "line" : 109,
            "column" : 10,
            "source_fragment" : "l2_ternary_table"
          },
          "key" : [
            {
              "match_type" : "ternary",
              "name" : "hdr.ethernet.dst_addr",
              "target" : ["ethernet", "dst_addr"],
              "mask" : null
            }
          ],
          "match_type" : "ternary",
          "type" : "simple",
          "max_size" : 1024,
          "with_counters" : false,
          "support_timeout" : false,
          "direct_meters" : null,
          "action_ids" : [3, 5, 1],
          "actions" : ["IngressPipeImpl.set_egress_port", "IngressPipeImpl.set_multicast_group", "IngressPipeImpl.drop"],
          "base_default_next" : null,
          "next_tables" : {
            "IngressPipeImpl.set_egress_port" : null,
            "IngressPipeImpl.set_multicast_group" : null,
            "IngressPipeImpl.drop" : null
          },
          "default_entry" : {
            "action_id" : 1,
            "action_const" : true,
            "action_data" : [],
            "action_entry_const" : true
          }
        }
      ],
      "action_profiles" : [],
      "conditionals" : [
        {
          "name" : "node_5",
          "id" : 0,
          "source_info" : {
            "filename" : "p4src/main.p4",
            "line" : 122,
            "column" : 12,
            "source_fragment" : "!l2_exact_table.apply().hit"
          },
          "expression" : {
            "type" : "expression",
            "value" : {
              "op" : "not",
              "left" : null,
              "right" : {
                "type" : "expression",
                "value" : {
                  "op" : "d2b",
                  "left" : null,
                  "right" : {
                    "type" : "field",
                    "value" : ["scalars", "tmp"]
                  }
                }
              }
            }
          },
          "false_next" : null,
          "true_next" : "IngressPipeImpl.l2_ternary_table"
        }
      ]
    },
    {
      "name" : "egress",
      "id" : 1,
      "source_info" : {
        "filename" : "p4src/main.p4",
        "line" : 134,
        "column" : 8,
        "source_fragment" : "EgressPipeImpl"
      },
      "init_table" : null,
      "tables" : [],
      "action_profiles" : [],
      "conditionals" : []
    }
  ],
  "checksums" : [],
  "force_arith" : [],
  "extern_instances" : [],
  "field_aliases" : [
    [
      "queueing_metadata.enq_timestamp",
      ["standard_metadata", "enq_timestamp"]
    ],
    [
      "queueing_metadata.enq_qdepth",
      ["standard_metadata", "enq_qdepth"]
    ],
    [
      "queueing_metadata.deq_timedelta",
      ["standard_metadata", "deq_timedelta"]
    ],
    [
      "queueing_metadata.deq_qdepth",
      ["standard_metadata", "deq_qdepth"]
    ],
    [
      "intrinsic_metadata.ingress_global_timestamp",
      ["standard_metadata", "ingress_global_timestamp"]
    ],
    [
      "intrinsic_metadata.egress_global_timestamp",
      ["standard_metadata", "egress_global_timestamp"]
    ],
    [
      "intrinsic_metadata.lf_field_list",
      ["standard_metadata", "lf_field_list"]
    ],
    [
      "intrinsic_metadata.mcast_grp",
      ["standard_metadata", "mcast_grp"]
    ],
    [
      "intrinsic_metadata.resubmit_flag",
      ["standard_metadata", "resubmit_flag"]
    ],
    [
      "intrinsic_metadata.egress_rid",
      ["standard_metadata", "egress_rid"]
    ],
    [
      "intrinsic_metadata.recirculate_flag",
      ["standard_metadata", "recirculate_flag"]
    ],
    [
      "intrinsic_metadata.priority",
      ["standard_metadata", "priority"]
    ]
  ],
  "program" : "p4src/main.p4",
  "__meta__" : {
    "version" : [2, 18],
    "compiler" : "https://github.com/p4lang/p4c"
  }
}
 No newline at end of file
Loading