Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
102 changes: 102 additions & 0 deletions tests/experiments_c/basic_monitor.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
#include "alkali.h"
#include "generic_handlers.h"

/* ---------------- Upstream ingress metadata ---------------- */
struct ingress_meta_t {
BITS_FIELD(32, ingress_port);
BITS_FIELD(32, r0);
BITS_FIELD(32, r1);
BITS_FIELD(32, r2);
};

/* ---------------- Ethernet header (split 32+16) ---------------- */
struct eth_header_t {
BITS_FIELD(32, dst_mac_1);
BITS_FIELD(16, dst_mac_2);
BITS_FIELD(32, src_mac_1);
BITS_FIELD(16, src_mac_2);
BITS_FIELD(16, ether_type);
};

/* ---------------- Dispatcher metadata ---------------- */
/* flags: bit0=drop (0 forward), bit1=mac_swapped */
struct monitor_meta_t {
BITS_FIELD(32, egress_port);
BITS_FIELD(32, flags);
BITS_FIELD(32, ingress_port);
BITS_FIELD(32, r0);
};

/* ---------------- Stats tables ---------------- */
/* key=0 => global counters */
struct global_stats_t {
BITS_FIELD(32, total_packets);
BITS_FIELD(32, r0);
};
ak_TABLE(4, BITS(32), struct global_stats_t, ) global_stats;

/* key=port => per-port counters */
struct port_stats_t {
BITS_FIELD(32, packets);
BITS_FIELD(32, r0);
};
ak_TABLE(256, BITS(32), struct port_stats_t, ) port_stats;

/* Swap src/dst MAC (no branches) */
static inline void
swap_mac(struct eth_header_t *eth) {
BITS(32) t32;
BITS(16) t16;

t32 = eth->dst_mac_1; eth->dst_mac_1 = eth->src_mac_1; eth->src_mac_1 = t32;
t16 = eth->dst_mac_2; eth->dst_mac_2 = eth->src_mac_2; eth->src_mac_2 = t16;
}

void NET_RECV__process_packet(buf_t packet) {
/* 0) Extract ingress port */
struct ingress_meta_t inmeta;
bufextract(packet, (void*)&inmeta);
BITS(32) ingress_port = inmeta.ingress_port;

/* 1) Extract Ethernet (for MAC swap) */
struct eth_header_t eth;
bufextract(packet, (void*)&eth);

/* 2) Update stats (global + per-port) */
{
BITS(32) k0 = (BITS(32))0;
struct global_stats_t gs;
gs.total_packets = (BITS(32))0;
gs.r0 = (BITS(32))0;
table_lookup(&global_stats, &k0, &gs);
gs.total_packets = gs.total_packets + (BITS(32))1;
table_update(&global_stats, &k0, &gs);
}

{
struct port_stats_t ps;
ps.packets = (BITS(32))0;
ps.r0 = (BITS(32))0;
table_lookup(&port_stats, &ingress_port, &ps);
ps.packets = ps.packets + (BITS(32))1;
table_update(&port_stats, &ingress_port, &ps);
}

/* 3) Swap MAC addresses */
swap_mac(&eth);

/* 4) Emit rewritten Ethernet back (depends on your pipeline's packet model) */
bufemit(packet, (void*)&eth);

/* 5) Emit dispatcher meta: hairpin to the same port */
struct monitor_meta_t outm;
outm.egress_port = ingress_port;
outm.flags = (BITS(32))0 | ((BITS(32))1 << 1); /* mac_swapped=1 */
outm.ingress_port = ingress_port;
outm.r0 = (BITS(32))0;

bufemit(packet, (void*)&outm);

/* 6) Terminate */
EXT__NET_SEND__net_send(packet);
}
41 changes: 41 additions & 0 deletions tests/experiments_c/bridge.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
#include "alkali.h"
#include "generic_handlers.h"

/* Upstream ingress metadata */
struct ingress_meta_t {
BITS_FIELD(32, ingress_port);
BITS_FIELD(32, r0);
BITS_FIELD(32, r1);
BITS_FIELD(32, r2);
};

/* Meta consumed by the dispatcher */
struct bridge_meta_t {
BITS_FIELD(32, egress_port);
BITS_FIELD(32, flags); /* bit0=drop (0 forward), other bits reserved */
BITS_FIELD(32, ingress_port); /* optional: for tracing/debug */
BITS_FIELD(32, r0);
};

