rBPF Femto-Container support

rBPF: RIOT-style Berkeley Packet Filters

The rBPF subsystem provides a minimal virtual machine for microcontrollers. This allows code to be run in isolated environments to increase platform security.

The particular implementation here is referred to as a Femto-Container as only a very limited set of functionality is supported compared to a regular Virtual Machine.

Container code is compiled into BPF object code, which is linked into the Sming application as a BLOB (Binary Large OBject). The generated code has a very simple 64-bit instruction set which is then executed using a runtime interpreter.

See rBPF: Riot-style Berkeley Packet Filters for further details.

Toolchain setup

The following applications are required:

From a sample or project directory, install as follows.

Ubuntu:

sudo apt-get install clang
make python-requirements

Windows:

winget install llvm
set CLANG="c:\Program Files\LLVM\bin\clang"
make python-requirements

Sming interface

All .c or .cpp files are compiled into eBPF object code. The resulting binary data is linked into the project using FSTR::Array objects. These can be executed using a rBPF::VirtualMachine instance.

Compiled containers are located in the :cpp:namespace:`rBPF::Container` namespace.

Note that parameters are passed to container functions as a pointer. You should always use a structured type for this as shown in the samples.

The compiler will use the first public function in each source file as the entry point. It is recommended that all other functions be declared static or placed within an anonymous namespace.

Low-level details

Clang is used to compile container source code to an eBPF object file. This is then converted to a Femto-Container-specific format using python.

For example, the increment.c container is compiled to increment.o then converted to increment.bin. The .o file can be inspected using standard binutils tools such as objdump.

The Femto-Container application binary can be inspected as follows:

make rbpf-dump

Output from the sample increment container looks like this:

Magic:          0x46504272
Version:        0
flags:  0x0
Data length:    0 B
RoData length:  16 B
Text length:    24 B
No. functions:  1

functions:
        "increment": 0x0

data:

rodata:
    0: 0x69 0x6e 0x63 0x72 0x65 0x6d 0x65 0x6e
    8: 0x74 0x00 0x00 0x00 0x00 0x00 0x00 0x00

text:
<increment>
    0x0:        79 10 00 00 00 00 00 00 r0 = *(uint64_t*)(r1 + 0)
    0x8:        07 00 00 00 01 00 00 00 r0 += 1
   0x10:        95 00 00 00 00 00 00 00 Return r0

This shows the:

  • application header

  • list of functions

  • read-only data containing the function name and some padding

  • the application code

The application code fetches the value from the pointer in r1 (the context argument) and increments the value in the second instruction. The return parameter is stored in register r0.

Build variables

RBPF_CONTAINER_PATH

default: container

Location of Femto-Container applications. Place all .c and .cpp source modules here.

BPF_STORE_NUM_VALUES

default: 16

Maximum number of stored values.

Space is shared between all stores (global and local).

API Documentation

namespace rBPF

Functions

String getErrorString(int error)

Get text for an error code.

class GlobalStore : public rBPF::Store, public rBPF::Store
#include <Store.h>

Public Functions

inline virtual bool update(Key key, Value value) override

Update value in store.

Parameters
  • key

  • value

Returns

bool – true on success, false if store is full

inline virtual bool fetch(Key key, Value &value) override

Fetch value from store.

If key is not found in the store then its added and set to 0.

Parameters
  • key

  • value

Returns

bool – true on success, false if store is full

virtual bool update(Key key, Value value) override

Update value in store.

Parameters
  • key

  • value

Returns

bool – true on success, false if store is full

virtual bool fetch(Key key, Value &value) override

Fetch value from store.

If key is not found in the store then its added and set to 0.

Parameters
  • key

  • value

Returns

bool – true on success, false if store is full

class LocalStore : public rBPF::Store, public rBPF::Store
#include <Store.h>

Public Functions

inline virtual bool update(Key key, Value value) override

Update value in store.

Parameters
  • key

  • value

Returns

bool – true on success, false if store is full

inline virtual bool fetch(Key key, Value &value) override

Fetch value from store.

If key is not found in the store then its added and set to 0.

Parameters
  • key

  • value

Returns

bool – true on success, false if store is full

virtual bool update(Key key, Value value) override

Update value in store.

Parameters
  • key

  • value

Returns

bool – true on success, false if store is full

virtual bool fetch(Key key, Value &value) override

Fetch value from store.

If key is not found in the store then its added and set to 0.

Parameters
  • key

  • value

Returns

bool – true on success, false if store is full

class Store
#include <Store.h>

Subclassed by rBPF::GlobalStore, rBPF::GlobalStore, rBPF::LocalStore, rBPF::LocalStore

Public Functions

virtual bool update(Key key, Value value) = 0

Update value in store.

Parameters
  • key

  • value

Returns

bool – true on success, false if store is full

virtual bool fetch(Key key, Value &value) = 0

Fetch value from store.

If key is not found in the store then its added and set to 0.

Parameters
  • key

  • value

Returns

bool – true on success, false if store is full

inline Value get(Key key)

Fetch value from store.

Parameters

key

class Entry
#include <Store.h>
class VirtualMachine
#include <VirtualMachine.h>

Create a VM and load a container

param container

Container code blob

bool load(const Container &container, size_t stackSize = defaultStackSize)

Load container and initialise it.

Returns

bool – true on success

void unload()

Unload container and free any allocated resources.

Run the container

param ctx

IN/OUT Passed to container. Must be persistent.

returns int64_t

Result returned from container

Public Functions

VirtualMachine()

Create an uninitialised VM.

inline int getLastError() const

Get error code from last call to execute()

Returns

int – 0 on success. Retrieve text for error code using getErrorString()

References

Used by

Environment Variables

SoC support

  • esp32

  • esp32c3

  • esp32s2

  • esp32s3

  • esp8266

  • host

  • rp2040