Remove support for compiling P4 programs (see #3682 for explanation).
Signed-off-by: Dave Marchevsky <davemarchevsky@fb.com>
+++ /dev/null
-# Compiling P4 to EBPF
-
-Mihai Budiu - mbudiu@barefootnetworks.com
-
-September 22, 2015
-
-## Abstract
-
-This document describes a prototype compiler that translates programs
-written in the P4 programming languages to eBPF programs. The
-translation is performed by generating programs written in a subset of
-the C programming language, that are converted to EBPF using the BPF
-Compiler Collection tools.
-
-The compiler code is licensed under an [Apache v2.0 license]
-(http://www.apache.org/licenses/LICENSE-2.0.html).
-
-## Preliminaries
-
-In this section we give a brief overview of P4 and EBPF. A detailed
-treatment of these topics is outside the scope of this text.
-
-### P4
-
-P4 (http://p4.org) is a domain-specific programming language for
-specifying the behavior of the dataplanes of network-forwarding
-elements. The name of the programming language comes from the title
-of a paper published in the proceedings of SIGCOMM Computer
-Communications Review in 2014:
-http://www.sigcomm.org/ccr/papers/2014/July/0000000.0000004:
-"Programming Protocol-Independent Packet Processors".
-
-P4 itself is protocol-independent but allows programmers to express a
-rich set of data plane behaviors and protocols. The core P4
-abstractions are:
-
-* Header definitions describe the format (the set of fields and their
- sizes) of each header within a packet.
-
-* Parse graphs (finite-state machines) describe the permitted header
- sequences within received packets.
-
-* Tables associate keys to actions. P4 tables generalize traditional
- forwarding tables; they can be used to implement routing tables,
- flow lookup tables, access-control lists, etc.
-
-* Actions describe how packet header fields and metadata are manipulated.
-
-* Match-action units stitch together tables and actions, and perform
- the following sequence of operations:
-
- * Construct lookup keys from packet fields or computed metadata,
-
- * Use the constructed lookup key to index into tables, choosing an
- action to execute,
-
- * Finally, execute the selected action.
-
-* Control flow is expressed as an imperative program describing the
- data-dependent packet processing within a pipeline, including the
- data-dependent sequence of match-action unit invocations.
-
-P4 programs describe the behavior of network-processing dataplanes. A
-P4 program is designed to operate in concert with a separate *control
-plane* program. The control plane is responsible for managing at
-runtime the contents of the P4 tables. P4 cannot be used to specify
-control-planes; however, a P4 program implicitly specifies the
-interface between the data-plane and the control-plane.
-
-The P4 language is under active development; the current stable
-version is 1.0.2 (see http://p4.org/spec); a reference implementation
-of a compiler and associated tools is freely available using a Apache
-2 open-source license (see http://p4.org/code).
-
-### EBPF
-
-#### Safe code
-
-EBPF is a acronym that stands for Extended Berkeley Packet Filters.
-In essence EBPF is a low-level programming language (similar to
-machine code); EBPF programs are traditionally executed by a virtual
-machine that resides in the Linux kernel. EBPF programs can be
-inserted and removed from a live kernel using dynamic code
-instrumentation. The main feature of EBPF programs is their *static
-safety*: prior to execution all EBPF programs have to be validated as
-being safe, and unsafe programs cannot be executed. A safe program
-provably cannot compromise the machine it is running on:
-
-* it can only access a restricted memory region (on the local stack)
-
-* it can run only for a limited amount of time; during execution it
- cannot block, sleep or take any locks
-
-* it cannot use any kernel resources with the exception of a limited
- set of kernel services which have been specifically whitelisted,
- including operations to manipulate tables (described below)
-
-#### Kernel hooks
-
-EBPF programs are inserted into the kernel using *hooks*. There are
-several types of hooks available:
-
-* any function entry point in the kernel can act as a hook; attaching
- an EBPF program to a function `foo()` will cause the EBPF program to
- execute every time some kernel thread executes `foo()`.
-
-* EBPF programs can also be attached using the Linux Traffic Control
- (TC) subsystem, in the network packet processing datapath. Such
- programs can be used as TC classifiers and actions.
-
-* EBPF programs can also be attached to sockets or network interfaces.
- In this case they can be used for processing packets that flow
- through the socket/interface.
-
-EBPF programs can be used for many purposes; the main use cases are
-dynamic tracing and monitoring, and packet processing. We are mostly
-interested in the latter use case in this document.
-
-#### EBPF Tables
-
-The EBPF runtime exposes a bi-directional kernel-userspace data
-communication channel, called *tables* (also called maps in some EBPF
-documents and code samples). EBPF tables are essentially key-value
-stores, where keys and values are arbitrary fixed-size bitstrings.
-The key width, value width and table size (maximum number of entries
-that can be stored) are declared statically, at table creation time.
-
-In user-space tables handles are exposed as file descriptors. Both
-user- and kernel-space programs can manipulate tables, by inserting,
-deleting, looking up, modifying, and enumerating entries in a table.
-
-In kernel space the keys and values are exposed as pointers to the raw
-underlying data stored in the table, whereas in user-space the
-pointers point to copies of the data.
-
-#### Concurrency
-
-An important aspect to understand related to EBPF is the execution
-model. An EBPF program is triggered by a kernel hook; multiple
-instances of the same kernel hook can be running simultaneously on
-different cores.
-
-Each table however has a single instances across all the cores. A
-single table may be accessed simultaneously by multiple instances of
-the same EBPF program running as separate kernel threads on different
-cores. EBPF tables are native kernel objects, and access to the table
-contents is protected using the kernel RCU mechanism. This makes
-access to table entries safe under concurrent execution; for example,
-the memory associated to a value cannot be accidentally freed while an
-EBPF program holds a pointer to the respective value. However,
-accessing tables is prone to data races; since EBPF programs cannot
-use locks, some of these races often cannot be avoided.
-
-EBPF and the associated tools are also under active development, and
-new capabilities are added frequently. The P4 compiler generates code
-that can be compiled using the BPF Compiler Collection (BCC)
-(https://github.com/iovisor/bcc)
-
-## Compiling P4 to EBPF
-
-From the above description it is apparent that the P4 and EBPF
-programming languages have different expressive powers. However,
-there is a significant overlap in their capabilities, in particular,
-in the domain of network packet processing. The following image
-illustrates the situation:
-
-![P4 and EBPF overlap in capabilities](scope.png)
-
-We expect that the overlapping region will grow in size as both P4 and
-EBPF continue to mature.
-
-The current version of the P4 to EBPF compiler translates programs
-written in the version 1.1 of the P4 programming language to programs
-written in a restricted subset of C. The subset of C is chosen such
-that it should be compilable to EBPF using BCC.
-
-```
- -------------- -------
-P4 ---> | P4-to-EBPF | ---> C ----> | BCC | --> EBPF
- -------------- -------
-```
-
-The P4 program only describes the packet processing *data plane*, that
-runs in the Linux kernel. The *control plane* must be separately
-implemented by the user. The BCC tools simplify this task
-considerably, by generating C and/or Python APIs that expose the
-dataplane/control-plane APIs.
-
-### Dependencies
-
-EBPF programs require a Linux kernel with version 4.2 or newer.
-
-In order to use the P4 to EBPF compiler the following software must be installed:
-
-* The compiler itself is written in the Python (v2.x) programming
- language.
-
-* the P4 compiler front-end: (https://github.com/p4lang/p4-hlir).
- This is required for parsing the P4 programs.
-
-* the BCC compiler collection tools: (https://github.com/iovisor/bcc).
- This is required for compiling the generated code. Also, BCC comes
- with a set of Python utilities which can be used to implement
- control-plane programs that operate in concert with the kernel EBPF
- datapath.
-
-The P4 to EBPF compiler generates code that is designed for being used
-as a classifier using the Linux TC subsystem.
-
-Furthermore, the test code provided is written using the Python (v3.x)
-programming language and requires several Python packages to be
-installed.
-
-### Supported capabilities
-
-The current version of the P4 to EBPF compiler supports a relatively
-narrow subset of the P4 language, but still powerful enough to write
-very complex packet filters and simple packet forwarding engines. In
-the spirit of open-source "release early, release often", we expect
-that the compiler's capabilities will improve gradually.
-
-* Packet filtering is performed using the `drop()` action. Packets
- that are not dropped will be forwarded.
-
-* Packet forwarding is performed by setting the
- `standard_metadata.egress_port` to the index of the destination
- network interface
-
-Here are some limitations imposed on the P4 programs:
-
-* Currently both the ingress and the egress P4 pipelines are executed
- at the same hook (wherever the user chooses to insert the generated
- EBPF program). In the future the compiler should probably generate
- two separate EBPF programs.
-
-* arbitrary parsers can be compiled, but the BCC compiler will reject
- parsers that contain cycles
-
-* arithmetic on data wider than 32 bits is not supported
-
-* checksum computations are not implemented. In consequence, programs
- that IP/TCP/UDP headers will produce incorrect packet headers.
-
-* EBPF does not offer support for ternary or LPM tables
-
-* P4 cloning and recirculation and not supported
-
-* meters and registers are not supported; only direct counters are
- currently supported. EBPF can potentially support registers and
- arbitrary counters, so these may appear in the future.
-
-* learning (i.e. `generate_digest`) is not implemented
-
-### Translating P4 to C
-
-To simplify the translation, the P4 programmer should refrain using
-identifiers whose name starts with `ebpf_`.
-
-The following table provides a brief summary of how each P4 construct
-is mapped to a corresponding C construct:
-
-#### Translating parsers
-
-P4 Construct | C Translation
-----------|------------
-`header_type` | `struct` type
-`header` | `struct` instance with an additional `valid` bit
-`metadata` | `struct` instance
-parser state | code block
-state transition | `goto` statement
-`extract` | load/shift/mask data from packet buffer
-
-#### Translating match-action pipelines
-
-P4 Construct | C Translation
-----------|------------
-table | 2 EBPF tables: second one used just for the default action
-table key | `struct` type
-table `actions` block | tagged `union` with all possible actions
-`action` arguments | `struct`
-table `reads` | EBPF table access
-`action` body | code block
-table `apply` | `switch` statement
-counters | additional EBPF table
-
-### Code organization
-
-The compiler code is organized in two folders:
-
-* `compiler`: the complete compiler source code, in Python v2.x
- The compiler entry point is `p4toEbpf.py`.
-
-* `test`: testing code and data. There are two testing programs:
- * `testP4toEbpf.py`: which compiles all P4 files in the testprograms folder
-
- * `endToEndTest.py`: which compiles and executes the simple.p4
- program, and includes a simple control plane
-
-Currently the compiler contains no installation capabilities.
-
-### Invoking the compiler
-
-Invoking the compiler is just a matter of invoking the python program
-with a suitable input P4 file:
-
-```
-p4toEbpf.py file.p4 -o file.c
-```
-
-#### Compiler options
-
-The P4 compiler first runs the C preprocessor on the input P4 file.
-Some of the command-line options are passed directly to the
-preprocessor.
-
-The following compiler options are available:
-
-Option | Meaning
--------|--------
-`-D macro` | Option passed to C preprocessor
-`-I path` | Option passed to C preprocessor
-`-U macro` | Option passed to C preprocessor
-`-g [router|filter]` | Controls whether the generated code behaves like a router or a filter.
-`-o outoutFile` | writes the generated C code to the specified output file.
-
-The `-g` option controls the nature of the generated code:
-
-* `-g filter` generates a filter; the only P4 action that has an
- effect is the `drop()` action. Setting metadata in P4 (e.g.,
- `egress_port`) has no effect.
-
-* `-g router` generates a simple router; both `drop()` and
- `egress_port` impact packet processing.
-
-#### Using the generated code
-
-The resulting file contains the complete data structures, tables, and
-a C function named `ebpf_filter` that implements the P4-specified
-data-plane. This C file can be manipulated using the BCC tools;
-please refer to the BCC project documentation and sample test files of
-the P4 to EBPF source code for an in-depth understanding. A minimal
-Python program that compiles and loads into the kernel the generated
-file into EBPF is:
-
-```
-#!/usr/bin/env python3
-from bcc import BPF
-
-b = BPF(src_file="file.c", debug=0)
-fn = b.load_func("ebpf_filter", BPF.SCHED_CLS)
-```
-
-##### Connecting the generated program with the TC
-
-The EBPF code that is generated is intended to be used as a classifier
-attached to the ingress packet path using the Linux TC subsystem. The
-same EBPF code should be attached to all interfaces. Note however
-that all EBPF code instances share a single set of tables, which are
-used to control the program behavior.
-
-The following code fragment illustrates how the EBPF code can be
-hooked up to the `eth0` interface using a Python program. (The `fn`
-variable is the one produced by the previous code fragment).
-
-```
-from pyroute2 import IPRoute
-
-ipr = IPRoute()
-interface_name="eth0"
-if_index = ipr.link_lookup(ifname=interface_name)[0]
-ipr.tc("add", "ingress", if_index, "ffff:")
-ipr.tc("add-filter", "bpf", if_index, ":1", fd=fn.fd,
- name=fn.name, parent="ffff:", action="ok", classid=1)
-```
+++ /dev/null
-This folder contains an implementation of a simple compiler that
-translates a programs written in a subset of P4 into C that can in
-turn be compiled into EBPF using the IOVisor bcc compiler.
-
+++ /dev/null
-# Copyright (c) Barefoot Networks, Inc.
-# Licensed under the Apache License, Version 2.0 (the "License")
-
-class CompilationException(Exception):
- """Signals an error during compilation"""
- def __init__(self, isBug, format, *message):
- # isBug: indicates that this is a compiler bug
- super(CompilationException, self).__init__()
-
- assert isinstance(format, str)
- assert isinstance(isBug, bool)
- self.message = message
- self.format = format
- self.isBug = isBug
-
- def show(self):
- # TODO: format this message nicely
- return self.format.format(*self.message)
-
-
-class NotSupportedException(Exception):
- archError = " not supported by EBPF"
-
- def __init__(self, format, *message):
- super(NotSupportedException, self).__init__()
-
- assert isinstance(format, str)
- self.message = message
- self.format = format
-
- def show(self):
- # TODO: format this message nicely
- return (self.format + NotSupportedException.archError).format(
- *self.message)
+++ /dev/null
-# Copyright (c) Barefoot Networks, Inc.
-# Licensed under the Apache License, Version 2.0 (the "License")
-
-from p4_hlir.hlir import p4_action, p4_field
-from p4_hlir.hlir import p4_signature_ref, p4_header_instance
-import ebpfProgram
-from programSerializer import ProgramSerializer
-from compilationException import *
-import ebpfScalarType
-import ebpfCounter
-import ebpfType
-import ebpfInstance
-
-
-class EbpfActionData(object):
- def __init__(self, name, argtype):
- self.name = name
- self.argtype = argtype
-
-
-class EbpfActionBase(object):
- def __init__(self, p4action):
- self.name = p4action.name
- self.hliraction = p4action
- self.builtin = False
- self.arguments = []
-
- def serializeArgumentsAsStruct(self, serializer):
- serializer.emitIndent()
- serializer.appendFormat("/* no arguments for {0} */", self.name)
- serializer.newline()
-
- def serializeBody(self, serializer, valueName, program):
- serializer.emitIndent()
- serializer.appendFormat("/* no body for {0} */", self.name)
- serializer.newline()
-
- def __str__(self):
- return "EbpfAction({0})".format(self.name)
-
-
-class EbpfAction(EbpfActionBase):
- unsupported = [
- # The following cannot be done in EBPF
- "add_header", "remove_header", "execute_meter",
- "clone_ingress_pkt_to_egress",
- "clone_egress_pkt_to_egress", "generate_digest", "resubmit",
- "modify_field_with_hash_based_offset", "truncate", "push", "pop",
- # The following could be done, but are not yet implemented
- # The situation with copy_header is complicated,
- # because we don't do checksums
- "copy_header", "count",
- "register_read", "register_write"]
-
- # noinspection PyUnresolvedReferences
- def __init__(self, p4action, program):
- super(EbpfAction, self).__init__(p4action)
- assert isinstance(p4action, p4_action)
- assert isinstance(program, ebpfProgram.EbpfProgram)
-
- self.builtin = False
- self.invalid = False # a leaf action which is never
- # called from a table can be invalid.
-
- for i in range(0, len(p4action.signature)):
- param = p4action.signature[i]
- width = p4action.signature_widths[i]
- if width is None:
- self.invalid = True
- return
- argtype = ebpfScalarType.EbpfScalarType(p4action, width,
- False, program.config)
- actionData = EbpfActionData(param, argtype)
- self.arguments.append(actionData)
-
- def serializeArgumentsAsStruct(self, serializer):
- if self.invalid:
- raise CompilationException(True,
- "{0} Attempting to generate code for an invalid action",
- self.hliraction)
-
- # Build a struct containing all action arguments.
- serializer.emitIndent()
- serializer.append("struct ")
- serializer.blockStart()
- assert isinstance(serializer, ProgramSerializer)
- for arg in self.arguments:
- assert isinstance(arg, EbpfActionData)
- serializer.emitIndent()
- argtype = arg.argtype
- assert isinstance(argtype, ebpfType.EbpfType)
- argtype.declare(serializer, arg.name, False)
- serializer.endOfStatement(True)
- serializer.blockEnd(False)
- serializer.space()
- serializer.append(self.name)
- serializer.endOfStatement(True)
-
- def serializeBody(self, serializer, dataContainer, program):
- if self.invalid:
- raise CompilationException(True,
- "{0} Attempting to generate code for an invalid action",
- self.hliraction)
-
- # TODO: generate PARALLEL implementation
- # dataContainer is a string containing the variable name
- # containing the action data
- assert isinstance(serializer, ProgramSerializer)
- assert isinstance(program, ebpfProgram.EbpfProgram)
- assert isinstance(dataContainer, str)
- callee_list = self.hliraction.flat_call_sequence
- for e in callee_list:
- action = e[0]
- assert isinstance(action, p4_action)
- arguments = e[1]
- assert isinstance(arguments, list)
- self.serializeCallee(self, action, arguments, serializer,
- dataContainer, program)
-
- def checkSize(self, call, args, program):
- size = None
- for a in args:
- if a is None:
- continue
- if size is None:
- size = a
- elif a != size:
- program.emitWarning(
- "{0}: Arguments do not have the same size {1} and {2}",
- call, size, a)
- return size
-
- @staticmethod
- def translateActionToOperator(actionName):
- if actionName == "add" or actionName == "add_to_field":
- return "+"
- elif actionName == "bit_and":
- return "&"
- elif actionName == "bit_or":
- return "|"
- elif actionName == "bit_xor":
- return "^"
- elif actionName == "subtract" or actionName == "subtract_from_field":
- return "-"
- else:
- raise CompilationException(True,
- "Unexpected primitive action {0}",
- actionName)
-
- def serializeCount(self, caller, arguments, serializer,
- dataContainer, program):
- assert isinstance(serializer, ProgramSerializer)
- assert isinstance(program, ebpfProgram.EbpfProgram)
- assert isinstance(arguments, list)
- assert len(arguments) == 2
-
- counter = arguments[0]
- index = ArgInfo(arguments[1], caller, dataContainer, program)
- ctr = program.getCounter(counter.name)
- assert isinstance(ctr, ebpfCounter.EbpfCounter)
- serializer.emitIndent()
- serializer.blockStart()
-
- # This is actually incorrect, since the key is not always an u32.
- # This code is currently disabled
- key = program.reservedPrefix + "index"
- serializer.emitIndent()
- serializer.appendFormat("u32 {0} = {1};", key, index.asString)
- serializer.newline()
-
- ctr.serializeCode(key, serializer, program)
-
- serializer.blockEnd(True)
-
- def serializeCallee(self, caller, callee, arguments,
- serializer, dataContainer, program):
- if self.invalid:
- raise CompilationException(
- True,
- "{0} Attempting to generate code for an invalid action",
- self.hliraction)
-
- assert isinstance(serializer, ProgramSerializer)
- assert isinstance(program, ebpfProgram.EbpfProgram)
- assert isinstance(callee, p4_action)
- assert isinstance(arguments, list)
-
- if callee.name in EbpfAction.unsupported:
- raise NotSupportedException("{0}", callee)
-
- # This is not yet ready
- #if callee.name == "count":
- # self.serializeCount(caller, arguments,
- # serializer, dataContainer, program)
- # return
-
- serializer.emitIndent()
- args = self.transformArguments(arguments, caller,
- dataContainer, program)
- if callee.name == "modify_field":
- dst = args[0]
- src = args[1]
-
- size = self.checkSize(callee,
- [a.widthInBits() for a in args],
- program)
- if size is None:
- raise CompilationException(
- True, "Cannot infer width for arguments {0}",
- callee)
- elif size <= 32:
- serializer.appendFormat("{0} = {1};",
- dst.asString,
- src.asString)
- else:
- if not dst.isLvalue:
- raise NotSupportedException(
- "Constants wider than 32-bit: {0}({1})",
- dst.caller, dst.asString)
- if not src.isLvalue:
- raise NotSupportedException(
- "Constants wider than 32-bit: {0}({1})",
- src.caller, src.asString)
- serializer.appendFormat("memcpy(&{0}, &{1}, {2});",
- dst.asString,
- src.asString,
- size / 8)
- elif (callee.name == "add" or
- callee.name == "bit_and" or
- callee.name == "bit_or" or
- callee.name == "bit_xor" or
- callee.name == "subtract"):
- size = self.checkSize(callee,
- [a.widthInBits() for a in args],
- program)
- if size is None:
- raise CompilationException(
- True,
- "Cannot infer width for arguments {0}",
- callee)
- if size > 32:
- raise NotSupportedException("{0}: Arithmetic on {1}-bits",
- callee, size)
- op = EbpfAction.translateActionToOperator(callee.name)
- serializer.appendFormat("{0} = {1} {2} {3};",
- args[0].asString,
- args[1].asString,
- op,
- args[2].asString)
- elif (callee.name == "add_to_field" or
- callee.name == "subtract_from_field"):
- size = self.checkSize(callee,
- [a.widthInBits() for a in args],
- program)
- if size is None:
- raise CompilationException(
- True, "Cannot infer width for arguments {0}", callee)
- if size > 32:
- raise NotSupportedException(
- "{0}: Arithmetic on {1}-bits", callee, size)
-
- op = EbpfAction.translateActionToOperator(callee.name)
- serializer.appendFormat("{0} = {0} {1} {2};",
- args[0].asString,
- op,
- args[1].asString)
- elif callee.name == "no_op":
- serializer.append("/* noop */")
- elif callee.name == "drop":
- serializer.appendFormat("{0} = 1;", program.dropBit)
- elif callee.name == "push" or callee.name == "pop":
- raise CompilationException(
- True, "{0} push/pop not yet implemented", callee)
- else:
- raise CompilationException(
- True, "Unexpected primitive action {0}", callee)
- serializer.newline()
-
- def transformArguments(self, arguments, caller, dataContainer, program):
- result = []
- for a in arguments:
- t = ArgInfo(a, caller, dataContainer, program)
- result.append(t)
- return result
-
-
-class BuiltinAction(EbpfActionBase):
- def __init__(self, p4action):
- super(BuiltinAction, self).__init__(p4action)
- self.builtin = True
-
- def serializeBody(self, serializer, valueName, program):
- # This is ugly; there should be a better way
- if self.name == "drop":
- serializer.emitIndent()
- serializer.appendFormat("{0} = 1;", program.dropBit)
- serializer.newline()
- else:
- serializer.emitIndent()
- serializer.appendFormat("/* no body for {0} */", self.name)
- serializer.newline()
-
-
-class ArgInfo(object):
- # noinspection PyUnresolvedReferences
- # Represents an argument passed to an action
- def __init__(self, argument, caller, dataContainer, program):
- self.width = None
- self.asString = None
- self.isLvalue = True
- self.caller = caller
-
- assert isinstance(program, ebpfProgram.EbpfProgram)
- assert isinstance(caller, EbpfAction)
-
- if isinstance(argument, int):
- self.asString = str(argument)
- self.isLvalue = False
- # size is unknown
- elif isinstance(argument, p4_field):
- if ebpfProgram.EbpfProgram.isArrayElementInstance(
- argument.instance):
- if isinstance(argument.instance.index, int):
- index = "[" + str(argument.instance.index) + "]"
- else:
- raise CompilationException(
- True,
- "Unexpected index for array {0}",
- argument.instance.index)
- stackInstance = program.getStackInstance(
- argument.instance.base_name)
- assert isinstance(stackInstance, ebpfInstance.EbpfHeaderStack)
- fieldtype = stackInstance.basetype.getField(argument.name)
- self.width = fieldtype.widthInBits()
- self.asString = "{0}.{1}{3}.{2}".format(
- program.headerStructName,
- stackInstance.name, argument.name, index)
- else:
- instance = program.getInstance(argument.instance.base_name)
- if isinstance(instance, ebpfInstance.EbpfHeader):
- parent = program.headerStructName
- else:
- parent = program.metadataStructName
- fieldtype = instance.type.getField(argument.name)
- self.width = fieldtype.widthInBits()
- self.asString = "{0}.{1}.{2}".format(
- parent, instance.name, argument.name)
- elif isinstance(argument, p4_signature_ref):
- refarg = caller.arguments[argument.idx]
- self.asString = "{0}->u.{1}.{2}".format(
- dataContainer, caller.name, refarg.name)
- self.width = caller.arguments[argument.idx].argtype.widthInBits()
- elif isinstance(argument, p4_header_instance):
- # This could be a header array element
- # Unfortunately for push and pop, the user mean the whole array,
- # but the representation contains just the first element here.
- # This looks like a bug in the HLIR.
- if ebpfProgram.EbpfProgram.isArrayElementInstance(argument):
- if isinstance(argument.index, int):
- index = "[" + str(argument.index) + "]"
- else:
- raise CompilationException(
- True,
- "Unexpected index for array {0}", argument.index)
- stackInstance = program.getStackInstance(argument.base_name)
- assert isinstance(stackInstance, ebpfInstance.EbpfHeaderStack)
- fieldtype = stackInstance.basetype
- self.width = fieldtype.widthInBits()
- self.asString = "{0}.{1}{2}".format(
- program.headerStructName, stackInstance.name, index)
- else:
- instance = program.getInstance(argument.name)
- instancetype = instance.type
- self.width = instancetype.widthInBits()
- self.asString = "{0}.{1}".format(
- program.headerStructName, argument.name)
- else:
- raise CompilationException(
- True, "Unexpected action argument {0}", argument)
-
- def widthInBits(self):
- return self.width
+++ /dev/null
-# Copyright (c) Barefoot Networks, Inc.
-# Licensed under the Apache License, Version 2.0 (the "License")
-
-from p4_hlir.hlir import p4_conditional_node, p4_expression
-from p4_hlir.hlir import p4_header_instance, p4_field
-from programSerializer import ProgramSerializer
-from compilationException import CompilationException
-import ebpfProgram
-import ebpfInstance
-
-
-class EbpfConditional(object):
- @staticmethod
- def translate(op):
- if op == "not":
- return "!"
- elif op == "or":
- return "||"
- elif op == "and":
- return "&&"
- return op
-
- def __init__(self, p4conditional, program):
- assert isinstance(p4conditional, p4_conditional_node)
- assert isinstance(program, ebpfProgram.EbpfProgram)
- self.hlirconditional = p4conditional
- self.name = p4conditional.name
-
- def emitNode(self, node, serializer, program):
- if isinstance(node, p4_expression):
- self.emitExpression(node, serializer, program, False)
- elif node is None:
- pass
- elif isinstance(node, int):
- serializer.append(node)
- elif isinstance(node, p4_header_instance):
- header = program.getInstance(node.name)
- assert isinstance(header, ebpfInstance.EbpfHeader)
- # TODO: stacks?
- serializer.appendFormat(
- "{0}.{1}", program.headerStructName, header.name)
- elif isinstance(node, p4_field):
- instance = node.instance
- einstance = program.getInstance(instance.name)
- if isinstance(einstance, ebpfInstance.EbpfHeader):
- base = program.headerStructName
- else:
- base = program.metadataStructName
- serializer.appendFormat(
- "{0}.{1}.{2}", base, einstance.name, node.name)
- else:
- raise CompilationException(True, "{0} Unexpected expression ", node)
-
- def emitExpression(self, expression, serializer, program, toplevel):
- assert isinstance(serializer, ProgramSerializer)
- assert isinstance(program, ebpfProgram.EbpfProgram)
- assert isinstance(expression, p4_expression)
- assert isinstance(toplevel, bool)
- left = expression.left
- op = expression.op
- right = expression.right
-
- assert isinstance(op, str)
-
- if op == "valid":
- self.emitNode(right, serializer, program)
- serializer.append(".valid")
- return
-
- if not toplevel:
- serializer.append("(")
- self.emitNode(left, serializer, program)
- op = EbpfConditional.translate(op)
- serializer.append(op)
- self.emitNode(right, serializer, program)
- if not toplevel:
- serializer.append(")")
-
- def generateCode(self, serializer, program, nextNode):
- assert isinstance(serializer, ProgramSerializer)
- assert isinstance(program, ebpfProgram.EbpfProgram)
- serializer.emitIndent()
- serializer.blockStart()
-
- trueBranch = self.hlirconditional.next_[True]
- if trueBranch is None:
- trueBranch = nextNode
- falseBranch = self.hlirconditional.next_[False]
- if falseBranch is None:
- falseBranch = nextNode
-
- serializer.emitIndent()
- serializer.appendFormat("{0}:", program.getLabel(self.hlirconditional))
- serializer.newline()
-
- serializer.emitIndent()
- serializer.append("if (")
- self.emitExpression(
- self.hlirconditional.condition, serializer, program, True)
- serializer.appendLine(")")
-
- serializer.increaseIndent()
- label = program.getLabel(trueBranch)
- serializer.emitIndent()
- serializer.appendFormat("goto {0};", label)
- serializer.newline()
- serializer.decreaseIndent()
-
- serializer.emitIndent()
- serializer.appendLine("else")
- serializer.increaseIndent()
- label = program.getLabel(falseBranch)
- serializer.emitIndent()
- serializer.appendFormat("goto {0};", label)
- serializer.newline()
- serializer.decreaseIndent()
-
- serializer.blockEnd(True)
+++ /dev/null
-# Copyright (c) Barefoot Networks, Inc.
-# Licensed under the Apache License, Version 2.0 (the "License")
-
-from p4_hlir.hlir import p4_counter, P4_DIRECT, P4_COUNTER_BYTES
-from programSerializer import ProgramSerializer
-from compilationException import *
-import ebpfTable
-import ebpfProgram
-
-
-class EbpfCounter(object):
- # noinspection PyUnresolvedReferences
- def __init__(self, hlircounter, program):
- assert isinstance(hlircounter, p4_counter)
- assert isinstance(program, ebpfProgram.EbpfProgram)
-
- self.name = hlircounter.name
- self.hlircounter = hlircounter
-
- width = hlircounter.min_width
- # ebpf counters only work on 64-bits
- if width <= 64:
- self.valueTypeName = program.config.uprefix + "64"
- else:
- raise NotSupportedException(
- "{0}: Counters with {1} bits", hlircounter, width)
-
- self.dataMapName = self.name
-
- if ((hlircounter.binding is None) or
- (hlircounter.binding[0] != P4_DIRECT)):
- raise NotSupportedException(
- "{0}: counter which is not direct", hlircounter)
-
- self.autoIncrement = (hlircounter.binding != None and
- hlircounter.binding[0] == P4_DIRECT)
-
- if hlircounter.type is P4_COUNTER_BYTES:
- self.increment = "{0}->len".format(program.packetName)
- else:
- self.increment = "1"
-
- def getSize(self, program):
- if self.hlircounter.instance_count is not None:
- return self.hlircounter.instance_count
- if self.autoIncrement:
- return self.getTable(program).size
- program.emitWarning(
- "{0} does not specify a max_size; using 1024", self.hlircounter)
- return 1024
-
- def getTable(self, program):
- table = program.getTable(self.hlircounter.binding[1].name)
- assert isinstance(table, ebpfTable.EbpfTable)
- return table
-
- def serialize(self, serializer, program):
- assert isinstance(serializer, ProgramSerializer)
-
- # Direct counters have the same key as the associated table
- # Static counters have integer keys
- if self.autoIncrement:
- keyTypeName = "struct " + self.getTable(program).keyTypeName
- else:
- keyTypeName = program.config.uprefix + "32"
- program.config.serializeTableDeclaration(
- serializer, self.dataMapName, True, keyTypeName,
- self.valueTypeName, self.getSize(program))
-
- def serializeCode(self, keyname, serializer, program):
- assert isinstance(serializer, ProgramSerializer)
- assert isinstance(program, ebpfProgram.EbpfProgram)
-
- serializer.emitIndent()
- serializer.appendFormat("/* Update counter {0} */", self.name)
- serializer.newline()
-
- valueName = "ctrvalue"
- initValuename = "init_val"
-
- serializer.emitIndent()
- serializer.appendFormat("{0} *{1};", self.valueTypeName, valueName)
- serializer.newline()
- serializer.emitIndent()
- serializer.appendFormat("{0} {1};", self.valueTypeName, initValuename)
- serializer.newline()
-
- serializer.emitIndent()
- serializer.appendLine("/* perform lookup */")
- serializer.emitIndent()
- program.config.serializeLookup(
- serializer, self.dataMapName, keyname, valueName)
- serializer.newline()
-
- serializer.emitIndent()
- serializer.appendFormat("if ({0} != NULL) ", valueName)
- serializer.newline()
- serializer.increaseIndent()
- serializer.emitIndent()
- serializer.appendFormat("__sync_fetch_and_add({0}, {1});",
- valueName, self.increment)
- serializer.newline()
- serializer.decreaseIndent()
- serializer.emitIndent()
-
- serializer.append("else ")
- serializer.blockStart()
- serializer.emitIndent()
- serializer.appendFormat("{0} = {1};", initValuename, self.increment)
- serializer.newline()
-
- serializer.emitIndent()
- program.config.serializeUpdate(
- serializer, self.dataMapName, keyname, initValuename)
- serializer.newline()
- serializer.blockEnd(True)
+++ /dev/null
-# Copyright (c) Barefoot Networks, Inc.
-# Licensed under the Apache License, Version 2.0 (the "License")
-
-from collections import defaultdict, OrderedDict
-from compilationException import CompilationException
-from p4_hlir.hlir import parse_call, p4_field, p4_parse_value_set, \
- P4_DEFAULT, p4_parse_state, p4_table, \
- p4_conditional_node, p4_parser_exception, \
- p4_header_instance, P4_NEXT
-
-import ebpfProgram
-import ebpfInstance
-import ebpfType
-import ebpfStructType
-from topoSorting import Graph
-from programSerializer import ProgramSerializer
-
-def produce_parser_topo_sorting(hlir):
- # This function is copied from the P4 behavioral model implementation
- header_graph = Graph()
-
- def walk_rec(hlir, parse_state, prev_hdr_node, tag_stacks_index):
- assert(isinstance(parse_state, p4_parse_state))
- for call in parse_state.call_sequence:
- call_type = call[0]
- if call_type == parse_call.extract:
- hdr = call[1]
-
- if hdr.virtual:
- base_name = hdr.base_name
- current_index = tag_stacks_index[base_name]
- if current_index > hdr.max_index:
- return
- tag_stacks_index[base_name] += 1
- name = base_name + "[%d]" % current_index
- hdr = hlir.p4_header_instances[name]
-
- if hdr not in header_graph:
- header_graph.add_node(hdr)
- hdr_node = header_graph.get_node(hdr)
-
- if prev_hdr_node:
- prev_hdr_node.add_edge_to(hdr_node)
- else:
- header_graph.root = hdr
- prev_hdr_node = hdr_node
-
- for branch_case, next_state in parse_state.branch_to.items():
- if not next_state:
- continue
- if not isinstance(next_state, p4_parse_state):
- continue
- walk_rec(hlir, next_state, prev_hdr_node, tag_stacks_index.copy())
-
- start_state = hlir.p4_parse_states["start"]
- walk_rec(hlir, start_state, None, defaultdict(int))
-
- header_topo_sorting = header_graph.produce_topo_sorting()
-
- return header_topo_sorting
-
-class EbpfDeparser(object):
- def __init__(self, hlir):
- header_topo_sorting = produce_parser_topo_sorting(hlir)
- self.headerOrder = [hdr.name for hdr in header_topo_sorting]
-
- def serialize(self, serializer, program):
- assert isinstance(serializer, ProgramSerializer)
- assert isinstance(program, ebpfProgram.EbpfProgram)
-
- serializer.emitIndent()
- serializer.blockStart()
- serializer.emitIndent()
- serializer.appendLine("/* Deparser */")
- serializer.emitIndent()
- serializer.appendFormat("{0} = 0;", program.offsetVariableName)
- serializer.newline()
- for h in self.headerOrder:
- header = program.getHeaderInstance(h)
- self.serializeHeaderEmit(header, serializer, program)
- serializer.blockEnd(True)
-
- def serializeHeaderEmit(self, header, serializer, program):
- assert isinstance(header, ebpfInstance.EbpfHeader)
- assert isinstance(serializer, ProgramSerializer)
- assert isinstance(program, ebpfProgram.EbpfProgram)
- p4header = header.hlirInstance
- assert isinstance(p4header, p4_header_instance)
-
- serializer.emitIndent()
- serializer.appendFormat("if ({0}.{1}.valid) ",
- program.headerStructName, header.name)
- serializer.blockStart()
-
- if ebpfProgram.EbpfProgram.isArrayElementInstance(p4header):
- ebpfStack = program.getStackInstance(p4header.base_name)
- assert isinstance(ebpfStack, ebpfInstance.EbpfHeaderStack)
-
- if isinstance(p4header.index, int):
- index = "[" + str(p4header.index) + "]"
- elif p4header.index is P4_NEXT:
- index = "[" + ebpfStack.indexVar + "]"
- else:
- raise CompilationException(
- True, "Unexpected index for array {0}",
- p4header.index)
- basetype = ebpfStack.basetype
- else:
- ebpfHeader = program.getHeaderInstance(p4header.name)
- basetype = ebpfHeader.type
- index = ""
-
- alignment = 0
- for field in basetype.fields:
- assert isinstance(field, ebpfStructType.EbpfField)
-
- self.serializeFieldEmit(serializer, p4header.base_name,
- index, field, alignment, program)
- alignment += field.widthInBits()
- alignment = alignment % 8
- serializer.blockEnd(True)
-
- def serializeFieldEmit(self, serializer, name, index,
- field, alignment, program):
- assert isinstance(index, str)
- assert isinstance(name, str)
- assert isinstance(field, ebpfStructType.EbpfField)
- assert isinstance(serializer, ProgramSerializer)
- assert isinstance(alignment, int)
- assert isinstance(program, ebpfProgram.EbpfProgram)
-
- if field.name == "valid":
- return
-
- fieldToEmit = (program.headerStructName + "." + name +
- index + "." + field.name)
- width = field.widthInBits()
- if width <= 32:
- store = self.generatePacketStore(fieldToEmit, 0, alignment,
- width, program)
- serializer.emitIndent()
- serializer.appendLine(store)
- else:
- # Destination is bigger than 4 bytes and
- # represented as a byte array.
- b = (width + 7) / 8
- for i in range(0, b):
- serializer.emitIndent()
- store = self.generatePacketStore(fieldToEmit + "["+str(i)+"]",
- i,
- alignment,
- 8, program)
- serializer.appendLine(store)
-
- serializer.emitIndent()
- serializer.appendFormat("{0} += {1};",
- program.offsetVariableName, width)
- serializer.newline()
-
- def generatePacketStore(self, value, offset, alignment, width, program):
- assert width > 0
- assert alignment < 8
- assert isinstance(width, int)
- assert isinstance(alignment, int)
-
- return "bpf_dins_pkt({0}, {1} / 8 + {2}, {3}, {4}, {5});".format(
- program.packetName,
- program.offsetVariableName,
- offset,
- alignment,
- width,
- value
- )
+++ /dev/null
-# Copyright (c) Barefoot Networks, Inc.
-# Licensed under the Apache License, Version 2.0 (the "License")
-
-from p4_hlir.hlir import p4_header_instance
-from ebpfType import EbpfType
-from compilationException import CompilationException
-from programSerializer import ProgramSerializer
-import typeFactory
-
-
-class EbpfInstanceBase(object):
- def __init__(self):
- pass
-
-
-class SimpleInstance(EbpfInstanceBase):
- # A header or a metadata instance (but not array elements)
- def __init__(self, hlirInstance, factory, isMetadata):
- super(SimpleInstance, self).__init__()
- self.hlirInstance = hlirInstance
- self.name = hlirInstance.base_name
- self.type = factory.build(hlirInstance.header_type, isMetadata)
-
- def declare(self, serializer):
- assert isinstance(serializer, ProgramSerializer)
- self.type.declare(serializer, self.name, False)
-
-
-class EbpfHeader(SimpleInstance):
- """ Represents a header instance from a P4 program """
- def __init__(self, hlirHeaderInstance, factory):
- super(EbpfHeader, self).__init__(hlirHeaderInstance, factory, False)
- if hlirHeaderInstance.metadata:
- raise CompilationException(True, "Metadata passed to EpbfHeader")
- if hlirHeaderInstance.index is not None:
- self.name += "_" + str(hlirHeaderInstance.index)
-
-
-class EbpfMetadata(SimpleInstance):
- """Represents a metadata instance from a P4 program"""
- def __init__(self, hlirMetadataInstance, factory):
- super(EbpfMetadata, self).__init__(hlirMetadataInstance, factory, True)
- if not hlirMetadataInstance.metadata:
- raise CompilationException(
- True, "Header instance passed to EpbfMetadata {0}",
- hlirMetadataInstance)
- if hlirMetadataInstance.index is not None:
- raise CompilationException(
- True, "Unexpected metadata array {0}", self.hlirInstance)
- if hasattr(hlirMetadataInstance, "initializer"):
- self.initializer = hlirMetadataInstance.initializer
- else:
- self.initializer = None
-
- def emitInitializer(self, serializer):
- assert isinstance(serializer, ProgramSerializer)
- if self.initializer is None:
- self.type.emitInitializer(serializer)
- else:
- for key in self.initializer.keys():
- serializer.appendFormat(
- ".{0} = {1},", key, self.initializer[key])
-
-
-class EbpfHeaderStack(EbpfInstanceBase):
- """Represents a header stack instance; there is one instance of
- this class for each STACK, and not for each
- element of the stack, as in the HLIR"""
- def __init__(self, hlirInstance, indexVar, factory):
- super(EbpfHeaderStack, self).__init__()
-
- # indexVar: name of the ebpf variable that
- # holds the current index for this stack
- assert isinstance(indexVar, str)
- assert isinstance(factory, typeFactory.EbpfTypeFactory)
- assert isinstance(hlirInstance, p4_header_instance)
-
- self.indexVar = indexVar
- self.name = hlirInstance.base_name
- self.basetype = factory.build(hlirInstance.header_type, False)
- assert isinstance(self.basetype, EbpfType)
- self.arraySize = hlirInstance.max_index + 1
- self.hlirInstance = hlirInstance
-
- def declare(self, serializer):
- assert isinstance(serializer, ProgramSerializer)
- self.basetype.declareArray(serializer, self.name, self.arraySize)
+++ /dev/null
-# Copyright (c) Barefoot Networks, Inc.
-# Licensed under the Apache License, Version 2.0 (the "License")
-
-from p4_hlir.hlir import parse_call, p4_field, p4_parse_value_set, \
- P4_DEFAULT, p4_parse_state, p4_table, \
- p4_conditional_node, p4_parser_exception, \
- p4_header_instance, P4_NEXT
-import ebpfProgram
-import ebpfStructType
-import ebpfInstance
-import programSerializer
-from compilationException import *
-
-
-class EbpfParser(object):
- def __init__(self, hlirParser): # hlirParser is a P4 parser
- self.parser = hlirParser
- self.name = hlirParser.name
-
- def serialize(self, serializer, program):
- assert isinstance(serializer, programSerializer.ProgramSerializer)
- assert isinstance(program, ebpfProgram.EbpfProgram)
-
- serializer.emitIndent()
- serializer.appendFormat("{0}: ", self.name)
- serializer.blockStart()
- for op in self.parser.call_sequence:
- self.serializeOperation(serializer, op, program)
-
- self.serializeBranch(serializer, self.parser.branch_on,
- self.parser.branch_to, program)
-
- serializer.blockEnd(True)
-
- def serializeSelect(self, selectVarName, serializer, branch_on, program):
- # selectVarName - name of temp variable to use for the select expression
- assert isinstance(selectVarName, str)
- assert isinstance(serializer, programSerializer.ProgramSerializer)
- assert isinstance(program, ebpfProgram.EbpfProgram)
-
- totalWidth = 0
- switchValue = ""
- for e in branch_on:
- if isinstance(e, p4_field):
- instance = e.instance
- assert isinstance(instance, p4_header_instance)
- index = ""
-
- if ebpfProgram.EbpfProgram.isArrayElementInstance(instance):
- ebpfStack = program.getStackInstance(instance.base_name)
- assert isinstance(ebpfStack, ebpfInstance.EbpfHeaderStack)
-
- if isinstance(instance.index, int):
- index = "[" + str(instance.index) + "]"
- elif instance.index is P4_NEXT:
- index = "[" + ebpfStack.indexVar + "]"
- else:
- raise CompilationException(True,
- "Unexpected index for array {0}", instance.index)
- basetype = ebpfStack.basetype
- name = ebpfStack.name
- else:
- ebpfHeader = program.getInstance(instance.name)
- assert isinstance(ebpfHeader, ebpfInstance.EbpfHeader)
- basetype = ebpfHeader.type
- name = ebpfHeader.name
-
- ebpfField = basetype.getField(e.name)
- assert isinstance(ebpfField, ebpfStructType.EbpfField)
-
- totalWidth += ebpfField.widthInBits()
- fieldReference = (program.headerStructName + "." + name +
- index + "." + ebpfField.name)
-
- if switchValue == "":
- switchValue = fieldReference
- else:
- switchValue = ("(" + switchValue + " << " +
- str(ebpfField.widthInBits()) + ")")
- switchValue = switchValue + " | " + fieldReference
- elif isinstance(e, tuple):
- switchValue = self.currentReferenceAsString(e, program)
- else:
- raise CompilationException(
- True, "Unexpected element in match {0}", e)
-
- if totalWidth > 32:
- raise NotSupportedException("{0}: Matching on {1}-bit value",
- branch_on, totalWidth)
- serializer.emitIndent()
- serializer.appendFormat("{0}32 {1} = {2};",
- program.config.uprefix,
- selectVarName, switchValue)
- serializer.newline()
-
- def generatePacketLoad(self, startBit, width, alignment, program):
- # Generates an expression that does a load_*, shift and mask
- # to load 'width' bits starting at startBit from the current
- # packet offset.
- # alignment is an integer <= 8 that holds the current alignment
- # of of the packet offset.
- assert width > 0
- assert alignment < 8
- assert isinstance(startBit, int)
- assert isinstance(width, int)
- assert isinstance(alignment, int)
-
- firstBitIndex = startBit + alignment
- lastBitIndex = startBit + width + alignment - 1
- firstWordIndex = firstBitIndex / 8
- lastWordIndex = lastBitIndex / 8
-
- wordsToRead = lastWordIndex - firstWordIndex + 1
- if wordsToRead == 1:
- load = "load_byte"
- loadSize = 8
- elif wordsToRead == 2:
- load = "load_half"
- loadSize = 16
- elif wordsToRead <= 4:
- load = "load_word"
- loadSize = 32
- elif wordsToRead <= 8:
- load = "load_dword"
- loadSize = 64
- else:
- raise CompilationException(True, "Attempt to load more than 1 word")
-
- readtype = program.config.uprefix + str(loadSize)
- loadInstruction = "{0}({1}, ({2} + {3}) / 8)".format(
- load, program.packetName, program.offsetVariableName, startBit)
- shift = loadSize - alignment - width
- load = "(({0}) >> ({1}))".format(loadInstruction, shift)
- if width != loadSize:
- mask = " & EBPF_MASK({0}, {1})".format(readtype, width)
- else:
- mask = ""
- return load + mask
-
- def currentReferenceAsString(self, tpl, program):
- # a string describing an expression of the form current(position, width)
- # The assumption is that at this point the packet cursor is ALWAYS
- # byte aligned. This should be true because headers are supposed
- # to have sizes an integral number of bytes.
- assert isinstance(tpl, tuple)
- if len(tpl) != 2:
- raise CompilationException(
- True, "{0} Expected a tuple with 2 elements", tpl)
-
- minIndex = tpl[0]
- totalWidth = tpl[1]
- result = self.generatePacketLoad(
- minIndex, totalWidth, 0, program) # alignment is 0
- return result
-
- def serializeCases(self, selectVarName, serializer, branch_to, program):
- assert isinstance(selectVarName, str)
- assert isinstance(serializer, programSerializer.ProgramSerializer)
- assert isinstance(program, ebpfProgram.EbpfProgram)
-
- branches = 0
- seenDefault = False
- for e in branch_to.keys():
- serializer.emitIndent()
- value = branch_to[e]
-
- if isinstance(e, int):
- serializer.appendFormat("if ({0} == {1})", selectVarName, e)
- elif isinstance(e, tuple):
- serializer.appendFormat(
- "if (({0} & {1}) == {2})", selectVarName, e[0], e[1])
- elif isinstance(e, p4_parse_value_set):
- raise NotSupportedException("{0}: Parser value sets", e)
- elif e is P4_DEFAULT:
- seenDefault = True
- if branches > 0:
- serializer.append("else")
- else:
- raise CompilationException(
- True, "Unexpected element in match case {0}", e)
-
- branches += 1
- serializer.newline()
- serializer.increaseIndent()
- serializer.emitIndent()
-
- label = program.getLabel(value)
-
- if isinstance(value, p4_parse_state):
- serializer.appendFormat("goto {0};", label)
- elif isinstance(value, p4_table):
- serializer.appendFormat("goto {0};", label)
- elif isinstance(value, p4_conditional_node):
- serializer.appendFormat("goto {0};", label)
- elif isinstance(value, p4_parser_exception):
- raise CompilationException(True, "Not yet implemented")
- else:
- raise CompilationException(
- True, "Unexpected element in match case {0}", value)
-
- serializer.decreaseIndent()
- serializer.newline()
-
- # Must create default if it is missing
- if not seenDefault:
- serializer.emitIndent()
- serializer.appendFormat(
- "{0} = p4_pe_unhandled_select;", program.errorName)
- serializer.newline()
- serializer.emitIndent()
- serializer.appendFormat("default: goto end;")
- serializer.newline()
-
- def serializeBranch(self, serializer, branch_on, branch_to, program):
- assert isinstance(serializer, programSerializer.ProgramSerializer)
- assert isinstance(program, ebpfProgram.EbpfProgram)
-
- if branch_on == []:
- dest = branch_to.values()[0]
- serializer.emitIndent()
- name = program.getLabel(dest)
- serializer.appendFormat("goto {0};", name)
- serializer.newline()
- elif isinstance(branch_on, list):
- tmpvar = program.generateNewName("tmp")
- self.serializeSelect(tmpvar, serializer, branch_on, program)
- self.serializeCases(tmpvar, serializer, branch_to, program)
- else:
- raise CompilationException(
- True, "Unexpected branch_on {0}", branch_on)
-
- def serializeOperation(self, serializer, op, program):
- assert isinstance(serializer, programSerializer.ProgramSerializer)
- assert isinstance(program, ebpfProgram.EbpfProgram)
-
- operation = op[0]
- if operation is parse_call.extract:
- self.serializeExtract(serializer, op[1], program)
- elif operation is parse_call.set:
- self.serializeMetadataSet(serializer, op[1], op[2], program)
- else:
- raise CompilationException(
- True, "Unexpected operation in parser {0}", op)
-
- def serializeFieldExtract(self, serializer, headerInstanceName,
- index, field, alignment, program):
- assert isinstance(index, str)
- assert isinstance(headerInstanceName, str)
- assert isinstance(field, ebpfStructType.EbpfField)
- assert isinstance(serializer, programSerializer.ProgramSerializer)
- assert isinstance(alignment, int)
- assert isinstance(program, ebpfProgram.EbpfProgram)
-
- fieldToExtractTo = headerInstanceName + index + "." + field.name
-
- serializer.emitIndent()
- width = field.widthInBits()
- if field.name == "valid":
- serializer.appendFormat(
- "{0}.{1} = 1;", program.headerStructName, fieldToExtractTo)
- serializer.newline()
- return
-
- serializer.appendFormat("if ({0}->len < BYTES({1} + {2})) ",
- program.packetName,
- program.offsetVariableName, width)
- serializer.blockStart()
- serializer.emitIndent()
- serializer.appendFormat("{0} = p4_pe_header_too_short;",
- program.errorName)
- serializer.newline()
- serializer.emitIndent()
- serializer.appendLine("goto end;")
- # TODO: jump to correct exception handler
- serializer.blockEnd(True)
-
- if width <= 32:
- serializer.emitIndent()
- load = self.generatePacketLoad(0, width, alignment, program)
-
- serializer.appendFormat("{0}.{1} = {2};",
- program.headerStructName,
- fieldToExtractTo, load)
- serializer.newline()
- else:
- # Destination is bigger than 4 bytes and
- # represented as a byte array.
- if alignment == 0:
- shift = 0
- else:
- shift = 8 - alignment
-
- assert shift >= 0
- if shift == 0:
- method = "load_byte"
- else:
- method = "load_half"
- b = (width + 7) / 8
- for i in range(0, b):
- serializer.emitIndent()
- serializer.appendFormat("{0}.{1}[{2}] = ({3}8)",
- program.headerStructName,
- fieldToExtractTo, i,
- program.config.uprefix)
- serializer.appendFormat("(({0}({1}, ({2} / 8) + {3}) >> {4})",
- method, program.packetName,
- program.offsetVariableName, i, shift)
- if (i == b - 1) and (width % 8 != 0):
- serializer.appendFormat(" & EBPF_MASK({0}8, {1})",
- program.config.uprefix, width % 8)
- serializer.append(")")
- serializer.endOfStatement(True)
-
- serializer.emitIndent()
- serializer.appendFormat("{0} += {1};",
- program.offsetVariableName, width)
- serializer.newline()
-
- def serializeExtract(self, serializer, headerInstance, program):
- assert isinstance(serializer, programSerializer.ProgramSerializer)
- assert isinstance(headerInstance, p4_header_instance)
- assert isinstance(program, ebpfProgram.EbpfProgram)
-
- if ebpfProgram.EbpfProgram.isArrayElementInstance(headerInstance):
- ebpfStack = program.getStackInstance(headerInstance.base_name)
- assert isinstance(ebpfStack, ebpfInstance.EbpfHeaderStack)
-
- # write bounds check
- serializer.emitIndent()
- serializer.appendFormat("if ({0} >= {1}) ",
- ebpfStack.indexVar, ebpfStack.arraySize)
- serializer.blockStart()
- serializer.emitIndent()
- serializer.appendFormat("{0} = p4_pe_index_out_of_bounds;",
- program.errorName)
- serializer.newline()
- serializer.emitIndent()
- serializer.appendLine("goto end;")
- serializer.blockEnd(True)
-
- if isinstance(headerInstance.index, int):
- index = "[" + str(headerInstance.index) + "]"
- elif headerInstance.index is P4_NEXT:
- index = "[" + ebpfStack.indexVar + "]"
- else:
- raise CompilationException(
- True, "Unexpected index for array {0}",
- headerInstance.index)
- basetype = ebpfStack.basetype
- else:
- ebpfHeader = program.getHeaderInstance(headerInstance.name)
- basetype = ebpfHeader.type
- index = ""
-
- # extract all fields
- alignment = 0
- for field in basetype.fields:
- assert isinstance(field, ebpfStructType.EbpfField)
-
- self.serializeFieldExtract(serializer, headerInstance.base_name,
- index, field, alignment, program)
- alignment += field.widthInBits()
- alignment = alignment % 8
-
- if ebpfProgram.EbpfProgram.isArrayElementInstance(headerInstance):
- # increment stack index
- ebpfStack = program.getStackInstance(headerInstance.base_name)
- assert isinstance(ebpfStack, ebpfInstance.EbpfHeaderStack)
-
- # write bounds check
- serializer.emitIndent()
- serializer.appendFormat("{0}++;", ebpfStack.indexVar)
- serializer.newline()
-
- def serializeMetadataSet(self, serializer, field, value, program):
- assert isinstance(serializer, programSerializer.ProgramSerializer)
- assert isinstance(program, ebpfProgram.EbpfProgram)
- assert isinstance(field, p4_field)
-
- dest = program.getInstance(field.instance.name)
- assert isinstance(dest, ebpfInstance.SimpleInstance)
- destType = dest.type
- assert isinstance(destType, ebpfStructType.EbpfStructType)
- destField = destType.getField(field.name)
-
- if destField.widthInBits() > 32:
- useMemcpy = True
- bytesToCopy = destField.widthInBits() / 8
- if destField.widthInBits() % 8 != 0:
- raise CompilationException(
- True,
- "{0}: Not implemented: wide field w. sz not multiple of 8",
- field)
- else:
- useMemcpy = False
- bytesToCopy = None # not needed, but compiler is confused
-
- serializer.emitIndent()
- destination = "{0}.{1}.{2}".format(
- program.metadataStructName, dest.name, destField.name)
- if isinstance(value, int):
- source = str(value)
- if useMemcpy:
- raise CompilationException(
- True,
- "{0}: Not implemented: copying from wide constant",
- value)
- elif isinstance(value, tuple):
- source = self.currentReferenceAsString(value, program)
- elif isinstance(value, p4_field):
- source = program.getInstance(value.instance.name)
- if isinstance(source, ebpfInstance.EbpfMetadata):
- sourceStruct = program.metadataStructName
- else:
- sourceStruct = program.headerStructName
- source = "{0}.{1}.{2}".format(sourceStruct, source.name, value.name)
- else:
- raise CompilationException(
- True, "Unexpected type for parse_call.set {0}", value)
-
- if useMemcpy:
- serializer.appendFormat("memcpy(&{0}, &{1}, {2})",
- destination, source, bytesToCopy)
- else:
- serializer.appendFormat("{0} = {1}", destination, source)
-
- serializer.endOfStatement(True)
+++ /dev/null
-# Copyright (c) Barefoot Networks, Inc.
-# Licensed under the Apache License, Version 2.0 (the "License")
-
-from p4_hlir.hlir import p4_header_instance, p4_table, \
- p4_conditional_node, p4_action, p4_parse_state
-from p4_hlir.main import HLIR
-import typeFactory
-import ebpfTable
-import ebpfParser
-import ebpfAction
-import ebpfInstance
-import ebpfConditional
-import ebpfCounter
-import ebpfDeparser
-import programSerializer
-import target
-from compilationException import *
-
-
-class EbpfProgram(object):
- def __init__(self, name, hlir, isRouter, config):
- """Representation of an EbpfProgram (in fact,
- a C program that is converted to EBPF)"""
- assert isinstance(hlir, HLIR)
- assert isinstance(isRouter, bool)
- assert isinstance(config, target.TargetConfig)
-
- self.hlir = hlir
- self.name = name
- self.uniqueNameCounter = 0
- self.config = config
- self.isRouter = isRouter
- self.reservedPrefix = "ebpf_"
-
- assert isinstance(config, target.TargetConfig)
-
- self.packetName = self.reservedPrefix + "packet"
- self.dropBit = self.reservedPrefix + "drop"
- self.license = "GPL"
- self.offsetVariableName = self.reservedPrefix + "packetOffsetInBits"
- self.zeroKeyName = self.reservedPrefix + "zero"
- self.arrayIndexType = self.config.uprefix + "32"
- # all array tables must be indexed with u32 values
-
- self.errorName = self.reservedPrefix + "error"
- self.functionName = self.reservedPrefix + "filter"
- self.egressPortName = "egress_port" # Hardwired in P4 definition
-
- self.typeFactory = typeFactory.EbpfTypeFactory(config)
- self.errorCodes = [
- "p4_pe_no_error",
- "p4_pe_index_out_of_bounds",
- "p4_pe_out_of_packet",
- "p4_pe_header_too_long",
- "p4_pe_header_too_short",
- "p4_pe_unhandled_select",
- "p4_pe_checksum"]
-
- self.actions = []
- self.conditionals = []
- self.tables = []
- self.headers = [] # header instances
- self.metadata = [] # metadata instances
- self.stacks = [] # header stack instances EbpfHeaderStack
- self.parsers = [] # all parsers
- self.deparser = None
- self.entryPoints = [] # control-flow entry points from parser
- self.counters = []
- self.entryPointLabels = {} # maps p4_node from entryPoints
- # to labels in the C program
- self.egressEntry = None
-
- self.construct()
-
- self.headersStructTypeName = self.reservedPrefix + "headers_t"
- self.headerStructName = self.reservedPrefix + "headers"
- self.metadataStructTypeName = self.reservedPrefix + "metadata_t"
- self.metadataStructName = self.reservedPrefix + "metadata"
-
- def construct(self):
- if len(self.hlir.p4_field_list_calculations) > 0:
- raise NotSupportedException(
- "{0} calculated field",
- self.hlir.p4_field_list_calculations.values()[0].name)
-
- for h in self.hlir.p4_header_instances.values():
- if h.max_index is not None:
- assert isinstance(h, p4_header_instance)
- if h.index == 0:
- # header stack; allocate only for zero-th index
- indexVarName = self.generateNewName(h.base_name + "_index")
- stack = ebpfInstance.EbpfHeaderStack(
- h, indexVarName, self.typeFactory)
- self.stacks.append(stack)
- elif h.metadata:
- metadata = ebpfInstance.EbpfMetadata(h, self.typeFactory)
- self.metadata.append(metadata)
- else:
- header = ebpfInstance.EbpfHeader(h, self.typeFactory)
- self.headers.append(header)
-
- for p in self.hlir.p4_parse_states.values():
- parser = ebpfParser.EbpfParser(p)
- self.parsers.append(parser)
-
- for a in self.hlir.p4_actions.values():
- if self.isInternalAction(a):
- continue
- action = ebpfAction.EbpfAction(a, self)
- self.actions.append(action)
-
- for c in self.hlir.p4_counters.values():
- counter = ebpfCounter.EbpfCounter(c, self)
- self.counters.append(counter)
-
- for t in self.hlir.p4_tables.values():
- table = ebpfTable.EbpfTable(t, self, self.config)
- self.tables.append(table)
-
- for n in self.hlir.p4_ingress_ptr.keys():
- self.entryPoints.append(n)
-
- for n in self.hlir.p4_conditional_nodes.values():
- conditional = ebpfConditional.EbpfConditional(n, self)
- self.conditionals.append(conditional)
-
- self.egressEntry = self.hlir.p4_egress_ptr
- self.deparser = ebpfDeparser.EbpfDeparser(self.hlir)
-
- def isInternalAction(self, action):
- # This is a heuristic really to guess which actions are built-in
- # Unfortunately there seems to be no other way to do this
- return action.lineno < 0
-
- @staticmethod
- def isArrayElementInstance(headerInstance):
- assert isinstance(headerInstance, p4_header_instance)
- return headerInstance.max_index is not None
-
- def emitWarning(self, formatString, *message):
- assert isinstance(formatString, str)
- print("WARNING: ", formatString.format(*message))
-
- def toC(self, serializer):
- assert isinstance(serializer, programSerializer.ProgramSerializer)
-
- self.generateIncludes(serializer)
- self.generatePreamble(serializer)
- self.generateTypes(serializer)
- self.generateTables(serializer)
-
- serializer.newline()
- serializer.emitIndent()
- self.config.serializeCodeSection(serializer)
- serializer.newline()
- serializer.emitIndent()
- serializer.appendFormat("int {0}(struct __sk_buff* {1}) ",
- self.functionName, self.packetName)
- serializer.blockStart()
-
- self.generateHeaderInstance(serializer)
- serializer.append(" = ")
- self.generateInitializeHeaders(serializer)
- serializer.endOfStatement(True)
-
- self.generateMetadataInstance(serializer)
- serializer.append(" = ")
- self.generateInitializeMetadata(serializer)
- serializer.endOfStatement(True)
-
- self.createLocalVariables(serializer)
- serializer.newline()
-
- serializer.emitIndent()
- serializer.appendLine("goto start;")
-
- self.generateParser(serializer)
- self.generatePipeline(serializer)
-
- self.generateDeparser(serializer)
-
- serializer.emitIndent()
- serializer.appendLine("end:")
- serializer.emitIndent()
-
- if isinstance(self.config, target.KernelSamplesConfig):
- serializer.appendFormat("return {0};", self.dropBit)
- serializer.newline()
- elif isinstance(self.config, target.BccConfig):
- if self.isRouter:
- serializer.appendFormat("if (!{0})", self.dropBit)
- serializer.newline()
- serializer.increaseIndent()
- serializer.emitIndent()
- serializer.appendFormat(
- "bpf_clone_redirect({0}, {1}.standard_metadata.{2}, 0);",
- self.packetName, self.metadataStructName,
- self.egressPortName)
- serializer.newline()
- serializer.decreaseIndent()
-
- serializer.emitIndent()
- serializer.appendLine(
- "return TC_ACT_SHOT /* drop packet; clone is forwarded */;")
- else:
- serializer.appendFormat(
- "return {1} ? TC_ACT_SHOT : TC_ACT_PIPE;",
- self.dropBit)
- serializer.newline()
- else:
- raise CompilationException(
- True, "Unexpected target configuration {0}",
- self.config.targetName)
- serializer.blockEnd(True)
-
- self.generateLicense(serializer)
-
- serializer.append(self.config.postamble)
-
- def generateLicense(self, serializer):
- self.config.serializeLicense(serializer, self.license)
-
- # noinspection PyMethodMayBeStatic
- def generateIncludes(self, serializer):
- assert isinstance(serializer, programSerializer.ProgramSerializer)
- serializer.append(self.config.getIncludes())
-
- def getLabel(self, p4node):
- # C label that corresponds to this point in the control-flow
- if p4node is None:
- return "end"
- elif isinstance(p4node, p4_parse_state):
- label = p4node.name
- self.entryPointLabels[p4node.name] = label
- if p4node.name not in self.entryPointLabels:
- label = self.generateNewName(p4node.name)
- self.entryPointLabels[p4node.name] = label
- return self.entryPointLabels[p4node.name]
-
- # noinspection PyMethodMayBeStatic
- def generatePreamble(self, serializer):
- assert isinstance(serializer, programSerializer.ProgramSerializer)
-
- serializer.emitIndent()
- serializer.append("enum ErrorCode ")
- serializer.blockStart()
- for error in self.errorCodes:
- serializer.emitIndent()
- serializer.appendFormat("{0},", error)
- serializer.newline()
- serializer.blockEnd(False)
- serializer.endOfStatement(True)
- serializer.newline()
-
- serializer.appendLine(
- "#define EBPF_MASK(t, w) ((((t)(1)) << (w)) - (t)1)")
- serializer.appendLine("#define BYTES(w) ((w + 7) / 8)")
-
- self.config.generateDword(serializer)
-
- # noinspection PyMethodMayBeStatic
- def generateNewName(self, base): # base is a string
- """Generates a fresh name based on the specified base name"""
- # TODO: this should be made "safer"
- assert isinstance(base, str)
-
- base += "_" + str(self.uniqueNameCounter)
- self.uniqueNameCounter += 1
- return base
-
- def generateTypes(self, serializer):
- assert isinstance(serializer, programSerializer.ProgramSerializer)
-
- for t in self.typeFactory.type_map.values():
- t.serialize(serializer)
-
- # generate a new struct type for the packet itself
- serializer.appendFormat("struct {0} ", self.headersStructTypeName)
- serializer.blockStart()
- for h in self.headers:
- serializer.emitIndent()
- h.declare(serializer)
- serializer.endOfStatement(True)
-
- for h in self.stacks:
- assert isinstance(h, ebpfInstance.EbpfHeaderStack)
-
- serializer.emitIndent()
- h.declare(serializer)
- serializer.endOfStatement(True)
-
- serializer.blockEnd(False)
- serializer.endOfStatement(True)
-
- # generate a new struct type for the metadata
- serializer.appendFormat("struct {0} ", self.metadataStructTypeName)
- serializer.blockStart()
- for h in self.metadata:
- assert isinstance(h, ebpfInstance.EbpfMetadata)
-
- serializer.emitIndent()
- h.declare(serializer)
- serializer.endOfStatement(True)
- serializer.blockEnd(False)
- serializer.endOfStatement(True)
-
- def generateTables(self, serializer):
- assert isinstance(serializer, programSerializer.ProgramSerializer)
-
- for t in self.tables:
- t.serialize(serializer, self)
-
- for c in self.counters:
- c.serialize(serializer, self)
-
- def generateHeaderInstance(self, serializer):
- assert isinstance(serializer, programSerializer.ProgramSerializer)
-
- serializer.emitIndent()
- serializer.appendFormat(
- "struct {0} {1}", self.headersStructTypeName, self.headerStructName)
-
- def generateInitializeHeaders(self, serializer):
- assert isinstance(serializer, programSerializer.ProgramSerializer)
-
- serializer.blockStart()
- for h in self.headers:
- serializer.emitIndent()
- serializer.appendFormat(".{0} = ", h.name)
- h.type.emitInitializer(serializer)
- serializer.appendLine(",")
- serializer.blockEnd(False)
-
- def generateMetadataInstance(self, serializer):
- assert isinstance(serializer, programSerializer.ProgramSerializer)
-
- serializer.emitIndent()
- serializer.appendFormat(
- "struct {0} {1}",
- self.metadataStructTypeName,
- self.metadataStructName)
-
- def generateDeparser(self, serializer):
- self.deparser.serialize(serializer, self)
-
- def generateInitializeMetadata(self, serializer):
- assert isinstance(serializer, programSerializer.ProgramSerializer)
-
- serializer.blockStart()
- for h in self.metadata:
- serializer.emitIndent()
- serializer.appendFormat(".{0} = ", h.name)
- h.emitInitializer(serializer)
- serializer.appendLine(",")
- serializer.blockEnd(False)
-
- def createLocalVariables(self, serializer):
- assert isinstance(serializer, programSerializer.ProgramSerializer)
-
- serializer.emitIndent()
- serializer.appendFormat("unsigned {0} = 0;", self.offsetVariableName)
- serializer.newline()
-
- serializer.emitIndent()
- serializer.appendFormat(
- "enum ErrorCode {0} = p4_pe_no_error;", self.errorName)
- serializer.newline()
-
- serializer.emitIndent()
- serializer.appendFormat(
- "{0}8 {1} = 0;", self.config.uprefix, self.dropBit)
- serializer.newline()
-
- serializer.emitIndent()
- serializer.appendFormat(
- "{0} {1} = 0;", self.arrayIndexType, self.zeroKeyName)
- serializer.newline()
-
- for h in self.stacks:
- serializer.emitIndent()
- serializer.appendFormat(
- "{0}8 {0} = 0;", self.config.uprefix, h.indexVar)
- serializer.newline()
-
- def getStackInstance(self, name):
- assert isinstance(name, str)
-
- for h in self.stacks:
- if h.name == name:
- assert isinstance(h, ebpfInstance.EbpfHeaderStack)
- return h
- raise CompilationException(
- True, "Could not locate header stack named {0}", name)
-
- def getHeaderInstance(self, name):
- assert isinstance(name, str)
-
- for h in self.headers:
- if h.name == name:
- assert isinstance(h, ebpfInstance.EbpfHeader)
- return h
- raise CompilationException(
- True, "Could not locate header instance named {0}", name)
-
- def getInstance(self, name):
- assert isinstance(name, str)
-
- for h in self.headers:
- if h.name == name:
- return h
- for h in self.metadata:
- if h.name == name:
- return h
- raise CompilationException(
- True, "Could not locate instance named {0}", name)
-
- def getAction(self, p4action):
- assert isinstance(p4action, p4_action)
- for a in self.actions:
- if a.name == p4action.name:
- return a
-
- newAction = ebpfAction.BuiltinAction(p4action)
- self.actions.append(newAction)
- return newAction
-
- def getTable(self, name):
- assert isinstance(name, str)
- for t in self.tables:
- if t.name == name:
- return t
- raise CompilationException(
- True, "Could not locate table named {0}", name)
-
- def getCounter(self, name):
- assert isinstance(name, str)
- for t in self.counters:
- if t.name == name:
- return t
- raise CompilationException(
- True, "Could not locate counters named {0}", name)
-
- def getConditional(self, name):
- assert isinstance(name, str)
- for c in self.conditionals:
- if c.name == name:
- return c
- raise CompilationException(
- True, "Could not locate conditional named {0}", name)
-
- def generateParser(self, serializer):
- assert isinstance(serializer, programSerializer.ProgramSerializer)
- for p in self.parsers:
- p.serialize(serializer, self)
-
- def generateIngressPipeline(self, serializer):
- assert isinstance(serializer, programSerializer.ProgramSerializer)
- for t in self.tables:
- assert isinstance(t, ebpfTable.EbpfTable)
- serializer.emitIndent()
- serializer.appendFormat("{0}:", t.name)
- serializer.newline()
-
- def generateControlFlowNode(self, serializer, node, nextEntryPoint):
- # nextEntryPoint is used as a target whenever the target is None
- # nextEntryPoint may also be None
- if isinstance(node, p4_table):
- table = self.getTable(node.name)
- assert isinstance(table, ebpfTable.EbpfTable)
- table.serializeCode(serializer, self, nextEntryPoint)
- elif isinstance(node, p4_conditional_node):
- conditional = self.getConditional(node.name)
- assert isinstance(conditional, ebpfConditional.EbpfConditional)
- conditional.generateCode(serializer, self, nextEntryPoint)
- else:
- raise CompilationException(
- True, "{0} Unexpected control flow node ", node)
-
- def generatePipelineInternal(self, serializer, nodestoadd, nextEntryPoint):
- assert isinstance(serializer, programSerializer.ProgramSerializer)
- assert isinstance(nodestoadd, set)
-
- done = set()
- while len(nodestoadd) > 0:
- todo = nodestoadd.pop()
- if todo in done:
- continue
- if todo is None:
- continue
-
- print("Generating ", todo.name)
-
- done.add(todo)
- self.generateControlFlowNode(serializer, todo, nextEntryPoint)
-
- for n in todo.next_.values():
- nodestoadd.add(n)
-
- def generatePipeline(self, serializer):
- todo = set()
- for e in self.entryPoints:
- todo.add(e)
- self.generatePipelineInternal(serializer, todo, self.egressEntry)
- todo = set()
- todo.add(self.egressEntry)
- self.generatePipelineInternal(serializer, todo, None)
+++ /dev/null
-# Copyright (c) Barefoot Networks, Inc.
-# Licensed under the Apache License, Version 2.0 (the "License")
-
-from p4_hlir.hlir import P4_AUTO_WIDTH
-from ebpfType import *
-from compilationException import *
-from programSerializer import ProgramSerializer
-
-
-class EbpfScalarType(EbpfType):
- __doc__ = "Represents a scalar type"
- def __init__(self, parent, widthInBits, isSigned, config):
- super(EbpfScalarType, self).__init__(None)
- assert isinstance(widthInBits, int)
- assert isinstance(isSigned, bool)
- self.width = widthInBits
- self.isSigned = isSigned
- self.config = config
- if widthInBits is P4_AUTO_WIDTH:
- raise NotSupportedException("{0} Variable-width field", parent)
-
- def widthInBits(self):
- return self.width
-
- @staticmethod
- def bytesRequired(width):
- return (width + 7) / 8
-
- def asString(self):
- if self.isSigned:
- prefix = self.config.iprefix
- else:
- prefix = self.config.uprefix
-
- if self.width <= 8:
- name = prefix + "8"
- elif self.width <= 16:
- name = prefix + "16"
- elif self.width <= 32:
- name = prefix + "32"
- else:
- name = "char*"
- return name
-
- def alignment(self):
- if self.width <= 8:
- return 1
- elif self.width <= 16:
- return 2
- elif self.width <= 32:
- return 4
- else:
- return 1 # Char array
-
- def serialize(self, serializer):
- assert isinstance(serializer, ProgramSerializer)
- serializer.append(self.asString())
-
- def declareArray(self, serializer, identifier, size):
- raise CompilationException(
- True, "Arrays of base type not expected in P4")
-
- def declare(self, serializer, identifier, asPointer):
- assert isinstance(serializer, ProgramSerializer)
- assert isinstance(asPointer, bool)
- assert isinstance(identifier, str)
-
- if self.width <= 32:
- self.serialize(serializer)
- if asPointer:
- serializer.append("*")
- serializer.space()
- serializer.append(identifier)
- else:
- if asPointer:
- serializer.append("char*")
- else:
- serializer.appendFormat(
- "char {0}[{1}]", identifier,
- EbpfScalarType.bytesRequired(self.width))
-
- def emitInitializer(self, serializer):
- assert isinstance(serializer, ProgramSerializer)
- serializer.append("0")
+++ /dev/null
-# Copyright (c) Barefoot Networks, Inc.
-# Licensed under the Apache License, Version 2.0 (the "License")
-
-from p4_hlir.hlir import P4_SIGNED, P4_SATURATING
-from ebpfScalarType import *
-
-
-class EbpfField(object):
- __doc__ = "represents a field in a struct type, not in an instance"
-
- def __init__(self, hlirParentType, name, widthInBits, attributes, config):
- self.name = name
- self.width = widthInBits
- self.hlirType = hlirParentType
- signed = False
- if P4_SIGNED in attributes:
- signed = True
- if P4_SATURATING in attributes:
- raise NotSupportedException(
- "{0}.{1}: Saturated types", self.hlirType, self.name)
-
- try:
- self.type = EbpfScalarType(
- self.hlirType, widthInBits, signed, config)
- except CompilationException as e:
- raise CompilationException(
- e.isBug, "{0}.{1}: {2}", hlirParentType, self.name, e.show())
-
- def widthInBits(self):
- return self.width
-
-
-class EbpfStructType(EbpfType):
- # Abstract base class for HeaderType and MetadataType.
- # They are both represented by a p4 header_type
- def __init__(self, hlirHeader, config):
- super(EbpfStructType, self).__init__(hlirHeader)
- self.name = hlirHeader.name
- self.fields = []
-
- for (fieldName, fieldSize) in self.hlirType.layout.items():
- attributes = self.hlirType.attributes[fieldName]
- field = EbpfField(
- hlirHeader, fieldName, fieldSize, attributes, config)
- self.fields.append(field)
-
- def serialize(self, serializer):
- assert isinstance(serializer, ProgramSerializer)
-
- serializer.emitIndent()
- serializer.appendFormat("struct {0} ", self.name)
- serializer.blockStart()
-
- for field in self.fields:
- serializer.emitIndent()
- field.type.declare(serializer, field.name, False)
- serializer.appendFormat("; /* {0} bits */", field.widthInBits())
- serializer.newline()
-
- serializer.blockEnd(False)
- serializer.endOfStatement(True)
-
- def declare(self, serializer, identifier, asPointer):
- assert isinstance(serializer, ProgramSerializer)
- assert isinstance(identifier, str)
- assert isinstance(asPointer, bool)
-
- serializer.appendFormat("struct {0} ", self.name)
- if asPointer:
- serializer.append("*")
- serializer.append(identifier)
-
- def widthInBits(self):
- return self.hlirType.length * 8
-
- def getField(self, name):
- assert isinstance(name, str)
-
- for f in self.fields:
- assert isinstance(f, EbpfField)
- if f.name == name:
- return f
- raise CompilationException(
- True, "Could not locate field {0}.{1}", self, name)
-
-
-class EbpfHeaderType(EbpfStructType):
- def __init__(self, hlirHeader, config):
- super(EbpfHeaderType, self).__init__(hlirHeader, config)
- validField = EbpfField(hlirHeader, "valid", 1, set(), config)
- # check that no "valid" field exists already
- for f in self.fields:
- if f.name == "valid":
- raise CompilationException(
- True,
- "Header type contains a field named `valid': {0}",
- f)
- self.fields.append(validField)
-
- def emitInitializer(self, serializer):
- assert isinstance(serializer, ProgramSerializer)
- serializer.blockStart()
- serializer.emitIndent()
- serializer.appendLine(".valid = 0")
- serializer.blockEnd(False)
-
- def declareArray(self, serializer, identifier, size):
- assert isinstance(serializer, ProgramSerializer)
- serializer.appendFormat(
- "struct {0} {1}[{2}]", self.name, identifier, size)
-
-
-class EbpfMetadataType(EbpfStructType):
- def __init__(self, hlirHeader, config):
- super(EbpfMetadataType, self).__init__(hlirHeader, config)
-
- def emitInitializer(self, serializer):
- assert isinstance(serializer, ProgramSerializer)
-
- serializer.blockStart()
- for field in self.fields:
- serializer.emitIndent()
- serializer.appendFormat(".{0} = ", field.name)
-
- field.type.emitInitializer(serializer)
- serializer.append(",")
- serializer.newline()
- serializer.blockEnd(False)
+++ /dev/null
-# Copyright (c) Barefoot Networks, Inc.
-# Licensed under the Apache License, Version 2.0 (the "License")
-
-from p4_hlir.hlir import p4_match_type, p4_field, p4_table, p4_header_instance
-from programSerializer import ProgramSerializer
-from compilationException import *
-import ebpfProgram
-import ebpfInstance
-import ebpfCounter
-import ebpfStructType
-import ebpfAction
-
-
-class EbpfTableKeyField(object):
- def __init__(self, fieldname, instance, field, mask):
- assert isinstance(instance, ebpfInstance.EbpfInstanceBase)
- assert isinstance(field, ebpfStructType.EbpfField)
-
- self.keyFieldName = fieldname
- self.instance = instance
- self.field = field
- self.mask = mask
-
- def serializeType(self, serializer):
- assert isinstance(serializer, ProgramSerializer)
- ftype = self.field.type
- serializer.emitIndent()
- ftype.declare(serializer, self.keyFieldName, False)
- serializer.endOfStatement(True)
-
- def serializeConstruction(self, keyName, serializer, program):
- assert isinstance(serializer, ProgramSerializer)
- assert isinstance(keyName, str)
- assert isinstance(program, ebpfProgram.EbpfProgram)
-
- if self.mask is not None:
- maskExpression = " & {0}".format(self.mask)
- else:
- maskExpression = ""
-
- if isinstance(self.instance, ebpfInstance.EbpfMetadata):
- base = program.metadataStructName
- else:
- base = program.headerStructName
-
- if isinstance(self.instance, ebpfInstance.SimpleInstance):
- source = "{0}.{1}.{2}".format(
- base, self.instance.name, self.field.name)
- else:
- assert isinstance(self.instance, ebpfInstance.EbpfHeaderStack)
- source = "{0}.{1}[{2}].{3}".format(
- base, self.instance.name,
- self.instance.hlirInstance.index, self.field.name)
- destination = "{0}.{1}".format(keyName, self.keyFieldName)
- size = self.field.widthInBits()
-
- serializer.emitIndent()
- if size <= 32:
- serializer.appendFormat("{0} = ({1}){2};",
- destination, source, maskExpression)
- else:
- if maskExpression != "":
- raise NotSupportedException(
- "{0} Mask wider than 32 bits", self.field.hlirType)
- serializer.appendFormat(
- "memcpy(&{0}, &{1}, {2});", destination, source, size / 8)
-
- serializer.newline()
-
-
-class EbpfTableKey(object):
- def __init__(self, match_fields, program):
- assert isinstance(program, ebpfProgram.EbpfProgram)
-
- self.expressions = []
- self.fields = []
- self.masks = []
- self.fieldNamePrefix = "key_field_"
- self.program = program
-
- fieldNumber = 0
- for f in match_fields:
- field = f[0]
- matchType = f[1]
- mask = f[2]
-
- if ((matchType is p4_match_type.P4_MATCH_TERNARY) or
- (matchType is p4_match_type.P4_MATCH_LPM) or
- (matchType is p4_match_type.P4_MATCH_RANGE)):
- raise NotSupportedException(
- False, "Match type {0}", matchType)
-
- if matchType is p4_match_type.P4_MATCH_VALID:
- # we should be really checking the valid field;
- # p4_field is a header instance
- assert isinstance(field, p4_header_instance)
- instance = field
- fieldname = "valid"
- else:
- assert isinstance(field, p4_field)
- instance = field.instance
- fieldname = field.name
-
- if ebpfProgram.EbpfProgram.isArrayElementInstance(instance):
- ebpfStack = program.getStackInstance(instance.base_name)
- assert isinstance(ebpfStack, ebpfInstance.EbpfHeaderStack)
- basetype = ebpfStack.basetype
- eInstance = program.getStackInstance(instance.base_name)
- else:
- ebpfHeader = program.getInstance(instance.name)
- assert isinstance(ebpfHeader, ebpfInstance.SimpleInstance)
- basetype = ebpfHeader.type
- eInstance = program.getInstance(instance.name)
-
- ebpfField = basetype.getField(fieldname)
- assert isinstance(ebpfField, ebpfStructType.EbpfField)
-
- fieldName = self.fieldNamePrefix + str(fieldNumber)
- fieldNumber += 1
- keyField = EbpfTableKeyField(fieldName, eInstance, ebpfField, mask)
-
- self.fields.append(keyField)
- self.masks.append(mask)
-
- @staticmethod
- def fieldRank(field):
- assert isinstance(field, EbpfTableKeyField)
- return field.field.type.alignment()
-
- def serializeType(self, serializer, keyTypeName):
- assert isinstance(serializer, ProgramSerializer)
- serializer.emitIndent()
- serializer.appendFormat("struct {0} ", keyTypeName)
- serializer.blockStart()
-
- # Sort fields in decreasing size; this will ensure that
- # there is no padding.
- # Padding may cause the ebpf verification to fail,
- # since padding fields are not initialized
- fieldOrder = sorted(
- self.fields, key=EbpfTableKey.fieldRank, reverse=True)
- for f in fieldOrder:
- assert isinstance(f, EbpfTableKeyField)
- f.serializeType(serializer)
-
- serializer.blockEnd(False)
- serializer.endOfStatement(True)
-
- def serializeConstruction(self, serializer, keyName, program):
- serializer.emitIndent()
- serializer.appendLine("/* construct key */")
-
- for f in self.fields:
- f.serializeConstruction(keyName, serializer, program)
-
-
-class EbpfTable(object):
- # noinspection PyUnresolvedReferences
- def __init__(self, hlirtable, program, config):
- assert isinstance(hlirtable, p4_table)
- assert isinstance(program, ebpfProgram.EbpfProgram)
-
- self.name = hlirtable.name
- self.hlirtable = hlirtable
- self.config = config
-
- self.defaultActionMapName = (program.reservedPrefix +
- self.name + "_miss")
- self.key = EbpfTableKey(hlirtable.match_fields, program)
- self.size = hlirtable.max_size
- if self.size is None:
- program.emitWarning(
- "{0} does not specify a max_size; using 1024", hlirtable)
- self.size = 1024
- self.isHash = True # TODO: try to guess arrays when possible
- self.dataMapName = self.name
- self.actionEnumName = program.generateNewName(self.name + "_actions")
- self.keyTypeName = program.generateNewName(self.name + "_key")
- self.valueTypeName = program.generateNewName(self.name + "_value")
- self.actions = []
-
- if hlirtable.action_profile is not None:
- raise NotSupportedException("{0}: action_profile tables",
- hlirtable)
- if hlirtable.support_timeout:
- program.emitWarning("{0}: table timeout {1}; ignoring",
- hlirtable, NotSupportedException.archError)
-
- self.counters = []
- if (hlirtable.attached_counters is not None):
- for c in hlirtable.attached_counters:
- ctr = program.getCounter(c.name)
- assert isinstance(ctr, ebpfCounter.EbpfCounter)
- self.counters.append(ctr)
-
- if (len(hlirtable.attached_meters) > 0 or
- len(hlirtable.attached_registers) > 0):
- program.emitWarning("{0}: meters/registers {1}; ignored",
- hlirtable, NotSupportedException.archError)
-
- for a in hlirtable.actions:
- action = program.getAction(a)
- self.actions.append(action)
-
- def serializeKeyType(self, serializer):
- assert isinstance(serializer, ProgramSerializer)
- self.key.serializeType(serializer, self.keyTypeName)
-
- def serializeActionArguments(self, serializer, action):
- assert isinstance(serializer, ProgramSerializer)
- assert isinstance(action, ebpfAction.EbpfActionBase)
- action.serializeArgumentsAsStruct(serializer)
-
- def serializeValueType(self, serializer):
- assert isinstance(serializer, ProgramSerializer)
- # create an enum with tags for all actions
- serializer.emitIndent()
- serializer.appendFormat("enum {0} ", self.actionEnumName)
- serializer.blockStart()
-
- for a in self.actions:
- name = a.name
- serializer.emitIndent()
- serializer.appendFormat("{0}_{1},", self.name, name)
- serializer.newline()
-
- serializer.blockEnd(False)
- serializer.endOfStatement(True)
-
- # a type-safe union: a struct with a tag and an union
- serializer.emitIndent()
- serializer.appendFormat("struct {0} ", self.valueTypeName)
- serializer.blockStart()
-
- serializer.emitIndent()
- #serializer.appendFormat("enum {0} action;", self.actionEnumName)
- # teporary workaround bcc bug
- serializer.appendFormat("{0}32 action;",
- self.config.uprefix)
- serializer.newline()
-
- serializer.emitIndent()
- serializer.append("union ")
- serializer.blockStart()
-
- for a in self.actions:
- self.serializeActionArguments(serializer, a)
-
- serializer.blockEnd(False)
- serializer.space()
- serializer.appendLine("u;")
- serializer.blockEnd(False)
- serializer.endOfStatement(True)
-
- def serialize(self, serializer, program):
- assert isinstance(serializer, ProgramSerializer)
- assert isinstance(program, ebpfProgram.EbpfProgram)
-
- self.serializeKeyType(serializer)
- self.serializeValueType(serializer)
-
- self.config.serializeTableDeclaration(
- serializer, self.dataMapName, self.isHash,
- "struct " + self.keyTypeName,
- "struct " + self.valueTypeName, self.size)
- self.config.serializeTableDeclaration(
- serializer, self.defaultActionMapName, False,
- program.arrayIndexType, "struct " + self.valueTypeName, 1)
-
- def serializeCode(self, serializer, program, nextNode):
- assert isinstance(serializer, ProgramSerializer)
- assert isinstance(program, ebpfProgram.EbpfProgram)
-
- hitVarName = program.reservedPrefix + "hit"
- keyname = "key"
- valueName = "value"
-
- serializer.newline()
- serializer.emitIndent()
- serializer.appendFormat("{0}:", program.getLabel(self))
- serializer.newline()
-
- serializer.emitIndent()
- serializer.blockStart()
-
- serializer.emitIndent()
- serializer.appendFormat("{0}8 {1};", program.config.uprefix, hitVarName)
- serializer.newline()
-
- serializer.emitIndent()
- serializer.appendFormat("struct {0} {1} = {{}};", self.keyTypeName, keyname)
- serializer.newline()
-
- serializer.emitIndent()
- serializer.appendFormat(
- "struct {0} *{1};", self.valueTypeName, valueName)
- serializer.newline()
-
- self.key.serializeConstruction(serializer, keyname, program)
-
- serializer.emitIndent()
- serializer.appendFormat("{0} = 1;", hitVarName)
- serializer.newline()
-
- serializer.emitIndent()
- serializer.appendLine("/* perform lookup */")
- serializer.emitIndent()
- program.config.serializeLookup(
- serializer, self.dataMapName, keyname, valueName)
- serializer.newline()
-
- serializer.emitIndent()
- serializer.appendFormat("if ({0} == NULL) ", valueName)
- serializer.blockStart()
-
- serializer.emitIndent()
- serializer.appendFormat("{0} = 0;", hitVarName)
- serializer.newline()
-
- serializer.emitIndent()
- serializer.appendLine("/* miss; find default action */")
- serializer.emitIndent()
- program.config.serializeLookup(
- serializer, self.defaultActionMapName,
- program.zeroKeyName, valueName)
- serializer.newline()
- serializer.blockEnd(True)
-
- if len(self.counters) > 0:
- serializer.emitIndent()
- serializer.append("else ")
- serializer.blockStart()
- for c in self.counters:
- assert isinstance(c, ebpfCounter.EbpfCounter)
- if c.autoIncrement:
- serializer.emitIndent()
- serializer.blockStart()
- c.serializeCode(keyname, serializer, program)
- serializer.blockEnd(True)
- serializer.blockEnd(True)
-
- serializer.emitIndent()
- serializer.appendFormat("if ({0} != NULL) ", valueName)
- serializer.blockStart()
- serializer.emitIndent()
- serializer.appendLine("/* run action */")
- self.runAction(serializer, self.name, valueName, program, nextNode)
-
- nextNode = self.hlirtable.next_
- if "hit" in nextNode:
- node = nextNode["hit"]
- if node is None:
- node = nextNode
- label = program.getLabel(node)
- serializer.emitIndent()
- serializer.appendFormat("if (hit) goto {0};", label)
- serializer.newline()
-
- node = nextNode["miss"]
- if node is None:
- node = nextNode
- label = program.getLabel(node)
- serializer.emitIndent()
- serializer.appendFormat("else goto {0};", label)
- serializer.newline()
-
- serializer.blockEnd(True)
- if not "hit" in nextNode:
- # Catch-all
- serializer.emitIndent()
- serializer.appendFormat("goto end;")
- serializer.newline()
-
- serializer.blockEnd(True)
-
- def runAction(self, serializer, tableName, valueName, program, nextNode):
- serializer.emitIndent()
- serializer.appendFormat("switch ({0}->action) ", valueName)
- serializer.blockStart()
-
- for a in self.actions:
- assert isinstance(a, ebpfAction.EbpfActionBase)
-
- serializer.emitIndent()
- serializer.appendFormat("case {0}_{1}: ", tableName, a.name)
- serializer.newline()
- serializer.emitIndent()
- serializer.blockStart()
- a.serializeBody(serializer, valueName, program)
- serializer.blockEnd(True)
- serializer.emitIndent()
-
- nextNodes = self.hlirtable.next_
- if a.hliraction in nextNodes:
- node = nextNodes[a.hliraction]
- if node is None:
- node = nextNode
- label = program.getLabel(node)
- serializer.appendFormat("goto {0};", label)
- else:
- serializer.appendFormat("break;")
- serializer.newline()
-
- serializer.blockEnd(True)
+++ /dev/null
-# Copyright (c) Barefoot Networks, Inc.
-# Licensed under the Apache License, Version 2.0 (the "License")
-
-from compilationException import CompilationException
-
-class EbpfType(object):
- __doc__ = "Base class for representing a P4 type"
-
- def __init__(self, hlirType):
- self.hlirType = hlirType
-
- # Methods to override
-
- def serialize(self, serializer):
- # the type itself
- raise CompilationException(True, "Method must be overridden")
-
- def declare(self, serializer, identifier, asPointer):
- # declaration of an identifier with this type
- # asPointer is a boolean;
- # if true, the identifier is declared as a pointer
- raise CompilationException(True, "Method must be overridden")
-
- def emitInitializer(self, serializer):
- # A default initializer suitable for this type
- raise CompilationException(True, "Method must be overridden")
-
- def declareArray(self, serializer, identifier, size):
- # Declare an identifier with an array type with the specified size
- raise CompilationException(True, "Method must be overridden")
+++ /dev/null
-#!/usr/bin/env python
-
-# Copyright (c) Barefoot Networks, Inc.
-# Licensed under the Apache License, Version 2.0 (the "License")
-
-# Compiler from P4 to EBPF
-# (See http://www.slideshare.net/PLUMgrid/ebpf-and-linux-networking).
-# This compiler in fact generates a C source file
-# which can be compiled to EBPF using the LLVM compiler
-# with the ebpf target.
-#
-# Main entry point.
-
-import argparse
-import os
-import traceback
-import sys
-import target
-from p4_hlir.main import HLIR
-from ebpfProgram import EbpfProgram
-from compilationException import *
-from programSerializer import ProgramSerializer
-
-
-def get_parser():
- parser = argparse.ArgumentParser(description='p4toEbpf arguments')
- parser.add_argument('source', metavar='source', type=str,
- help='a P4 source file to compile')
- parser.add_argument('-g', dest='generated', default="router",
- help="kind of output produced: filter or router")
- parser.add_argument('-o', dest='output_file', default="output.c",
- help="generated C file name")
- return parser
-
-
-def process(input_args):
- parser = get_parser()
- args, unparsed_args = parser.parse_known_args(input_args)
-
- has_remaining_args = False
- preprocessor_args = []
- for a in unparsed_args:
- if a[:2] == "-D" or a[:2] == "-I" or a[:2] == "-U":
- input_args.remove(a)
- preprocessor_args.append(a)
- else:
- has_remaining_args = True
-
- # trigger error
- if has_remaining_args:
- parser.parse_args(input_args)
-
- if args.generated == "router":
- isRouter = True
- elif args.generated == "filter":
- isRouter = False
- else:
- print("-g should be one of 'filter' or 'router'")
-
- print("*** Compiling ", args.source)
- return compileP4(args.source, args.output_file, isRouter, preprocessor_args)
-
-
-class CompileResult(object):
- def __init__(self, kind, error):
- self.kind = kind
- self.error = error
-
- def __str__(self):
- if self.kind == "OK":
- return "Compilation successful"
- else:
- return "Compilation failed with error: " + self.error
-
-
-def compileP4(inputFile, gen_file, isRouter, preprocessor_args):
- h = HLIR(inputFile)
-
- for parg in preprocessor_args:
- h.add_preprocessor_args(parg)
- if not h.build():
- return CompileResult("HLIR", "Error while building HLIR")
-
- try:
- basename = os.path.basename(inputFile)
- basename = os.path.splitext(basename)[0]
-
- config = target.BccConfig()
- e = EbpfProgram(basename, h, isRouter, config)
- serializer = ProgramSerializer()
- e.toC(serializer)
- f = open(gen_file, 'w')
- f.write(serializer.toString())
- return CompileResult("OK", "")
- except CompilationException as e:
- prefix = ""
- if e.isBug:
- prefix = "### Compiler bug: "
- return CompileResult("bug", prefix + e.show())
- except NotSupportedException as e:
- return CompileResult("not supported", e.show())
- except:
- return CompileResult("exception", traceback.format_exc())
-
-
-# main entry point
-if __name__ == "__main__":
- result = process(sys.argv[1:])
- if result.kind != "OK":
- print(str(result))
+++ /dev/null
-#!/usr/bin/env python
-
-# helper for building C program source text
-
-from compilationException import *
-
-
-class ProgramSerializer(object):
- def __init__(self):
- self.program = ""
- self.eol = "\n"
- self.currentIndent = 0
- self.INDENT_AMOUNT = 4 # default indent amount
-
- def __str__(self):
- return self.program
-
- def increaseIndent(self):
- self.currentIndent += self.INDENT_AMOUNT
-
- def decreaseIndent(self):
- self.currentIndent -= self.INDENT_AMOUNT
- if self.currentIndent < 0:
- raise CompilationException(True, "Negative indentation level")
-
- def toString(self):
- return self.program
-
- def space(self):
- self.append(" ")
-
- def newline(self):
- self.program += self.eol
-
- def endOfStatement(self, addNewline):
- self.append(";")
- if addNewline:
- self.newline()
-
- def append(self, string):
- self.program += str(string)
-
- def appendFormat(self, format, *args):
- string = format.format(*args)
- self.append(string)
-
- def appendLine(self, string):
- self.append(string)
- self.newline()
-
- def emitIndent(self):
- self.program += " " * self.currentIndent
-
- def blockStart(self):
- self.append("{")
- self.newline()
- self.increaseIndent()
-
- def blockEnd(self, addNewline):
- self.decreaseIndent()
- self.emitIndent()
- self.append("}")
- if addNewline:
- self.newline()
+++ /dev/null
-# Copyright (c) Barefoot Networks, Inc.
-# Licensed under the Apache License, Version 2.0 (the "License")
-
-from programSerializer import ProgramSerializer
-
-# abstraction for isolating target-specific features
-
-# Base class for representing target-specific configuration
-class TargetConfig(object):
- def __init__(self, target):
- self.targetName = target
-
- def getIncludes(self):
- return ""
-
- def serializeLookup(self, serializer, tableName, key, value):
- serializer.appendFormat("{0} = bpf_map_lookup_elem(&{1}, &{2});",
- value, tableName, key)
-
- def serializeUpdate(self, serializer, tableName, key, value):
- serializer.appendFormat(
- "bpf_map_update_elem(&{0}, &{1}, &{2}, BPF_ANY);",
- tableName, key, value)
-
- def serializeLicense(self, serializer, licenseString):
- assert isinstance(serializer, ProgramSerializer)
- serializer.emitIndent()
- serializer.appendFormat(
- "char _license[] {0}(\"license\") = \"{1}\";",
- self.config.section, licenseString)
- serializer.newline()
-
- def serializeCodeSection(self, serializer):
- assert isinstance(serializer, ProgramSerializer)
- serializer.appendFormat("{0}(\"{1}\")", self.section, self.entrySection)
-
- def serializeTableDeclaration(self, serializer, tableName,
- isHash, keyType, valueType, size):
- assert isinstance(serializer, ProgramSerializer)
- assert isinstance(tableName, str)
- assert isinstance(isHash, bool)
- assert isinstance(keyType, str)
- assert isinstance(valueType, str)
- assert isinstance(size, int)
-
- serializer.emitIndent()
- serializer.appendFormat("struct {0} {1}(\"maps\") {2} = ",
- self.tableName, self.section, tableName)
- serializer.blockStart()
-
- serializer.emitIndent()
- serializer.append(".type = ")
- if isHash:
- serializer.appendLine("BPF_MAP_TYPE_HASH,")
- else:
- serializer.appendLine("BPF_MAP_TYPE_ARRAY,")
-
- serializer.emitIndent()
- serializer.appendFormat(".{0} = sizeof(struct {1}), ",
- self.tableKeyAttribute, keyType)
- serializer.newline()
-
- serializer.emitIndent()
- serializer.appendFormat(".{0} = sizeof(struct {1}), ",
- self.tableValueAttribute, valueType)
- serializer.newline()
-
- serializer.emitIndent()
- serializer.appendFormat(".{0} = {1}, ", self.tableSizeAttribute, size)
- serializer.newline()
-
- serializer.blockEnd(False)
- serializer.endOfStatement(True)
-
- def generateDword(self, serializer):
- serializer.appendFormat(
- "static inline {0}64 load_dword(void *skb, {0}64 off)",
- self.uprefix)
- serializer.newline()
- serializer.blockStart()
- serializer.emitIndent()
- serializer.appendFormat(
- ("return (({0}64)load_word(skb, off) << 32) | " +
- "load_word(skb, off + 4);"),
- self.uprefix)
- serializer.newline()
- serializer.blockEnd(True)
-
-
-# Represents a target that is compiled within the kernel
-# source tree samples folder and which attaches to a socket
-class KernelSamplesConfig(TargetConfig):
- def __init__(self):
- super(TargetConfig, self).__init__("Socket")
- self.entrySection = "socket1"
- self.section = "SEC"
- self.uprefix = "u"
- self.iprefix = "i"
- self.tableKeyAttribute = "key_size"
- self.tableValueAttribute = "value_size"
- self.tableSizeAttribute = "max_entries"
- self.tableName = "bpf_map_def"
- self.postamble = ""
-
- def getIncludes(self):
- return """
-#include <uapi/linux/bpf.h>
-#include <uapi/linux/if_ether.h>
-#include <uapi/linux/if_packet.h>
-#include <uapi/linux/ip.h>
-#include <linux/skbuff.h>
-#include <linux/netdevice.h>
-#include "bpf_helpers.h"
-"""
-
-
-# Represents a target compiled by bcc that uses the TC
-class BccConfig(TargetConfig):
- def __init__(self):
- super(BccConfig, self).__init__("BCC")
- self.uprefix = "u"
- self.iprefix = "i"
- self.postamble = ""
-
- def serializeTableDeclaration(self, serializer, tableName,
- isHash, keyType, valueType, size):
- assert isinstance(serializer, ProgramSerializer)
- assert isinstance(tableName, str)
- assert isinstance(isHash, bool)
- assert isinstance(keyType, str)
- assert isinstance(valueType, str)
- assert isinstance(size, int)
-
- serializer.emitIndent()
- if isHash:
- kind = "hash"
- else:
- kind = "array"
- serializer.appendFormat(
- "BPF_TABLE(\"{0}\", {1}, {2}, {3}, {4});",
- kind, keyType, valueType, tableName, size)
- serializer.newline()
-
- def serializeLookup(self, serializer, tableName, key, value):
- serializer.appendFormat("{0} = {1}.lookup(&{2});",
- value, tableName, key)
-
- def serializeUpdate(self, serializer, tableName, key, value):
- serializer.appendFormat("{0}.update(&{1}, &{2});",
- tableName, key, value)
-
- def generateDword(self, serializer):
- pass
-
- def serializeCodeSection(self, serializer):
- pass
-
- def getIncludes(self):
- return """
-#include <uapi/linux/bpf.h>
-#include <uapi/linux/if_ether.h>
-#include <uapi/linux/if_packet.h>
-#include <uapi/linux/ip.h>
-#include <linux/skbuff.h>
-#include <linux/netdevice.h>
-#include <linux/pkt_cls.h>
-"""
-
- def serializeLicense(self, serializer, licenseString):
- assert isinstance(serializer, ProgramSerializer)
- pass
+++ /dev/null
-# Copyright 2013-present Barefoot Networks, Inc.
-#
-# 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.
-#
-
-#
-# Antonin Bas (antonin@barefootnetworks.com)
-#
-#
-
-# -*- coding: utf-8 -*-
-
-from __future__ import print_function
-
-class Node(object):
- def __init__(self, n):
- self.n = n
- self.edges = set()
-
- def add_edge_to(self, other):
- assert(isinstance(other, Node))
- self.edges.add(other)
-
- def __str__(self):
- return str(self.n)
-
-
-class Graph(object):
- def __init__(self):
- self.nodes = {}
- self.root = None
-
- def add_node(self, node):
- assert(node not in self.nodes)
- self.nodes[node] = Node(node)
-
- def __contains__(self, node):
- return node in self.nodes
-
- def get_node(self, node):
- return self.nodes[node]
-
- def produce_topo_sorting(self):
- def visit(node, topo_sorting, sequence=None):
- if sequence is not None:
- sequence += [str(node)]
- if node._behavioral_topo_sorting_mark == 1:
- if sequence is not None:
- print("cycle", sequence)
- return False
- if node._behavioral_topo_sorting_mark != 2:
- node._behavioral_topo_sorting_mark = 1
- for next_node in node.edges:
- res = visit(next_node, topo_sorting, sequence)
- if not res:
- return False
- node._behavioral_topo_sorting_mark = 2
- topo_sorting.insert(0, node.n)
- return True
-
- has_cycle = False
- topo_sorting = []
-
- for node in self.nodes.values():
- # 0 is unmarked, 1 is temp, 2 is permanent
- node._behavioral_topo_sorting_mark = 0
- for node in self.nodes.values():
- if node._behavioral_topo_sorting_mark == 0:
- if not visit(node, topo_sorting, sequence=[]):
- has_cycle = True
- break
- # removing mark
- for node in self.nodes.values():
- del node._behavioral_topo_sorting_mark
-
- if has_cycle:
- return None
-
- return topo_sorting
+++ /dev/null
-# Copyright (c) Barefoot Networks, Inc.
-# Licensed under the Apache License, Version 2.0 (the "License")
-
-from p4_hlir.hlir import p4_header
-from ebpfStructType import *
-
-class EbpfTypeFactory(object):
- def __init__(self, config):
- self.type_map = {}
- self.config = config
-
- def build(self, hlirType, asMetadata):
- name = hlirType.name
- if hlirType.name in self.type_map:
- retval = self.type_map[name]
- if ((not asMetadata and isinstance(retval, EbpfMetadataType)) or
- (asMetadata and isinstance(retval, EbpfHeaderType))):
- raise CompilationException(
- True, "Same type used both as a header and metadata {0}",
- hlirType)
-
- if isinstance(hlirType, p4_header):
- if asMetadata:
- type = EbpfMetadataType(hlirType, self.config)
- else:
- type = EbpfHeaderType(hlirType, self.config)
- else:
- raise CompilationException(True, "Unexpected type {0}", hlirType)
- self.registerType(name, type)
- return type
-
- def registerType(self, name, ebpfType):
- self.type_map[name] = ebpfType
+++ /dev/null
-# External references
-
-See [p4toEbpf-bcc.pdf](https://github.com/iovisor/bpf-docs/blob/master/p4/p4toEbpf-bcc.pdf)
+++ /dev/null
-This folder contains tests for the P4->C->EBPF compiler
-
-- cleanup.sh should be run if for some reason endToEndTest.py crashes
- and leaves garbage namespaces or links
-
-- testP4toEbpf.py compiles all P4 files in the testprograms folder and
- deposits the corresponding C files in the testoutputs folder
-
-- endToEndTest.py runs a complete end-to-end test compiling the
- testprograms/simple.p4 program, creating a virtual network with 3
- boxes (using network namespaces): client, server, switch, loading
- the EBPF into the kernel of the switch box using the TC, and
- implementing the forwarding in the switch solely using the P4
- program.
-
-
+++ /dev/null
-#!/bin/bash
-# Run this script if for some reason the endToEndTest.py crashed
-# and left some garbage state
-
-ip netns del sw
-ip netns del srv
-ip netns del clt
-
-ip link del dev veth-clt-sw
-ip link del dev veth-srv-sw
-
+++ /dev/null
-#!/usr/bin/env python3
-
-# Copyright (c) Barefoot Networks, Inc.
-# Licensed under the Apache License, Version 2.0 (the "License")
-
-# Testing example for P4->EBPF compiler
-#
-# This program exercises the simple.c EBPF program
-# generated from the simple.p4 source file.
-
-from __future__ import print_function
-import subprocess
-import ctypes
-import time
-import sys
-import os
-from bcc import BPF
-from pyroute2 import IPRoute, NSPopen, NetNS
-from netaddr import IPAddress
-
-### This part is a simple generic network simulaton toolkit
-
-class Base(object):
- def __init__(self):
- self.verbose = True
-
- def message(self, *args):
- if self.verbose:
- print(*args)
-
-
-class Endpoint(Base):
- # a network interface really
- def __init__(self, ipaddress, ethaddress):
- Base.__init__(self)
- self.mac_addr = ethaddress
- self.ipaddress = ipaddress
- self.prefixlen = 24
- self.parent = None
-
- def __str__(self):
- return "Endpoint " + str(self.ipaddress)
-
- def set_parent(self, parent):
- assert isinstance(parent, Node)
- self.parent = parent
-
- def get_ip_address(self):
- return IPAddress(self.ipaddress)
-
-
-class Node(Base):
- # Used to represent one of clt, sw, srv
- # Each lives in its own namespace
- def __init__(self, name):
- Base.__init__(self)
- self.name = name
- self.endpoints = []
- self.get_ns() # as a side-effect creates namespace
-
- def add_endpoint(self, endpoint):
- assert isinstance(endpoint, Endpoint)
- self.endpoints.append(endpoint)
- endpoint.set_parent(self)
-
- def __str__(self):
- return "Node " + self.name
-
- def get_ns_name(self):
- return self.name
-
- def get_ns(self):
- nsname = self.get_ns_name()
- ns = NetNS(nsname)
- return ns
-
- def remove(self):
- ns = self.get_ns();
- ns.close()
- ns.remove()
-
- def execute(self, command):
- # Run a command in the node's namespace
- # Return the command's exit code
- self.message(self.name, "Executing", command)
- nsn = self.get_ns_name()
- pipe = NSPopen(nsn, command)
- result = pipe.wait()
- pipe.release()
- return result
-
- def set_arp(self, destination):
- assert isinstance(destination, Endpoint)
- command = ["arp", "-s", str(destination.ipaddress),
- str(destination.mac_addr)]
- self.execute(command)
-
-
-class NetworkBase(Base):
- def __init__(self):
- Base.__init__(self)
- self.ipr = IPRoute()
- self.nodes = []
-
- def add_node(self, node):
- assert isinstance(node, Node)
- self.nodes.append(node)
-
- def get_interface_name(self, source, dest):
- assert isinstance(source, Node)
- assert isinstance(dest, Node)
- interface_name = "veth-" + source.name + "-" + dest.name
- return interface_name
-
- def get_interface(self, ifname):
- interfaces = self.ipr.link_lookup(ifname=ifname)
- if len(interfaces) != 1:
- raise Exception("Could not identify interface " + ifname)
- ix = interfaces[0]
- assert isinstance(ix, int)
- return ix
-
- def set_interface_ipaddress(self, node, ifname, address, mask):
- # Ask a node to set the specified interface address
- if address is None:
- return
-
- assert isinstance(node, Node)
- command = ["ip", "addr", "add", str(address) + "/" + str(mask),
- "dev", str(ifname)]
- result = node.execute(command)
- assert(result == 0)
-
- def create_link(self, src, dest):
- assert isinstance(src, Endpoint)
- assert isinstance(dest, Endpoint)
-
- ifname = self.get_interface_name(src.parent, dest.parent)
- destname = self.get_interface_name(dest.parent, src.parent)
- self.ipr.link_create(ifname=ifname, kind="veth", peer=destname)
-
- self.message("Create", ifname, "link")
-
- # Set source endpoint information
- ix = self.get_interface(ifname)
- self.ipr.link("set", index=ix, address=src.mac_addr)
- # push source endpoint into source namespace
- self.ipr.link("set", index=ix,
- net_ns_fd=src.parent.get_ns_name(), state="up")
- # Set interface ip address; seems to be
- # lost of set prior to moving to namespace
- self.set_interface_ipaddress(
- src.parent, ifname, src.ipaddress , src.prefixlen)
-
- # Sef destination endpoint information
- ix = self.get_interface(destname)
- self.ipr.link("set", index=ix, address=dest.mac_addr)
- # push destination endpoint into the destination namespace
- self.ipr.link("set", index=ix,
- net_ns_fd=dest.parent.get_ns_name(), state="up")
- # Set interface ip address
- self.set_interface_ipaddress(dest.parent, destname,
- dest.ipaddress, dest.prefixlen)
-
- def show_interfaces(self, node):
- cmd = ["ip", "addr"]
- if node is None:
- # Run with no namespace
- subprocess.call(cmd)
- else:
- # Run in node's namespace
- assert isinstance(node, Node)
- self.message("Enumerating all interfaces in ", node.name)
- node.execute(cmd)
-
- def delete(self):
- self.message("Deleting virtual network")
- for n in self.nodes:
- n.remove()
- self.ipr.close()
-
-
-### Here begins the concrete instantiation of the network
-# Network setup:
-# Each of these is a separate namespace.
-#
-# 62:ce:1b:48:3e:61 a2:59:94:cf:51:09
-# 96:a4:85:fe:2a:11 62:ce:1b:48:3e:60
-# /------------------\ /-----------------\
-# ---------- -------- ---------
-# | clt | | sw | | srv |
-# ---------- -------- ---------
-# 10.0.0.11 10.0.0.10
-#
-
-class SimulatedNetwork(NetworkBase):
- def __init__(self):
- NetworkBase.__init__(self)
-
- self.client = Node("clt")
- self.add_node(self.client)
- self.client_endpoint = Endpoint("10.0.0.11", "96:a4:85:fe:2a:11")
- self.client.add_endpoint(self.client_endpoint)
-
- self.server = Node("srv")
- self.add_node(self.server)
- self.server_endpoint = Endpoint("10.0.0.10", "a2:59:94:cf:51:09")
- self.server.add_endpoint(self.server_endpoint)
-
- self.switch = Node("sw")
- self.add_node(self.switch)
- self.sw_clt_endpoint = Endpoint(None, "62:ce:1b:48:3e:61")
- self.sw_srv_endpoint = Endpoint(None, "62:ce:1b:48:3e:60")
- self.switch.add_endpoint(self.sw_clt_endpoint)
- self.switch.add_endpoint(self.sw_srv_endpoint)
-
- def run_method_in_node(self, node, method, args):
- # run a method of the SimulatedNetwork class in a different namespace
- # return the exit code
- assert isinstance(node, Node)
- assert isinstance(args, list)
- torun = __file__
- args.insert(0, torun)
- args.insert(1, method)
- return node.execute(args) # runs the command argv[0] method args
-
- def instantiate(self):
- # Creates the various namespaces
- self.message("Creating virtual network")
-
- self.message("Create client-switch link")
- self.create_link(self.client_endpoint, self.sw_clt_endpoint)
-
- self.message("Create server-switch link")
- self.create_link(self.server_endpoint, self.sw_srv_endpoint)
-
- self.show_interfaces(self.client)
- self.show_interfaces(self.server)
- self.show_interfaces(self.switch)
-
- self.message("Set ARP mappings")
- self.client.set_arp(self.server_endpoint)
- self.server.set_arp(self.client_endpoint)
-
- def setup_switch(self):
- # This method is run in the switch namespace.
- self.message("Compiling and loading BPF program")
-
- b = BPF(src_file="./simple.c", debug=0)
- fn = b.load_func("ebpf_filter", BPF.SCHED_CLS)
-
- self.message("BPF program loaded")
-
- self.message("Discovering tables")
- routing_tbl = b.get_table("routing")
- routing_miss_tbl = b.get_table("ebpf_routing_miss")
- cnt_tbl = b.get_table("cnt")
-
- self.message("Hooking up BPF classifiers using TC")
-
- interfname = self.get_interface_name(self.switch, self.server)
- sw_srv_idx = self.get_interface(interfname)
- self.ipr.tc("add", "ingress", sw_srv_idx, "ffff:")
- self.ipr.tc("add-filter", "bpf", sw_srv_idx, ":1", fd=fn.fd,
- name=fn.name, parent="ffff:", action="ok", classid=1)
-
- interfname = self.get_interface_name(self.switch, self.client)
- sw_clt_idx = self.get_interface(interfname)
- self.ipr.tc("add", "ingress", sw_clt_idx, "ffff:")
- self.ipr.tc("add-filter", "bpf", sw_clt_idx, ":1", fd=fn.fd,
- name=fn.name, parent="ffff:", action="ok", classid=1)
-
- self.message("Populating tables from the control plane")
- cltip = self.client_endpoint.get_ip_address()
- srvip = self.server_endpoint.get_ip_address()
-
- # BCC does not support tbl.Leaf when the type contains a union,
- # so we have to make up the value type manually. Unfortunately
- # these sizes are not portable...
-
- class Forward(ctypes.Structure):
- _fields_ = [("port", ctypes.c_ushort)]
-
- class Nop(ctypes.Structure):
- _fields_ = []
-
- class Union(ctypes.Union):
- _fields_ = [("nop", Nop),
- ("forward", Forward)]
-
- class Value(ctypes.Structure):
- _fields_ = [("action", ctypes.c_uint),
- ("u", Union)]
-
- if False:
- # This is how it should ideally be done, but it does not work
- routing_tbl[routing_tbl.Key(int(cltip))] = routing_tbl.Leaf(
- 1, sw_clt_idx)
- routing_tbl[routing_tbl.Key(int(srvip))] = routing_tbl.Leaf(
- 1, sw_srv_idx)
- else:
- v1 = Value()
- v1.action = 1
- v1.u.forward.port = sw_clt_idx
-
- v2 = Value()
- v2.action = 1;
- v2.u.forward.port = sw_srv_idx
-
- routing_tbl[routing_tbl.Key(int(cltip))] = v1
- routing_tbl[routing_tbl.Key(int(srvip))] = v2
-
- self.message("Dumping table contents")
- for key, leaf in routing_tbl.items():
- self.message(str(IPAddress(key.key_field_0)),
- leaf.action, leaf.u.forward.port)
-
- def run(self):
- self.message("Pinging server from client")
- ping = ["ping", self.server_endpoint.ipaddress, "-c", "2"]
- result = self.client.execute(ping)
- if result != 0:
- raise Exception("Test failed")
- else:
- print("Test succeeded!")
-
- def prepare_switch(self):
- self.message("Configuring switch")
- # Re-invokes this script in the switch namespace;
- # this causes the setup_switch method to be run in that context.
- # This is the same as running self.setup_switch()
- # but in the switch namespace
- self.run_method_in_node(self.switch, "setup_switch", [])
-
-
-def compile(source, destination):
- try:
- status = subprocess.call(
- "../compiler/p4toEbpf.py " + source + " -o " + destination,
- shell=True)
- if status < 0:
- print("Child was terminated by signal", -status, file=sys.stderr)
- else:
- print("Child returned", status, file=sys.stderr)
- except OSError as e:
- print("Execution failed:", e, file=sys.stderr)
- raise e
-
-def start_simulation():
- compile("testprograms/simple.p4", "simple.c")
- network = SimulatedNetwork()
- network.instantiate()
- network.prepare_switch()
- network.run()
- network.delete()
- os.remove("simple.c")
-
-def main(argv):
- print(str(argv))
- if len(argv) == 1:
- # Main entry point: start simulation
- start_simulation()
- else:
- # We are invoked with some arguments (probably in a different namespace)
- # First argument is a method name, rest are method arguments.
- # Create a SimulatedNetwork and invoke the specified method with the
- # specified arguments.
- network = SimulatedNetwork()
- methodname = argv[1]
- arguments = argv[2:]
- method = getattr(network, methodname)
- method(*arguments)
-
-if __name__ == '__main__':
- main(sys.argv)
-
+++ /dev/null
-#!/usr/bin/env python
-
-# Copyright (c) Barefoot Networks, Inc.
-# Licensed under the Apache License, Version 2.0 (the "License")
-
-# Runs the compiler on all files in the 'testprograms' folder
-# Writes outputs in the 'testoutputs' folder
-
-from __future__ import print_function
-from bcc import BPF
-import os, sys
-sys.path.append("../compiler") # To get hold of p4toEbpf
- # We want to run it without installing it
-import p4toEbpf
-import os
-
-def drop_extension(filename):
- return os.path.splitext(os.path.basename(filename))[0]
-
-filesFailed = {} # map error kind -> list[ (file, error) ]
-
-def set_error(kind, file, error):
- if kind in filesFailed:
- filesFailed[kind].append((file, error))
- else:
- filesFailed[kind] = [(file, error)]
-
-def is_root():
- # Is this code portable?
- return os.getuid() == 0
-
-def main():
- testpath = "testprograms"
- destFolder = "testoutputs"
- files = os.listdir(testpath)
- files.sort()
- filesDone = 0
- errors = 0
-
- if not is_root():
- print("Loading EBPF programs requires root privilege.")
- print("Will only test compilation, not loading.")
- print("(Run with sudo to test program loading.)")
-
- for f in files:
- path = os.path.join(testpath, f)
-
- if not os.path.isfile(path):
- continue
- if not path.endswith(".p4"):
- continue
-
- destname = drop_extension(path) + ".c"
- destname = os.path.join(destFolder, destname)
-
- args = [path, "-o", destname]
-
- result = p4toEbpf.process(args)
- if result.kind != "OK":
- errors += 1
- print(path, result.error)
- set_error(result.kind, path, result.error)
- else:
- # Try to load the compiled function
- if is_root():
- try:
- print("Compiling and loading BPF program")
- b = BPF(src_file=destname, debug=0)
- fn = b.load_func("ebpf_filter", BPF.SCHED_CLS)
- except Exception as e:
- print(e)
- set_error("BPF error", path, str(e))
-
- filesDone += 1
-
- print("Compiled", filesDone, "files", errors, "errors")
- for key in sorted(filesFailed):
- print(key, ":", len(filesFailed[key]), "programs")
- for v in filesFailed[key]:
- print("\t", v)
- exit(len(filesFailed) != 0)
-
-
-if __name__ == "__main__":
- main()
+++ /dev/null
-header_type ethernet_t {
- fields {
- dstAddr : 48;
- srcAddr : 48;
- etherType : 16;
- }
-}
-
-parser start {
- return parse_ethernet;
-}
-
-header ethernet_t ethernet;
-
-parser parse_ethernet {
- extract(ethernet);
- return ingress;
-}
-
-action nop()
-{}
-
-table routing {
- reads {
- ethernet.dstAddr: exact;
- }
- actions { nop; }
- size : 512;
-}
-
-control ingress
-{
- apply(routing);
-}
\ No newline at end of file
+++ /dev/null
-/*
-Copyright 2013-present Barefoot Networks, Inc.
-
-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.
-*/
-
-header_type ethernet_t {
- fields {
- dstAddr : 48;
- srcAddr : 48;
- etherType : 16;
- }
-}
-
-header_type ipv4_t {
- fields {
- version : 4;
- ihl : 4;
- diffserv : 8;
- totalLen : 16;
- identification : 16;
- flags : 3;
- fragOffset : 13;
- ttl : 8;
- protocol : 8;
- hdrChecksum : 16;
- srcAddr : 32;
- dstAddr: 32;
- }
-}
-
-parser start {
- return parse_ethernet;
-}
-
-#define ETHERTYPE_IPV4 0x0800
-
-header ethernet_t ethernet;
-
-parser parse_ethernet {
- extract(ethernet);
- return select(latest.etherType) {
- ETHERTYPE_IPV4 : parse_ipv4;
- default: ingress;
- }
-}
-
-header ipv4_t ipv4;
-
-/* Not yet supported on EBPF target
-
-field_list ipv4_checksum_list {
- ipv4.version;
- ipv4.ihl;
- ipv4.diffserv;
- ipv4.totalLen;
- ipv4.identification;
- ipv4.flags;
- ipv4.fragOffset;
- ipv4.ttl;
- ipv4.protocol;
- ipv4.srcAddr;
- ipv4.dstAddr;
-}
-
-field_list_calculation ipv4_checksum {
- input {
- ipv4_checksum_list;
- }
- algorithm : csum16;
- output_width : 16;
-}
-
-calculated_field ipv4.hdrChecksum {
- verify ipv4_checksum;
- update ipv4_checksum;
-}
-*/
-
-parser parse_ipv4 {
- extract(ipv4);
- return ingress;
-}
-
-#define PORT_VLAN_TABLE_SIZE 32768
-#define BD_TABLE_SIZE 65536
-#define IPV4_LPM_TABLE_SIZE 16384
-#define IPV4_HOST_TABLE_SIZE 131072
-#define NEXTHOP_TABLE_SIZE 32768
-#define REWRITE_MAC_TABLE_SIZE 32768
-
-#define VRF_BIT_WIDTH 12
-#define BD_BIT_WIDTH 16
-#define IFINDEX_BIT_WIDTH 10
-
-/* METADATA */
-header_type ingress_metadata_t {
- fields {
- vrf : VRF_BIT_WIDTH; /* VRF */
- bd : BD_BIT_WIDTH; /* ingress BD */
- nexthop_index : 16; /* final next hop index */
- }
-}
-
-metadata ingress_metadata_t ingress_metadata;
-
-action on_miss() {
-}
-
-action set_bd(bd) {
- modify_field(ingress_metadata.bd, bd);
-}
-
-table port_mapping {
- reads {
- standard_metadata.ingress_port : exact;
- }
- actions {
- set_bd;
- }
- size : PORT_VLAN_TABLE_SIZE;
-}
-
-action set_vrf(vrf) {
- modify_field(ingress_metadata.vrf, vrf);
-}
-
-table bd {
- reads {
- ingress_metadata.bd : exact;
- }
- actions {
- set_vrf;
- }
- size : BD_TABLE_SIZE;
-}
-
-action fib_hit_nexthop(nexthop_index) {
- modify_field(ingress_metadata.nexthop_index, nexthop_index);
- subtract_from_field(ipv4.ttl, 1);
-}
-
-table ipv4_fib {
- reads {
- ingress_metadata.vrf : exact;
- ipv4.dstAddr : exact;
- }
- actions {
- on_miss;
- fib_hit_nexthop;
- }
- size : IPV4_HOST_TABLE_SIZE;
-}
-
-table ipv4_fib_lpm {
- reads {
- ingress_metadata.vrf : exact;
- ipv4.dstAddr : exact; // lpm not supported
- }
- actions {
- on_miss;
- fib_hit_nexthop;
- }
- size : IPV4_LPM_TABLE_SIZE;
-}
-
-action set_egress_details(egress_spec) {
- modify_field(standard_metadata.egress_spec, egress_spec);
-}
-
-table nexthop {
- reads {
- ingress_metadata.nexthop_index : exact;
- }
- actions {
- on_miss;
- set_egress_details;
- }
- size : NEXTHOP_TABLE_SIZE;
-}
-
-control ingress {
- if (valid(ipv4)) {
- /* derive ingress_metadata.bd */
- apply(port_mapping);
-
- /* derive ingress_metadata.vrf */
- apply(bd);
-
- /* fib lookup, set ingress_metadata.nexthop_index */
- apply(ipv4_fib) {
- on_miss {
- apply(ipv4_fib_lpm);
- }
- }
-
- /* derive standard_metadata.egress_spec from ingress_metadata.nexthop_index */
- apply(nexthop);
- }
-}
-
-action rewrite_src_dst_mac(smac, dmac) {
- modify_field(ethernet.srcAddr, smac);
- modify_field(ethernet.dstAddr, dmac);
-}
-
-table rewrite_mac {
- reads {
- ingress_metadata.nexthop_index : exact;
- }
- actions {
- on_miss;
- rewrite_src_dst_mac;
- }
- size : REWRITE_MAC_TABLE_SIZE;
-}
-
-control egress {
- /* set smac and dmac from ingress_metadata.nexthop_index */
- apply(rewrite_mac);
-}
\ No newline at end of file
+++ /dev/null
-header_type ht
-{
- fields
- {
- f1 : 1;
- f2 : 2;
- f3 : 3;
- f4 : 4;
- f5 : 5;
- f6 : 6;
- f7 : 7;
- f8 : 8;
- f9 : 9;
- f10 : 10;
- f11 : 11;
- f12 : 12;
- f13 : 13;
- f14 : 14;
- f15 : 15;
- f16 : 16;
- f17 : 17;
- f18 : 18;
- f19 : 19;
- f20 : 20;
- f21 : 21;
- f22 : 22;
- f23 : 23;
- f24 : 24;
- f25 : 25;
- f26 : 26;
- f27 : 27;
- f28 : 28;
- f29 : 29;
- f30 : 30;
- f31 : 31;
- f32 : 32;
- }
-}
-
-header_type larget
-{
- fields
- {
- f48 : 48;
- f1: 1;
- f49 : 48;
- f2 : 1;
- f64 : 64;
- f3 : 1;
- f128 : 128;
- }
-}
-
-header ht h;
-header larget large;
-
-parser start
-{
- extract(h);
- extract(large);
- return ingress;
-}
-
-control ingress
-{
-}
+++ /dev/null
-header_type ethernet_t {
- fields {
- dstAddr : 48;
- }
-}
-
-header_type ipv4_t {
- fields {
- srcAddr : 32;
- }
-}
-
-parser start {
- return parse_ethernet;
-}
-
-header ethernet_t ethernet;
-
-parser parse_ethernet {
- extract(ethernet);
- return parse_ipv4;
-}
-
-action nop()
-{}
-
-header ipv4_t ipv4;
-
-parser parse_ipv4 {
- extract(ipv4);
- return ingress;
-}
-
-table routing {
- reads {
- ethernet.dstAddr: exact;
- ipv4.srcAddr: exact;
- }
- actions { nop; }
- size : 512;
-}
-
-control ingress
-{
- apply(routing);
-}
\ No newline at end of file
+++ /dev/null
-header_type ethernet_t {
- fields {
- dstAddr : 48;
- srcAddr : 48;
- etherType : 16;
- }
-}
-
-header_type ipv4_t {
- fields {
- version : 4;
- ihl : 4;
- diffserv : 8;
- totalLen : 16;
- identification : 16;
- flags : 3;
- fragOffset : 13;
- ttl : 8;
- protocol : 8;
- hdrChecksum : 16;
- srcAddr : 32;
- dstAddr: 32;
- }
-}
-
-parser start {
- return parse_ethernet;
-}
-
-header ethernet_t ethernet;
-
-parser parse_ethernet {
- extract(ethernet);
- return select(latest.etherType) {
- 0x800 : parse_ipv4;
- default: ingress;
- }
-}
-
-action nop()
-{}
-
-action forward(port)
-{
- modify_field(standard_metadata.egress_port, port);
-}
-
-header ipv4_t ipv4;
-
-parser parse_ipv4 {
- extract(ipv4);
- return ingress;
-}
-
-table routing {
- reads {
- ipv4.dstAddr: exact;
- ipv4.srcAddr: exact;
- }
- actions { nop; forward; }
- size : 512;
-}
-
-counter cnt {
- type: bytes;
- direct: routing;
-}
-
-control ingress
-{
- apply(routing);
-}
\ No newline at end of file
+++ /dev/null
-/* Sample P4 program */
-header_type ethernet_t {
- fields {
- dstAddr : 48;
- srcAddr : 48;
- etherType : 16;
- }
-}
-
-parser start {
- return parse_ethernet;
-}
-
-header ethernet_t ethernet;
-
-parser parse_ethernet {
- extract(ethernet);
- return ingress;
-}
-
-action action_0(){
- no_op();
-}
-
-table table_0 {
- reads {
- ethernet.etherType : exact;
- }
- actions {
- action_0;
- }
-}
-
-control ingress {
- apply(table_0);
-}
+++ /dev/null
-// Routes a packet to an interface based on its IPv4 address
-// Maintains a set of counters on the routing table
-
-header_type ethernet_t {
- fields {
- dstAddr : 48;
- srcAddr : 48;
- etherType : 16;
- }
-}
-
-header_type ipv4_t {
- fields {
- version : 4;
- ihl : 4;
- diffserv : 8;
- totalLen : 16;
- identification : 16;
- flags : 3;
- fragOffset : 13;
- ttl : 8;
- protocol : 8;
- hdrChecksum : 16;
- srcAddr : 32;
- dstAddr: 32;
- }
-}
-
-parser start {
- return parse_ethernet;
-}
-
-header ethernet_t ethernet;
-
-parser parse_ethernet {
- extract(ethernet);
- return select(latest.etherType) {
- 0x800 : parse_ipv4;
- default: ingress;
- }
-}
-
-action nop()
-{}
-
-action forward(port)
-{
- modify_field(standard_metadata.egress_port, port);
-}
-
-header ipv4_t ipv4;
-
-parser parse_ipv4 {
- extract(ipv4);
- return ingress;
-}
-
-table routing {
- reads {
- ipv4.dstAddr: exact;
- }
- actions { nop; forward; }
- size : 512;
-}
-
-counter cnt {
- type: bytes;
- direct: routing;
-}
-
-control ingress
-{
- apply(routing);
-}
\ No newline at end of file