void NET_RECV__process_packet(buf_t packet) {
/* 0) Extract upstream ingress metadata */
struct ingress_meta_t inmeta;
bufextract(packet, (void*)&inmeta);

BITS(32) in_port = inmeta.ingress_port;

/* 1) Compute egress port: swap 0 <-> 1 */
BITS(32) out_port = in_port ^ (BITS(32))1;

/* 2) Emit dispatcher metadata */
struct bridge_meta_t outm;
outm.egress_port = out_port;
outm.flags = (BITS(32))0; /* no drop */
outm.ingress_port = in_port;
outm.r0 = (BITS(32))0;

bufemit(packet, (void*)&outm);

/* 3) Terminate stage */
EXT__NET_SEND__net_send(packet);
}
74 changes: 74 additions & 0 deletions tests/experiments_c/firewall.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
#include "alkali.h"
#include "generic_handlers.h"

/* Ethernet header */
struct eth_header_t {
BITS_FIELD(48, dst_mac);
BITS_FIELD(48, src_mac);
BITS_FIELD(16, ether_type);
};

/* IPv4 header (fixed 20B) */
struct ip_header_t {
BITS_FIELD(16, misc);
BITS_FIELD(16, length);
BITS_FIELD(16, identification);
BITS_FIELD(16, fragment_offset);
BITS_FIELD(8, ttl);
BITS_FIELD(8, protocol);
BITS_FIELD(16, hdr_checksum);
BITS_FIELD(32, source_ip);
BITS_FIELD(32, dst_ip);
};

/*
* Firewall table:
* key = src_ip
* value = rule (0 => allow, non-zero => drop)
*
* IMPORTANT: Use BITS(32) directly to avoid struct value lowering issues.
*/
ak_TABLE(64, BITS(32), BITS(32), ) firewall_ip_table;

void NET_RECV__process_packet(buf_t packet) {
struct eth_header_t eth;
struct ip_header_t ip;

/* Always extract in a fixed order */
bufextract(packet, (void*)&eth);
bufextract(packet, (void*)&ip);

/* Default: drop */
BITS(32) rule = (BITS(32))1;
BITS(32) allow = (BITS(32))0;

/* Only apply firewall to IPv4 */
if (eth.ether_type == (BITS(16))0x0800) {
BITS(32) key = ip.source_ip;

/*
* On miss, rule should remain default=1.
* So we pass a pre-initialized rule into lookup as the "default".
*/
BITS(32) looked_rule = (BITS(32))1;
table_lookup(&firewall_ip_table, &key, &looked_rule);

rule = looked_rule;

if (rule == (BITS(32))0) {
allow = (BITS(32))1;
} else {
allow = (BITS(32))0;
}
}

/*
* Append meta (rule, allow) WITHOUT using a struct.
* This avoids ep2 struct_update / stackslot init bugs.
*/
bufemit(packet, (void*)&rule);
bufemit(packet, (void*)&allow);

/* Must terminate by calling the next stage */
EXT__NET_SEND__net_send(packet);
}
52 changes: 52 additions & 0 deletions tests/experiments_c/forward.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
#include "alkali.h"
#include "generic_handlers.h"

// ---- compile-time configuration (replaces CLI arguments) ----
#define DESTINATION_SVC 3
#define PRINT_DELAY 1000000

// ---- state: use a table to store a global packet counter (fixed key = 0) ----
struct counter_state_t {
BITS_FIELD(32, cnt);
};
ak_TABLE(64, BITS(32), struct counter_state_t, ) counter_table;

// ---- custom forwarding metadata (replaces onvm_pkt_meta.destination) ----
struct forward_meta_t {
BITS_FIELD(32, destination); // destination service identifier
BITS_FIELD(32, flags); // bit0 = should_report
BITS_FIELD(32, seq); // local packet counter snapshot
BITS_FIELD(32, reserved);
};

void NET_RECV__process_packet(buf_t packet) {
// 1) Load and increment the global packet counter
BITS(32) key = 0;
struct counter_state_t st;
table_lookup(&counter_table, &key, &st);

BITS(32) new_cnt = st.cnt + 1;

// Compute hit without control-flow:
// hit = 1 if new_cnt >= PRINT_DELAY, else 0
BITS(32) hit = (new_cnt >= PRINT_DELAY);

// Reset counter without using branches:
// if hit == 1 -> st.cnt = 0
// else -> st.cnt = new_cnt
st.cnt = new_cnt * (1 - hit);

table_update(&counter_table, &key, &st);

// 2) Emit forwarding metadata
struct forward_meta_t meta;
meta.destination = DESTINATION_SVC;
meta.flags = hit; // downstream stages can act on this flag
meta.seq = new_cnt; // snapshot of the counter before reset
meta.reserved = 0;

bufemit(packet, &meta);

// 3) Forward the packet to the next pipeline stage
EXT__NET_SEND__net_send(packet);
}
112 changes: 112 additions & 0 deletions tests/experiments_c/forward_tb.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
#include "alkali.h"
#include "generic_handlers.h"

