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
2 changes: 1 addition & 1 deletion api/gr_api.h
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
#include <stdlib.h>

// Must be bumped when making non-backward compatible changes in API headers
#define GR_API_VERSION 6
#define GR_API_VERSION 8

// API request header.
struct gr_api_request {
Expand Down
20 changes: 19 additions & 1 deletion modules/infra/api/gr_infra.h
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,8 @@ typedef enum : uint16_t {
GR_IFACE_F_PACKET_TRACE = GR_BIT16(2),
GR_IFACE_F_SNAT_STATIC = GR_BIT16(3),
GR_IFACE_F_SNAT_DYNAMIC = GR_BIT16(4),
GR_IFACE_F_PING_IGNORE = GR_BIT16(5),
GR_IFACE_F_ERR_IGNORE = GR_BIT16(6),
} gr_iface_flags_t;

// Interface state flags.
Expand Down Expand Up @@ -445,14 +447,30 @@ struct gr_graph_dump_resp {

GR_REQ(GR_GRAPH_DUMP, struct gr_graph_dump_req, struct gr_graph_dump_resp);

typedef enum : uint16_t {
GR_GRAPH_SET_RX_BURST = GR_BIT16(0),
GR_GRAPH_SET_VECTOR = GR_BIT16(1),
GR_GRAPH_SET_ICMP_ERROR = GR_BIT16(2),
GR_GRAPH_SET_ARP = GR_BIT16(3),
GR_GRAPH_SET_ICMP = GR_BIT16(4),
} gr_graph_conf_set_attr_t;

struct gr_graph_conf {
uint16_t rx_burst_max; // default 64, max 256
uint16_t vector_max; // default 64, max 256
uint16_t icmp_error_rate; // ICMP errors/sec per node per worker, 0 = no limit, default 1000
uint16_t arp_rate; // ARP packets/sec per worker, 0 = no limit, default 1000
uint16_t icmp_rate; // ICMP/ICMPv6 input packets/sec per worker, 0 = no limit, default 1000
};

GR_REQ(GR_GRAPH_CONF_GET, struct gr_empty, struct gr_graph_conf);

GR_REQ(GR_GRAPH_CONF_SET, struct gr_graph_conf, struct gr_empty);
struct gr_graph_conf_set_req {
struct gr_graph_conf;
gr_graph_conf_set_attr_t set_attrs;
};

GR_REQ(GR_GRAPH_CONF_SET, struct gr_graph_conf_set_req, struct gr_empty);

// packet tracing //////////////////////////////////////////////////////////////

Expand Down
13 changes: 8 additions & 5 deletions modules/infra/cli/cli_iface.h
Original file line number Diff line number Diff line change
Expand Up @@ -74,14 +74,17 @@ int arg_iface(
CLI_CONTEXT(root, INTERFACE_ARG, CTX_ARG("set", "Modify an existing interface."))

#define IFACE_ATTRS_CMD \
"(up|down),(promisc PROMISC),(mtu MTU),((vrf VRF)|(domain DOMAIN)),(description DESCR)"
"(up|down),(promisc PROMISC),(ping-ignore PING_IGNORE),(err-ignore ERR_IGNORE)," \
"(mtu MTU),((vrf VRF)|(domain DOMAIN)),(description DESCR)"

#define IFACE_FLAG_ARG(placeholder, desc) \
with_help(desc, EC_NODE_OR(placeholder, ec_node_str(desc, "on"), ec_node_str(desc, "off")))

#define IFACE_ATTRS_ARGS \
with_help("Set the interface UP.", ec_node_str("up", "up")), \
with_help( \
"Enable/disable promiscuous mode.", \
EC_NODE_OR("PROMISC", ec_node_str("", "on"), ec_node_str("", "off")) \
), \
IFACE_FLAG_ARG("PROMISC", "Enable/disable promiscuous mode."), \
IFACE_FLAG_ARG("PING_IGNORE", "Ignore ICMP echo requests."), \
IFACE_FLAG_ARG("ERR_IGNORE", "Omit sending ICMP errors to their originating IP."), \
with_help("Set the interface DOWN.", ec_node_str("down", "down")), \
with_help( \
"Maximum transmission unit size.", \
Expand Down
46 changes: 41 additions & 5 deletions modules/infra/cli/graph.c
Original file line number Diff line number Diff line change
Expand Up @@ -13,11 +13,31 @@
#include <stdio.h>

static cmd_status_t graph_conf_set(struct gr_api_client *c, const struct ec_pnode *p) {
struct gr_graph_conf req = {0};
struct gr_graph_conf_set_req req = {.set_attrs = 0};

if (arg_u16(p, "VECTOR", &req.vector_max) < 0 && errno != ENOENT)
if (arg_u16(p, "VECTOR", &req.vector_max) == 0)
req.set_attrs |= GR_GRAPH_SET_VECTOR;
else if (errno != ENOENT)
return CMD_ERROR;
if (arg_u16(p, "BURST", &req.rx_burst_max) < 0 && errno != ENOENT)

if (arg_u16(p, "BURST", &req.rx_burst_max) == 0)
req.set_attrs |= GR_GRAPH_SET_RX_BURST;
else if (errno != ENOENT)
return CMD_ERROR;

if (arg_u16(p, "ICMP_ERROR_RATE", &req.icmp_error_rate) == 0)
req.set_attrs |= GR_GRAPH_SET_ICMP_ERROR;
else if (errno != ENOENT)
return CMD_ERROR;

if (arg_u16(p, "ARP_RATE", &req.arp_rate) == 0)
req.set_attrs |= GR_GRAPH_SET_ARP;
else if (errno != ENOENT)
return CMD_ERROR;

if (arg_u16(p, "ICMP_RATE", &req.icmp_rate) == 0)
req.set_attrs |= GR_GRAPH_SET_ICMP;
else if (errno != ENOENT)
return CMD_ERROR;

if (gr_api_client_send_recv(c, GR_GRAPH_CONF_SET, sizeof(req), &req, NULL) < 0)
Expand All @@ -38,6 +58,9 @@ static cmd_status_t graph_conf_show(struct gr_api_client *c, const struct ec_pno
struct gr_object *o = gr_object_new(NULL);
gr_object_field(o, "vector_max", GR_DISP_INT, "%u", sizes->vector_max);
gr_object_field(o, "rx_burst_max", GR_DISP_INT, "%u", sizes->rx_burst_max);
gr_object_field(o, "icmp_error_rate", GR_DISP_INT, "%u", sizes->icmp_error_rate);
gr_object_field(o, "arp_rate", GR_DISP_INT, "%u", sizes->arp_rate);
gr_object_field(o, "icmp_rate", GR_DISP_INT, "%u", sizes->icmp_rate);
gr_object_free(o);

free(resp_ptr);
Expand Down Expand Up @@ -76,14 +99,27 @@ static int ctx_init(struct ec_node *root) {

ret = CLI_COMMAND(
CONF_CTX(root),
"set (vector-max VECTOR),(rx-burst-max BURST)",
"set (vector-max VECTOR),(rx-burst-max BURST),(icmp-error-rate ICMP_ERROR_RATE),"
"(arp-rate ARP_RATE),(icmp-rate ICMP_RATE)",
graph_conf_set,
"Configure maximum burst sizes of the packet processing graph.",
"Configure packet processing graph parameters.",
with_help(
"Maximum size of graph vectors.", ec_node_uint("VECTOR", 1, UINT16_MAX, 10)
),
with_help(
"Maximum size of RX queue burst.", ec_node_uint("BURST", 1, UINT16_MAX, 10)
),
with_help(
"ICMP errors/sec per node per worker (0 = no limit).",
ec_node_uint("ICMP_ERROR_RATE", 0, UINT16_MAX, 10)
),
with_help(
"ARP packets/sec per worker (0 = no limit).",
ec_node_uint("ARP_RATE", 0, UINT16_MAX, 10)
),
with_help(
"ICMP/ICMPv6 input packets/sec per worker (0 = no limit).",
ec_node_uint("ICMP_RATE", 0, UINT16_MAX, 10)
Comment on lines +112 to +122

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What about setting the lower bound to RTE_GRAPH_BURST_SIZE, to have an explicit value for the user ?

With the current code: ctx->tokens -= RTE_MIN(ctx->tokens, nb_pkts);, if the user sets the number of tokens to 1, we may still send up to RTE_GRAPH_BURST_SIZE errors, or ICMP packets, etc.

@rjarry rjarry Apr 23, 2026

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

But then how would you disable it?

Indeed, the limit is soft and we allow the full burst to pass even if there is less than nb_pkts left in the bucket. But since it is transient, I thought it was fine.

)
);
if (ret < 0)
Expand Down
35 changes: 26 additions & 9 deletions modules/infra/cli/iface.c
Original file line number Diff line number Diff line change
Expand Up @@ -213,21 +213,42 @@ static ssize_t iface_flags_format(char *buf, size_t len, const struct gr_iface *
SAFE_BUF(snprintf, len, " tracing");
if (iface->flags & (GR_IFACE_F_SNAT_STATIC | GR_IFACE_F_SNAT_DYNAMIC))
SAFE_BUF(snprintf, len, " snat");
if (iface->flags & GR_IFACE_F_PING_IGNORE)
SAFE_BUF(snprintf, len, " ping-ignore");
if (iface->flags & GR_IFACE_F_ERR_IGNORE)
SAFE_BUF(snprintf, len, " err-ignore");

return n;
err:
return -1;
}

static uint64_t on_off_flag(
struct gr_iface *iface,
const struct ec_pnode *p,
const char *placeholder,
gr_iface_flags_t flag
) {
const char *value = arg_str(p, placeholder);
if (value != NULL && strcmp(value, "on") == 0) {
iface->flags |= flag;
return GR_IFACE_SET_FLAGS;
} else if (value != NULL && strcmp(value, "off") == 0) {
iface->flags &= ~flag;
return GR_IFACE_SET_FLAGS;
}
return 0;
}

uint64_t parse_iface_args(
struct gr_api_client *c,
const struct ec_pnode *p,
struct gr_iface *iface,
size_t info_size,
bool update
) {
const char *name, *promisc;
uint64_t set_attrs = 0;
const char *name;

name = arg_str(p, "NAME");
if (update) {
Expand All @@ -252,14 +273,10 @@ uint64_t parse_iface_args(
iface->flags &= ~GR_IFACE_F_UP;
set_attrs |= GR_IFACE_SET_FLAGS;
}
promisc = arg_str(p, "PROMISC");
if (promisc != NULL && strcmp(promisc, "on") == 0) {
iface->flags |= GR_IFACE_F_PROMISC;
set_attrs |= GR_IFACE_SET_FLAGS;
} else if (promisc != NULL && strcmp(promisc, "off") == 0) {
iface->flags &= ~GR_IFACE_F_PROMISC;
set_attrs |= GR_IFACE_SET_FLAGS;
}

set_attrs |= on_off_flag(iface, p, "PROMISC", GR_IFACE_F_PROMISC);
set_attrs |= on_off_flag(iface, p, "PING_IGNORE", GR_IFACE_F_PING_IGNORE);
set_attrs |= on_off_flag(iface, p, "ERR_IGNORE", GR_IFACE_F_ERR_IGNORE);

if (arg_u16(p, "MTU", &iface->mtu) == 0)
set_attrs |= GR_IFACE_SET_MTU;
Expand Down
32 changes: 24 additions & 8 deletions modules/infra/control/graph.c
Original file line number Diff line number Diff line change
Expand Up @@ -85,9 +85,12 @@ static struct iface_info_port *find_port(vec struct iface_info_port **ports, uin
return port;
}

static struct gr_graph_conf graph_conf = {
struct gr_graph_conf graph_conf = {
.rx_burst_max = 64,
.vector_max = 64,
.icmp_error_rate = 0,
.arp_rate = 0,
.icmp_rate = 0,
};

static int
Expand Down Expand Up @@ -610,19 +613,32 @@ static struct api_out graph_conf_get(const void * /*request*/, struct api_ctx *)
}

static struct api_out graph_conf_set(const void *request, struct api_ctx *) {
const struct gr_graph_conf *req = request;
struct gr_graph_conf prev = graph_conf;
const struct gr_graph_conf_set_req *req = request;
vec struct iface_info_port **ports = NULL;
struct gr_graph_conf prev = graph_conf;
struct iface *iface = NULL;
int ret;

if (req->rx_burst_max > RTE_GRAPH_BURST_SIZE || req->vector_max > RTE_GRAPH_BURST_SIZE)
return api_out(EOVERFLOW, 0, NULL);

if (req->rx_burst_max > 0)
if (req->set_attrs & GR_GRAPH_SET_RX_BURST) {
if (req->rx_burst_max == 0)
return api_out(EDOM, 0, NULL);
if (req->rx_burst_max > RTE_GRAPH_BURST_SIZE)
return api_out(EOVERFLOW, 0, NULL);
graph_conf.rx_burst_max = req->rx_burst_max;
if (req->vector_max > 0)
}
if (req->set_attrs & GR_GRAPH_SET_VECTOR) {
if (req->vector_max == 0)
return api_out(EDOM, 0, NULL);
if (req->vector_max > RTE_GRAPH_BURST_SIZE)
return api_out(EOVERFLOW, 0, NULL);
graph_conf.vector_max = req->vector_max;
}
if (req->set_attrs & GR_GRAPH_SET_ICMP_ERROR)
graph_conf.icmp_error_rate = req->icmp_error_rate;
if (req->set_attrs & GR_GRAPH_SET_ARP)
graph_conf.arp_rate = req->arp_rate;
if (req->set_attrs & GR_GRAPH_SET_ICMP)
graph_conf.icmp_rate = req->icmp_rate;

if (graph_conf.rx_burst_max == prev.rx_burst_max
&& graph_conf.vector_max == prev.vector_max)
Expand Down
2 changes: 2 additions & 0 deletions modules/infra/control/graph.h
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,8 @@ rte_node_enqueue_x1(struct rte_graph *, struct rte_node *, rte_edge_t next, void

rte_edge_t gr_node_attach_parent(const char *parent, const char *node);

extern struct gr_graph_conf graph_conf;

uint16_t drop_packets(struct rte_graph *, struct rte_node *, void **, uint16_t);
int drop_format(char *buf, size_t buf_len, const void *data, size_t data_len);

Expand Down
10 changes: 10 additions & 0 deletions modules/infra/control/iface.c
Original file line number Diff line number Diff line change
Expand Up @@ -388,6 +388,16 @@ int iface_reconfig(
}
if ((ret = iface_set_up_down(iface, conf->flags & GR_IFACE_F_UP)) < 0)
goto err;

if (conf->flags & GR_IFACE_F_PING_IGNORE)
iface->flags |= GR_IFACE_F_PING_IGNORE;
else
iface->flags &= ~GR_IFACE_F_PING_IGNORE;

if (conf->flags & GR_IFACE_F_ERR_IGNORE)
iface->flags |= GR_IFACE_F_ERR_IGNORE;
else
iface->flags &= ~GR_IFACE_F_ERR_IGNORE;
}

if (set_attrs & GR_IFACE_SET_VRF) {
Expand Down
33 changes: 33 additions & 0 deletions modules/infra/datapath/datapath.h
Original file line number Diff line number Diff line change
Expand Up @@ -3,4 +3,37 @@

#pragma once

#include <rte_cycles.h>

#include <stdint.h>

void *gr_datapath_loop(void *priv);

struct rate_limit_ctx {
uint16_t tokens;
uint64_t last_refill;
} __attribute__((packed));

static inline bool
rate_limited(struct rate_limit_ctx *ctx, const uint16_t max_rate, const uint16_t nb_pkts) {
uint64_t now = rte_rdtsc();
uint16_t add;

assert(ctx != NULL);

if (max_rate == 0)
return false;

add = (now - ctx->last_refill) * max_rate / rte_get_tsc_hz();
if (add > 0) {
ctx->tokens = RTE_MIN((uint32_t)ctx->tokens + add, (uint32_t)max_rate);
ctx->last_refill = now;
}

if (ctx->tokens == 0)
return true;

ctx->tokens -= RTE_MIN(ctx->tokens, nb_pkts);

return false;
}
19 changes: 19 additions & 0 deletions modules/ip/datapath/arp_input.c
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
// SPDX-License-Identifier: BSD-3-Clause
// Copyright (c) 2024 Robin Jarry

#include "datapath.h"
#include "eth.h"
#include "graph.h"
#include "mbuf.h"
Expand All @@ -9,20 +10,29 @@
#include <rte_arp.h>
#include <rte_ether.h>

GR_NODE_CTX_TYPE(arp_input_ctx, { struct rate_limit_ctx limit; });

enum {
OP_REQUEST = 0,
OP_REPLY,
OP_UNSUPP,
PROTO_UNSUPP,
RATE_LIMITED,
EDGE_COUNT,
};

static uint16_t
arp_input_process(struct rte_graph *graph, struct rte_node *node, void **objs, uint16_t nb_objs) {
struct arp_input_ctx *ctx = arp_input_ctx(node);
struct rte_arp_hdr *arp, *t;
struct rte_mbuf *mbuf;
rte_edge_t edge;

if (rate_limited(&ctx->limit, graph_conf.arp_rate, nb_objs)) {
rte_node_next_stream_move(graph, node, RATE_LIMITED);
return nb_objs;
}

for (uint16_t i = 0; i < nb_objs; i++) {
mbuf = objs[i];

Expand Down Expand Up @@ -58,6 +68,13 @@ arp_input_process(struct rte_graph *graph, struct rte_node *node, void **objs, u
return nb_objs;
}

static int arp_input_init(const struct rte_graph *, struct rte_node *node) {
struct arp_input_ctx *ctx = arp_input_ctx(node);
ctx->limit.tokens = graph_conf.arp_rate;
ctx->limit.last_refill = rte_rdtsc();
return 0;
}

static void arp_input_register(void) {
gr_eth_input_add_type(RTE_BE16(RTE_ETHER_TYPE_ARP), "arp_input");
}
Expand All @@ -66,13 +83,15 @@ static struct rte_node_register node = {
.name = "arp_input",

.process = arp_input_process,
.init = arp_input_init,

.nb_edges = EDGE_COUNT,
.next_nodes = {
[OP_REQUEST] = "arp_input_request",
[OP_REPLY] = "arp_input_reply",
[OP_UNSUPP] = "arp_input_op_unsupp",
[PROTO_UNSUPP] = "arp_input_proto_unsupp",
[RATE_LIMITED] = "error_rate_limited",
},
};

Expand Down
Loading