For building pktlab monitors!
libpktlab
with SSL support. This can be obtained by following the instructions in installing the PacketLab software package (from pre-built binaries or from source).- The latest release of WASI-SDK, where this link provides the instructions to install the WASI SDK (from pre-built binaries or from source).
WASI SDK is used to compile the monitor C code into a WebAssembly binary (i.e. the monitor binary), with the
wasi-libc
library support for the memory manipulation functions. We won't use any system calls provided by thewasi-libc
due to security concerns.
Run pktmon.sh
to compile the C code into a monitor binary.
The usage of the pktmon.sh
script is as follows:
# You should have the C code named `<name-of-monitor>.c` in the current directory
# Default name of the monitor is "monitor", which needs `monitor.c` to be present in the current directory
bash pktmon.sh <path-to-pktlab-pkg> <path-to-wasi-sdk> <name-of-monitor(default to "monitor")>
For example, if the PacketLab software package is installed at the /opt/pktlab
directory,
WASI SDK is installed at the /opt/wasi-sdk
directory,
and you have a monitor C code named any_monitor.c
, then you can run the following command:
bash pktmon.sh /opt/pktlab /opt/wasi-sdk any_monitor
This will generate a monitor binary named any_monitor.wasm
in the current
directory, and output the SHA256 digest of the monitor in the any_monitor.digest
file.
You can then use ppksman
from the pktlab
Python3 package to sign a new exppriv
or delpriv
certificate with the monitor's digest, which you can then distribute with the compiled monitor to expermenters for them to use to contact your pktlab endpoint.
The template for the monitor is provided in the monitor.template.c
file. You
can start by copying the contents of this file into a new file, and then
implement the corresponding checks in the template.
There are two optional functions that you can implement in the monitor, which will be invoked by the endpoint before processing the Packetlab message and after the connection is about to be closed. These functions are:
/**
* This function is called before the endpoint processes the Packetlab message.
* You can use this function to initialize the monitor state for the message
* checking, as well as report the monitor's status back to the controller.
*
* @param info[out]: The information to be reported back to the controller.
* @param persistent_memory[in]: The persistent memory region for the monitor.
* @param persistent_memory_len[in]: The length of the persistent memory region.
* @return: Size of the info parameter. The maximum size is defined by
* PKTLAB_LINFO_INFO_MAX in pktlab.h. If the size of the info parameter
* is greater than PKTLAB_LINFO_INFO_MAX, the sender will truncate the
* response message in `info` to PKTLAB_LINFO_INFO_MAX.
*/
uint32_t initialize(void* info, void* persistent_memory, uint32_t persistent_memory_len);
/**
* This function is called when the connection is about to be closed. You can
* use this function to clean up the monitor state and report the monitor's
* status back to the controller.
*
* @param info[out]: The information to be reported back to the controller.
* @param persistent_memory[in]: The persistent memory region for the monitor.
* @param persistent_memory_len[in]: The length of the persistent memory region.
* @return: Size of the info parameter. The maximum size is defined by
* PKTLAB_LINFO_INFO_MAX in pktlab.h. If the size of the info parameter
* is greater than PKTLAB_LINFO_INFO_MAX, the sender will truncate the
* response message in `info` to PKTLAB_LINFO_INFO_MAX.
*/
uint32_t finallize(void *info, void* persistent_memory, uint32_t persistent_memory_len);
Currently, the monitor is implemented to check the following Packetlab messages with the corresponding functions:
nopen
:check_pktlab_message_nopen
nclose
:check_pktlab_message_nclose
nsend
:check_pktlab_message_nsend
ndata
:check_pktlab_message_ndata
ncap
:check_pktlab_message_ncap
Also, the monitor can be implemented to handle the result messages from the
endpoint after the endpoint processes Packetlab messages. These functions have
the same arguments as the corresponding check functions, and have one additional
argument enum pktlab_status errid
to indicate the result of the endpoint
processing the Packetlab message. These functions will be useful when the endpoint
fails to process the Packetlab message for some reason, and the monitor can take
corrective actions based on the result of the endpoint processing the Packetlab
message.
The corresponding functions are:
pktlab_message_nopen_result
pktlab_message_nclose_result
pktlab_message_nsend_result
pktlab_message_ncap_result
All of these result functions are optional, and you can choose to implement only
the functions that are relevant to your monitor. ndata
messages is sent by the
endpoint to the controller so it doesn't have a corresponding result function.
The monitor check functions's return value will determine if the request/notification message is allowed or not.
Specifically, the return value rv
of the check functions should be:
rv == 0
: The message is allowed.1 <= rv < UINT16_MAX
: The message is not allowed, andrv
is the reason for dropping the message.rv >= UINT16_MAX
: The message is not allowed, and the reason for dropping the message is an error in the monitor.rv < 0
: The message is not allowed, and the reason for dropping the packet is intentionally hidden by the monitor.
For not allowed requests (non-ndata
messages for now), a monitor reject (PKTLAB_ECTLMONREJ
) result message is returned when 1 <= rv < UINT16_MAX
(rv
returned as errid
), or rv < 0
(errid
is 0), or rv >= UINT16_MAX
when the endpoint is not configured to report crash (report_crash
for reference endpoint pktendpt
/pktlabme
; errid
is 0);
a monitor unknown (PKTLAB_ECTLMONUKN
) result message is returned when rv >= UINT16_MAX
and the endpoint is configured to report crash.
For not allowed notifications (ndata
message for now), a nblock notification is returned when 1 <= rv < UINT16_MAX
(rv
returned as monerrid
) or when rv >= UINT16_MAX
and the endpoint is configured to report crash (monerrid
is 0).
The not allowed notifications are otherwise just dropped silently.
The rejecting/complaining monitor indx is always returned when a monitor reject/monitor unknown/nblock message is returned.
Some helper functions are provided in the monitor_helper.h
file, which can be
used in the monitor implementation. The following helper functions are provided:
/**
* This function is used to read the content (packet data and other fields
* defined in virtual memory) from the virtual memory region.
*
* @param addr[in]: The address of the virtual memory region to read from.
* @param buf[out]: The buffer to store the content read from the virtual
* memory.
* @param len[in]: The length of the content to read from the virtual memory.
* @return: 0 on success, other values on failure.
*/
extern int read_vmem_region(uint32_t addr, void *buf, uint32_t len);
/**
* This function flushes the persistent memory
* region to the disk immediately. This is useful when you want to ensure that
* the changes to the persistent memory are reflected on the disk. The persistent
* memory will always be flushed to the disk when the monitor connection is
* closed.
*
* @return: 0 on success, other values on failure.
*/
extern int persistent_memory_flush();
Some example monitors are provided in the example_monitors
directory. You can
use these monitors as a reference to write your own monitor.
-
icmp_limit_monitor.c
: This monitor is used to limit the number of ICMP packets sent by the controller. The controller can send a maximum of10
ICMP packets, and the monitor will drop any ICMP packet sent after the limit is reached. No matter whether the controller is restarted or the endpoint is restarted, the monitor will keep the state of the number of ICMP packets sent based on the persistent memory. -
http_monitor.c
: This monitor will only allow the controller to send HTTP requests and receive HTTP responses. The monitor is designed to drop any packet that is not an HTTP request or response. However, to simplify the implementation, the monitor will only check request methods and "HTTP" in the response status line. So, strictly, this monitor is not designed to be used in a real-world scenario, it is just an example to show how to write a monitor. -
tcp_only_monitor.c
: This monitor will only allow the controller to communicate over TCP. -
pass_all_monitor.c
: This monitor will allow everything.
Use the following commands to build the example monitors:
bash pktmon.sh <path-to-pktlab-pkg> <path-to-wasi-sdk> examples/icmp_limit_monitor
bash pktmon.sh <path-to-pktlab-pkg> <path-to-wasi-sdk> examples/http_monitor
bash pktmon.sh <path-to-pktlab-pkg> <path-to-wasi-sdk> examples/tcp_only_monitor
bash pktmon.sh <path-to-pktlab-pkg> <path-to-wasi-sdk> examples/pass_all_monitor