// Compile-time defaults (can be replaced by cfg_table written by control-plane)
#define DESTINATION_SVC 3

// -------------------- Packet headers (minimal) --------------------
struct eth_header_t {
BITS_FIELD(48, dst_mac);
BITS_FIELD(48, src_mac);
BITS_FIELD(16, ether_type);
};

struct ip_header_t {
BITS_FIELD(16, misc);
BITS_FIELD(16, length); // IPv4 total length (bytes): header + payload
BITS_FIELD(16, identification);
BITS_FIELD(16, fragment_offset);
BITS_FIELD(16, TTL_transport);
BITS_FIELD(16, checksum);
BITS_FIELD(32, source_ip);
BITS_FIELD(32, dst_ip);
BITS_FIELD(32, options);
};

// -------------------- Token bucket state/config --------------------
// State: current tokens (bytes)
struct tb_state_t {
BITS_FIELD(32, tokens_bytes);
};

// Config: bucket depth (bytes). (Rate is not used in dataplane; refill is external.)
struct tb_cfg_t {
BITS_FIELD(32, depth_bytes);
};

// key = 0 => single global bucket (you can extend to per-flow later)
ak_TABLE(64, BITS(32), struct tb_state_t, ) tb_state_table;
ak_TABLE(64, BITS(32), struct tb_cfg_t, ) tb_cfg_table;

// -------------------- Metadata emitted to packet --------------------
// flags bit0: drop (1 = should drop)
// flags bit1: allow (1 = allowed)
// You can add more bits later.
struct tb_meta_t {
BITS_FIELD(32, destination);
BITS_FIELD(32, flags);
BITS_FIELD(32, pkt_len_bytes);
BITS_FIELD(32, tokens_after);
};

void NET_RECV__process_packet(buf_t packet) {
// 0) Parse headers to compute packet length (best-effort)
// NOTE: If your input packets are not guaranteed to be IPv4, you should
// place a classifier stage before this NF. We avoid if/else here.
struct eth_header_t eth;
struct ip_header_t ip;

bufextract(packet, (void*)&eth);
bufextract(packet, (void*)&ip);

// Approx L2+L3 total length:
// IPv4 total length field does NOT include Ethernet header.
// Add 14 bytes Ethernet header as in your ONVM example.
BITS(32) pkt_len = (BITS(32))ip.length + (BITS(32))14;

// 1) Load config/state from tables (key = 0)
BITS(32) key = 0;

struct tb_cfg_t cfg;
table_lookup(&tb_cfg_table, &key, &cfg);

struct tb_state_t st;
table_lookup(&tb_state_table, &key, &st);

BITS(32) depth = cfg.depth_bytes;
BITS(32) tokens = st.tokens_bytes;

// 2) Compute allow/drop decisions WITHOUT branching
// too_big = 1 if pkt_len > depth else 0
BITS(32) too_big = (pkt_len > depth);

// enough = 1 if tokens >= pkt_len else 0
BITS(32) enough = (tokens >= pkt_len);

// allow = (not too_big) AND enough
// Since values are 0/1, we can use multiplication for AND.
BITS(32) allow = (1 - too_big) * enough;

// drop = 1 - allow
BITS(32) drop = 1 - allow;

// 3) Update tokens WITHOUT branching
// If allow==1: tokens_after = tokens - pkt_len
// If allow==0: tokens_after = tokens
BITS(32) tokens_after = tokens - (pkt_len * allow);

st.tokens_bytes = tokens_after;
table_update(&tb_state_table, &key, &st);

// 4) Emit metadata for downstream stages
struct tb_meta_t meta;
meta.destination = DESTINATION_SVC;
meta.flags = (drop) | (allow << 1);
meta.pkt_len_bytes = pkt_len;
meta.tokens_after = tokens_after;

bufemit(packet, &meta);

// 5) Forward packet (downstream can drop if meta.flags bit0 is set)
EXT__NET_SEND__net_send(packet);
}
Loading