* [PATCH v2 00/15] RFC: Read-only dynamic update implementation
@ 2026-03-19 6:11 David Gibson
2026-03-19 6:11 ` [PATCH v2 01/15] treewide: Spell ASSERT() as assert() David Gibson
` (14 more replies)
0 siblings, 15 replies; 16+ messages in thread
From: David Gibson @ 2026-03-19 6:11 UTC (permalink / raw)
To: Stefano Brivio, passt-dev; +Cc: David Gibson
I've taken Stefano's draft implementation of dynamic updates, and
polished it up to have a read-only implementation of the dynamic
update protocol. So far it retrieves the pif names and ruleset from
passt/pasta and displays it. Sending a new ruleset back comes next.
Patches 1..6/15 are preliminary reworks that make moderate sense even
without pesto - feel free to apply if you're happy with them.
Changes in v2:
* Removed already applied cleanups
* Reworked assert() patch to handle -DNDEBUG properly
* Numerous extra patches:
* Factored out serialisation helpers and use them for migration as
well
* Reworked to allow ip.[ch] and inany.[ch] to be shared with pesto
* Reworks to share some forwarding rule datatypes with pesto
* Implemented sending pif names and current ruleset to pesto
David Gibson (15):
treewide: Spell ASSERT() as assert()
serialise: Split functions user for serialisation from util.c
serialise: Add helpers for serialising unsigned integers
fwd: Move selecting correct scan bitmap into fwd_sync_one()
fwd: Look up rule index in fwd_sync_one()
fwd: Store forwarding tables indexed by (origin) pif
pesto: Introduce stub configuration interface and tool
pesto: Add command line option parsing and debug messages
pesto: Expose list of pifs to pesto
ip: Prepare ip.[ch] for sharing with pesto tool
inany: Prepare inany.[ch] for sharing with pesto tool
fwd: Split forwading rule specification from its implementation state
ip: Define a bound for the string returned by ipproto_name()
fwd_rule: Move forwarding rule text formatting to common code
pesto: Read current ruleset from passt/pasta and display it
.gitignore | 2 +
Makefile | 43 ++++---
common.h | 79 ++++++++++++
conf.c | 294 ++++++++++++++++++++++++++++++++++++++++----
conf.h | 2 +
epoll_type.h | 4 +
flow.c | 117 +++++++++---------
flow_table.h | 2 +-
fwd.c | 218 +++++++++++++++-----------------
fwd.h | 35 ++----
fwd_rule.c | 113 +++++++++++++++++
fwd_rule.h | 59 +++++++++
icmp.c | 14 +--
inany.c | 16 ++-
inany.h | 20 +--
iov.c | 4 +-
ip.c | 74 +++--------
ip.h | 4 +-
isolation.c | 2 +-
lineread.c | 5 +-
log.c | 1 +
migrate.c | 9 +-
netlink.c | 2 +-
packet.c | 4 +-
passt.1 | 5 +
passt.c | 11 +-
passt.h | 9 +-
pcap.c | 3 +-
pesto.1 | 46 +++++++
pesto.c | 341 +++++++++++++++++++++++++++++++++++++++++++++++++++
pesto.h | 34 +++++
pif.c | 4 +-
serialise.c | 147 ++++++++++++++++++++++
serialise.h | 27 ++++
siphash.h | 13 ++
tap.c | 58 ++++++++-
tcp.c | 43 +++----
tcp_splice.c | 10 +-
tcp_vu.c | 8 +-
udp.c | 22 ++--
udp_flow.c | 4 +-
udp_vu.c | 4 +-
util.c | 82 ++-----------
util.h | 84 ++-----------
vhost_user.c | 8 +-
virtio.c | 6 +-
vu_common.c | 4 +-
47 files changed, 1535 insertions(+), 561 deletions(-)
create mode 100644 common.h
create mode 100644 fwd_rule.c
create mode 100644 fwd_rule.h
create mode 100644 pesto.1
create mode 100644 pesto.c
create mode 100644 pesto.h
create mode 100644 serialise.c
create mode 100644 serialise.h
--
2.53.0
^ permalink raw reply [flat|nested] 16+ messages in thread
* [PATCH v2 01/15] treewide: Spell ASSERT() as assert()
2026-03-19 6:11 [PATCH v2 00/15] RFC: Read-only dynamic update implementation David Gibson
@ 2026-03-19 6:11 ` David Gibson
2026-03-19 6:11 ` [PATCH v2 02/15] serialise: Split functions user for serialisation from util.c David Gibson
` (13 subsequent siblings)
14 siblings, 0 replies; 16+ messages in thread
From: David Gibson @ 2026-03-19 6:11 UTC (permalink / raw)
To: Stefano Brivio, passt-dev; +Cc: David Gibson
The standard library assert(3), at least with glibc, hits our seccomp
filter and dies with SIGSYS before it's able to print a message, making it
near useless. Therefore, since 7a8ed9459dfe ("Make assertions actually
useful") we've instead used our own implementation, named ASSERT().
This makes our code look slightly odd though - ASSERT() has the same
overall effect as assert(), it's just a different implementation. More
importantly this makes it awkward to share code between passt/pasta proper
and things that compile in a more typical environment. We're going to want
that for our upcoming dynamic configuration tool.
Address this by overriding the standard library's assert() implementation
with our own, instead of giving ours its own name.
The standard assert() is supposed to be omitted if NDEBUG is defined,
which ours doesn't do. Implement that as well, so ours doesn't
unexpectedly differ. For the -DNDEBUG case we do this by *not* overriding
assert(), since it will be a no-op anyway. This requires a few places to
add a #include <assert.h> to let us compile (albeit with warnings) when
-DNDEBUG.
Signed-off-by: David Gibson <david@gibson.dropbear.id.au>
---
Makefile | 2 +-
conf.c | 6 ++--
flow.c | 80 ++++++++++++++++++++++++++--------------------------
flow_table.h | 2 +-
fwd.c | 14 ++++-----
icmp.c | 14 ++++-----
inany.h | 4 +--
iov.c | 3 +-
isolation.c | 2 +-
lineread.c | 5 ++--
netlink.c | 2 +-
packet.c | 4 +--
passt.c | 2 +-
pif.c | 4 +--
tap.c | 6 ++--
tcp.c | 24 ++++++++--------
tcp_splice.c | 10 +++----
tcp_vu.c | 8 +++---
udp.c | 22 +++++++--------
udp_flow.c | 4 +--
udp_vu.c | 4 +--
util.c | 6 ++--
util.h | 15 ++++++++--
vhost_user.c | 8 +++---
virtio.c | 5 ++--
vu_common.c | 4 +--
26 files changed, 136 insertions(+), 124 deletions(-)
diff --git a/Makefile b/Makefile
index fe016f30..513dc6c6 100644
--- a/Makefile
+++ b/Makefile
@@ -188,5 +188,5 @@ cppcheck: $(PASST_SRCS) $(HEADERS)
--inline-suppr \
--suppress=missingIncludeSystem \
--suppress=unusedStructMember \
- $(filter -D%,$(FLAGS) $(CFLAGS) $(CPPFLAGS)) -D CPPCHECK_6936 \
+ $(filter -D%,$(FLAGS) $(CFLAGS) $(CPPFLAGS)) -D CPPCHECK_6936 \
$^
diff --git a/conf.c b/conf.c
index dafac463..940fb9e9 100644
--- a/conf.c
+++ b/conf.c
@@ -161,7 +161,7 @@ static void conf_ports_range_except(const struct ctx *c, char optname,
else if (optname == 'u' || optname == 'U')
proto = IPPROTO_UDP;
else
- ASSERT(0);
+ assert(0);
if (addr) {
if (!c->ifi4 && inany_v4(addr)) {
@@ -186,7 +186,7 @@ static void conf_ports_range_except(const struct ctx *c, char optname,
/* FIXME: Once the fwd bitmaps are removed, move this
* workaround to the caller
*/
- ASSERT(!addr && ifname && !strcmp(ifname, "lo"));
+ assert(!addr && ifname && !strcmp(ifname, "lo"));
warn(
"SO_BINDTODEVICE unavailable, forwarding only 127.0.0.1 and ::1 for '-%c %s'",
optname, optarg);
@@ -1743,7 +1743,7 @@ void conf(struct ctx *c, int argc, char **argv)
die("Invalid host nameserver address: %s", optarg);
case 25:
/* Already handled in conf_mode() */
- ASSERT(c->mode == MODE_VU);
+ assert(c->mode == MODE_VU);
break;
case 26:
vu_print_capabilities();
diff --git a/flow.c b/flow.c
index 735d3c5f..4282da2e 100644
--- a/flow.c
+++ b/flow.c
@@ -216,7 +216,7 @@ int flowside_sock_l4(const struct ctx *c, enum epoll_type type, uint8_t pif,
const char *ifname = NULL;
union sockaddr_inany sa;
- ASSERT(pif_is_socket(pif));
+ assert(pif_is_socket(pif));
pif_sockaddr(c, &sa, pif, &tgt->oaddr, tgt->oport);
@@ -244,7 +244,7 @@ int flowside_sock_l4(const struct ctx *c, enum epoll_type type, uint8_t pif,
/* If we add new socket pifs, they'll need to be implemented
* here
*/
- ASSERT(0);
+ assert(0);
}
}
@@ -341,8 +341,8 @@ static void flow_set_state(struct flow_common *f, enum flow_state state)
{
uint8_t oldstate = f->state;
- ASSERT(state < FLOW_NUM_STATES);
- ASSERT(oldstate < FLOW_NUM_STATES);
+ assert(state < FLOW_NUM_STATES);
+ assert(oldstate < FLOW_NUM_STATES);
f->state = state;
flow_log_(f, true, LOG_DEBUG, "%s -> %s", flow_state_str[oldstate],
@@ -369,7 +369,7 @@ int flow_epollfd(const struct flow_common *f)
*/
void flow_epollid_set(struct flow_common *f, int epollid)
{
- ASSERT(epollid < EPOLLFD_ID_SIZE);
+ assert(epollid < EPOLLFD_ID_SIZE);
f->epollid = epollid;
}
@@ -407,7 +407,7 @@ int flow_epoll_set(const struct flow_common *f, int command, uint32_t events,
*/
void flow_epollid_register(int epollid, int epollfd)
{
- ASSERT(epollid < EPOLLFD_ID_SIZE);
+ assert(epollid < EPOLLFD_ID_SIZE);
epoll_id_to_fd[epollid] = epollfd;
}
@@ -421,10 +421,10 @@ static void flow_initiate_(union flow *flow, uint8_t pif)
{
struct flow_common *f = &flow->f;
- ASSERT(pif != PIF_NONE);
- ASSERT(flow_new_entry == flow && f->state == FLOW_STATE_NEW);
- ASSERT(f->type == FLOW_TYPE_NONE);
- ASSERT(f->pif[INISIDE] == PIF_NONE && f->pif[TGTSIDE] == PIF_NONE);
+ assert(pif != PIF_NONE);
+ assert(flow_new_entry == flow && f->state == FLOW_STATE_NEW);
+ assert(f->type == FLOW_TYPE_NONE);
+ assert(f->pif[INISIDE] == PIF_NONE && f->pif[TGTSIDE] == PIF_NONE);
f->pif[INISIDE] = pif;
flow_set_state(f, FLOW_STATE_INI);
@@ -474,7 +474,7 @@ struct flowside *flow_initiate_sa(union flow *flow, uint8_t pif,
if (inany_from_sockaddr(&ini->eaddr, &ini->eport, ssa) < 0) {
char str[SOCKADDR_STRLEN];
- ASSERT_WITH_MSG(0, "Bad socket address %s",
+ assert_with_msg(0, "Bad socket address %s",
sockaddr_ntop(ssa, str, sizeof(str)));
}
if (daddr)
@@ -508,10 +508,10 @@ struct flowside *flow_target(const struct ctx *c, union flow *flow,
const struct fwd_table *fwd;
uint8_t tgtpif = PIF_NONE;
- ASSERT(flow_new_entry == flow && f->state == FLOW_STATE_INI);
- ASSERT(f->type == FLOW_TYPE_NONE);
- ASSERT(f->pif[INISIDE] != PIF_NONE && f->pif[TGTSIDE] == PIF_NONE);
- ASSERT(flow->f.state == FLOW_STATE_INI);
+ assert(flow_new_entry == flow && f->state == FLOW_STATE_INI);
+ assert(f->type == FLOW_TYPE_NONE);
+ assert(f->pif[INISIDE] != PIF_NONE && f->pif[TGTSIDE] == PIF_NONE);
+ assert(flow->f.state == FLOW_STATE_INI);
switch (f->pif[INISIDE]) {
case PIF_TAP:
@@ -574,10 +574,10 @@ union flow *flow_set_type(union flow *flow, enum flow_type type)
{
struct flow_common *f = &flow->f;
- ASSERT(type != FLOW_TYPE_NONE);
- ASSERT(flow_new_entry == flow && f->state == FLOW_STATE_TGT);
- ASSERT(f->type == FLOW_TYPE_NONE);
- ASSERT(f->pif[INISIDE] != PIF_NONE && f->pif[TGTSIDE] != PIF_NONE);
+ assert(type != FLOW_TYPE_NONE);
+ assert(flow_new_entry == flow && f->state == FLOW_STATE_TGT);
+ assert(f->type == FLOW_TYPE_NONE);
+ assert(f->pif[INISIDE] != PIF_NONE && f->pif[TGTSIDE] != PIF_NONE);
f->type = type;
flow_set_state(f, FLOW_STATE_TYPED);
@@ -590,8 +590,8 @@ union flow *flow_set_type(union flow *flow, enum flow_type type)
*/
void flow_activate(struct flow_common *f)
{
- ASSERT(&flow_new_entry->f == f && f->state == FLOW_STATE_TYPED);
- ASSERT(f->pif[INISIDE] != PIF_NONE && f->pif[TGTSIDE] != PIF_NONE);
+ assert(&flow_new_entry->f == f && f->state == FLOW_STATE_TYPED);
+ assert(f->pif[INISIDE] != PIF_NONE && f->pif[TGTSIDE] != PIF_NONE);
flow_set_state(f, FLOW_STATE_ACTIVE);
flow_new_entry = NULL;
@@ -606,26 +606,26 @@ union flow *flow_alloc(void)
{
union flow *flow = &flowtab[flow_first_free];
- ASSERT(!flow_new_entry);
+ assert(!flow_new_entry);
if (flow_first_free >= FLOW_MAX)
return NULL;
- ASSERT(flow->f.state == FLOW_STATE_FREE);
- ASSERT(flow->f.type == FLOW_TYPE_NONE);
- ASSERT(flow->free.n >= 1);
- ASSERT(flow_first_free + flow->free.n <= FLOW_MAX);
+ assert(flow->f.state == FLOW_STATE_FREE);
+ assert(flow->f.type == FLOW_TYPE_NONE);
+ assert(flow->free.n >= 1);
+ assert(flow_first_free + flow->free.n <= FLOW_MAX);
if (flow->free.n > 1) {
union flow *next;
/* Use one entry from the cluster */
- ASSERT(flow_first_free <= FLOW_MAX - 2);
+ assert(flow_first_free <= FLOW_MAX - 2);
next = &flowtab[++flow_first_free];
- ASSERT(FLOW_IDX(next) < FLOW_MAX);
- ASSERT(next->f.type == FLOW_TYPE_NONE);
- ASSERT(next->free.n == 0);
+ assert(FLOW_IDX(next) < FLOW_MAX);
+ assert(next->f.type == FLOW_TYPE_NONE);
+ assert(next->free.n == 0);
next->free.n = flow->free.n - 1;
next->free.next = flow->free.next;
@@ -649,12 +649,12 @@ union flow *flow_alloc(void)
*/
void flow_alloc_cancel(union flow *flow)
{
- ASSERT(flow_new_entry == flow);
- ASSERT(flow->f.state == FLOW_STATE_NEW ||
+ assert(flow_new_entry == flow);
+ assert(flow->f.state == FLOW_STATE_NEW ||
flow->f.state == FLOW_STATE_INI ||
flow->f.state == FLOW_STATE_TGT ||
flow->f.state == FLOW_STATE_TYPED);
- ASSERT(flow_first_free > FLOW_IDX(flow));
+ assert(flow_first_free > FLOW_IDX(flow));
flow_set_state(&flow->f, FLOW_STATE_FREE);
memset(flow, 0, sizeof(*flow));
@@ -704,7 +704,7 @@ static uint64_t flow_sidx_hash(const struct ctx *c, flow_sidx_t sidx)
const struct flowside *side = &f->side[sidx.sidei];
uint8_t pif = f->pif[sidx.sidei];
- ASSERT(pif != PIF_NONE);
+ assert(pif != PIF_NONE);
return flow_hash(c, FLOW_PROTO(f), pif, side);
}
@@ -897,7 +897,7 @@ void flow_defer_handler(const struct ctx *c, const struct timespec *now)
flow_timer_run = *now;
}
- ASSERT(!flow_new_entry); /* Incomplete flow at end of cycle */
+ assert(!flow_new_entry); /* Incomplete flow at end of cycle */
/* Check which flows we might need to close first, but don't free them
* yet as it's not safe to do that in the middle of flow_foreach().
@@ -907,7 +907,7 @@ void flow_defer_handler(const struct ctx *c, const struct timespec *now)
switch (flow->f.type) {
case FLOW_TYPE_NONE:
- ASSERT(false);
+ assert(false);
break;
case FLOW_TCP:
closed = tcp_flow_defer(&flow->tcp);
@@ -942,7 +942,7 @@ void flow_defer_handler(const struct ctx *c, const struct timespec *now)
unsigned skip = flow->free.n;
/* First entry of a free cluster must have n >= 1 */
- ASSERT(skip);
+ assert(skip);
if (free_head) {
/* Merge into preceding free cluster */
@@ -965,7 +965,7 @@ void flow_defer_handler(const struct ctx *c, const struct timespec *now)
case FLOW_STATE_TGT:
case FLOW_STATE_TYPED:
/* Incomplete flow at end of cycle */
- ASSERT(false);
+ assert(false);
break;
case FLOW_STATE_ACTIVE:
@@ -975,7 +975,7 @@ void flow_defer_handler(const struct ctx *c, const struct timespec *now)
if (free_head) {
/* Add slot to current free cluster */
- ASSERT(FLOW_IDX(flow) ==
+ assert(FLOW_IDX(flow) ==
FLOW_IDX(free_head) + free_head->n);
free_head->n++;
flow->free.n = flow->free.next = 0;
@@ -992,7 +992,7 @@ void flow_defer_handler(const struct ctx *c, const struct timespec *now)
break;
default:
- ASSERT(false);
+ assert(false);
}
}
diff --git a/flow_table.h b/flow_table.h
index 8fb7b5c3..7694e726 100644
--- a/flow_table.h
+++ b/flow_table.h
@@ -176,7 +176,7 @@ static inline flow_sidx_t flow_sidx(const struct flow_common *f,
unsigned sidei)
{
/* cppcheck-suppress [knownConditionTrueFalse, unmatchedSuppression] */
- ASSERT(sidei == !!sidei);
+ assert(sidei == !!sidei);
return (flow_sidx_t){
.sidei = sidei,
diff --git a/fwd.c b/fwd.c
index bedbf98a..f3b4bf2a 100644
--- a/fwd.c
+++ b/fwd.c
@@ -352,7 +352,7 @@ void fwd_rule_add(struct fwd_table *fwd, uint8_t proto, uint8_t flags,
struct fwd_rule *new;
unsigned i, port;
- ASSERT(!(flags & ~allowed_flags));
+ assert(!(flags & ~allowed_flags));
if (fwd->count >= ARRAY_SIZE(fwd->rules))
die("Too many port forwarding ranges");
@@ -402,7 +402,7 @@ void fwd_rule_add(struct fwd_table *fwd, uint8_t proto, uint8_t flags,
die("Invalid interface name: %s", ifname);
}
- ASSERT(first <= last);
+ assert(first <= last);
new->first = first;
new->last = last;
@@ -450,7 +450,7 @@ const struct fwd_rule *fwd_rule_search(const struct fwd_table *fwd,
char ostr[INANY_ADDRSTRLEN], rstr[INANY_ADDRSTRLEN];
const struct fwd_rule *rule = &fwd->rules[hint];
- ASSERT((unsigned)hint < fwd->count);
+ assert((unsigned)hint < fwd->count);
if (fwd_rule_match(rule, ini, proto))
return rule;
@@ -521,14 +521,14 @@ static int fwd_sync_one(const struct ctx *c, const struct fwd_table *fwd,
bool bound_one = false;
unsigned port, idx;
- ASSERT(pif_is_socket(pif));
+ assert(pif_is_socket(pif));
if (!*ifname)
ifname = NULL;
idx = rule - fwd->rules;
- ASSERT(idx < MAX_FWD_RULES);
- ASSERT(!(rule->flags & FWD_SCAN && !scanmap));
+ assert(idx < MAX_FWD_RULES);
+ assert(!(rule->flags & FWD_SCAN && !scanmap));
for (port = rule->first; port <= rule->last; port++) {
int fd = rule->socks[port - rule->first];
@@ -554,7 +554,7 @@ static int fwd_sync_one(const struct ctx *c, const struct fwd_table *fwd,
else if (rule->proto == IPPROTO_UDP)
fd = udp_listen(c, pif, idx, addr, ifname, port);
else
- ASSERT(0);
+ assert(0);
if (fd < 0) {
char astr[INANY_ADDRSTRLEN];
diff --git a/icmp.c b/icmp.c
index 0b0f593c..18b6106a 100644
--- a/icmp.c
+++ b/icmp.c
@@ -58,7 +58,7 @@ static struct icmp_ping_flow *ping_at_sidx(flow_sidx_t sidx)
if (!flow)
return NULL;
- ASSERT(flow->f.type == FLOW_PING4 || flow->f.type == FLOW_PING6);
+ assert(flow->f.type == FLOW_PING4 || flow->f.type == FLOW_PING6);
return &flow->ping;
}
@@ -80,7 +80,7 @@ void icmp_sock_handler(const struct ctx *c, union epoll_ref ref)
if (c->no_icmp)
return;
- ASSERT(pingf);
+ assert(pingf);
n = recvfrom(ref.fd, buf, sizeof(buf), 0, &sr.sa, &sl);
if (n < 0) {
@@ -109,7 +109,7 @@ void icmp_sock_handler(const struct ctx *c, union epoll_ref ref)
ih6->icmp6_identifier = htons(ini->eport);
seq = ntohs(ih6->icmp6_sequence);
} else {
- ASSERT(0);
+ assert(0);
}
/* In PASTA mode, we'll get any reply we send, discard them. */
@@ -131,7 +131,7 @@ void icmp_sock_handler(const struct ctx *c, union epoll_ref ref)
const struct in_addr *saddr = inany_v4(&ini->oaddr);
const struct in_addr *daddr = inany_v4(&ini->eaddr);
- ASSERT(saddr && daddr); /* Must have IPv4 addresses */
+ assert(saddr && daddr); /* Must have IPv4 addresses */
tap_icmp4_send(c, *saddr, *daddr, buf, pingf->f.tap_omac, n);
} else if (pingf->f.type == FLOW_PING6) {
const struct in6_addr *saddr = &ini->oaddr.a6;
@@ -256,7 +256,7 @@ int icmp_tap_handler(const struct ctx *c, uint8_t pif, sa_family_t af,
int cnt;
(void)saddr;
- ASSERT(pif == PIF_TAP);
+ assert(pif == PIF_TAP);
if (af == AF_INET) {
struct icmphdr ih_storage;
@@ -287,7 +287,7 @@ int icmp_tap_handler(const struct ctx *c, uint8_t pif, sa_family_t af,
id = ntohs(ih->icmp6_identifier);
seq = ntohs(ih->icmp6_sequence);
} else {
- ASSERT(0);
+ assert(0);
}
cnt = iov_tail_clone(&iov[0], MAX_IOV_ICMP, data);
@@ -304,7 +304,7 @@ int icmp_tap_handler(const struct ctx *c, uint8_t pif, sa_family_t af,
tgt = &pingf->f.side[TGTSIDE];
- ASSERT(flow_proto[pingf->f.type] == proto);
+ assert(flow_proto[pingf->f.type] == proto);
pingf->ts = now->tv_sec;
pif_sockaddr(c, &sa, PIF_HOST, &tgt->eaddr, 0);
diff --git a/inany.h b/inany.h
index 9891ed6b..30e24164 100644
--- a/inany.h
+++ b/inany.h
@@ -86,7 +86,7 @@ static inline socklen_t socklen_inany(const union sockaddr_inany *sa)
case AF_INET6:
return sizeof(sa->sa6);
default:
- ASSERT(0);
+ assert(0);
}
}
@@ -268,7 +268,7 @@ static inline void inany_from_af(union inany_addr *aa,
aa->v4mapped.a4 = *((struct in_addr *)addr);
} else {
/* Not valid to call with other address families */
- ASSERT(0);
+ assert(0);
}
}
diff --git a/iov.c b/iov.c
index 31a3f5bc..ae074393 100644
--- a/iov.c
+++ b/iov.c
@@ -21,6 +21,7 @@
* GNU GPL, version 2 or (at your option) any later version.
*/
+#include <assert.h>
#include <sys/socket.h>
#include "util.h"
@@ -118,7 +119,7 @@ size_t iov_to_buf(const struct iovec *iov, size_t iov_cnt,
for (copied = 0; copied < bytes && i < iov_cnt; i++) {
size_t len = MIN(iov[i].iov_len - offset, bytes - copied);
- ASSERT(iov[i].iov_base);
+ assert(iov[i].iov_base);
memcpy((char *)buf + copied, (char *)iov[i].iov_base + offset,
len);
diff --git a/isolation.c b/isolation.c
index b25f3498..7e6225df 100644
--- a/isolation.c
+++ b/isolation.c
@@ -396,7 +396,7 @@ void isolate_postfork(const struct ctx *c)
prog.filter = filter_vu;
break;
default:
- ASSERT(0);
+ assert(0);
}
if (prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0) ||
diff --git a/lineread.c b/lineread.c
index 4225de61..b9ceae10 100644
--- a/lineread.c
+++ b/lineread.c
@@ -12,6 +12,7 @@
* Author: David Gibson <david@gibson.dropbear.id.au>
*/
+#include <assert.h>
#include <stddef.h>
#include <fcntl.h>
#include <string.h>
@@ -44,8 +45,8 @@ static ssize_t peek_line(struct lineread *lr, bool eof)
char *nl;
/* Sanity checks (which also document invariants) */
- ASSERT(lr->next_line + lr->count >= lr->next_line);
- ASSERT(lr->next_line + lr->count <= LINEREAD_BUFFER_SIZE);
+ assert(lr->next_line + lr->count >= lr->next_line);
+ assert(lr->next_line + lr->count <= LINEREAD_BUFFER_SIZE);
nl = memchr(lr->buf + lr->next_line, '\n', lr->count);
diff --git a/netlink.c b/netlink.c
index e07b47f4..6b74e882 100644
--- a/netlink.c
+++ b/netlink.c
@@ -160,7 +160,7 @@ static uint32_t nl_send(int s, void *req, uint16_t type,
*/
static int nl_status(const struct nlmsghdr *nh, ssize_t n, uint32_t seq)
{
- ASSERT(NLMSG_OK(nh, n));
+ assert(NLMSG_OK(nh, n));
if (nh->nlmsg_seq != seq)
die("netlink: Unexpected sequence number (%u != %u)",
diff --git a/packet.c b/packet.c
index 890561bb..1cb74b74 100644
--- a/packet.c
+++ b/packet.c
@@ -168,7 +168,7 @@ bool packet_get_do(const struct pool *p, size_t idx,
{
size_t i;
- ASSERT_WITH_MSG(p->count <= p->size,
+ assert_with_msg(p->count <= p->size,
"Corrupted pool count: %zu, size: %zu, %s:%i",
p->count, p->size, func, line);
@@ -188,7 +188,7 @@ bool packet_get_do(const struct pool *p, size_t idx,
data->off = 0;
for (i = 0; i < data->cnt; i++) {
- ASSERT_WITH_MSG(!packet_check_range(p, data->iov[i].iov_base,
+ assert_with_msg(!packet_check_range(p, data->iov[i].iov_base,
data->iov[i].iov_len,
func, line),
"Corrupt packet pool, %s:%i", func, line);
diff --git a/passt.c b/passt.c
index fc3b89b8..f84419c7 100644
--- a/passt.c
+++ b/passt.c
@@ -305,7 +305,7 @@ static void passt_worker(void *opaque, int nfds, struct epoll_event *events)
break;
default:
/* Can't happen */
- ASSERT(0);
+ assert(0);
}
stats.events[ref.type]++;
print_stats(c, &stats, &now);
diff --git a/pif.c b/pif.c
index 82a3b5e4..1e807247 100644
--- a/pif.c
+++ b/pif.c
@@ -39,7 +39,7 @@ void pif_sockaddr(const struct ctx *c, union sockaddr_inany *sa,
{
const struct in_addr *v4 = inany_v4(addr);
- ASSERT(pif_is_socket(pif));
+ assert(pif_is_socket(pif));
if (v4) {
sa->sa_family = AF_INET;
@@ -83,7 +83,7 @@ int pif_listen(const struct ctx *c, enum epoll_type type, uint8_t pif,
union epoll_ref ref;
int ret;
- ASSERT(pif_is_socket(pif));
+ assert(pif_is_socket(pif));
if (!addr) {
ref.fd = sock_l4_dualstack_any(c, type, port, ifname);
diff --git a/tap.c b/tap.c
index eaa6111a..1049e023 100644
--- a/tap.c
+++ b/tap.c
@@ -117,7 +117,7 @@ unsigned long tap_l2_max_len(const struct ctx *c)
return L2_MAX_LEN_VU;
}
/* NOLINTEND(bugprone-branch-clone) */
- ASSERT(0);
+ assert(0);
return 0; /* Unreachable, for cppcheck's sake */
}
@@ -543,7 +543,7 @@ size_t tap_send_frames(const struct ctx *c, const struct iovec *iov,
case MODE_VU:
/* fall through */
default:
- ASSERT(0);
+ assert(0);
}
if (m < nframes)
@@ -1536,7 +1536,7 @@ void tap_backend_init(struct ctx *c)
}
if (c->fd_tap != -1) { /* Passed as --fd */
- ASSERT(c->one_off);
+ assert(c->one_off);
tap_start_connection(c);
return;
}
diff --git a/tcp.c b/tcp.c
index 9d91c3c8..b1458624 100644
--- a/tcp.c
+++ b/tcp.c
@@ -461,7 +461,7 @@ static struct tcp_tap_conn *conn_at_sidx(flow_sidx_t sidx)
if (!flow)
return NULL;
- ASSERT(flow->f.type == FLOW_TCP);
+ assert(flow->f.type == FLOW_TCP);
return &flow->tcp;
}
@@ -966,7 +966,7 @@ size_t tcp_fill_headers(const struct ctx *c, struct tcp_tap_conn *conn,
const struct in_addr *src4 = inany_v4(&tapside->oaddr);
const struct in_addr *dst4 = inany_v4(&tapside->eaddr);
- ASSERT(src4 && dst4);
+ assert(src4 && dst4);
l3len += + sizeof(*ip4h);
@@ -1879,7 +1879,7 @@ static int tcp_data_from_tap(const struct ctx *c, struct tcp_tap_conn *conn,
if (conn->events == CLOSED)
return p->count - idx;
- ASSERT(conn->events & ESTABLISHED);
+ assert(conn->events & ESTABLISHED);
for (i = idx, iov_i = 0; i < (int)p->count; i++) {
uint32_t seq, seq_offset, ack_seq;
@@ -2260,8 +2260,8 @@ int tcp_tap_handler(const struct ctx *c, uint8_t pif, sa_family_t af,
return 1;
}
- ASSERT(flow->f.type == FLOW_TCP);
- ASSERT(pif_at_sidx(sidx) == PIF_TAP);
+ assert(flow->f.type == FLOW_TCP);
+ assert(pif_at_sidx(sidx) == PIF_TAP);
conn = &flow->tcp;
flow_trace(conn, "packet length %zu from tap", l4len);
@@ -2500,7 +2500,7 @@ void tcp_listen_handler(const struct ctx *c, union epoll_ref ref,
union flow *flow;
int s;
- ASSERT(!c->no_tcp);
+ assert(!c->no_tcp);
if (!(flow = flow_alloc()))
return;
@@ -2570,8 +2570,8 @@ void tcp_timer_handler(const struct ctx *c, union epoll_ref ref)
struct itimerspec check_armed = { { 0 }, { 0 } };
struct tcp_tap_conn *conn = &FLOW(ref.flow)->tcp;
- ASSERT(!c->no_tcp);
- ASSERT(conn->f.type == FLOW_TCP);
+ assert(!c->no_tcp);
+ assert(conn->f.type == FLOW_TCP);
/* We don't reset timers on ~ACK_FROM_TAP_DUE, ~ACK_TO_TAP_DUE. If the
* timer is currently armed, this event came from a previous setting,
@@ -2632,8 +2632,8 @@ void tcp_sock_handler(const struct ctx *c, union epoll_ref ref,
{
struct tcp_tap_conn *conn = conn_at_sidx(ref.flowside);
- ASSERT(!c->no_tcp);
- ASSERT(pif_at_sidx(ref.flowside) != PIF_TAP);
+ assert(!c->no_tcp);
+ assert(pif_at_sidx(ref.flowside) != PIF_TAP);
if (conn->events == CLOSED)
return;
@@ -2701,7 +2701,7 @@ int tcp_listen(const struct ctx *c, uint8_t pif, unsigned rule,
{
int s;
- ASSERT(!c->no_tcp);
+ assert(!c->no_tcp);
if (!c->ifi4) {
if (!addr)
@@ -2853,7 +2853,7 @@ static void tcp_get_rto_params(struct ctx *c)
*/
int tcp_init(struct ctx *c)
{
- ASSERT(!c->no_tcp);
+ assert(!c->no_tcp);
tcp_get_rto_params(c);
diff --git a/tcp_splice.c b/tcp_splice.c
index d60981ca..42ee8abc 100644
--- a/tcp_splice.c
+++ b/tcp_splice.c
@@ -105,7 +105,7 @@ static struct tcp_splice_conn *conn_at_sidx(flow_sidx_t sidx)
if (!flow)
return NULL;
- ASSERT(flow->f.type == FLOW_TCP_SPLICE);
+ assert(flow->f.type == FLOW_TCP_SPLICE);
return &flow->tcp_splice;
}
@@ -369,7 +369,7 @@ static int tcp_splice_connect(const struct ctx *c, struct tcp_splice_conn *conn)
else if (tgtpif == PIF_SPLICE)
conn->s[1] = tcp_conn_sock_ns(c, af);
else
- ASSERT(0);
+ assert(0);
if (conn->s[1] < 0)
return -1;
@@ -457,7 +457,7 @@ void tcp_splice_conn_from_sock(const struct ctx *c, union flow *flow, int s0)
struct tcp_splice_conn *conn = FLOW_SET_TYPE(flow, FLOW_TCP_SPLICE,
tcp_splice);
- ASSERT(c->mode == MODE_PASTA);
+ assert(c->mode == MODE_PASTA);
conn->s[0] = s0;
conn->s[1] = -1;
@@ -489,7 +489,7 @@ void tcp_splice_sock_handler(struct ctx *c, union epoll_ref ref,
uint8_t lowat_set_flag, lowat_act_flag;
int eof, never_read;
- ASSERT(conn->f.type == FLOW_TCP_SPLICE);
+ assert(conn->f.type == FLOW_TCP_SPLICE);
if (conn->events == SPLICE_CLOSED)
return;
@@ -779,7 +779,7 @@ void tcp_splice_timer(struct tcp_splice_conn *conn)
{
unsigned sidei;
- ASSERT(!(conn->flags & CLOSING));
+ assert(!(conn->flags & CLOSING));
flow_foreach_sidei(sidei) {
if ((conn->flags & RCVLOWAT_SET(sidei)) &&
diff --git a/tcp_vu.c b/tcp_vu.c
index fd734e85..826a38d1 100644
--- a/tcp_vu.c
+++ b/tcp_vu.c
@@ -94,7 +94,7 @@ int tcp_vu_send_flag(const struct ctx *c, struct tcp_tap_conn *conn, int flags)
if (elem_cnt != 1)
return -1;
- ASSERT(flags_elem[0].in_sg[0].iov_len >=
+ assert(flags_elem[0].in_sg[0].iov_len >=
MAX(hdrlen + sizeof(*opts), ETH_ZLEN + VNET_HLEN));
vu_set_vnethdr(flags_elem[0].in_sg[0].iov_base, 1);
@@ -221,7 +221,7 @@ static ssize_t tcp_vu_sock_recv(const struct ctx *c, struct vu_virtq *vq,
/* reserve space for headers in iov */
iov = &elem[elem_cnt].in_sg[0];
- ASSERT(iov->iov_len >= hdrlen);
+ assert(iov->iov_len >= hdrlen);
iov->iov_base = (char *)iov->iov_base + hdrlen;
iov->iov_len -= hdrlen;
head[(*head_cnt)++] = elem_cnt;
@@ -298,7 +298,7 @@ static void tcp_vu_prepare(const struct ctx *c, struct tcp_tap_conn *conn,
/* we guess the first iovec provided by the guest can embed
* all the headers needed by L2 frame, including any padding
*/
- ASSERT(iov[0].iov_len >= hdrlen);
+ assert(iov[0].iov_len >= hdrlen);
eh = vu_eth(base);
@@ -445,7 +445,7 @@ int tcp_vu_data_from_sock(const struct ctx *c, struct tcp_tap_conn *conn)
ssize_t dlen;
size_t l2len;
- ASSERT(frame_size >= hdrlen);
+ assert(frame_size >= hdrlen);
dlen = frame_size - hdrlen;
vu_set_vnethdr(iov->iov_base, buf_cnt);
diff --git a/udp.c b/udp.c
index 2275c16b..1fc5a42c 100644
--- a/udp.c
+++ b/udp.c
@@ -271,7 +271,7 @@ size_t udp_update_hdr4(struct iphdr *ip4h, struct udp_payload_t *bp,
size_t l4len = dlen + sizeof(bp->uh);
size_t l3len = l4len + sizeof(*ip4h);
- ASSERT(src && dst);
+ assert(src && dst);
ip4h->tot_len = htons(l3len);
ip4h->daddr = dst->s_addr;
@@ -431,7 +431,7 @@ static void udp_send_tap_icmp4(const struct ctx *c,
size_t msglen = sizeof(msg) - sizeof(msg.data) + dlen;
size_t l4len = dlen + sizeof(struct udphdr);
- ASSERT(dlen <= ICMP4_MAX_DLEN);
+ assert(dlen <= ICMP4_MAX_DLEN);
memset(&msg, 0, sizeof(msg));
msg.icmp4h.type = ee->ee_type;
msg.icmp4h.code = ee->ee_code;
@@ -480,7 +480,7 @@ static void udp_send_tap_icmp6(const struct ctx *c,
size_t msglen = sizeof(msg) - sizeof(msg.data) + dlen;
size_t l4len = dlen + sizeof(struct udphdr);
- ASSERT(dlen <= ICMP6_MAX_DLEN);
+ assert(dlen <= ICMP6_MAX_DLEN);
memset(&msg, 0, sizeof(msg));
msg.icmp6h.icmp6_type = ee->ee_type;
msg.icmp6h.icmp6_code = ee->ee_code;
@@ -628,7 +628,7 @@ static int udp_sock_recverr(const struct ctx *c, int s, flow_sidx_t sidx,
}
uflow = udp_at_sidx(sidx);
- ASSERT(uflow);
+ assert(uflow);
fromside = &uflow->f.side[sidx.sidei];
toside = &uflow->f.side[!sidx.sidei];
topif = uflow->f.pif[!sidx.sidei];
@@ -692,7 +692,7 @@ static int udp_sock_errs(const struct ctx *c, int s, flow_sidx_t sidx,
socklen_t errlen;
int rc, err;
- ASSERT(!c->no_udp);
+ assert(!c->no_udp);
/* Empty the error queue */
while ((rc = udp_sock_recverr(c, s, sidx, pif, port)) > 0)
@@ -772,7 +772,7 @@ static int udp_peek_addr(int s, union sockaddr_inany *src,
*/
static int udp_sock_recv(const struct ctx *c, int s, struct mmsghdr *mmh, int n)
{
- ASSERT(!c->no_udp);
+ assert(!c->no_udp);
n = recvmmsg(s, mmh, n, 0, NULL);
if (n < 0) {
@@ -940,7 +940,7 @@ void udp_sock_handler(const struct ctx *c, union epoll_ref ref,
{
struct udp_flow *uflow = udp_at_sidx(ref.flowside);
- ASSERT(!c->no_udp && uflow);
+ assert(!c->no_udp && uflow);
if (events & EPOLLERR) {
if (udp_sock_errs(c, ref.fd, ref.flowside, PIF_NONE, 0) < 0) {
@@ -1023,7 +1023,7 @@ int udp_tap_handler(const struct ctx *c, uint8_t pif,
in_port_t src, dst;
uint8_t topif;
- ASSERT(!c->no_udp);
+ assert(!c->no_udp);
if (!packet_get(p, idx, &data))
return 1;
@@ -1061,7 +1061,7 @@ int udp_tap_handler(const struct ctx *c, uint8_t pif,
toside = flowside_at_sidx(tosidx);
s = uflow->s[tosidx.sidei];
- ASSERT(s >= 0);
+ assert(s >= 0);
pif_sockaddr(c, &to_sa, topif, &toside->eaddr, toside->eport);
@@ -1141,7 +1141,7 @@ int udp_listen(const struct ctx *c, uint8_t pif, unsigned rule,
{
int s;
- ASSERT(!c->no_udp);
+ assert(!c->no_udp);
if (!c->ifi4) {
if (!addr)
@@ -1209,7 +1209,7 @@ static void udp_get_timeout_params(struct ctx *c)
*/
int udp_init(struct ctx *c)
{
- ASSERT(!c->no_udp);
+ assert(!c->no_udp);
udp_get_timeout_params(c);
diff --git a/udp_flow.c b/udp_flow.c
index 00238372..7e2453e7 100644
--- a/udp_flow.c
+++ b/udp_flow.c
@@ -31,7 +31,7 @@ struct udp_flow *udp_at_sidx(flow_sidx_t sidx)
if (!flow)
return NULL;
- ASSERT(flow->f.type == FLOW_UDP);
+ assert(flow->f.type == FLOW_UDP);
return &flow->udp;
}
@@ -280,7 +280,7 @@ flow_sidx_t udp_flow_from_tap(const struct ctx *c,
union flow *flow;
flow_sidx_t sidx;
- ASSERT(pif == PIF_TAP);
+ assert(pif == PIF_TAP);
sidx = flow_lookup_af(c, IPPROTO_UDP, pif, af, saddr, daddr,
srcport, dstport);
diff --git a/udp_vu.c b/udp_vu.c
index 5effca77..7939290f 100644
--- a/udp_vu.c
+++ b/udp_vu.c
@@ -75,7 +75,7 @@ static int udp_vu_sock_recv(const struct ctx *c, struct vu_virtq *vq, int s,
int iov_cnt, iov_used;
size_t hdrlen, l2len;
- ASSERT(!c->no_udp);
+ assert(!c->no_udp);
if (!vu_queue_enabled(vq) || !vu_queue_started(vq)) {
debug("Got UDP packet, but RX virtqueue not usable yet");
@@ -97,7 +97,7 @@ static int udp_vu_sock_recv(const struct ctx *c, struct vu_virtq *vq, int s,
return -1;
/* reserve space for the headers */
- ASSERT(iov_vu[0].iov_len >= MAX(hdrlen, ETH_ZLEN + VNET_HLEN));
+ assert(iov_vu[0].iov_len >= MAX(hdrlen, ETH_ZLEN + VNET_HLEN));
iov_vu[0].iov_base = (char *)iov_vu[0].iov_base + hdrlen;
iov_vu[0].iov_len -= hdrlen;
diff --git a/util.c b/util.c
index a4f2be42..22318c00 100644
--- a/util.c
+++ b/util.c
@@ -84,7 +84,7 @@ static int sock_l4_(const struct ctx *c, enum epoll_type type,
socktype = SOCK_DGRAM | SOCK_NONBLOCK;
break;
default:
- ASSERT(0);
+ assert(0);
}
fd = socket(af, socktype, proto);
@@ -884,7 +884,7 @@ int read_all_buf(int fd, void *buf, size_t len)
while (left) {
ssize_t rc;
- ASSERT(left <= len);
+ assert(left <= len);
do
rc = read(fd, p, left);
@@ -924,7 +924,7 @@ int read_remainder(int fd, const struct iovec *iov, size_t cnt, size_t skip)
ssize_t rc;
if (offset) {
- ASSERT(offset < iov[i].iov_len);
+ assert(offset < iov[i].iov_len);
/* Read the remainder of the partially read buffer */
if (read_all_buf(fd, (char *)iov[i].iov_base + offset,
iov[i].iov_len - offset) < 0)
diff --git a/util.h b/util.h
index 4cbb5da3..2fc5cd74 100644
--- a/util.h
+++ b/util.h
@@ -73,11 +73,20 @@ void abort_with_msg(const char *fmt, ...)
* Therefore, avoid using the usual do while wrapper we use to force the macro
* to act like a single statement requiring a ';'.
*/
-#define ASSERT_WITH_MSG(expr, ...) \
+#ifndef NDEBUG
+#define assert_with_msg(expr, ...) \
((expr) ? (void)0 : abort_with_msg(__VA_ARGS__))
-#define ASSERT(expr) \
- ASSERT_WITH_MSG((expr), "ASSERTION FAILED in %s (%s:%d): %s", \
+/* The standard library assert() hits our seccomp filter and dies before it can
+ * actually print a message. So, replace it with our own version.
+ */
+#undef assert
+#define assert(expr) \
+ assert_with_msg((expr), "ASSERTION FAILED in %s (%s:%d): %s", \
__func__, __FILE__, __LINE__, STRINGIFY(expr))
+#else
+#define assert_with_msg(expr, ...) \
+ ((void)(expr), 0 ? (void)0 : abort_with_msg(__VA_ARGS__))
+#endif
#ifdef P_tmpdir
#define TMPDIR P_tmpdir
diff --git a/vhost_user.c b/vhost_user.c
index 9fe12411..75665ec6 100644
--- a/vhost_user.c
+++ b/vhost_user.c
@@ -216,9 +216,9 @@ static int vu_message_read_default(int conn_fd, struct vhost_user_msg *vmsg)
cmsg->cmsg_type == SCM_RIGHTS) {
size_t fd_size;
- ASSERT(cmsg->cmsg_len >= CMSG_LEN(0));
+ assert(cmsg->cmsg_len >= CMSG_LEN(0));
fd_size = cmsg->cmsg_len - CMSG_LEN(0);
- ASSERT(fd_size <= sizeof(vmsg->fds));
+ assert(fd_size <= sizeof(vmsg->fds));
vmsg->fd_num = fd_size / sizeof(int);
memcpy(vmsg->fds, CMSG_DATA(cmsg), fd_size);
break;
@@ -272,7 +272,7 @@ static void vu_message_write(int conn_fd, struct vhost_user_msg *vmsg)
};
int rc;
- ASSERT(vmsg->fd_num <= VHOST_MEMORY_BASELINE_NREGIONS);
+ assert(vmsg->fd_num <= VHOST_MEMORY_BASELINE_NREGIONS);
if (vmsg->fd_num > 0) {
size_t fdsize = vmsg->fd_num * sizeof(int);
struct cmsghdr *cmsg;
@@ -483,7 +483,7 @@ static bool vu_set_mem_table_exec(struct vu_dev *vdev,
}
}
- ASSERT(vdev->memory.nregions < VHOST_USER_MAX_RAM_SLOTS);
+ assert(vdev->memory.nregions < VHOST_USER_MAX_RAM_SLOTS);
return false;
}
diff --git a/virtio.c b/virtio.c
index 447137ee..f71dea1d 100644
--- a/virtio.c
+++ b/virtio.c
@@ -72,6 +72,7 @@
* SUCH DAMAGE.
*/
+#include <assert.h>
#include <stddef.h>
#include <endian.h>
#include <string.h>
@@ -402,8 +403,8 @@ static bool virtqueue_map_desc(const struct vu_dev *dev,
{
unsigned int num_sg = *p_num_sg;
- ASSERT(num_sg < max_num_sg);
- ASSERT(sz);
+ assert(num_sg < max_num_sg);
+ assert(sz);
while (sz) {
uint64_t len = sz;
diff --git a/vu_common.c b/vu_common.c
index 5f2ce18e..75ad43d3 100644
--- a/vu_common.c
+++ b/vu_common.c
@@ -43,7 +43,7 @@ int vu_packet_check_range(struct vdev_memory *memory,
/* NOLINTNEXTLINE(performance-no-int-to-ptr) */
const char *base = (const char *)base_addr;
- ASSERT(base_addr >= dev_region[i].mmap_addr);
+ assert(base_addr >= dev_region[i].mmap_addr);
if (len <= dev_region[i].size && base <= ptr &&
(size_t)(ptr - base) <= dev_region[i].size - len)
@@ -167,7 +167,7 @@ static void vu_handle_tx(struct vu_dev *vdev, int index,
int out_sg_count;
int count;
- ASSERT(VHOST_USER_IS_QUEUE_TX(index));
+ assert(VHOST_USER_IS_QUEUE_TX(index));
tap_flush_pools();
--
2.53.0
^ permalink raw reply [flat|nested] 16+ messages in thread
* [PATCH v2 02/15] serialise: Split functions user for serialisation from util.c
2026-03-19 6:11 [PATCH v2 00/15] RFC: Read-only dynamic update implementation David Gibson
2026-03-19 6:11 ` [PATCH v2 01/15] treewide: Spell ASSERT() as assert() David Gibson
@ 2026-03-19 6:11 ` David Gibson
2026-03-19 6:11 ` [PATCH v2 03/15] serialise: Add helpers for serialising unsigned integers David Gibson
` (12 subsequent siblings)
14 siblings, 0 replies; 16+ messages in thread
From: David Gibson @ 2026-03-19 6:11 UTC (permalink / raw)
To: Stefano Brivio, passt-dev; +Cc: David Gibson
The read_all_buf() and write_all_buf() functions in util.c are
primarily used for serialising data structures to a stream during
migraiton. We're going to have further use for such serialisation
when we add dynamic configuration updates, where we'll want to share
the code with the client program.
To make that easier move the functions into a new serialisation.c
file, and rename thematically. While we're there add some macros for
the common idiom of sending all of a given variable using sizeof().
Signed-off-by: David Gibson <david@gibson.dropbear.id.au>
---
Makefile | 11 ++++---
flow.c | 5 +--
migrate.c | 9 +++---
pcap.c | 3 +-
serialise.c | 88 +++++++++++++++++++++++++++++++++++++++++++++++++++++
serialise.h | 17 +++++++++++
tcp.c | 19 ++++++------
util.c | 78 +++--------------------------------------------
util.h | 2 --
9 files changed, 136 insertions(+), 96 deletions(-)
create mode 100644 serialise.c
create mode 100644 serialise.h
diff --git a/Makefile b/Makefile
index 513dc6c6..5b6891d7 100644
--- a/Makefile
+++ b/Makefile
@@ -40,8 +40,8 @@ FLAGS += -DDUAL_STACK_SOCKETS=$(DUAL_STACK_SOCKETS)
PASST_SRCS = arch.c arp.c checksum.c conf.c dhcp.c dhcpv6.c epoll_ctl.c \
flow.c fwd.c icmp.c igmp.c inany.c iov.c ip.c isolation.c lineread.c \
log.c mld.c ndp.c netlink.c migrate.c packet.c passt.c pasta.c pcap.c \
- pif.c repair.c tap.c tcp.c tcp_buf.c tcp_splice.c tcp_vu.c udp.c \
- udp_flow.c udp_vu.c util.c vhost_user.c virtio.c vu_common.c
+ pif.c repair.c serialise.c tap.c tcp.c tcp_buf.c tcp_splice.c tcp_vu.c \
+ udp.c udp_flow.c udp_vu.c util.c vhost_user.c virtio.c vu_common.c
QRAP_SRCS = qrap.c
PASST_REPAIR_SRCS = passt-repair.c
SRCS = $(PASST_SRCS) $(QRAP_SRCS) $(PASST_REPAIR_SRCS)
@@ -51,9 +51,10 @@ MANPAGES = passt.1 pasta.1 qrap.1 passt-repair.1
PASST_HEADERS = arch.h arp.h checksum.h conf.h dhcp.h dhcpv6.h epoll_ctl.h \
flow.h fwd.h flow_table.h icmp.h icmp_flow.h inany.h iov.h ip.h \
isolation.h lineread.h log.h migrate.h ndp.h netlink.h packet.h \
- passt.h pasta.h pcap.h pif.h repair.h siphash.h tap.h tcp.h tcp_buf.h \
- tcp_conn.h tcp_internal.h tcp_splice.h tcp_vu.h udp.h udp_flow.h \
- udp_internal.h udp_vu.h util.h vhost_user.h virtio.h vu_common.h
+ passt.h pasta.h pcap.h pif.h repair.h serialise.h siphash.h tap.h tcp.h \
+ tcp_buf.h tcp_conn.h tcp_internal.h tcp_splice.h tcp_vu.h udp.h \
+ udp_flow.h udp_internal.h udp_vu.h util.h vhost_user.h virtio.h \
+ vu_common.h
HEADERS = $(PASST_HEADERS) seccomp.h
C := \#include <sys/random.h>\nint main(){int a=getrandom(0, 0, 0);}
diff --git a/flow.c b/flow.c
index 4282da2e..7d3c5ae6 100644
--- a/flow.c
+++ b/flow.c
@@ -21,6 +21,7 @@
#include "flow_table.h"
#include "repair.h"
#include "epoll_ctl.h"
+#include "serialise.h"
const char *flow_state_str[] = {
[FLOW_STATE_FREE] = "FREE",
@@ -1135,7 +1136,7 @@ int flow_migrate_source(struct ctx *c, const struct migrate_stage *stage,
}
count = htonl(count);
- if (write_all_buf(fd, &count, sizeof(count))) {
+ if (sewrite_var(fd, &count)) {
rc = errno;
err_perror("Can't send flow count (%u)", ntohl(count));
return flow_migrate_source_rollback(c, FLOW_MAX, rc);
@@ -1220,7 +1221,7 @@ int flow_migrate_target(struct ctx *c, const struct migrate_stage *stage,
(void)stage;
- if (read_all_buf(fd, &count, sizeof(count)))
+ if (seread_var(fd, &count))
return errno;
count = ntohl(count);
diff --git a/migrate.c b/migrate.c
index 1e8858a3..50f494b4 100644
--- a/migrate.c
+++ b/migrate.c
@@ -24,6 +24,7 @@
#include "migrate.h"
#include "repair.h"
+#include "serialise.h"
/* Magic identifier for migration data */
#define MIGRATE_MAGIC 0xB1BB1D1B0BB1D1B0
@@ -64,7 +65,7 @@ static int seen_addrs_source_v2(struct ctx *c,
memcpy(addrs.mac, c->guest_mac, sizeof(addrs.mac));
- if (write_all_buf(fd, &addrs, sizeof(addrs)))
+ if (sewrite_var(fd, &addrs))
return errno;
return 0;
@@ -85,7 +86,7 @@ static int seen_addrs_target_v2(struct ctx *c,
(void)stage;
- if (read_all_buf(fd, &addrs, sizeof(addrs)))
+ if (seread_var(fd, &addrs))
return errno;
c->ip6.addr_seen = addrs.addr6;
@@ -146,7 +147,7 @@ static int migrate_source(struct ctx *c, int fd)
const struct migrate_stage *s;
int ret;
- if (write_all_buf(fd, &header, sizeof(header))) {
+ if (sewrite_var(fd, &header)) {
ret = errno;
err("Can't send migration header: %s, abort", strerror_(ret));
return ret;
@@ -180,7 +181,7 @@ static const struct migrate_version *migrate_target_read_header(int fd)
uint32_t id, compat_id;
unsigned i;
- if (read_all_buf(fd, &h, sizeof(h)))
+ if (seread_var(fd, &h))
return NULL;
id = ntohl(h.version);
diff --git a/pcap.c b/pcap.c
index 54fba5c4..291112b6 100644
--- a/pcap.c
+++ b/pcap.c
@@ -34,6 +34,7 @@
#include "pcap.h"
#include "iov.h"
#include "tap.h"
+#include "serialise.h"
#define PCAP_VERSION_MINOR 4
@@ -64,7 +65,7 @@ static void pcap_frame(const struct iovec *iov, size_t iovcnt,
.len = l2len
};
- if (write_all_buf(pcap_fd, &h, sizeof(h)) < 0 ||
+ if (sewrite_var(pcap_fd, &h) < 0 ||
write_remainder(pcap_fd, iov, iovcnt, offset) < 0)
debug_perror("Cannot log packet, length %zu", l2len);
}
diff --git a/serialise.c b/serialise.c
new file mode 100644
index 00000000..68054722
--- /dev/null
+++ b/serialise.c
@@ -0,0 +1,88 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+/* PASST - Plug A Simple Socket Transport
+ * for qemu/UNIX domain socket mode
+ *
+ * PASTA - Pack A Subtle Tap Abstraction
+ * for network namespace/tap device mode
+ *
+ * serialise.c - Serialisation of data structures over bytestreams
+ *
+ * Copyright Red Hat
+ * Author: David Gibson <david@gibson.dropbear.id.au>
+ */
+
+#include <assert.h>
+#include <errno.h>
+#include <unistd.h>
+
+#include "serialise.h"
+
+/**
+ * seread_buf() - Fill a whole buffer from a file descriptor
+ * @fd: File descriptor
+ * @buf: Pointer to base of buffer
+ * @len: Length of buffer
+ *
+ * Return: 0 on success, -1 on error (with errno set)
+ *
+ * #syscalls read
+ */
+int seread_buf(int fd, void *buf, size_t len)
+{
+ size_t left = len;
+ char *p = buf;
+
+ while (left) {
+ ssize_t rc;
+
+ assert(left <= len);
+
+ do
+ rc = read(fd, p, left);
+ while ((rc < 0) && errno == EINTR);
+
+ if (rc < 0)
+ return -1;
+
+ if (rc == 0) {
+ errno = ENODATA;
+ return -1;
+ }
+
+ p += rc;
+ left -= rc;
+ }
+ return 0;
+}
+
+/**
+ * sewrite_buf() - write all of a buffer to an fd
+ * @fd: File descriptor
+ * @buf: Pointer to base of buffer
+ * @len: Length of buffer
+ *
+ * Return: 0 on success, -1 on error (with errno set)
+ *
+ * #syscalls write
+ */
+int sewrite_buf(int fd, const void *buf, size_t len)
+{
+ const char *p = buf;
+ size_t left = len;
+
+ while (left) {
+ ssize_t rc;
+
+ do
+ rc = write(fd, p, left);
+ while ((rc < 0) && errno == EINTR);
+
+ if (rc < 0)
+ return -1;
+
+ p += rc;
+ left -= rc;
+ }
+ return 0;
+}
diff --git a/serialise.h b/serialise.h
new file mode 100644
index 00000000..ec95287f
--- /dev/null
+++ b/serialise.h
@@ -0,0 +1,17 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later
+ * Copyright Red Hat
+ * Author: David Gibson <david@gibson.dropbear.id.au>
+ */
+
+#ifndef SERIALISE_H
+#define SERIALISE_H
+
+#include <stddef.h>
+
+int seread_buf(int fd, void *buf, size_t len);
+int sewrite_buf(int fd, const void *buf, size_t len);
+
+#define seread_var(fd_, var_) (seread_buf((fd_), (var_), sizeof(*(var_))))
+#define sewrite_var(fd_, var_) (sewrite_buf((fd_), (var_), sizeof(*(var_))))
+
+#endif /* _SERIALISE_H */
diff --git a/tcp.c b/tcp.c
index b1458624..8649d2db 100644
--- a/tcp.c
+++ b/tcp.c
@@ -308,6 +308,7 @@
#include "flow.h"
#include "repair.h"
#include "linux_dep.h"
+#include "serialise.h"
#include "flow_table.h"
#include "tcp_internal.h"
@@ -3472,7 +3473,7 @@ int tcp_flow_migrate_source(int fd, struct tcp_tap_conn *conn)
memcpy(&t.pif, conn->f.pif, sizeof(t.pif));
memcpy(&t.side, conn->f.side, sizeof(t.side));
- if (write_all_buf(fd, &t, sizeof(t))) {
+ if (sewrite_var(fd, &t)) {
int rc = -errno;
err_perror("Can't write migration data, socket %i", conn->sock);
return rc;
@@ -3570,17 +3571,17 @@ int tcp_flow_migrate_source_ext(const struct ctx *c,
t->rcv_wnd = htonl(t->rcv_wnd);
t->rcv_wup = htonl(t->rcv_wup);
- if (write_all_buf(fd, t, sizeof(*t))) {
+ if (sewrite_var(fd, t)) {
flow_perror(conn, "Failed to write extended data");
return -EIO;
}
- if (write_all_buf(fd, tcp_migrate_snd_queue, ntohl(t->sndq))) {
+ if (sewrite_buf(fd, tcp_migrate_snd_queue, ntohl(t->sndq))) {
flow_perror(conn, "Failed to write send queue data");
return -EIO;
}
- if (write_all_buf(fd, tcp_migrate_rcv_queue, ntohl(t->rcvq))) {
+ if (sewrite_buf(fd, tcp_migrate_rcv_queue, ntohl(t->rcvq))) {
flow_perror(conn, "Failed to write receive queue data");
return -EIO;
}
@@ -3595,7 +3596,7 @@ fail:
*/
t->tcpi_state = 0; /* Not defined: tell the target to skip this flow */
- if (write_all_buf(fd, t, sizeof(*t))) {
+ if (sewrite_var(fd, t)) {
flow_perror(conn, "Failed to write extended data");
return -EIO;
}
@@ -3710,7 +3711,7 @@ int tcp_flow_migrate_target(struct ctx *c, int fd)
return 0;
}
- if (read_all_buf(fd, &t, sizeof(t))) {
+ if (seread_var(fd, &t)) {
flow_perror(flow, "Failed to receive migration data");
flow_alloc_cancel(flow);
return -errno;
@@ -3774,7 +3775,7 @@ int tcp_flow_migrate_target_ext(struct ctx *c, struct tcp_tap_conn *conn, int fd
struct tcp_tap_transfer_ext t;
int s = conn->sock, rc;
- if (read_all_buf(fd, &t, sizeof(t))) {
+ if (seread_var(fd, &t)) {
rc = -errno;
flow_perror(conn, "Failed to read extended data");
return rc;
@@ -3819,13 +3820,13 @@ int tcp_flow_migrate_target_ext(struct ctx *c, struct tcp_tap_conn *conn, int fd
return -EINVAL;
}
- if (read_all_buf(fd, tcp_migrate_snd_queue, t.sndq)) {
+ if (seread_buf(fd, tcp_migrate_snd_queue, t.sndq)) {
rc = -errno;
flow_perror(conn, "Failed to read send queue data");
return rc;
}
- if (read_all_buf(fd, tcp_migrate_rcv_queue, t.rcvq)) {
+ if (seread_buf(fd, tcp_migrate_rcv_queue, t.rcvq)) {
rc = -errno;
flow_perror(conn, "Failed to read receive queue data");
return rc;
diff --git a/util.c b/util.c
index 22318c00..c64a1a61 100644
--- a/util.c
+++ b/util.c
@@ -37,6 +37,7 @@
#include "pcap.h"
#include "epoll_ctl.h"
#include "pasta.h"
+#include "serialise.h"
#ifdef HAS_GETRANDOM
#include <sys/random.h>
#endif
@@ -799,37 +800,6 @@ int do_clone(int (*fn)(void *), char *stack_area, size_t stack_size, int flags,
#endif
}
-/**
- * write_all_buf() - write all of a buffer to an fd
- * @fd: File descriptor
- * @buf: Pointer to base of buffer
- * @len: Length of buffer
- *
- * Return: 0 on success, -1 on error (with errno set)
- *
- * #syscalls write
- */
-int write_all_buf(int fd, const void *buf, size_t len)
-{
- const char *p = buf;
- size_t left = len;
-
- while (left) {
- ssize_t rc;
-
- do
- rc = write(fd, p, left);
- while ((rc < 0) && errno == EINTR);
-
- if (rc < 0)
- return -1;
-
- p += rc;
- left -= rc;
- }
- return 0;
-}
-
/**
* write_remainder() - write the tail of an IO vector to an fd
* @fd: File descriptor
@@ -850,8 +820,8 @@ int write_remainder(int fd, const struct iovec *iov, size_t iovcnt, size_t skip)
if (offset) {
/* Write the remainder of the partially written buffer */
- if (write_all_buf(fd, (char *)iov[i].iov_base + offset,
- iov[i].iov_len - offset) < 0)
+ if (sewrite_buf(fd, (char *)iov[i].iov_base + offset,
+ iov[i].iov_len - offset) < 0)
return -1;
i++;
}
@@ -866,44 +836,6 @@ int write_remainder(int fd, const struct iovec *iov, size_t iovcnt, size_t skip)
return 0;
}
-/**
- * read_all_buf() - Fill a whole buffer from a file descriptor
- * @fd: File descriptor
- * @buf: Pointer to base of buffer
- * @len: Length of buffer
- *
- * Return: 0 on success, -1 on error (with errno set)
- *
- * #syscalls read
- */
-int read_all_buf(int fd, void *buf, size_t len)
-{
- size_t left = len;
- char *p = buf;
-
- while (left) {
- ssize_t rc;
-
- assert(left <= len);
-
- do
- rc = read(fd, p, left);
- while ((rc < 0) && errno == EINTR);
-
- if (rc < 0)
- return -1;
-
- if (rc == 0) {
- errno = ENODATA;
- return -1;
- }
-
- p += rc;
- left -= rc;
- }
- return 0;
-}
-
/**
* read_remainder() - Read the tail of an IO vector from a file descriptor
* @fd: File descriptor
@@ -926,8 +858,8 @@ int read_remainder(int fd, const struct iovec *iov, size_t cnt, size_t skip)
if (offset) {
assert(offset < iov[i].iov_len);
/* Read the remainder of the partially read buffer */
- if (read_all_buf(fd, (char *)iov[i].iov_base + offset,
- iov[i].iov_len - offset) < 0)
+ if (seread_buf(fd, (char *)iov[i].iov_base + offset,
+ iov[i].iov_len - offset) < 0)
return -1;
i++;
}
diff --git a/util.h b/util.h
index 2fc5cd74..cb669105 100644
--- a/util.h
+++ b/util.h
@@ -245,9 +245,7 @@ int fls(unsigned long x);
int ilog2(unsigned long x);
int write_file(const char *path, const char *buf);
intmax_t read_file_integer(const char *path, intmax_t fallback);
-int write_all_buf(int fd, const void *buf, size_t len);
int write_remainder(int fd, const struct iovec *iov, size_t iovcnt, size_t skip);
-int read_all_buf(int fd, void *buf, size_t len);
int read_remainder(int fd, const struct iovec *iov, size_t cnt, size_t skip);
void close_open_files(int argc, char **argv);
bool snprintf_check(char *str, size_t size, const char *format, ...);
--
2.53.0
^ permalink raw reply [flat|nested] 16+ messages in thread
* [PATCH v2 03/15] serialise: Add helpers for serialising unsigned integers
2026-03-19 6:11 [PATCH v2 00/15] RFC: Read-only dynamic update implementation David Gibson
2026-03-19 6:11 ` [PATCH v2 01/15] treewide: Spell ASSERT() as assert() David Gibson
2026-03-19 6:11 ` [PATCH v2 02/15] serialise: Split functions user for serialisation from util.c David Gibson
@ 2026-03-19 6:11 ` David Gibson
2026-03-19 6:11 ` [PATCH v2 04/15] fwd: Move selecting correct scan bitmap into fwd_sync_one() David Gibson
` (11 subsequent siblings)
14 siblings, 0 replies; 16+ messages in thread
From: David Gibson @ 2026-03-19 6:11 UTC (permalink / raw)
To: Stefano Brivio, passt-dev; +Cc: David Gibson
Add helpers to serialise/deserialise unsigned integers, handling endian
conversion so that the stream format is consistent regardless of host
endiannness. Currently we only use this on one place: sending the number
of flows during migration. We're going to have more use for this as we
add dynamic configuration updates, so these will become more useful.
For now we only need a 32-bit version, however define the functions with
a macro so we can easily add other integer widths when we need them.
Signed-off-by: David Gibson <david@gibson.dropbear.id.au>
---
flow.c | 10 ++++------
serialise.c | 35 +++++++++++++++++++++++++++++++++++
serialise.h | 7 +++++++
3 files changed, 46 insertions(+), 6 deletions(-)
diff --git a/flow.c b/flow.c
index 7d3c5ae6..81333dbc 100644
--- a/flow.c
+++ b/flow.c
@@ -1135,10 +1135,9 @@ int flow_migrate_source(struct ctx *c, const struct migrate_stage *stage,
count++;
}
- count = htonl(count);
- if (sewrite_var(fd, &count)) {
+ if (sewrite_u32(fd, count)) {
rc = errno;
- err_perror("Can't send flow count (%u)", ntohl(count));
+ err_perror("Can't send flow count (%u)", count);
return flow_migrate_source_rollback(c, FLOW_MAX, rc);
}
@@ -1151,7 +1150,7 @@ int flow_migrate_source(struct ctx *c, const struct migrate_stage *stage,
debug("Stop listen()s");
fwd_listen_close(&c->fwd_in);
- debug("Sending %u flows", ntohl(count));
+ debug("Sending %u flows", count);
if (!count)
return 0;
@@ -1221,10 +1220,9 @@ int flow_migrate_target(struct ctx *c, const struct migrate_stage *stage,
(void)stage;
- if (seread_var(fd, &count))
+ if (seread_u32(fd, &count))
return errno;
- count = ntohl(count);
debug("Receiving %u flows", count);
if (!count)
diff --git a/serialise.c b/serialise.c
index 68054722..098ae35c 100644
--- a/serialise.c
+++ b/serialise.c
@@ -13,7 +13,9 @@
*/
#include <assert.h>
+#include <endian.h>
#include <errno.h>
+#include <stdint.h>
#include <unistd.h>
#include "serialise.h"
@@ -86,3 +88,36 @@ int sewrite_buf(int fd, const void *buf, size_t len)
}
return 0;
}
+
+/**
+ * seread_uXXX() - Receive a uXXX value from an fd
+ * @fd: File descriptor to read from
+ * @valp: Pointer to variable to update with read value
+ *
+ * Return: 0 on success, -1 on error
+ */
+/**
+ * sewrite_uXXX() - Send a uXXX value to an fd
+ * @fd: File descriptor to write to
+ * @val: Value to send
+ *
+ * Return: 0 on success, -1 on error
+ */
+#define SERIALISE_UINT(bits) \
+ int seread_u##bits(int fd, uint##bits##_t *val) \
+ { \
+ uint##bits##_t beval; \
+ if (seread_var(fd, &beval) < 0) \
+ return -1; \
+ *val = be##bits##toh(beval); \
+ return 0; \
+ } \
+ int sewrite_u##bits(int fd, uint##bits##_t val) \
+ { \
+ uint##bits##_t beval = htobe##bits(val); \
+ return sewrite_var(fd, &beval); \
+ }
+
+SERIALISE_UINT(32)
+
+#undef SERIALISE_UNIT
diff --git a/serialise.h b/serialise.h
index ec95287f..f2e0aa8d 100644
--- a/serialise.h
+++ b/serialise.h
@@ -7,6 +7,7 @@
#define SERIALISE_H
#include <stddef.h>
+#include <stdint.h>
int seread_buf(int fd, void *buf, size_t len);
int sewrite_buf(int fd, const void *buf, size_t len);
@@ -14,4 +15,10 @@ int sewrite_buf(int fd, const void *buf, size_t len);
#define seread_var(fd_, var_) (seread_buf((fd_), (var_), sizeof(*(var_))))
#define sewrite_var(fd_, var_) (sewrite_buf((fd_), (var_), sizeof(*(var_))))
+#define SERIALISE_UINT_DECL(bits) \
+ int seread_u##bits(int fd, uint##bits##_t *val); \
+ int sewrite_u##bits(int fd, uint##bits##_t val);
+
+SERIALISE_UINT_DECL(32)
+
#endif /* _SERIALISE_H */
--
2.53.0
^ permalink raw reply [flat|nested] 16+ messages in thread
* [PATCH v2 04/15] fwd: Move selecting correct scan bitmap into fwd_sync_one()
2026-03-19 6:11 [PATCH v2 00/15] RFC: Read-only dynamic update implementation David Gibson
` (2 preceding siblings ...)
2026-03-19 6:11 ` [PATCH v2 03/15] serialise: Add helpers for serialising unsigned integers David Gibson
@ 2026-03-19 6:11 ` David Gibson
2026-03-19 6:11 ` [PATCH v2 05/15] fwd: Look up rule index in fwd_sync_one() David Gibson
` (10 subsequent siblings)
14 siblings, 0 replies; 16+ messages in thread
From: David Gibson @ 2026-03-19 6:11 UTC (permalink / raw)
To: Stefano Brivio, passt-dev; +Cc: David Gibson
Currently fwd_listen_sync_() determines which of the two port scanned
bitmaps is the correct one for a rule before passing it into
fwd_sync_one(). However, fwd_sync_one() is already looking at the rule
internals so is better placed to do this.
Signed-off-by: David Gibson <david@gibson.dropbear.id.au>
---
fwd.c | 28 +++++++++++++++-------------
1 file changed, 15 insertions(+), 13 deletions(-)
diff --git a/fwd.c b/fwd.c
index f3b4bf2a..a306b409 100644
--- a/fwd.c
+++ b/fwd.c
@@ -508,16 +508,18 @@ void fwd_rules_print(const struct fwd_table *fwd)
* @fwd: Forwarding table
* @rule: Forwarding rule
* @pif: Interface to create listening sockets for
- * @scanmap: Bitmap of ports to listen for on FWD_SCAN entries
+ * @tcp: Bitmap of TCP ports to listen for on FWD_SCAN entries
+ * @udp: Bitmap of UDP ports to listen for on FWD_SCAN entries
*
* Return: 0 on success, -1 on failure
*/
static int fwd_sync_one(const struct ctx *c, const struct fwd_table *fwd,
const struct fwd_rule *rule, uint8_t pif,
- const uint8_t *scanmap)
+ const uint8_t *tcp, const uint8_t *udp)
{
const union inany_addr *addr = fwd_rule_addr(rule);
const char *ifname = rule->ifname;
+ const uint8_t *map = NULL;
bool bound_one = false;
unsigned port, idx;
@@ -528,12 +530,19 @@ static int fwd_sync_one(const struct ctx *c, const struct fwd_table *fwd,
idx = rule - fwd->rules;
assert(idx < MAX_FWD_RULES);
- assert(!(rule->flags & FWD_SCAN && !scanmap));
-
+
+ if (rule->flags & FWD_SCAN) {
+ if (rule->proto == IPPROTO_TCP)
+ map = tcp;
+ else if (rule->proto == IPPROTO_UDP)
+ map = udp;
+ assert(map);
+ }
+
for (port = rule->first; port <= rule->last; port++) {
int fd = rule->socks[port - rule->first];
- if ((rule->flags & FWD_SCAN) && !bitmap_isset(scanmap, port)) {
+ if (map && !bitmap_isset(map, port)) {
/* We don't want to listen on this port */
if (fd >= 0) {
/* We already are, so stop */
@@ -619,15 +628,8 @@ static int fwd_listen_sync_(void *arg)
ns_enter(a->c);
for (i = 0; i < a->fwd->count; i++) {
- const uint8_t *scanmap = NULL;
-
- if (a->fwd->rules[i].proto == IPPROTO_TCP)
- scanmap = a->tcpmap;
- else if (a->fwd->rules[i].proto == IPPROTO_UDP)
- scanmap = a->udpmap;
-
a->ret = fwd_sync_one(a->c, a->fwd, &a->fwd->rules[i],
- a->pif, scanmap);
+ a->pif, a->tcpmap, a->udpmap);
if (a->ret < 0)
break;
}
--
2.53.0
^ permalink raw reply [flat|nested] 16+ messages in thread
* [PATCH v2 05/15] fwd: Look up rule index in fwd_sync_one()
2026-03-19 6:11 [PATCH v2 00/15] RFC: Read-only dynamic update implementation David Gibson
` (3 preceding siblings ...)
2026-03-19 6:11 ` [PATCH v2 04/15] fwd: Move selecting correct scan bitmap into fwd_sync_one() David Gibson
@ 2026-03-19 6:11 ` David Gibson
2026-03-19 6:11 ` [PATCH v2 06/15] fwd: Store forwarding tables indexed by (origin) pif David Gibson
` (9 subsequent siblings)
14 siblings, 0 replies; 16+ messages in thread
From: David Gibson @ 2026-03-19 6:11 UTC (permalink / raw)
To: Stefano Brivio, passt-dev; +Cc: David Gibson
Currently we have the slightly silly setup that fwd_listen_sync_() looks
up the pointer to a rule based on its index, then fwd_sync_one() turns it
back into an index. Avoid this by passing the index directly into
fwd_sync_one().
Signed-off-by: David Gibson <david@gibson.dropbear.id.au>
---
fwd.c | 14 ++++++--------
1 file changed, 6 insertions(+), 8 deletions(-)
diff --git a/fwd.c b/fwd.c
index a306b409..7844a674 100644
--- a/fwd.c
+++ b/fwd.c
@@ -506,7 +506,7 @@ void fwd_rules_print(const struct fwd_table *fwd)
/** fwd_sync_one() - Create or remove listening sockets for a forward entry
* @c: Execution context
* @fwd: Forwarding table
- * @rule: Forwarding rule
+ * @idx: Rule index
* @pif: Interface to create listening sockets for
* @tcp: Bitmap of TCP ports to listen for on FWD_SCAN entries
* @udp: Bitmap of UDP ports to listen for on FWD_SCAN entries
@@ -514,23 +514,21 @@ void fwd_rules_print(const struct fwd_table *fwd)
* Return: 0 on success, -1 on failure
*/
static int fwd_sync_one(const struct ctx *c, const struct fwd_table *fwd,
- const struct fwd_rule *rule, uint8_t pif,
+ unsigned idx, uint8_t pif,
const uint8_t *tcp, const uint8_t *udp)
{
+ const struct fwd_rule *rule = &fwd->rules[idx];
const union inany_addr *addr = fwd_rule_addr(rule);
const char *ifname = rule->ifname;
const uint8_t *map = NULL;
bool bound_one = false;
- unsigned port, idx;
+ unsigned port;
assert(pif_is_socket(pif));
if (!*ifname)
ifname = NULL;
- idx = rule - fwd->rules;
- assert(idx < MAX_FWD_RULES);
-
if (rule->flags & FWD_SCAN) {
if (rule->proto == IPPROTO_TCP)
map = tcp;
@@ -628,8 +626,8 @@ static int fwd_listen_sync_(void *arg)
ns_enter(a->c);
for (i = 0; i < a->fwd->count; i++) {
- a->ret = fwd_sync_one(a->c, a->fwd, &a->fwd->rules[i],
- a->pif, a->tcpmap, a->udpmap);
+ a->ret = fwd_sync_one(a->c, a->fwd, i, a->pif,
+ a->tcpmap, a->udpmap);
if (a->ret < 0)
break;
}
--
2.53.0
^ permalink raw reply [flat|nested] 16+ messages in thread
* [PATCH v2 06/15] fwd: Store forwarding tables indexed by (origin) pif
2026-03-19 6:11 [PATCH v2 00/15] RFC: Read-only dynamic update implementation David Gibson
` (4 preceding siblings ...)
2026-03-19 6:11 ` [PATCH v2 05/15] fwd: Look up rule index in fwd_sync_one() David Gibson
@ 2026-03-19 6:11 ` David Gibson
2026-03-19 6:11 ` [PATCH v2 07/15] pesto: Introduce stub configuration interface and tool David Gibson
` (8 subsequent siblings)
14 siblings, 0 replies; 16+ messages in thread
From: David Gibson @ 2026-03-19 6:11 UTC (permalink / raw)
To: Stefano Brivio, passt-dev; +Cc: David Gibson
Currently we store the inbound (PIF_HOST) and outbound (PIF_SPLICE)
forwarding tables in separate fields of struct ctx. In a number of places
this requires somewhat awkward if or switch constructs to select the
right table for updates. Conceptually simplify that by using an index of
forwarding tables by pif, which as a bonus keeps track generically which
pifs have implemented forwarding tables so far.
For now this doesn't simplify a lot textually, because many places that
need this also have other special cases to apply by pif. It does simplify
a few crucial places though, and we expect it will become more useful as
the flexibility of the forwarding table is improved.
Signed-off-by: David Gibson <david@gibson.dropbear.id.au>
---
conf.c | 53 +++++++++++++++++++++++++++-------------------
flow.c | 22 +++++++------------
fwd.c | 65 ++++++++++++++++++++++++++++++---------------------------
fwd.h | 4 ++--
passt.h | 3 +--
5 files changed, 77 insertions(+), 70 deletions(-)
diff --git a/conf.c b/conf.c
index 940fb9e9..6af3c8a5 100644
--- a/conf.c
+++ b/conf.c
@@ -1252,11 +1252,12 @@ dns6:
}
}
- info("Inbound forwarding:");
- fwd_rules_print(&c->fwd_in);
- if (c->mode == MODE_PASTA) {
- info("Outbound forwarding:");
- fwd_rules_print(&c->fwd_out);
+ for (i = 0; i < PIF_NUM_TYPES; i++) {
+ if (!c->fwd[i])
+ continue;
+
+ info("Forwarding from %s:", pif_name(i));
+ fwd_rules_print(c->fwd[i]);
}
}
@@ -2154,18 +2155,24 @@ void conf(struct ctx *c, int argc, char **argv)
/* Forwarding options can be parsed now, after IPv4/IPv6 settings */
fwd_probe_ephemeral();
+ fwd_rule_init(c);
optind = 0;
do {
name = getopt_long(argc, argv, optstring, options, NULL);
- if (name == 't')
- conf_ports(c, name, optarg, &c->fwd_in, &tcp_in_mode);
- else if (name == 'u')
- conf_ports(c, name, optarg, &c->fwd_in, &udp_in_mode);
- else if (name == 'T')
- conf_ports(c, name, optarg, &c->fwd_out, &tcp_out_mode);
- else if (name == 'U')
- conf_ports(c, name, optarg, &c->fwd_out, &udp_out_mode);
+ if (name == 't') {
+ conf_ports(c, name, optarg, c->fwd[PIF_HOST],
+ &tcp_in_mode);
+ } else if (name == 'u') {
+ conf_ports(c, name, optarg, c->fwd[PIF_HOST],
+ &udp_in_mode);
+ } else if (name == 'T') {
+ conf_ports(c, name, optarg, c->fwd[PIF_SPLICE],
+ &tcp_out_mode);
+ } else if (name == 'U') {
+ conf_ports(c, name, optarg, c->fwd[PIF_SPLICE],
+ &udp_out_mode);
+ }
} while (name != -1);
if (c->mode == MODE_PASTA)
@@ -2224,20 +2231,24 @@ void conf(struct ctx *c, int argc, char **argv)
udp_out_mode = fwd_default;
if (tcp_in_mode == FWD_MODE_AUTO) {
- conf_ports_range_except(c, 't', "auto", &c->fwd_in, NULL, NULL,
- 1, NUM_PORTS - 1, NULL, 1, FWD_SCAN);
+ conf_ports_range_except(c, 't', "auto", c->fwd[PIF_HOST],
+ NULL, NULL, 1, NUM_PORTS - 1, NULL, 1,
+ FWD_SCAN);
}
if (tcp_out_mode == FWD_MODE_AUTO) {
- conf_ports_range_except(c, 'T', "auto", &c->fwd_out, NULL, "lo",
- 1, NUM_PORTS - 1, NULL, 1, FWD_SCAN);
+ conf_ports_range_except(c, 'T', "auto", c->fwd[PIF_SPLICE],
+ NULL, "lo", 1, NUM_PORTS - 1, NULL, 1,
+ FWD_SCAN);
}
if (udp_in_mode == FWD_MODE_AUTO) {
- conf_ports_range_except(c, 'u', "auto", &c->fwd_in, NULL, NULL,
- 1, NUM_PORTS - 1, NULL, 1, FWD_SCAN);
+ conf_ports_range_except(c, 'u', "auto", c->fwd[PIF_HOST],
+ NULL, NULL, 1, NUM_PORTS - 1, NULL, 1,
+ FWD_SCAN);
}
if (udp_out_mode == FWD_MODE_AUTO) {
- conf_ports_range_except(c, 'U', "auto", &c->fwd_out, NULL, "lo",
- 1, NUM_PORTS - 1, NULL, 1, FWD_SCAN);
+ conf_ports_range_except(c, 'U', "auto", c->fwd[PIF_SPLICE],
+ NULL, "lo", 1, NUM_PORTS - 1, NULL, 1,
+ FWD_SCAN);
}
if (!c->quiet)
diff --git a/flow.c b/flow.c
index 81333dbc..11cd5752 100644
--- a/flow.c
+++ b/flow.c
@@ -503,10 +503,10 @@ struct flowside *flow_target(const struct ctx *c, union flow *flow,
{
char estr[INANY_ADDRSTRLEN], ostr[INANY_ADDRSTRLEN];
struct flow_common *f = &flow->f;
+ const struct fwd_table *fwd = c->fwd[f->pif[INISIDE]];
const struct flowside *ini = &f->side[INISIDE];
struct flowside *tgt = &f->side[TGTSIDE];
const struct fwd_rule *rule = NULL;
- const struct fwd_table *fwd;
uint8_t tgtpif = PIF_NONE;
assert(flow_new_entry == flow && f->state == FLOW_STATE_INI);
@@ -514,6 +514,11 @@ struct flowside *flow_target(const struct ctx *c, union flow *flow,
assert(f->pif[INISIDE] != PIF_NONE && f->pif[TGTSIDE] == PIF_NONE);
assert(flow->f.state == FLOW_STATE_INI);
+ if (fwd) {
+ if (!(rule = fwd_rule_search(fwd, ini, proto, rule_hint)))
+ goto norule;
+ }
+
switch (f->pif[INISIDE]) {
case PIF_TAP:
memcpy(f->tap_omac, MAC_UNDEF, ETH_ALEN);
@@ -521,20 +526,10 @@ struct flowside *flow_target(const struct ctx *c, union flow *flow,
break;
case PIF_SPLICE:
- fwd = &c->fwd_out;
-
- if (!(rule = fwd_rule_search(fwd, ini, proto, rule_hint)))
- goto norule;
-
tgtpif = fwd_nat_from_splice(rule, proto, ini, tgt);
break;
case PIF_HOST:
- fwd = &c->fwd_in;
-
- if (!(rule = fwd_rule_search(fwd, ini, proto, rule_hint)))
- goto norule;
-
tgtpif = fwd_nat_from_host(c, rule, proto, ini, tgt);
fwd_neigh_mac_get(c, &tgt->oaddr, f->tap_omac);
break;
@@ -1014,8 +1009,7 @@ static int flow_migrate_source_rollback(struct ctx *c, unsigned bound, int ret)
debug("...roll back migration");
- if (fwd_listen_sync(c, &c->fwd_in, PIF_HOST,
- &c->tcp.scan_in, &c->udp.scan_in) < 0)
+ if (fwd_listen_sync(c, PIF_HOST, &c->tcp.scan_in, &c->udp.scan_in) < 0)
die("Failed to re-establish listening sockets");
foreach_established_tcp_flow(flow) {
@@ -1148,7 +1142,7 @@ int flow_migrate_source(struct ctx *c, const struct migrate_stage *stage,
* fix that is to not allow local to local migration, which arguably we
* should (use namespaces for testing instead). */
debug("Stop listen()s");
- fwd_listen_close(&c->fwd_in);
+ fwd_listen_close(c->fwd[PIF_HOST]);
debug("Sending %u flows", count);
diff --git a/fwd.c b/fwd.c
index 7844a674..3395a28e 100644
--- a/fwd.c
+++ b/fwd.c
@@ -331,6 +331,21 @@ bool fwd_port_is_ephemeral(in_port_t port)
return (port >= fwd_ephemeral_min) && (port <= fwd_ephemeral_max);
}
+/* Forwarding table storage, generally accessed via pointers in struct ctx */
+static struct fwd_table fwd_in;
+static struct fwd_table fwd_out;
+
+/**
+ * fwd_rule_init() - Initialise forwarding tables
+ * @c: Execution context
+ */
+void fwd_rule_init(struct ctx *c)
+{
+ c->fwd[PIF_HOST] = &fwd_in;
+ if (c->mode == MODE_PASTA)
+ c->fwd[PIF_SPLICE] = &fwd_out;
+}
+
/**
* fwd_rule_add() - Add a rule to a forwarding table
* @fwd: Table to add to
@@ -505,19 +520,17 @@ void fwd_rules_print(const struct fwd_table *fwd)
/** fwd_sync_one() - Create or remove listening sockets for a forward entry
* @c: Execution context
- * @fwd: Forwarding table
- * @idx: Rule index
* @pif: Interface to create listening sockets for
+ * @idx: Rule index
* @tcp: Bitmap of TCP ports to listen for on FWD_SCAN entries
* @udp: Bitmap of UDP ports to listen for on FWD_SCAN entries
*
* Return: 0 on success, -1 on failure
*/
-static int fwd_sync_one(const struct ctx *c, const struct fwd_table *fwd,
- unsigned idx, uint8_t pif,
+static int fwd_sync_one(const struct ctx *c, uint8_t pif, unsigned idx,
const uint8_t *tcp, const uint8_t *udp)
{
- const struct fwd_rule *rule = &fwd->rules[idx];
+ const struct fwd_rule *rule = &c->fwd[pif]->rules[idx];
const union inany_addr *addr = fwd_rule_addr(rule);
const char *ifname = rule->ifname;
const uint8_t *map = NULL;
@@ -598,7 +611,6 @@ static int fwd_sync_one(const struct ctx *c, const struct fwd_table *fwd,
/** struct fwd_listen_args - arguments for fwd_listen_init_()
* @c: Execution context
- * @fwd: Forwarding table
* @tcpmap: Bitmap of TCP ports to auto-forward
* @udpmap: Bitmap of TCP ports to auto-forward
* @pif: Interface to create listening sockets for
@@ -606,7 +618,6 @@ static int fwd_sync_one(const struct ctx *c, const struct fwd_table *fwd,
*/
struct fwd_listen_args {
const struct ctx *c;
- const struct fwd_table *fwd;
const uint8_t *tcpmap, *udpmap;
uint8_t pif;
int ret;
@@ -625,9 +636,8 @@ static int fwd_listen_sync_(void *arg)
if (a->pif == PIF_SPLICE)
ns_enter(a->c);
- for (i = 0; i < a->fwd->count; i++) {
- a->ret = fwd_sync_one(a->c, a->fwd, i, a->pif,
- a->tcpmap, a->udpmap);
+ for (i = 0; i < a->c->fwd[a->pif]->count; i++) {
+ a->ret = fwd_sync_one(a->c, a->pif, i, a->tcpmap, a->udpmap);
if (a->ret < 0)
break;
}
@@ -637,21 +647,17 @@ static int fwd_listen_sync_(void *arg)
/** fwd_listen_sync() - Call fwd_listen_sync_() in correct namespace
* @c: Execution context
- * @fwd: Forwarding information
* @pif: Interface to create listening sockets for
* @tcp: Scanning state for TCP
* @udp: Scanning state for UDP
*
* Return: 0 on success, -1 on failure
*/
-int fwd_listen_sync(const struct ctx *c, const struct fwd_table *fwd,
- uint8_t pif,
+int fwd_listen_sync(const struct ctx *c, uint8_t pif,
const struct fwd_scan *tcp, const struct fwd_scan *udp)
{
struct fwd_listen_args a = {
- .c = c, .fwd = fwd,
- .tcpmap = tcp->map, .udpmap = udp->map,
- .pif = pif,
+ .c = c, .tcpmap = tcp->map, .udpmap = udp->map, .pif = pif,
};
if (pif == PIF_SPLICE)
@@ -695,12 +701,11 @@ void fwd_listen_close(const struct fwd_table *fwd)
*/
int fwd_listen_init(const struct ctx *c)
{
- if (fwd_listen_sync(c, &c->fwd_in, PIF_HOST,
- &c->tcp.scan_in, &c->udp.scan_in) < 0)
+ if (fwd_listen_sync(c, PIF_HOST, &c->tcp.scan_in, &c->udp.scan_in) < 0)
return -1;
if (c->mode == MODE_PASTA) {
- if (fwd_listen_sync(c, &c->fwd_out, PIF_SPLICE,
+ if (fwd_listen_sync(c, PIF_SPLICE,
&c->tcp.scan_out, &c->udp.scan_out) < 0)
return -1;
}
@@ -851,16 +856,16 @@ static void fwd_scan_ports(struct ctx *c)
uint8_t excl_tcp_out[PORT_BITMAP_SIZE], excl_udp_out[PORT_BITMAP_SIZE];
uint8_t excl_tcp_in[PORT_BITMAP_SIZE], excl_udp_in[PORT_BITMAP_SIZE];
- current_listen_map(excl_tcp_out, &c->fwd_in, IPPROTO_TCP);
- current_listen_map(excl_tcp_in, &c->fwd_out, IPPROTO_TCP);
- current_listen_map(excl_udp_out, &c->fwd_in, IPPROTO_UDP);
- current_listen_map(excl_udp_in, &c->fwd_out, IPPROTO_UDP);
+ current_listen_map(excl_tcp_out, c->fwd[PIF_HOST], IPPROTO_TCP);
+ current_listen_map(excl_tcp_in, c->fwd[PIF_SPLICE], IPPROTO_TCP);
+ current_listen_map(excl_udp_out, c->fwd[PIF_HOST], IPPROTO_UDP);
+ current_listen_map(excl_udp_in, c->fwd[PIF_SPLICE], IPPROTO_UDP);
- fwd_scan_ports_tcp(&c->fwd_out, &c->tcp.scan_out, excl_tcp_out);
- fwd_scan_ports_tcp(&c->fwd_in, &c->tcp.scan_in, excl_tcp_in);
- fwd_scan_ports_udp(&c->fwd_out, &c->udp.scan_out,
+ fwd_scan_ports_tcp(c->fwd[PIF_SPLICE], &c->tcp.scan_out, excl_tcp_out);
+ fwd_scan_ports_tcp(c->fwd[PIF_HOST], &c->tcp.scan_in, excl_tcp_in);
+ fwd_scan_ports_udp(c->fwd[PIF_SPLICE], &c->udp.scan_out,
&c->tcp.scan_out, excl_udp_out);
- fwd_scan_ports_udp(&c->fwd_in, &c->udp.scan_in,
+ fwd_scan_ports_udp(c->fwd[PIF_HOST], &c->udp.scan_in,
&c->tcp.scan_in, excl_udp_in);
}
@@ -912,10 +917,8 @@ void fwd_scan_ports_timer(struct ctx *c, const struct timespec *now)
fwd_scan_ports(c);
- fwd_listen_sync(c, &c->fwd_in, PIF_HOST,
- &c->tcp.scan_in, &c->udp.scan_in);
- fwd_listen_sync(c, &c->fwd_out, PIF_SPLICE,
- &c->tcp.scan_out, &c->udp.scan_out);
+ fwd_listen_sync(c, PIF_HOST, &c->tcp.scan_in, &c->udp.scan_in);
+ fwd_listen_sync(c, PIF_SPLICE, &c->tcp.scan_out, &c->udp.scan_out);
}
/**
diff --git a/fwd.h b/fwd.h
index 958eee25..b387d926 100644
--- a/fwd.h
+++ b/fwd.h
@@ -108,6 +108,7 @@ struct fwd_scan {
#define FWD_PORT_SCAN_INTERVAL 1000 /* ms */
+void fwd_rule_init(struct ctx *c);
void fwd_rule_add(struct fwd_table *fwd, uint8_t proto, uint8_t flags,
const union inany_addr *addr, const char *ifname,
in_port_t first, in_port_t last, in_port_t to);
@@ -119,8 +120,7 @@ void fwd_rules_print(const struct fwd_table *fwd);
void fwd_scan_ports_init(struct ctx *c);
void fwd_scan_ports_timer(struct ctx * c, const struct timespec *now);
-int fwd_listen_sync(const struct ctx *c, const struct fwd_table *fwd,
- uint8_t pif,
+int fwd_listen_sync(const struct ctx *c, uint8_t pif,
const struct fwd_scan *tcp, const struct fwd_scan *udp);
void fwd_listen_close(const struct fwd_table *fwd);
int fwd_listen_init(const struct ctx *c);
diff --git a/passt.h b/passt.h
index b614bdf0..5fc4e07f 100644
--- a/passt.h
+++ b/passt.h
@@ -264,8 +264,7 @@ struct ctx {
unsigned int pasta_ifi;
int pasta_conf_ns;
- struct fwd_table fwd_in;
- struct fwd_table fwd_out;
+ struct fwd_table *fwd[PIF_NUM_TYPES];
int no_tcp;
struct tcp_ctx tcp;
--
2.53.0
^ permalink raw reply [flat|nested] 16+ messages in thread
* [PATCH v2 07/15] pesto: Introduce stub configuration interface and tool
2026-03-19 6:11 [PATCH v2 00/15] RFC: Read-only dynamic update implementation David Gibson
` (5 preceding siblings ...)
2026-03-19 6:11 ` [PATCH v2 06/15] fwd: Store forwarding tables indexed by (origin) pif David Gibson
@ 2026-03-19 6:11 ` David Gibson
2026-03-19 6:11 ` [PATCH v2 08/15] pesto: Add command line option parsing and debug messages David Gibson
` (7 subsequent siblings)
14 siblings, 0 replies; 16+ messages in thread
From: David Gibson @ 2026-03-19 6:11 UTC (permalink / raw)
To: Stefano Brivio, passt-dev; +Cc: David Gibson
Add a control socket to passt/pasta for updating configuration at runtime.
Add a tool (pesto) for communicating with the control socket.
For now this is a stub implementation that forms the connection and sends
some version information, but nothing more.
Example:
./pasta --debug --config-net -c /tmp/pasta.conf -t none
./pesto /tmp/pasta.conf
Signed-off-by: Stefano Brivio <sbrivio@redhat.com>
[dwg: Based on an early draft from Stefano]
Signed-off-by: David Gibson <david@gibson.dropbear.id.au>
---
.gitignore | 2 +
Makefile | 30 +++++++----
common.h | 14 +++++
conf.c | 150 ++++++++++++++++++++++++++++++++++++++++++++++++++-
conf.h | 2 +
epoll_type.h | 4 ++
log.c | 1 +
passt.1 | 5 ++
passt.c | 9 ++++
passt.h | 6 +++
pesto.1 | 46 ++++++++++++++++
pesto.c | 113 ++++++++++++++++++++++++++++++++++++++
pesto.h | 34 ++++++++++++
serialise.c | 3 ++
util.h | 3 --
15 files changed, 407 insertions(+), 15 deletions(-)
create mode 100644 common.h
create mode 100644 pesto.1
create mode 100644 pesto.c
create mode 100644 pesto.h
diff --git a/.gitignore b/.gitignore
index 3c16adc7..3e40d9f7 100644
--- a/.gitignore
+++ b/.gitignore
@@ -4,9 +4,11 @@
/pasta
/pasta.avx2
/passt-repair
+/pesto
/qrap
/pasta.1
/seccomp.h
+/seccomp_pesto.h
/seccomp_repair.h
/c*.json
README.plain.md
diff --git a/Makefile b/Makefile
index 5b6891d7..d085c9c1 100644
--- a/Makefile
+++ b/Makefile
@@ -44,17 +44,19 @@ PASST_SRCS = arch.c arp.c checksum.c conf.c dhcp.c dhcpv6.c epoll_ctl.c \
udp.c udp_flow.c udp_vu.c util.c vhost_user.c virtio.c vu_common.c
QRAP_SRCS = qrap.c
PASST_REPAIR_SRCS = passt-repair.c
-SRCS = $(PASST_SRCS) $(QRAP_SRCS) $(PASST_REPAIR_SRCS)
+PESTO_SRCS = pesto.c serialise.c
+SRCS = $(PASST_SRCS) $(QRAP_SRCS) $(PASST_REPAIR_SRCS) $(PESTO_SRCS)
-MANPAGES = passt.1 pasta.1 qrap.1 passt-repair.1
+MANPAGES = passt.1 pasta.1 pesto.1 qrap.1 passt-repair.1
+PESTO_HEADERS = common.h pesto.h serialise.h
PASST_HEADERS = arch.h arp.h checksum.h conf.h dhcp.h dhcpv6.h epoll_ctl.h \
flow.h fwd.h flow_table.h icmp.h icmp_flow.h inany.h iov.h ip.h \
isolation.h lineread.h log.h migrate.h ndp.h netlink.h packet.h \
- passt.h pasta.h pcap.h pif.h repair.h serialise.h siphash.h tap.h tcp.h \
- tcp_buf.h tcp_conn.h tcp_internal.h tcp_splice.h tcp_vu.h udp.h \
- udp_flow.h udp_internal.h udp_vu.h util.h vhost_user.h virtio.h \
- vu_common.h
+ passt.h pasta.h pcap.h pif.h repair.h siphash.h tap.h tcp.h tcp_buf.h \
+ tcp_conn.h tcp_internal.h tcp_splice.h tcp_vu.h udp.h udp_flow.h \
+ udp_internal.h udp_vu.h util.h vhost_user.h virtio.h vu_common.h \
+ $(PESTO_HEADERS)
HEADERS = $(PASST_HEADERS) seccomp.h
C := \#include <sys/random.h>\nint main(){int a=getrandom(0, 0, 0);}
@@ -75,9 +77,9 @@ mandir ?= $(datarootdir)/man
man1dir ?= $(mandir)/man1
ifeq ($(TARGET_ARCH),x86_64)
-BIN := passt passt.avx2 pasta pasta.avx2 qrap passt-repair
+BIN := passt passt.avx2 pasta pasta.avx2 qrap passt-repair pesto
else
-BIN := passt pasta qrap passt-repair
+BIN := passt pasta qrap passt-repair pesto
endif
all: $(BIN) $(MANPAGES) docs
@@ -91,6 +93,9 @@ seccomp.h: seccomp.sh $(PASST_SRCS) $(PASST_HEADERS)
seccomp_repair.h: seccomp.sh $(PASST_REPAIR_SRCS)
@ ARCH="$(TARGET_ARCH)" CC="$(CC)" ./seccomp.sh seccomp_repair.h $(PASST_REPAIR_SRCS)
+seccomp_pesto.h: seccomp.sh $(PESTO_SRCS)
+ @ ARCH="$(TARGET_ARCH)" CC="$(CC)" ./seccomp.sh seccomp_pesto.h $(PESTO_SRCS)
+
passt: $(PASST_SRCS) $(HEADERS)
$(CC) $(FLAGS) $(CFLAGS) $(CPPFLAGS) $(PASST_SRCS) -o passt $(LDFLAGS)
@@ -110,6 +115,9 @@ qrap: $(QRAP_SRCS) passt.h
passt-repair: $(PASST_REPAIR_SRCS) seccomp_repair.h
$(CC) $(FLAGS) $(CFLAGS) $(CPPFLAGS) $(PASST_REPAIR_SRCS) -o passt-repair $(LDFLAGS)
+pesto: $(PESTO_SRCS) $(PESTO_HEADERS) seccomp_pesto.h
+ $(CC) $(FLAGS) $(CFLAGS) $(CPPFLAGS) $(PESTO_SRCS) -o pesto $(LDFLAGS)
+
valgrind: EXTRA_SYSCALLS += rt_sigprocmask rt_sigtimedwait rt_sigaction \
rt_sigreturn getpid gettid kill clock_gettime \
mmap|mmap2 munmap open unlink gettimeofday futex \
@@ -119,7 +127,7 @@ valgrind: all
.PHONY: clean
clean:
- $(RM) $(BIN) *~ *.o seccomp.h seccomp_repair.h pasta.1 \
+ $(RM) $(BIN) *~ *.o seccomp.h seccomp_repair.h seccomp_pesto.h pasta.1 \
passt.tar passt.tar.gz *.deb *.rpm \
passt.pid README.plain.md
@@ -173,11 +181,11 @@ docs: README.md
done < README.md; \
) > README.plain.md
-clang-tidy: $(PASST_SRCS)
+clang-tidy: $(PASST_SRCS) $(PESTO_SRCS)
clang-tidy $^ -- $(filter-out -pie,$(FLAGS) $(CFLAGS) $(CPPFLAGS)) \
-DCLANG_TIDY_58992
-cppcheck: $(PASST_SRCS) $(HEADERS)
+cppcheck: $(PASST_SRCS) $(PESTO_SRCS) $(HEADERS)
if cppcheck --check-level=exhaustive /dev/null > /dev/null 2>&1; then \
CPPCHECK_EXHAUSTIVE="--check-level=exhaustive"; \
else \
diff --git a/common.h b/common.h
new file mode 100644
index 00000000..76a95609
--- /dev/null
+++ b/common.h
@@ -0,0 +1,14 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later
+ * Copyright Red Hat
+ * Author: David Gibson <david@gibson.dropbear.id.au>
+ *
+ * Definitions used by both passt/pasta and other tools
+ */
+
+#ifndef COMMON_H
+#define COMMON_H
+
+/* FPRINTF() intentionally silences cert-err33-c clang-tidy warnings */
+#define FPRINTF(f, ...) (void)fprintf(f, __VA_ARGS__)
+
+#endif /* _COMMON_H */
diff --git a/conf.c b/conf.c
index 6af3c8a5..031de352 100644
--- a/conf.c
+++ b/conf.c
@@ -35,6 +35,7 @@
#include <netinet/in.h>
#include <netinet/if_ether.h>
+#include "common.h"
#include "util.h"
#include "ip.h"
#include "passt.h"
@@ -47,6 +48,10 @@
#include "isolation.h"
#include "log.h"
#include "vhost_user.h"
+#include "epoll_ctl.h"
+#include "conf.h"
+#include "pesto.h"
+#include "serialise.h"
#define NETNS_RUN_DIR "/run/netns"
@@ -894,6 +899,7 @@ static void usage(const char *name, FILE *f, int status)
" --runas UID|UID:GID Run as given UID, GID, which can be\n"
" numeric, or login and group names\n"
" default: drop to user \"nobody\"\n"
+ " -c, --conf-path PATH Configuration socket path\n"
" -h, --help Display this help message and exit\n"
" --version Show version and exit\n");
@@ -1426,6 +1432,17 @@ static void conf_open_files(struct ctx *c)
if (c->pidfile_fd < 0)
die_perror("Couldn't open PID file %s", c->pidfile);
}
+
+ c->fd_conf = -1;
+ if (*c->conf_path) {
+ c->fd_conf_listen = sock_unix(c->conf_path);
+ if (c->fd_conf_listen < 0) {
+ die_perror("Couldn't open control socket %s",
+ c->conf_path);
+ }
+ } else {
+ c->fd_conf_listen = -1;
+ }
}
/**
@@ -1461,6 +1478,25 @@ fail:
die("Invalid MAC address: %s", str);
}
+/**
+ * conf_sock_listen() - Start listening for connections on configuration socket
+ * @c: Execution context
+ */
+static void conf_sock_listen(const struct ctx *c)
+{
+ union epoll_ref ref = { .type = EPOLL_TYPE_CONF_LISTEN };
+
+ if (c->fd_conf_listen < 0)
+ return;
+
+ if (listen(c->fd_conf_listen, 0))
+ die_perror("Couldn't listen on configuration socket");
+
+ ref.fd = c->fd_conf_listen;
+ if (epoll_add(c->epollfd, EPOLLIN | EPOLLET, ref))
+ die_perror("Couldn't add configuration socket to epoll");
+}
+
/**
* conf() - Process command-line arguments and set configuration
* @c: Execution context
@@ -1543,9 +1579,10 @@ void conf(struct ctx *c, int argc, char **argv)
{"migrate-exit", no_argument, NULL, 29 },
{"migrate-no-linger", no_argument, NULL, 30 },
{"stats", required_argument, NULL, 31 },
+ {"conf-path", required_argument, NULL, 'c' },
{ 0 },
};
- const char *optstring = "+dqfel:hs:F:I:p:P:m:a:n:M:g:i:o:D:S:H:461t:u:T:U:";
+ const char *optstring = "+dqfel:hs:c:F:I:p:P:m:a:n:M:g:i:o:D:S:H:461t:u:T:U:";
const char *logname = (c->mode == MODE_PASTA) ? "pasta" : "passt";
char userns[PATH_MAX] = { 0 }, netns[PATH_MAX] = { 0 };
bool copy_addrs_opt = false, copy_routes_opt = false;
@@ -1809,6 +1846,13 @@ void conf(struct ctx *c, int argc, char **argv)
c->fd_tap = -1;
break;
+ case 'c':
+ ret = snprintf(c->conf_path, sizeof(c->conf_path), "%s",
+ optarg);
+ if (ret <= 0 || ret >= (int)sizeof(c->sock_path))
+ die("Invalid configuration path: %s", optarg);
+ c->fd_conf_listen = c->fd_conf = -1;
+ break;
case 'F':
errno = 0;
fd_tap_opt = strtol(optarg, NULL, 0);
@@ -2253,4 +2297,108 @@ void conf(struct ctx *c, int argc, char **argv)
if (!c->quiet)
conf_print(c);
+
+ conf_sock_listen(c);
+}
+
+/**
+ * conf_listen_handler() - Handle events on configuration listening socket
+ * @c: Execution context
+ * @events: epoll events
+ */
+void conf_listen_handler(struct ctx *c, uint32_t events)
+{
+ struct pesto_hello hello = {
+ .magic = PESTO_SERVER_MAGIC,
+ .version = htonl(PESTO_PROTOCOL_VERSION),
+ };
+ union epoll_ref ref = { .type = EPOLL_TYPE_CONF };
+ struct ucred uc = { 0 };
+ socklen_t len = sizeof(uc);
+ int fd, rc;
+
+ if (events != EPOLLIN) {
+ err("Unexpected event 0x%04x on configuration socket", events);
+ return;
+ }
+
+ fd = accept4(c->fd_conf_listen, NULL, NULL, SOCK_NONBLOCK);
+ if (fd < 0) {
+ warn_perror("accept4() on configuration listening socket");
+ return;
+ }
+
+ if (getsockopt(fd, SOL_SOCKET, SO_PEERCRED, &uc, &len) < 0)
+ warn_perror("Can't get configuration client credentials");
+
+ /* Another client is already connected: accept and close right away. */
+ if (c->fd_conf != -1) {
+ info("Discarding configuration client, PID %i", uc.pid);
+ goto fail;
+ }
+
+ ref.fd = fd;
+ rc = epoll_add(c->epollfd, EPOLLIN | EPOLLET, ref);
+ if (rc < 0) {
+ warn_perror("epoll_ctl() on configuration socket");
+ goto fail;
+ }
+
+ rc = sewrite_var(fd, &hello);
+ if (rc < 0) {
+ warn_perror("Error writing configuration protocol hello");
+ goto fail;
+ }
+
+ c->fd_conf = fd;
+ info("Accepted configuration client, PID %i", uc.pid);
+ if (!PESTO_PROTOCOL_VERSION) {
+ warn(
+"Warning: Using experimental unsupported configuration protocol");
+ }
+
+ return;
+
+fail:
+ close(fd);
+}
+
+/**
+ * conf_handler() - Handle events on configuration socket
+ * @c: Execution context
+ * @events: epoll events
+ */
+void conf_handler(struct ctx *c, uint32_t events)
+{
+ if (events & EPOLLIN) {
+ char discard[BUFSIZ];
+ ssize_t n;
+
+ do {
+ n = read(c->fd_conf, discard, sizeof(discard));
+ if (n > 0)
+ debug("Discarded %zd bytes of config data", n);
+ } while (n > 0);
+ if (n == 0) {
+ debug("Configuration client EOF");
+ goto close;
+ }
+ if (errno != EAGAIN && errno != EWOULDBLOCK) {
+ err_perror("Error reading config data");
+ goto close;
+ }
+ }
+
+ if (events & EPOLLHUP) {
+ debug("Configuration client hangup");
+ goto close;
+ }
+
+ return;
+
+close:
+ debug("Closing configuration socket");
+ epoll_ctl(c->epollfd, EPOLL_CTL_DEL, c->fd_conf, NULL);
+ close(c->fd_conf);
+ c->fd_conf = -1;
}
diff --git a/conf.h b/conf.h
index b45ad746..16f97189 100644
--- a/conf.h
+++ b/conf.h
@@ -8,5 +8,7 @@
enum passt_modes conf_mode(int argc, char *argv[]);
void conf(struct ctx *c, int argc, char **argv);
+void conf_listen_handler(struct ctx *c, uint32_t events);
+void conf_handler(struct ctx *c, uint32_t events);
#endif /* CONF_H */
diff --git a/epoll_type.h b/epoll_type.h
index a90ffb67..061325aa 100644
--- a/epoll_type.h
+++ b/epoll_type.h
@@ -46,6 +46,10 @@ enum epoll_type {
EPOLL_TYPE_REPAIR,
/* Netlink neighbour subscription socket */
EPOLL_TYPE_NL_NEIGH,
+ /* Configuration listening socket */
+ EPOLL_TYPE_CONF_LISTEN,
+ /* Configuration socket */
+ EPOLL_TYPE_CONF,
EPOLL_NUM_TYPES,
};
diff --git a/log.c b/log.c
index 21e3673e..99404e25 100644
--- a/log.c
+++ b/log.c
@@ -26,6 +26,7 @@
#include <stdarg.h>
#include <sys/socket.h>
+#include "common.h"
#include "linux_dep.h"
#include "log.h"
#include "util.h"
diff --git a/passt.1 b/passt.1
index 13e8df9d..32d70c8d 100644
--- a/passt.1
+++ b/passt.1
@@ -127,6 +127,11 @@ login name and group name can be passed. This requires privileges (either
initial effective UID 0 or CAP_SETUID capability) to work.
Default is to change to user \fInobody\fR if started as root.
+.TP
+.BR \-c ", " \-\-conf-path " " \fIpath " " (EXPERIMENTAL)
+Path for configuration and control socket used by \fBpesto\fR(1) to
+dynamically update passt or pasta's configuration.
+
.TP
.BR \-h ", " \-\-help
Display a help message and exit.
diff --git a/passt.c b/passt.c
index f84419c7..d37beef3 100644
--- a/passt.c
+++ b/passt.c
@@ -36,6 +36,7 @@
#include <netinet/if_ether.h>
#include <libgen.h>
+#include "common.h"
#include "util.h"
#include "passt.h"
#include "dhcp.h"
@@ -80,6 +81,8 @@ char *epoll_type_str[] = {
[EPOLL_TYPE_REPAIR_LISTEN] = "TCP_REPAIR helper listening socket",
[EPOLL_TYPE_REPAIR] = "TCP_REPAIR helper socket",
[EPOLL_TYPE_NL_NEIGH] = "netlink neighbour notifier socket",
+ [EPOLL_TYPE_CONF_LISTEN] = "configuration listening socket",
+ [EPOLL_TYPE_CONF] = "configuration socket",
};
static_assert(ARRAY_SIZE(epoll_type_str) == EPOLL_NUM_TYPES,
"epoll_type_str[] doesn't match enum epoll_type");
@@ -303,6 +306,12 @@ static void passt_worker(void *opaque, int nfds, struct epoll_event *events)
case EPOLL_TYPE_NL_NEIGH:
nl_neigh_notify_handler(c);
break;
+ case EPOLL_TYPE_CONF_LISTEN:
+ conf_listen_handler(c, eventmask);
+ break;
+ case EPOLL_TYPE_CONF:
+ conf_handler(c, eventmask);
+ break;
default:
/* Can't happen */
assert(0);
diff --git a/passt.h b/passt.h
index 5fc4e07f..6bb748fb 100644
--- a/passt.h
+++ b/passt.h
@@ -158,6 +158,7 @@ struct ip6_ctx {
* @foreground: Run in foreground, don't log to stderr by default
* @nofile: Maximum number of open files (ulimit -n)
* @sock_path: Path for UNIX domain socket
+ * @conf_path: Path for configuration UNIX domain socket
* @repair_path: TCP_REPAIR helper path, can be "none", empty for default
* @pcap: Path for packet capture file
* @pidfile: Path to PID file, empty string if not configured
@@ -169,6 +170,8 @@ struct ip6_ctx {
* @epollfd: File descriptor for epoll instance
* @fd_tap_listen: File descriptor for listening AF_UNIX socket, if any
* @fd_tap: AF_UNIX socket, tuntap device, or pre-opened socket
+ * @fd_conf_listen: Listening configuration socket, if any
+ * @fd_conf: Configuration socket, if any
* @fd_repair_listen: File descriptor for listening TCP_REPAIR socket, if any
* @fd_repair: Connected AF_UNIX socket for TCP_REPAIR helper
* @our_tap_mac: Pasta/passt's MAC on the tap link
@@ -224,6 +227,7 @@ struct ctx {
int foreground;
int nofile;
char sock_path[UNIX_PATH_MAX];
+ char conf_path[UNIX_PATH_MAX];
char repair_path[UNIX_PATH_MAX];
char pcap[PATH_MAX];
@@ -241,6 +245,8 @@ struct ctx {
int epollfd;
int fd_tap_listen;
int fd_tap;
+ int fd_conf_listen;
+ int fd_conf;
int fd_repair_listen;
int fd_repair;
unsigned char our_tap_mac[ETH_ALEN];
diff --git a/pesto.1 b/pesto.1
new file mode 100644
index 00000000..338fb8a6
--- /dev/null
+++ b/pesto.1
@@ -0,0 +1,46 @@
+.\" SPDX-License-Identifier: GPL-2.0-or-later
+.\" Copyright Red Hat
+.\" Author: David Gibson <david@gibson.dropbear.id.au>
+.TH pesto 1
+
+.SH NAME
+.B pesto
+\- Configure a running \fBpasst\fR(1) or \fBpasta\fR(1) instance.
+
+.SH SYNOPSIS
+.B pesto
+\fIPATH\fR
+
+.SH DESCRIPTION
+
+.B pesto
+is an experimental client to view and update the port forwarding
+configuration of a running \fBpasst\fR(1) or \fBpasta\fR(1) instance.
+
+\fIPATH\fR gives the path to the UNIX domain socket created by
+\fBpasst\fR or \fBpasta\fR. It should match the \fB-c\fR command line
+option given to that instance.
+
+.SH AUTHORS
+
+Stefano Brivio <sbrivio@redhat.com>,
+David Gibson <david@gibson.dropbear.id.au>.
+
+.SH REPORTING BUGS
+
+Please report issues on the bug tracker at https://bugs.passt.top/, or
+send a message to the passt-user@passt.top mailing list, see
+https://lists.passt.top/.
+
+.SH COPYRIGHT
+
+Copyright Red Hat
+
+\fBpesto\fR is free software: you can redistribute them and/or modify
+them under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 2 of the License, or (at
+your option) any later version.
+
+.SH SEE ALSO
+
+\fBpasst\fR(1), \fBpasta\fR(1), \fBunix\fR(7).
diff --git a/pesto.c b/pesto.c
new file mode 100644
index 00000000..edc07e77
--- /dev/null
+++ b/pesto.c
@@ -0,0 +1,113 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+/* PESTO - Programmable Extensible Socket Translation Orchestrator
+ * front-end for passt(1) and pasta(1) forwarding configuration
+ *
+ * pesto.c - Main program (it's not actually extensible)
+ *
+ * Copyright (c) 2026 Red Hat GmbH
+ * Author: Stefano Brivio <sbrivio@redhat.com>
+ */
+
+#include <arpa/inet.h>
+#include <sys/prctl.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/un.h>
+#include <errno.h>
+#include <inttypes.h>
+#include <stdbool.h>
+#include <stddef.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <limits.h>
+#include <unistd.h>
+
+#include <linux/audit.h>
+#include <linux/capability.h>
+#include <linux/filter.h>
+#include <linux/seccomp.h>
+
+#include "common.h"
+#include "seccomp_pesto.h"
+#include "serialise.h"
+#include "pesto.h"
+
+#define die(...) \
+ do { \
+ FPRINTF(stderr, __VA_ARGS__); \
+ FPRINTF(stderr, "\n"); \
+ exit(EXIT_FAILURE); \
+ } while (0)
+
+/**
+ * main() - Entry point and whole program with loop
+ * @argc: Argument count
+ * @argv: Arguments: socket path, operation, port specifiers
+ *
+ * Return: 0 on success, won't return on failure
+ *
+ * #syscalls:pesto connect write close exit_group fstat brk
+ * #syscalls:pesto socket s390x:socketcall i686:socketcall
+ * #syscalls:pesto recvfrom recvmsg arm:recv ppc64le:recv
+ * #syscalls:pesto sendto sendmsg arm:send ppc64le:send
+ */
+int main(int argc, char **argv)
+{
+ struct sockaddr_un a = { AF_UNIX, "" };
+ struct pesto_hello hello;
+ struct sock_fprog prog;
+ uint32_t s_version;
+ int ret, s;
+
+ prctl(PR_SET_DUMPABLE, 0);
+
+ prog.len = (unsigned short)sizeof(filter_pesto) /
+ sizeof(filter_pesto[0]);
+ prog.filter = filter_pesto;
+ if (prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0) ||
+ prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER, &prog))
+ die("Failed to apply seccomp filter");
+
+ if (argc < 2)
+ die("Usage: %s CONTROLPATH", argv[0]);
+
+ if ((s = socket(AF_UNIX, SOCK_STREAM, 0)) < 0)
+ die("Failed to create AF_UNIX socket: %s", strerror(errno));
+
+ ret = snprintf(a.sun_path, sizeof(a.sun_path), "%s", argv[1]);
+ if (ret <= 0 || ret >= (int)sizeof(a.sun_path))
+ die("Invalid socket path \"%s\"", argv[1]);
+
+ ret = connect(s, (struct sockaddr *)&a, sizeof(a));
+ if (ret < 0) {
+ die("Failed to connect to %s: %s",
+ a.sun_path, strerror(errno));
+ }
+
+ ret = seread_var(s, &hello);
+ if (ret < 0)
+ die("Couldn't read server greeting: %s", strerror(errno));
+
+ if (memcmp(hello.magic, PESTO_SERVER_MAGIC, sizeof(hello.magic)))
+ die("Bad magic number from server");
+
+ s_version = ntohl(hello.version);
+
+ if (s_version > PESTO_PROTOCOL_VERSION) {
+ die("Unknown server protocol version %"PRIu32" > %"PRIu32"\n",
+ s_version, PESTO_PROTOCOL_VERSION);
+ }
+
+ /* cppcheck-suppress knownConditionTrueFalse */
+ if (!s_version) {
+ if (PESTO_PROTOCOL_VERSION)
+ die("Unsupported experimental server protocol");
+ FPRINTF(stderr,
+"Warning: Using experimental protocol version, client and server must match\n");
+ }
+
+ exit(0);
+}
diff --git a/pesto.h b/pesto.h
new file mode 100644
index 00000000..92d4df3a
--- /dev/null
+++ b/pesto.h
@@ -0,0 +1,34 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later
+ * Copyright Red Hat
+ * Author: David Gibson <david@gibson.dropbear.id.au>
+ *
+ * Definitions and functions used by both client and server of the configuration
+ * update protocol (pesto).
+ */
+
+#ifndef PESTO_H
+#define PESTO_H
+
+#include <assert.h>
+#include <stdint.h>
+
+#define PESTO_SERVER_MAGIC "pesto:s"
+
+/* Version 0 is reserved for unreleased / unsupported experimental versions */
+#define PESTO_PROTOCOL_VERSION 0
+
+/**
+ * struct pesto_hello - Server introduction message
+ * @magic: PESTO_SERVER_MAGIC
+ * @version: Version number
+ */
+struct pesto_hello {
+ char magic[8];
+ uint32_t version;
+} __attribute__ ((__packed__));
+
+static_assert(sizeof(PESTO_SERVER_MAGIC)
+ == sizeof(((struct pesto_hello *)0)->magic),
+ "PESTO_SERVER_MAGIC has wrong size");
+
+#endif /* PESTO_H */
diff --git a/serialise.c b/serialise.c
index 098ae35c..4fc0d116 100644
--- a/serialise.c
+++ b/serialise.c
@@ -6,6 +6,9 @@
* PASTA - Pack A Subtle Tap Abstraction
* for network namespace/tap device mode
*
+ * PESTO - Programmable Extensible Socket Translation Orchestrator
+ * front-end for passt(1) and pasta(1) forwarding configuration
+ *
* serialise.c - Serialisation of data structures over bytestreams
*
* Copyright Red Hat
diff --git a/util.h b/util.h
index cb669105..e7993f4d 100644
--- a/util.h
+++ b/util.h
@@ -317,9 +317,6 @@ static inline bool mod_between(unsigned x, unsigned i, unsigned j, unsigned m)
return mod_sub(x, i, m) < mod_sub(j, i, m);
}
-/* FPRINTF() intentionally silences cert-err33-c clang-tidy warnings */
-#define FPRINTF(f, ...) (void)fprintf(f, __VA_ARGS__)
-
void raw_random(void *buf, size_t buflen);
/*
--
2.53.0
^ permalink raw reply [flat|nested] 16+ messages in thread
* [PATCH v2 08/15] pesto: Add command line option parsing and debug messages
2026-03-19 6:11 [PATCH v2 00/15] RFC: Read-only dynamic update implementation David Gibson
` (6 preceding siblings ...)
2026-03-19 6:11 ` [PATCH v2 07/15] pesto: Introduce stub configuration interface and tool David Gibson
@ 2026-03-19 6:11 ` David Gibson
2026-03-19 6:11 ` [PATCH v2 09/15] pesto: Expose list of pifs to pesto David Gibson
` (6 subsequent siblings)
14 siblings, 0 replies; 16+ messages in thread
From: David Gibson @ 2026-03-19 6:11 UTC (permalink / raw)
To: Stefano Brivio, passt-dev; +Cc: David Gibson
Add basic command line option parsing using getopt_long() to pesto.
Implement --help, --version, --quiet and --verbose options, along with a
debug() macro to print debugging messages when given the verbose option.
Signed-off-by: David Gibson <david@gibson.dropbear.id.au>
---
common.h | 8 ++++++
conf.c | 45 +++++++++++++++++----------------
passt.h | 12 ++++-----
pesto.c | 77 ++++++++++++++++++++++++++++++++++++++++++++++++++------
util.h | 8 ------
5 files changed, 108 insertions(+), 42 deletions(-)
diff --git a/common.h b/common.h
index 76a95609..927d20a4 100644
--- a/common.h
+++ b/common.h
@@ -8,6 +8,14 @@
#ifndef COMMON_H
#define COMMON_H
+#define VERSION_BLOB \
+ VERSION "\n" \
+ "Copyright Red Hat\n" \
+ "GNU General Public License, version 2 or later\n" \
+ " <https://www.gnu.org/licenses/old-licenses/gpl-2.0.html>\n" \
+ "This is free software: you are free to change and redistribute it.\n" \
+ "There is NO WARRANTY, to the extent permitted by law.\n\n"
+
/* FPRINTF() intentionally silences cert-err33-c clang-tidy warnings */
#define FPRINTF(f, ...) (void)fprintf(f, __VA_ARGS__)
diff --git a/conf.c b/conf.c
index 031de352..e862b023 100644
--- a/conf.c
+++ b/conf.c
@@ -1146,6 +1146,9 @@ static void conf_print(const struct ctx *c)
char bufmac[ETH_ADDRSTRLEN], ifn[IFNAMSIZ];
int i;
+ if (c->fd_control_listen >= 0)
+ info("Configuration socket: %s", c->control_path);
+
if (c->ifi4 > 0 || c->ifi6 > 0) {
info("Template interface: %s%s%s%s%s",
c->ifi4 > 0 ? if_indextoname(c->ifi4, ifn) : "",
@@ -1433,15 +1436,15 @@ static void conf_open_files(struct ctx *c)
die_perror("Couldn't open PID file %s", c->pidfile);
}
- c->fd_conf = -1;
- if (*c->conf_path) {
- c->fd_conf_listen = sock_unix(c->conf_path);
- if (c->fd_conf_listen < 0) {
+ c->fd_control = -1;
+ if (*c->control_path) {
+ c->fd_control_listen = sock_unix(c->control_path);
+ if (c->fd_control_listen < 0) {
die_perror("Couldn't open control socket %s",
- c->conf_path);
+ c->control_path);
}
} else {
- c->fd_conf_listen = -1;
+ c->fd_control_listen = -1;
}
}
@@ -1486,13 +1489,13 @@ static void conf_sock_listen(const struct ctx *c)
{
union epoll_ref ref = { .type = EPOLL_TYPE_CONF_LISTEN };
- if (c->fd_conf_listen < 0)
+ if (c->fd_control_listen < 0)
return;
- if (listen(c->fd_conf_listen, 0))
+ if (listen(c->fd_control_listen, 0))
die_perror("Couldn't listen on configuration socket");
- ref.fd = c->fd_conf_listen;
+ ref.fd = c->fd_control_listen;
if (epoll_add(c->epollfd, EPOLLIN | EPOLLET, ref))
die_perror("Couldn't add configuration socket to epoll");
}
@@ -1847,11 +1850,11 @@ void conf(struct ctx *c, int argc, char **argv)
c->fd_tap = -1;
break;
case 'c':
- ret = snprintf(c->conf_path, sizeof(c->conf_path), "%s",
- optarg);
+ ret = snprintf(c->control_path, sizeof(c->control_path),
+ "%s", optarg);
if (ret <= 0 || ret >= (int)sizeof(c->sock_path))
die("Invalid configuration path: %s", optarg);
- c->fd_conf_listen = c->fd_conf = -1;
+ c->fd_control_listen = c->fd_control = -1;
break;
case 'F':
errno = 0;
@@ -2295,10 +2298,10 @@ void conf(struct ctx *c, int argc, char **argv)
FWD_SCAN);
}
+ conf_sock_listen(c);
+
if (!c->quiet)
conf_print(c);
-
- conf_sock_listen(c);
}
/**
@@ -2322,7 +2325,7 @@ void conf_listen_handler(struct ctx *c, uint32_t events)
return;
}
- fd = accept4(c->fd_conf_listen, NULL, NULL, SOCK_NONBLOCK);
+ fd = accept4(c->fd_control_listen, NULL, NULL, SOCK_NONBLOCK);
if (fd < 0) {
warn_perror("accept4() on configuration listening socket");
return;
@@ -2332,7 +2335,7 @@ void conf_listen_handler(struct ctx *c, uint32_t events)
warn_perror("Can't get configuration client credentials");
/* Another client is already connected: accept and close right away. */
- if (c->fd_conf != -1) {
+ if (c->fd_control != -1) {
info("Discarding configuration client, PID %i", uc.pid);
goto fail;
}
@@ -2350,7 +2353,7 @@ void conf_listen_handler(struct ctx *c, uint32_t events)
goto fail;
}
- c->fd_conf = fd;
+ c->fd_control = fd;
info("Accepted configuration client, PID %i", uc.pid);
if (!PESTO_PROTOCOL_VERSION) {
warn(
@@ -2375,7 +2378,7 @@ void conf_handler(struct ctx *c, uint32_t events)
ssize_t n;
do {
- n = read(c->fd_conf, discard, sizeof(discard));
+ n = read(c->fd_control, discard, sizeof(discard));
if (n > 0)
debug("Discarded %zd bytes of config data", n);
} while (n > 0);
@@ -2398,7 +2401,7 @@ void conf_handler(struct ctx *c, uint32_t events)
close:
debug("Closing configuration socket");
- epoll_ctl(c->epollfd, EPOLL_CTL_DEL, c->fd_conf, NULL);
- close(c->fd_conf);
- c->fd_conf = -1;
+ epoll_ctl(c->epollfd, EPOLL_CTL_DEL, c->fd_control, NULL);
+ close(c->fd_control);
+ c->fd_control = -1;
}
diff --git a/passt.h b/passt.h
index 6bb748fb..4245c4c8 100644
--- a/passt.h
+++ b/passt.h
@@ -158,7 +158,7 @@ struct ip6_ctx {
* @foreground: Run in foreground, don't log to stderr by default
* @nofile: Maximum number of open files (ulimit -n)
* @sock_path: Path for UNIX domain socket
- * @conf_path: Path for configuration UNIX domain socket
+ * @control_path: Path for control/configuration UNIX domain socket
* @repair_path: TCP_REPAIR helper path, can be "none", empty for default
* @pcap: Path for packet capture file
* @pidfile: Path to PID file, empty string if not configured
@@ -170,8 +170,8 @@ struct ip6_ctx {
* @epollfd: File descriptor for epoll instance
* @fd_tap_listen: File descriptor for listening AF_UNIX socket, if any
* @fd_tap: AF_UNIX socket, tuntap device, or pre-opened socket
- * @fd_conf_listen: Listening configuration socket, if any
- * @fd_conf: Configuration socket, if any
+ * @fd_control_listen: Listening control/configuration socket, if any
+ * @fd_control: Control/configuration socket, if any
* @fd_repair_listen: File descriptor for listening TCP_REPAIR socket, if any
* @fd_repair: Connected AF_UNIX socket for TCP_REPAIR helper
* @our_tap_mac: Pasta/passt's MAC on the tap link
@@ -227,7 +227,7 @@ struct ctx {
int foreground;
int nofile;
char sock_path[UNIX_PATH_MAX];
- char conf_path[UNIX_PATH_MAX];
+ char control_path[UNIX_PATH_MAX];
char repair_path[UNIX_PATH_MAX];
char pcap[PATH_MAX];
@@ -245,8 +245,8 @@ struct ctx {
int epollfd;
int fd_tap_listen;
int fd_tap;
- int fd_conf_listen;
- int fd_conf;
+ int fd_control_listen;
+ int fd_control;
int fd_repair_listen;
int fd_repair;
unsigned char our_tap_mac[ETH_ALEN];
diff --git a/pesto.c b/pesto.c
index edc07e77..a8c134f5 100644
--- a/pesto.c
+++ b/pesto.c
@@ -15,6 +15,7 @@
#include <sys/socket.h>
#include <sys/un.h>
#include <errno.h>
+#include <getopt.h>
#include <inttypes.h>
#include <stdbool.h>
#include <stddef.h>
@@ -42,6 +43,32 @@
exit(EXIT_FAILURE); \
} while (0)
+#define debug(...) \
+ do { \
+ if (verbosity > 1) { \
+ FPRINTF(stderr, __VA_ARGS__); \
+ FPRINTF(stderr, "\n"); \
+ } \
+ } while (0)
+
+/**
+ * usage() - Print usage, exit with given status code
+ * @name: Executable name
+ * @f: Stream to print usage info to
+ * @status: Status code for exit(2)
+ */
+static void usage(const char *name, FILE *f, int status)
+{
+ FPRINTF(f, "Usage: %s [OPTION]... PATH\n", name);
+ FPRINTF(f,
+ "\n"
+ " -v, --verbose Be more verbose\n"
+ " -q, --quiet Be less verbose\n"
+ " -h, --help Display this help message and exit\n"
+ " --version Show version and exit\n");
+ exit(status);
+}
+
/**
* main() - Entry point and whole program with loop
* @argc: Argument count
@@ -56,13 +83,20 @@
*/
int main(int argc, char **argv)
{
+ const struct option options[] = {
+ {"quiet", no_argument, NULL, 'q' },
+ {"verbose", no_argument, NULL, 'v' },
+ {"help", no_argument, NULL, 'h' },
+ {"version", no_argument, NULL, 1 },
+ { 0 },
+ };
struct sockaddr_un a = { AF_UNIX, "" };
+ const char *optstring = "vh";
struct pesto_hello hello;
struct sock_fprog prog;
+ int optname, ret, s;
uint32_t s_version;
- int ret, s;
-
- prctl(PR_SET_DUMPABLE, 0);
+ int verbosity = 1;
prog.len = (unsigned short)sizeof(filter_pesto) /
sizeof(filter_pesto[0]);
@@ -71,15 +105,40 @@ int main(int argc, char **argv)
prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER, &prog))
die("Failed to apply seccomp filter");
- if (argc < 2)
- die("Usage: %s CONTROLPATH", argv[0]);
+ do {
+ optname = getopt_long(argc, argv, optstring, options, NULL);
+
+ switch (optname) {
+ case -1:
+ case 0:
+ break;
+ case 'h':
+ usage(argv[0], stdout, EXIT_SUCCESS);
+ break;
+ case 'q':
+ verbosity--;
+ break;
+ case 'v':
+ verbosity++;
+ break;
+ case 1:
+ FPRINTF(stdout, "pesto ");
+ FPRINTF(stdout, VERSION_BLOB);
+ exit(EXIT_SUCCESS);
+ default:
+ usage(argv[0], stderr, EXIT_FAILURE);
+ }
+ } while (optname != -1);
+
+ if (argc - optind != 1)
+ usage(argv[0], stderr, EXIT_FAILURE);
if ((s = socket(AF_UNIX, SOCK_STREAM, 0)) < 0)
die("Failed to create AF_UNIX socket: %s", strerror(errno));
- ret = snprintf(a.sun_path, sizeof(a.sun_path), "%s", argv[1]);
+ ret = snprintf(a.sun_path, sizeof(a.sun_path), "%s", argv[optind]);
if (ret <= 0 || ret >= (int)sizeof(a.sun_path))
- die("Invalid socket path \"%s\"", argv[1]);
+ die("Invalid socket path \"%s\"", argv[optind]);
ret = connect(s, (struct sockaddr *)&a, sizeof(a));
if (ret < 0) {
@@ -87,6 +146,8 @@ int main(int argc, char **argv)
a.sun_path, strerror(errno));
}
+ debug("Connected to passt/pasta control socket");
+
ret = seread_var(s, &hello);
if (ret < 0)
die("Couldn't read server greeting: %s", strerror(errno));
@@ -96,6 +157,8 @@ int main(int argc, char **argv)
s_version = ntohl(hello.version);
+ debug("Server protocol version: %"PRIu32, s_version);
+
if (s_version > PESTO_PROTOCOL_VERSION) {
die("Unknown server protocol version %"PRIu32" > %"PRIu32"\n",
s_version, PESTO_PROTOCOL_VERSION);
diff --git a/util.h b/util.h
index e7993f4d..d7c397f6 100644
--- a/util.h
+++ b/util.h
@@ -21,14 +21,6 @@
#include "log.h"
-#define VERSION_BLOB \
- VERSION "\n" \
- "Copyright Red Hat\n" \
- "GNU General Public License, version 2 or later\n" \
- " <https://www.gnu.org/licenses/old-licenses/gpl-2.0.html>\n" \
- "This is free software: you are free to change and redistribute it.\n" \
- "There is NO WARRANTY, to the extent permitted by law.\n\n"
-
#ifndef SECCOMP_RET_KILL_PROCESS
#define SECCOMP_RET_KILL_PROCESS SECCOMP_RET_KILL
#endif
--
2.53.0
^ permalink raw reply [flat|nested] 16+ messages in thread
* [PATCH v2 09/15] pesto: Expose list of pifs to pesto
2026-03-19 6:11 [PATCH v2 00/15] RFC: Read-only dynamic update implementation David Gibson
` (7 preceding siblings ...)
2026-03-19 6:11 ` [PATCH v2 08/15] pesto: Add command line option parsing and debug messages David Gibson
@ 2026-03-19 6:11 ` David Gibson
2026-03-19 6:11 ` [PATCH v2 10/15] ip: Prepare ip.[ch] for sharing with pesto tool David Gibson
` (5 subsequent siblings)
14 siblings, 0 replies; 16+ messages in thread
From: David Gibson @ 2026-03-19 6:11 UTC (permalink / raw)
To: Stefano Brivio, passt-dev; +Cc: David Gibson
Extend the dynamic update protocol to expose the pif indices and names
from a running passt/pasta to the pesto tool. pesto records that data
and (for now) prints it out.
Signed-off-by: David Gibson <david@gibson.dropbear.id.au>
---
conf.c | 38 +++++++++++++++++++++
pesto.c | 98 ++++++++++++++++++++++++++++++++++++++++++++++++++++-
serialise.c | 21 ++++++++++++
serialise.h | 3 ++
4 files changed, 159 insertions(+), 1 deletion(-)
diff --git a/conf.c b/conf.c
index e862b023..603ca2ac 100644
--- a/conf.c
+++ b/conf.c
@@ -2304,6 +2304,41 @@ void conf(struct ctx *c, int argc, char **argv)
conf_print(c);
}
+/**
+ * conf_send_pifs() - Send list of pifs to dynamic update client (pesto)
+ * @c: Execution context
+ * @fd: Socket to the client
+ *
+ * Return: 0 on success, -1 on failure
+ */
+static int conf_send_pifs(const struct ctx *c, int fd)
+{
+ uint32_t num = 0;
+ unsigned pif;
+
+ /* First count the number of pifs with tables */
+ for (pif = 0; pif < PIF_NUM_TYPES; pif++) {
+ if (c->fwd[pif])
+ num++;
+ }
+
+ if (sewrite_u32(fd, num))
+ return -1;
+
+ for (pif = 0; pif < PIF_NUM_TYPES; pif++) {
+ if (!c->fwd[pif])
+ continue;
+
+ if (sewrite_u8(fd, pif))
+ return -1;
+
+ if (sewrite_str(fd, pif_name(pif)) < 0)
+ return -1;
+ }
+
+ return 0;
+}
+
/**
* conf_listen_handler() - Handle events on configuration listening socket
* @c: Execution context
@@ -2360,6 +2395,9 @@ void conf_listen_handler(struct ctx *c, uint32_t events)
"Warning: Using experimental unsupported configuration protocol");
}
+ if (conf_send_pifs(c, fd) < 0)
+ goto fail;
+
return;
fail:
diff --git a/pesto.c b/pesto.c
index a8c134f5..dbc27a58 100644
--- a/pesto.c
+++ b/pesto.c
@@ -36,6 +36,8 @@
#include "serialise.h"
#include "pesto.h"
+static int verbosity = 1;
+
#define die(...) \
do { \
FPRINTF(stderr, __VA_ARGS__); \
@@ -51,6 +53,19 @@
} \
} while (0)
+/**
+ * xmalloc() - Allocate memory, with fatal error on failure
+ * @size: Number of bytes to allocate
+ */
+static void *xmalloc(size_t size)
+{
+ void *p = malloc(size);
+
+ if (!p)
+ die("Memory allocation failure");
+ return p;
+}
+
/**
* usage() - Print usage, exit with given status code
* @name: Executable name
@@ -69,6 +84,83 @@ static void usage(const char *name, FILE *f, int status)
exit(status);
}
+/**
+ * pesto_recv_str() - Receive a string from passt/pasta
+ * @fd: Control socket
+ *
+ * Return: pointer to malloc()ed string
+ */
+static const char *pesto_recv_str(int fd)
+{
+ uint32_t len;
+ char *buf;
+
+ if (seread_u32(fd, &len) < 0)
+ die("Error reading from control socket");
+
+ buf = xmalloc(len);
+ if (seread_buf(fd, buf, len) < 0)
+ die("Error reading from control socket");
+
+ return buf;
+}
+
+struct pif_state {
+ uint8_t pif;
+ const char *name;
+};
+
+struct conf_state {
+ uint32_t npifs;
+ struct pif_state pif[];
+};
+
+/**
+ * pesto_read_pifs() - Read pif names and IDs from passt/pasta
+ * @fd: Control socket
+ */
+static const struct conf_state *pesto_read_pifs(int fd)
+{
+ uint32_t num;
+ struct conf_state *state;
+ unsigned i;
+
+ if (seread_u32(fd, &num) < 0)
+ die("Error reading from control socket");
+
+ debug("Receiving %"PRIu32" interface names", num);
+
+ state = xmalloc(sizeof(*state) + num * sizeof(struct pif_state));
+ state->npifs = num;
+
+ for (i = 0; i < num; i++) {
+ struct pif_state *ps = &state->pif[i];
+
+ if (seread_u8(fd, &ps->pif) < 0)
+ die("Error reading from control socket");
+ ps->name = pesto_recv_str(fd);
+
+ debug("%u: %s", ps->pif, ps->name);
+ }
+
+ return state;
+}
+
+/**
+ * show_state() - Show current rule state obtained from passt/pasta
+ * @pifs: PIF name information
+ */
+static void show_state(const struct conf_state *state)
+{
+ unsigned i;
+
+ for (i = 0; i < state->npifs; i++) {
+ const struct pif_state *ps = &state->pif[i];
+ printf("Forwarding rules for %s interface\n", ps->name);
+ printf("\tTBD\n");
+ }
+}
+
/**
* main() - Entry point and whole program with loop
* @argc: Argument count
@@ -91,12 +183,12 @@ int main(int argc, char **argv)
{ 0 },
};
struct sockaddr_un a = { AF_UNIX, "" };
+ const struct conf_state *state;
const char *optstring = "vh";
struct pesto_hello hello;
struct sock_fprog prog;
int optname, ret, s;
uint32_t s_version;
- int verbosity = 1;
prog.len = (unsigned short)sizeof(filter_pesto) /
sizeof(filter_pesto[0]);
@@ -172,5 +264,9 @@ int main(int argc, char **argv)
"Warning: Using experimental protocol version, client and server must match\n");
}
+ state = pesto_read_pifs(s);
+
+ show_state(state);
+
exit(0);
}
diff --git a/serialise.c b/serialise.c
index 4fc0d116..4645c58e 100644
--- a/serialise.c
+++ b/serialise.c
@@ -19,6 +19,7 @@
#include <endian.h>
#include <errno.h>
#include <stdint.h>
+#include <string.h>
#include <unistd.h>
#include "serialise.h"
@@ -121,6 +122,26 @@ int sewrite_buf(int fd, const void *buf, size_t len)
return sewrite_var(fd, &beval); \
}
+#define be8toh(x) (x)
+#define htobe8(x) (x)
+
+SERIALISE_UINT(8)
SERIALISE_UINT(32)
#undef SERIALISE_UNIT
+
+/**
+ * sewrite_str() - Write a string to an fd in length/value format
+ * @fd: Socket to the client
+ * @s: String to send
+ *
+ * Return: 0 on success, -1 on error
+ */
+int sewrite_str(int fd, const char *s)
+{
+ uint32_t len = strlen(s) + 1; /* Include \0 */
+
+ if (sewrite_u32(fd, len) < 0)
+ return -1;
+ return sewrite_buf(fd, s, len);
+}
diff --git a/serialise.h b/serialise.h
index f2e0aa8d..1f0747c7 100644
--- a/serialise.h
+++ b/serialise.h
@@ -19,6 +19,9 @@ int sewrite_buf(int fd, const void *buf, size_t len);
int seread_u##bits(int fd, uint##bits##_t *val); \
int sewrite_u##bits(int fd, uint##bits##_t val);
+SERIALISE_UINT_DECL(8)
SERIALISE_UINT_DECL(32)
+int sewrite_str(int fd, const char *s);
+
#endif /* _SERIALISE_H */
--
2.53.0
^ permalink raw reply [flat|nested] 16+ messages in thread
* [PATCH v2 10/15] ip: Prepare ip.[ch] for sharing with pesto tool
2026-03-19 6:11 [PATCH v2 00/15] RFC: Read-only dynamic update implementation David Gibson
` (8 preceding siblings ...)
2026-03-19 6:11 ` [PATCH v2 09/15] pesto: Expose list of pifs to pesto David Gibson
@ 2026-03-19 6:11 ` David Gibson
2026-03-19 6:11 ` [PATCH v2 11/15] inany: Prepare inany.[ch] " David Gibson
` (4 subsequent siblings)
14 siblings, 0 replies; 16+ messages in thread
From: David Gibson @ 2026-03-19 6:11 UTC (permalink / raw)
To: Stefano Brivio, passt-dev; +Cc: David Gibson
Most things in ip.[ch] related purely to IP addresses and headers with
no dependency on other passt/pasta internals. A number of these will be
useful to re-use in pesto. The exception is ipv6_l4hdr() which uses
iov_tail.
The only caller of this is in tap.c, so move the function there. Along
with moving the constant byteswapping functions to common.h, that lets
ip.[ch] to be linked into pesto as well as passt/pasta.
Signed-off-by: David Gibson <david@gibson.dropbear.id.au>
---
Makefile | 15 +++++++--------
common.h | 50 ++++++++++++++++++++++++++++++++++++++++++++++++++
ip.c | 56 +++-----------------------------------------------------
ip.h | 3 +--
tap.c | 52 ++++++++++++++++++++++++++++++++++++++++++++++++++++
util.h | 48 ------------------------------------------------
6 files changed, 113 insertions(+), 111 deletions(-)
diff --git a/Makefile b/Makefile
index d085c9c1..b9f20bb5 100644
--- a/Makefile
+++ b/Makefile
@@ -44,19 +44,18 @@ PASST_SRCS = arch.c arp.c checksum.c conf.c dhcp.c dhcpv6.c epoll_ctl.c \
udp.c udp_flow.c udp_vu.c util.c vhost_user.c virtio.c vu_common.c
QRAP_SRCS = qrap.c
PASST_REPAIR_SRCS = passt-repair.c
-PESTO_SRCS = pesto.c serialise.c
+PESTO_SRCS = pesto.c ip.c serialise.c
SRCS = $(PASST_SRCS) $(QRAP_SRCS) $(PASST_REPAIR_SRCS) $(PESTO_SRCS)
MANPAGES = passt.1 pasta.1 pesto.1 qrap.1 passt-repair.1
-PESTO_HEADERS = common.h pesto.h serialise.h
+PESTO_HEADERS = common.h ip.h pesto.h serialise.h
PASST_HEADERS = arch.h arp.h checksum.h conf.h dhcp.h dhcpv6.h epoll_ctl.h \
- flow.h fwd.h flow_table.h icmp.h icmp_flow.h inany.h iov.h ip.h \
- isolation.h lineread.h log.h migrate.h ndp.h netlink.h packet.h \
- passt.h pasta.h pcap.h pif.h repair.h siphash.h tap.h tcp.h tcp_buf.h \
- tcp_conn.h tcp_internal.h tcp_splice.h tcp_vu.h udp.h udp_flow.h \
- udp_internal.h udp_vu.h util.h vhost_user.h virtio.h vu_common.h \
- $(PESTO_HEADERS)
+ flow.h fwd.h flow_table.h icmp.h icmp_flow.h inany.h iov.h isolation.h \
+ lineread.h log.h migrate.h ndp.h netlink.h packet.h passt.h pasta.h \
+ pcap.h pif.h repair.h siphash.h tap.h tcp.h tcp_buf.h tcp_conn.h \
+ tcp_internal.h tcp_splice.h tcp_vu.h udp.h udp_flow.h udp_internal.h \
+ udp_vu.h util.h vhost_user.h virtio.h vu_common.h $(PESTO_HEADERS)
HEADERS = $(PASST_HEADERS) seccomp.h
C := \#include <sys/random.h>\nint main(){int a=getrandom(0, 0, 0);}
diff --git a/common.h b/common.h
index 927d20a4..7d6ba5c1 100644
--- a/common.h
+++ b/common.h
@@ -19,4 +19,54 @@
/* FPRINTF() intentionally silences cert-err33-c clang-tidy warnings */
#define FPRINTF(f, ...) (void)fprintf(f, __VA_ARGS__)
+#define BIT(n) (1UL << (n))
+
+#ifndef __bswap_constant_16
+#define __bswap_constant_16(x) \
+ ((uint16_t) ((((x) >> 8) & 0xff) | (((x) & 0xff) << 8)))
+#endif
+
+#ifndef __bswap_constant_32
+#define __bswap_constant_32(x) \
+ ((((x) & 0xff000000) >> 24) | (((x) & 0x00ff0000) >> 8) | \
+ (((x) & 0x0000ff00) << 8) | (((x) & 0x000000ff) << 24))
+#endif
+
+#ifndef __bswap_constant_32
+#define __bswap_constant_32(x) \
+ ((((x) & 0xff000000) >> 24) | (((x) & 0x00ff0000) >> 8) | \
+ (((x) & 0x0000ff00) << 8) | (((x) & 0x000000ff) << 24))
+#endif
+
+#ifndef __bswap_constant_64
+#define __bswap_constant_64(x) \
+ ((((x) & 0xff00000000000000ULL) >> 56) | \
+ (((x) & 0x00ff000000000000ULL) >> 40) | \
+ (((x) & 0x0000ff0000000000ULL) >> 24) | \
+ (((x) & 0x000000ff00000000ULL) >> 8) | \
+ (((x) & 0x00000000ff000000ULL) << 8) | \
+ (((x) & 0x0000000000ff0000ULL) << 24) | \
+ (((x) & 0x000000000000ff00ULL) << 40) | \
+ (((x) & 0x00000000000000ffULL) << 56))
+#endif
+
+#if __BYTE_ORDER == __BIG_ENDIAN
+#define htons_constant(x) (x)
+#define htonl_constant(x) (x)
+#define htonll_constant(x) (x)
+#define ntohs_constant(x) (x)
+#define ntohl_constant(x) (x)
+#define ntohll_constant(x) (x)
+#else
+#define htons_constant(x) (__bswap_constant_16(x))
+#define htonl_constant(x) (__bswap_constant_32(x))
+#define htonll_constant(x) (__bswap_constant_64(x))
+#define ntohs_constant(x) (__bswap_constant_16(x))
+#define ntohl_constant(x) (__bswap_constant_32(x))
+#define ntohll_constant(x) (__bswap_constant_64(x))
+#endif
+
+#define ntohll(x) (be64toh((x)))
+#define htonll(x) (htobe64((x)))
+
#endif /* _COMMON_H */
diff --git a/ip.c b/ip.c
index 0ea62998..4e4e0bf5 100644
--- a/ip.c
+++ b/ip.c
@@ -6,6 +6,9 @@
* PASTA - Pack A Subtle Tap Abstraction
* for network namespace/tap device mode
*
+ * PESTO - Programmable Extensible Socket Translation Orchestrator
+ * front-end for passt(1) and pasta(1) forwarding configuration
+ *
* ip.c - IP related functions
*
* Copyright (c) 2020-2021 Red Hat GmbH
@@ -15,61 +18,8 @@
#include <stddef.h>
#include <netinet/in.h>
-#include "util.h"
#include "ip.h"
-#define IPV6_NH_OPT(nh) \
- ((nh) == 0 || (nh) == 43 || (nh) == 44 || (nh) == 50 || \
- (nh) == 51 || (nh) == 60 || (nh) == 135 || (nh) == 139 || \
- (nh) == 140 || (nh) == 253 || (nh) == 254)
-
-/**
- * ipv6_l4hdr() - Find pointer to L4 header in IPv6 packet and extract protocol
- * @data: IPv6 packet
- * @proto: Filled with L4 protocol number
- * @dlen: Data length (payload excluding header extensions), set on return
- *
- * Return: true if the L4 header is found and @data, @proto, @dlen are set,
- * false on error. Outputs are indeterminate on failure.
- */
-bool ipv6_l4hdr(struct iov_tail *data, uint8_t *proto, size_t *dlen)
-{
- struct ipv6_opt_hdr o_storage;
- const struct ipv6_opt_hdr *o;
- struct ipv6hdr ip6h_storage;
- const struct ipv6hdr *ip6h;
- int hdrlen;
- uint8_t nh;
-
- ip6h = IOV_REMOVE_HEADER(data, ip6h_storage);
- if (!ip6h)
- return false;
-
- nh = ip6h->nexthdr;
- if (!IPV6_NH_OPT(nh))
- goto found;
-
- while ((o = IOV_PEEK_HEADER(data, o_storage))) {
- nh = o->nexthdr;
- hdrlen = (o->hdrlen + 1) * 8;
-
- if (IPV6_NH_OPT(nh))
- iov_drop_header(data, hdrlen);
- else
- goto found;
- }
-
- return false;
-
-found:
- if (nh == IPPROTO_NONE)
- return false;
-
- *dlen = iov_tail_size(data);
- *proto = nh;
- return true;
-}
-
/**
* ipproto_name() - Get IP protocol name from number
* @proto: IP protocol number
diff --git a/ip.h b/ip.h
index d0de6c8d..f6c29e00 100644
--- a/ip.h
+++ b/ip.h
@@ -9,7 +9,7 @@
#include <netinet/ip.h>
#include <netinet/ip6.h>
-#include "util.h"
+#include "common.h"
#define IN4_IS_ADDR_UNSPECIFIED(a) \
(((struct in_addr *)(a))->s_addr == htonl_constant(INADDR_ANY))
@@ -117,7 +117,6 @@ static inline uint32_t ip6_get_flow_lbl(const struct ipv6hdr *ip6h)
ip6h->flow_lbl[2];
}
-bool ipv6_l4hdr(struct iov_tail *data, uint8_t *proto, size_t *dlen);
const char *ipproto_name(uint8_t proto);
/* IPv6 link-local all-nodes multicast address, ff02::1 */
diff --git a/tap.c b/tap.c
index 1049e023..185ab471 100644
--- a/tap.c
+++ b/tap.c
@@ -881,6 +881,58 @@ append:
return in->count;
}
+#define IPV6_NH_OPT(nh) \
+ ((nh) == 0 || (nh) == 43 || (nh) == 44 || (nh) == 50 || \
+ (nh) == 51 || (nh) == 60 || (nh) == 135 || (nh) == 139 || \
+ (nh) == 140 || (nh) == 253 || (nh) == 254)
+
+/**
+ * ipv6_l4hdr() - Find pointer to L4 header in IPv6 packet and extract protocol
+ * @data: IPv6 packet
+ * @proto: Filled with L4 protocol number
+ * @dlen: Data length (payload excluding header extensions), set on return
+ *
+ * Return: true if the L4 header is found and @data, @proto, @dlen are set,
+ * false on error. Outputs are indeterminate on failure.
+ */
+static bool ipv6_l4hdr(struct iov_tail *data, uint8_t *proto, size_t *dlen)
+{
+ struct ipv6_opt_hdr o_storage;
+ const struct ipv6_opt_hdr *o;
+ struct ipv6hdr ip6h_storage;
+ const struct ipv6hdr *ip6h;
+ int hdrlen;
+ uint8_t nh;
+
+ ip6h = IOV_REMOVE_HEADER(data, ip6h_storage);
+ if (!ip6h)
+ return false;
+
+ nh = ip6h->nexthdr;
+ if (!IPV6_NH_OPT(nh))
+ goto found;
+
+ while ((o = IOV_PEEK_HEADER(data, o_storage))) {
+ nh = o->nexthdr;
+ hdrlen = (o->hdrlen + 1) * 8;
+
+ if (IPV6_NH_OPT(nh))
+ iov_drop_header(data, hdrlen);
+ else
+ goto found;
+ }
+
+ return false;
+
+found:
+ if (nh == IPPROTO_NONE)
+ return false;
+
+ *dlen = iov_tail_size(data);
+ *proto = nh;
+ return true;
+}
+
/**
* tap6_handler() - IPv6 packet handler for tap file descriptor
* @c: Execution context
diff --git a/util.h b/util.h
index d7c397f6..5357814c 100644
--- a/util.h
+++ b/util.h
@@ -106,54 +106,6 @@ void abort_with_msg(const char *fmt, ...)
#define MAC_UNDEF MAC_BROADCAST
#define MAC_IS_UNDEF(addr) (!memcmp((addr), MAC_UNDEF, ETH_ALEN))
-#ifndef __bswap_constant_16
-#define __bswap_constant_16(x) \
- ((uint16_t) ((((x) >> 8) & 0xff) | (((x) & 0xff) << 8)))
-#endif
-
-#ifndef __bswap_constant_32
-#define __bswap_constant_32(x) \
- ((((x) & 0xff000000) >> 24) | (((x) & 0x00ff0000) >> 8) | \
- (((x) & 0x0000ff00) << 8) | (((x) & 0x000000ff) << 24))
-#endif
-
-#ifndef __bswap_constant_32
-#define __bswap_constant_32(x) \
- ((((x) & 0xff000000) >> 24) | (((x) & 0x00ff0000) >> 8) | \
- (((x) & 0x0000ff00) << 8) | (((x) & 0x000000ff) << 24))
-#endif
-
-#ifndef __bswap_constant_64
-#define __bswap_constant_64(x) \
- ((((x) & 0xff00000000000000ULL) >> 56) | \
- (((x) & 0x00ff000000000000ULL) >> 40) | \
- (((x) & 0x0000ff0000000000ULL) >> 24) | \
- (((x) & 0x000000ff00000000ULL) >> 8) | \
- (((x) & 0x00000000ff000000ULL) << 8) | \
- (((x) & 0x0000000000ff0000ULL) << 24) | \
- (((x) & 0x000000000000ff00ULL) << 40) | \
- (((x) & 0x00000000000000ffULL) << 56))
-#endif
-
-#if __BYTE_ORDER == __BIG_ENDIAN
-#define htons_constant(x) (x)
-#define htonl_constant(x) (x)
-#define htonll_constant(x) (x)
-#define ntohs_constant(x) (x)
-#define ntohl_constant(x) (x)
-#define ntohll_constant(x) (x)
-#else
-#define htons_constant(x) (__bswap_constant_16(x))
-#define htonl_constant(x) (__bswap_constant_32(x))
-#define htonll_constant(x) (__bswap_constant_64(x))
-#define ntohs_constant(x) (__bswap_constant_16(x))
-#define ntohl_constant(x) (__bswap_constant_32(x))
-#define ntohll_constant(x) (__bswap_constant_64(x))
-#endif
-
-#define ntohll(x) (be64toh((x)))
-#define htonll(x) (htobe64((x)))
-
extern uint8_t eth_pad[ETH_ZLEN];
/**
--
2.53.0
^ permalink raw reply [flat|nested] 16+ messages in thread
* [PATCH v2 11/15] inany: Prepare inany.[ch] for sharing with pesto tool
2026-03-19 6:11 [PATCH v2 00/15] RFC: Read-only dynamic update implementation David Gibson
` (9 preceding siblings ...)
2026-03-19 6:11 ` [PATCH v2 10/15] ip: Prepare ip.[ch] for sharing with pesto tool David Gibson
@ 2026-03-19 6:11 ` David Gibson
2026-03-19 6:11 ` [PATCH v2 12/15] fwd: Split forwading rule specification from its implementation state David Gibson
` (3 subsequent siblings)
14 siblings, 0 replies; 16+ messages in thread
From: David Gibson @ 2026-03-19 6:11 UTC (permalink / raw)
To: Stefano Brivio, passt-dev; +Cc: David Gibson
inany contains a number of helpful functions for dealing with addresses
which might be IPv4 or IPv6. We're going to want to use that in pesto.
For the most part inany doesn't depend on other passt/pasta internals,
however it does depend on siphash.h, which pesto doesn't need.
Move the single dependent function, inany_siphash_feed() to siphash.h,
renaming to match. Use that include inany.[ch] into pesto as well as
passt/pasta. While we're there reformat pesto.c's header comment to match
the convention used in most other files.
Signed-off-by: David Gibson <david@gibson.dropbear.id.au>
---
Makefile | 6 +++---
common.h | 7 +++++++
flow.c | 4 ++--
fwd.c | 2 +-
inany.c | 16 +++++++++++++---
inany.h | 16 ++--------------
iov.c | 1 +
siphash.h | 13 +++++++++++++
util.h | 7 -------
virtio.c | 1 +
10 files changed, 43 insertions(+), 30 deletions(-)
diff --git a/Makefile b/Makefile
index b9f20bb5..92809813 100644
--- a/Makefile
+++ b/Makefile
@@ -44,14 +44,14 @@ PASST_SRCS = arch.c arp.c checksum.c conf.c dhcp.c dhcpv6.c epoll_ctl.c \
udp.c udp_flow.c udp_vu.c util.c vhost_user.c virtio.c vu_common.c
QRAP_SRCS = qrap.c
PASST_REPAIR_SRCS = passt-repair.c
-PESTO_SRCS = pesto.c ip.c serialise.c
+PESTO_SRCS = pesto.c inany.c ip.c serialise.c
SRCS = $(PASST_SRCS) $(QRAP_SRCS) $(PASST_REPAIR_SRCS) $(PESTO_SRCS)
MANPAGES = passt.1 pasta.1 pesto.1 qrap.1 passt-repair.1
-PESTO_HEADERS = common.h ip.h pesto.h serialise.h
+PESTO_HEADERS = common.h inany.h ip.h pesto.h serialise.h
PASST_HEADERS = arch.h arp.h checksum.h conf.h dhcp.h dhcpv6.h epoll_ctl.h \
- flow.h fwd.h flow_table.h icmp.h icmp_flow.h inany.h iov.h isolation.h \
+ flow.h fwd.h flow_table.h icmp.h icmp_flow.h iov.h isolation.h \
lineread.h log.h migrate.h ndp.h netlink.h packet.h passt.h pasta.h \
pcap.h pif.h repair.h siphash.h tap.h tcp.h tcp_buf.h tcp_conn.h \
tcp_internal.h tcp_splice.h tcp_vu.h udp.h udp_flow.h udp_internal.h \
diff --git a/common.h b/common.h
index 7d6ba5c1..7d37b926 100644
--- a/common.h
+++ b/common.h
@@ -19,6 +19,13 @@
/* FPRINTF() intentionally silences cert-err33-c clang-tidy warnings */
#define FPRINTF(f, ...) (void)fprintf(f, __VA_ARGS__)
+#ifndef MIN
+#define MIN(x, y) (((x) < (y)) ? (x) : (y))
+#endif
+#ifndef MAX
+#define MAX(x, y) (((x) > (y)) ? (x) : (y))
+#endif
+
#define BIT(n) (1UL << (n))
#ifndef __bswap_constant_16
diff --git a/flow.c b/flow.c
index 11cd5752..607d0a41 100644
--- a/flow.c
+++ b/flow.c
@@ -678,8 +678,8 @@ static uint64_t flow_hash(const struct ctx *c, uint8_t proto, uint8_t pif,
{
struct siphash_state state = SIPHASH_INIT(c->hash_secret);
- inany_siphash_feed(&state, &side->oaddr);
- inany_siphash_feed(&state, &side->eaddr);
+ siphash_feed_inany(&state, &side->oaddr);
+ siphash_feed_inany(&state, &side->eaddr);
return siphash_final(&state, 38, (uint64_t)proto << 40 |
(uint64_t)pif << 32 |
diff --git a/fwd.c b/fwd.c
index 3395a28e..192969b7 100644
--- a/fwd.c
+++ b/fwd.c
@@ -86,7 +86,7 @@ static size_t neigh_table_slot(const struct ctx *c,
struct siphash_state st = SIPHASH_INIT(c->hash_secret);
uint32_t i;
- inany_siphash_feed(&st, key);
+ siphash_feed_inany(&st, key);
i = siphash_final(&st, sizeof(*key), 0);
return ((size_t)i) & (NEIGH_TABLE_SIZE - 1);
diff --git a/inany.c b/inany.c
index 2a586ed1..426d2a64 100644
--- a/inany.c
+++ b/inany.c
@@ -1,9 +1,19 @@
-/* SPDX-License-Identifier: GPL-2.0-or-later
- * Copyright Red Hat
- * Author: David Gibson <david@gibson.dropbear.id.au>
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+/* PASST - Plug A Simple Socket Transport
+ * for qemu/UNIX domain socket mode
+ *
+ * PASTA - Pack A Subtle Tap Abstraction
+ * for network namespace/tap device mode
+ *
+ * PESTO - Programmable Extensible Socket Translation Orchestrator
+ * front-end for passt(1) and pasta(1) forwarding configuration
*
* inany.c - Types and helpers for handling addresses which could be
* IPv6 or IPv4 (encoded as IPv4-mapped IPv6 addresses)
+ *
+ * Copyright Red Hat
+ * Author: David Gibson <david@gibson.dropbear.id.au>
*/
#include <stdlib.h>
diff --git a/inany.h b/inany.h
index 30e24164..6a1ff3c0 100644
--- a/inany.h
+++ b/inany.h
@@ -10,12 +10,11 @@
#define INANY_H
#include <assert.h>
+#include <stdbool.h>
+#include <stddef.h>
#include <string.h>
#include "ip.h"
-#include "siphash.h"
-
-struct siphash_state;
/** union inany_addr - Represents either an IPv4 or IPv6 address
* @a6: Address as an IPv6 address, may be IPv4-mapped
@@ -299,17 +298,6 @@ static inline int inany_from_sockaddr(union inany_addr *dst, in_port_t *port,
return -1;
}
-/** inany_siphash_feed- Fold IPv[46] address into an in-progress siphash
- * @state: siphash state
- * @aa: inany to hash
- */
-static inline void inany_siphash_feed(struct siphash_state *state,
- const union inany_addr *aa)
-{
- siphash_feed(state, (uint64_t)aa->u32[0] << 32 | aa->u32[1]);
- siphash_feed(state, (uint64_t)aa->u32[2] << 32 | aa->u32[3]);
-}
-
#define INANY_ADDRSTRLEN MAX(INET_ADDRSTRLEN, INET6_ADDRSTRLEN)
bool inany_matches(const union inany_addr *a, const union inany_addr *b);
diff --git a/iov.c b/iov.c
index ae074393..d291ee51 100644
--- a/iov.c
+++ b/iov.c
@@ -24,6 +24,7 @@
#include <assert.h>
#include <sys/socket.h>
+#include "common.h"
#include "util.h"
#include "iov.h"
diff --git a/siphash.h b/siphash.h
index bbddcac0..313b8947 100644
--- a/siphash.h
+++ b/siphash.h
@@ -47,6 +47,8 @@
#include <stddef.h>
#include <stdint.h>
+#include "inany.h"
+
/**
* struct siphash_state - Internal state of siphash calculation
*/
@@ -101,6 +103,17 @@ static inline void siphash_feed(struct siphash_state *state, uint64_t in)
state->v[0] ^= in;
}
+/** siphash_feed_inany() - Fold IPv[46] address into an in-progress siphash
+ * @state: siphash state
+ * @aa: inany to hash
+ */
+static inline void siphash_feed_inany(struct siphash_state *state,
+ const union inany_addr *aa)
+{
+ siphash_feed(state, (uint64_t)aa->u32[0] << 32 | aa->u32[1]);
+ siphash_feed(state, (uint64_t)aa->u32[2] << 32 | aa->u32[3]);
+}
+
/**
* siphash_final() - Finalize SipHash calculations
* @v: siphash state (4 x 64-bit integers)
diff --git a/util.h b/util.h
index 5357814c..45792f46 100644
--- a/util.h
+++ b/util.h
@@ -28,13 +28,6 @@
#define IP_MAX_MTU USHRT_MAX
#endif
-#ifndef MIN
-#define MIN(x, y) (((x) < (y)) ? (x) : (y))
-#endif
-#ifndef MAX
-#define MAX(x, y) (((x) > (y)) ? (x) : (y))
-#endif
-
#define DIV_ROUND_UP(n, d) (((n) + (d) - 1) / (d))
#define DIV_ROUND_CLOSEST(n, d) (((n) + (d) / 2) / (d))
#define ROUND_DOWN(x, y) ((x) & ~((y) - 1))
diff --git a/virtio.c b/virtio.c
index f71dea1d..72b78af1 100644
--- a/virtio.c
+++ b/virtio.c
@@ -80,6 +80,7 @@
#include <sys/eventfd.h>
#include <sys/socket.h>
+#include "common.h"
#include "util.h"
#include "virtio.h"
#include "vhost_user.h"
--
2.53.0
^ permalink raw reply [flat|nested] 16+ messages in thread
* [PATCH v2 12/15] fwd: Split forwading rule specification from its implementation state
2026-03-19 6:11 [PATCH v2 00/15] RFC: Read-only dynamic update implementation David Gibson
` (10 preceding siblings ...)
2026-03-19 6:11 ` [PATCH v2 11/15] inany: Prepare inany.[ch] " David Gibson
@ 2026-03-19 6:11 ` David Gibson
2026-03-19 6:11 ` [PATCH v2 13/15] ip: Define a bound for the string returned by ipproto_name() David Gibson
` (2 subsequent siblings)
14 siblings, 0 replies; 16+ messages in thread
From: David Gibson @ 2026-03-19 6:11 UTC (permalink / raw)
To: Stefano Brivio, passt-dev; +Cc: David Gibson
Most of the fields in struct fwd_rule give the parameters of the forwarding
rule itself. The @socks field gives the list of listening sockets we use
to implement it. In order to share code with the configuraiton update tool
pesto, we want to split these. Move the rule specification itself into
fwd_rule.h, which can be shared with pesto.
Signed-off-by: David Gibson <david@gibson.dropbear.id.au>
---
Makefile | 2 +-
fwd.c | 77 ++++++++++++++++++++++++++++--------------------------
fwd.h | 31 +++++-----------------
fwd_rule.h | 45 +++++++++++++++++++++++++++++++
util.h | 1 -
5 files changed, 92 insertions(+), 64 deletions(-)
create mode 100644 fwd_rule.h
diff --git a/Makefile b/Makefile
index 92809813..bc325482 100644
--- a/Makefile
+++ b/Makefile
@@ -49,7 +49,7 @@ SRCS = $(PASST_SRCS) $(QRAP_SRCS) $(PASST_REPAIR_SRCS) $(PESTO_SRCS)
MANPAGES = passt.1 pasta.1 pesto.1 qrap.1 passt-repair.1
-PESTO_HEADERS = common.h inany.h ip.h pesto.h serialise.h
+PESTO_HEADERS = common.h fwd_rule.h inany.h ip.h pesto.h serialise.h
PASST_HEADERS = arch.h arp.h checksum.h conf.h dhcp.h dhcpv6.h epoll_ctl.h \
flow.h fwd.h flow_table.h icmp.h icmp_flow.h iov.h isolation.h \
lineread.h log.h migrate.h ndp.h netlink.h packet.h passt.h pasta.h \
diff --git a/fwd.c b/fwd.c
index 192969b7..f157eeb6 100644
--- a/fwd.c
+++ b/fwd.c
@@ -364,7 +364,7 @@ void fwd_rule_add(struct fwd_table *fwd, uint8_t proto, uint8_t flags,
/* Flags which can be set from the caller */
const uint8_t allowed_flags = FWD_WEAK | FWD_SCAN;
unsigned num = (unsigned)last - first + 1;
- struct fwd_rule *new;
+ struct fwd_rule_state *new;
unsigned i, port;
assert(!(flags & ~allowed_flags));
@@ -377,57 +377,59 @@ void fwd_rule_add(struct fwd_table *fwd, uint8_t proto, uint8_t flags,
/* Check for any conflicting entries */
for (i = 0; i < fwd->count; i++) {
char newstr[INANY_ADDRSTRLEN], rulestr[INANY_ADDRSTRLEN];
- struct fwd_rule *rule = &fwd->rules[i];
+ struct fwd_rule_state *rule = &fwd->rules[i];
- if (proto != rule->proto)
+ if (proto != rule->rule.proto)
/* Non-conflicting protocols */
continue;
- if (!inany_matches(addr, fwd_rule_addr(rule)))
+ if (!inany_matches(addr, fwd_rule_addr(&rule->rule)))
/* Non-conflicting addresses */
continue;
- if (last < rule->first || rule->last < first)
+ if (last < rule->rule.first || rule->rule.last < first)
/* Port ranges don't overlap */
continue;
die("Forwarding configuration conflict: %s/%u-%u versus %s/%u-%u",
inany_ntop(addr, newstr, sizeof(newstr)), first, last,
- inany_ntop(fwd_rule_addr(rule), rulestr, sizeof(rulestr)),
- rule->first, rule->last);
+ inany_ntop(fwd_rule_addr(&rule->rule),
+ rulestr, sizeof(rulestr)),
+ rule->rule.first, rule->rule.last);
}
new = &fwd->rules[fwd->count++];
- new->proto = proto;
- new->flags = flags;
+ new->rule.proto = proto;
+ new->rule.flags = flags;
if (addr) {
- new->addr = *addr;
+ new->rule.addr = *addr;
} else {
- new->addr = inany_any6;
- new->flags |= FWD_DUAL_STACK_ANY;
+ new->rule.addr = inany_any6;
+ new->rule.flags |= FWD_DUAL_STACK_ANY;
}
- memset(new->ifname, 0, sizeof(new->ifname));
+ memset(new->rule.ifname, 0, sizeof(new->rule.ifname));
if (ifname) {
int ret;
- ret = snprintf(new->ifname, sizeof(new->ifname), "%s", ifname);
- if (ret <= 0 || (size_t)ret >= sizeof(new->ifname))
+ ret = snprintf(new->rule.ifname, sizeof(new->rule.ifname),
+ "%s", ifname);
+ if (ret <= 0 || (size_t)ret >= sizeof(new->rule.ifname))
die("Invalid interface name: %s", ifname);
}
assert(first <= last);
- new->first = first;
- new->last = last;
+ new->rule.first = first;
+ new->rule.last = last;
- new->to = to;
+ new->rule.to = to;
new->socks = &fwd->socks[fwd->sock_count];
fwd->sock_count += num;
- for (port = new->first; port <= new->last; port++)
- new->socks[port - new->first] = -1;
+ for (port = new->rule.first; port <= new->rule.last; port++)
+ new->socks[port - new->rule.first] = -1;
}
/**
@@ -463,7 +465,7 @@ const struct fwd_rule *fwd_rule_search(const struct fwd_table *fwd,
if (hint >= 0) {
char ostr[INANY_ADDRSTRLEN], rstr[INANY_ADDRSTRLEN];
- const struct fwd_rule *rule = &fwd->rules[hint];
+ const struct fwd_rule *rule = &fwd->rules[hint].rule;
assert((unsigned)hint < fwd->count);
if (fwd_rule_match(rule, ini, proto))
@@ -477,8 +479,8 @@ const struct fwd_rule *fwd_rule_search(const struct fwd_table *fwd,
}
for (i = 0; i < fwd->count; i++) {
- if (fwd_rule_match(&fwd->rules[i], ini, proto))
- return &fwd->rules[i];
+ if (fwd_rule_match(&fwd->rules[i].rule, ini, proto))
+ return &fwd->rules[i].rule;
}
return NULL;
@@ -493,7 +495,7 @@ void fwd_rules_print(const struct fwd_table *fwd)
unsigned i;
for (i = 0; i < fwd->count; i++) {
- const struct fwd_rule *rule = &fwd->rules[i];
+ const struct fwd_rule *rule = &fwd->rules[i].rule;
const char *percent = *rule->ifname ? "%" : "";
const char *weak = "", *scan = "";
char addr[INANY_ADDRSTRLEN];
@@ -530,7 +532,8 @@ void fwd_rules_print(const struct fwd_table *fwd)
static int fwd_sync_one(const struct ctx *c, uint8_t pif, unsigned idx,
const uint8_t *tcp, const uint8_t *udp)
{
- const struct fwd_rule *rule = &c->fwd[pif]->rules[idx];
+ const struct fwd_rule_state *rs = &c->fwd[pif]->rules[idx];
+ const struct fwd_rule *rule = &rs->rule;
const union inany_addr *addr = fwd_rule_addr(rule);
const char *ifname = rule->ifname;
const uint8_t *map = NULL;
@@ -551,7 +554,7 @@ static int fwd_sync_one(const struct ctx *c, uint8_t pif, unsigned idx,
}
for (port = rule->first; port <= rule->last; port++) {
- int fd = rule->socks[port - rule->first];
+ int fd = rs->socks[port - rule->first];
if (map && !bitmap_isset(map, port)) {
/* We don't want to listen on this port */
@@ -559,7 +562,7 @@ static int fwd_sync_one(const struct ctx *c, uint8_t pif, unsigned idx,
/* We already are, so stop */
epoll_del(c->epollfd, fd);
close(fd);
- rule->socks[port - rule->first] = -1;
+ rs->socks[port - rule->first] = -1;
}
continue;
}
@@ -591,7 +594,7 @@ static int fwd_sync_one(const struct ctx *c, uint8_t pif, unsigned idx,
continue;
}
- rule->socks[port - rule->first] = fd;
+ rs->socks[port - rule->first] = fd;
bound_one = true;
}
@@ -681,11 +684,11 @@ void fwd_listen_close(const struct fwd_table *fwd)
unsigned i;
for (i = 0; i < fwd->count; i++) {
- const struct fwd_rule *rule = &fwd->rules[i];
+ const struct fwd_rule_state *rs = &fwd->rules[i];
unsigned port;
- for (port = rule->first; port <= rule->last; port++) {
- int *fdp = &rule->socks[port - rule->first];
+ for (port = rs->rule.first; port <= rs->rule.last; port++) {
+ int *fdp = &rs->socks[port - rs->rule.first];
if (*fdp >= 0) {
close(*fdp);
*fdp = -1;
@@ -765,8 +768,8 @@ static bool has_scan_rules(const struct fwd_table *fwd, uint8_t proto)
unsigned i;
for (i = 0; i < fwd->count; i++) {
- if (fwd->rules[i].proto == proto &&
- fwd->rules[i].flags & FWD_SCAN)
+ if (fwd->rules[i].rule.proto == proto &&
+ fwd->rules[i].rule.flags & FWD_SCAN)
return true;
}
return false;
@@ -834,14 +837,14 @@ static void current_listen_map(uint8_t *map, const struct fwd_table *fwd,
memset(map, 0, PORT_BITMAP_SIZE);
for (i = 0; i < fwd->count; i++) {
- const struct fwd_rule *rule = &fwd->rules[i];
+ const struct fwd_rule_state *rs = &fwd->rules[i];
unsigned port;
- if (rule->proto != proto)
+ if (rs->rule.proto != proto)
continue;
- for (port = rule->first; port <= rule->last; port++) {
- if (rule->socks[port - rule->first] >= 0)
+ for (port = rs->rule.first; port <= rs->rule.last; port++) {
+ if (rs->socks[port - rs->rule.first] >= 0)
bitmap_set(map, port);
}
}
diff --git a/fwd.h b/fwd.h
index b387d926..00f96860 100644
--- a/fwd.h
+++ b/fwd.h
@@ -15,6 +15,7 @@
#include <netinet/in.h>
#include "inany.h"
+#include "fwd_rule.h"
struct flowside;
@@ -25,32 +26,12 @@ void fwd_probe_ephemeral(void);
bool fwd_port_is_ephemeral(in_port_t port);
/**
- * struct fwd_rule - Forwarding rule governing a range of ports
- * @addr: Address to forward from
- * @ifname: Interface to forward from
- * @first: First port number to forward
- * @last: Last port number to forward
- * @to: Target port for @first, port n goes to @to + (n - @first)
- * @proto: Protocol to forward
- * @flags: Flag mask
- * FWD_DUAL_STACK_ANY - match any IPv4 or IPv6 address (@addr should be ::)
- * FWD_WEAK - Don't give an error if binds fail for some forwards
- * FWD_SCAN - Only forward if the matching port in the target is listening
+ * struct fwd_rule_state - Forwarding rule and associated state
+ * @rule: Rule specification
* @socks: Array of listening sockets for this entry
- *
- * FIXME: @addr and @ifname currently ignored for outbound tables
*/
-struct fwd_rule {
- union inany_addr addr;
- char ifname[IFNAMSIZ];
- in_port_t first;
- in_port_t last;
- in_port_t to;
- uint8_t proto;
-#define FWD_DUAL_STACK_ANY BIT(0)
-#define FWD_WEAK BIT(1)
-#define FWD_SCAN BIT(2)
- uint8_t flags;
+struct fwd_rule_state {
+ struct fwd_rule rule;
int *socks;
};
@@ -87,7 +68,7 @@ struct fwd_listen_ref {
*/
struct fwd_table {
unsigned count;
- struct fwd_rule rules[MAX_FWD_RULES];
+ struct fwd_rule_state rules[MAX_FWD_RULES];
unsigned sock_count;
int socks[MAX_LISTEN_SOCKS];
};
diff --git a/fwd_rule.h b/fwd_rule.h
new file mode 100644
index 00000000..84ec5cbe
--- /dev/null
+++ b/fwd_rule.h
@@ -0,0 +1,45 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later
+ * Copyright Red Hat
+ * Author: Stefano Brivio <sbrivio@redhat.com>
+ * Author: David Gibson <david@gibson.dropbear.id.au>
+ *
+ * Forwarding rule definitions shared between passt/pasta and pesto
+ */
+
+#ifndef FWD_RULE_H
+#define FWD_RULE_H
+
+#include <stdint.h>
+#include <net/if.h>
+#include <netinet/in.h>
+
+#include "common.h"
+#include "inany.h"
+
+/**
+ * struct fwd_rule - Forwarding rule governing a range of ports
+ * @addr: Address to forward from
+ * @ifname: Interface to forward from
+ * @first: First port number to forward
+ * @last: Last port number to forward
+ * @to: Target port for @first, port n goes to @to + (n - @first)
+ * @proto: Protocol to forward
+ * @flags: Flag mask
+ * FWD_DUAL_STACK_ANY - match any IPv4 or IPv6 address (@addr should be ::)
+ * FWD_WEAK - Don't give an error if binds fail for some forwards
+ * FWD_SCAN - Only forward if the matching port in the target is listening
+ */
+struct fwd_rule {
+ union inany_addr addr;
+ char ifname[IFNAMSIZ];
+ in_port_t first;
+ in_port_t last;
+ in_port_t to;
+ uint8_t proto;
+#define FWD_DUAL_STACK_ANY BIT(0)
+#define FWD_WEAK BIT(1)
+#define FWD_SCAN BIT(2)
+ uint8_t flags;
+};
+
+#endif /* FWD_RULE_H */
diff --git a/util.h b/util.h
index 45792f46..8495ed9e 100644
--- a/util.h
+++ b/util.h
@@ -35,7 +35,6 @@
#define MAX_FROM_BITS(n) (((1U << (n)) - 1))
-#define BIT(n) (1UL << (n))
#define BITMAP_BIT(n) (BIT((n) % (sizeof(long) * 8)))
#define BITMAP_WORD(n) (n / (sizeof(long) * 8))
--
2.53.0
^ permalink raw reply [flat|nested] 16+ messages in thread
* [PATCH v2 13/15] ip: Define a bound for the string returned by ipproto_name()
2026-03-19 6:11 [PATCH v2 00/15] RFC: Read-only dynamic update implementation David Gibson
` (11 preceding siblings ...)
2026-03-19 6:11 ` [PATCH v2 12/15] fwd: Split forwading rule specification from its implementation state David Gibson
@ 2026-03-19 6:11 ` David Gibson
2026-03-19 6:11 ` [PATCH v2 14/15] fwd_rule: Move forwarding rule text formatting to common code David Gibson
2026-03-19 6:11 ` [PATCH v2 15/15] pesto: Read current ruleset from passt/pasta and display it David Gibson
14 siblings, 0 replies; 16+ messages in thread
From: David Gibson @ 2026-03-19 6:11 UTC (permalink / raw)
To: Stefano Brivio, passt-dev; +Cc: David Gibson
ipproto_name() returns a static string of theoretically unbounded length.
That's going to be inconvenient in future, so introduce IPPROTO_STRLEN
giving an explicit bound on the length. Use static_assert() and some
macros to ensure nothing we return can exceed this.
Signed-off-by: David Gibson <david@gibson.dropbear.id.au>
---
ip.c | 18 ++++++++++++------
ip.h | 1 +
2 files changed, 13 insertions(+), 6 deletions(-)
diff --git a/ip.c b/ip.c
index 4e4e0bf5..f2506bb1 100644
--- a/ip.c
+++ b/ip.c
@@ -15,6 +15,7 @@
* Author: Stefano Brivio <sbrivio@redhat.com>
*/
+#include <assert.h>
#include <stddef.h>
#include <netinet/in.h>
@@ -24,7 +25,7 @@
* ipproto_name() - Get IP protocol name from number
* @proto: IP protocol number
*
- * Return: pointer to name of protocol @proto
+ * Return: pointer to name of protocol @proto (<= IPPROTO_STRLEN bytes)
*
* Usually this would be done with getprotobynumber(3) but that reads
* /etc/protocols and might allocate, which isn't possible for us once
@@ -33,16 +34,21 @@
const char *ipproto_name(uint8_t proto)
{
switch (proto) {
+#define CASE(s) \
+ static_assert(sizeof(s) <= IPPROTO_STRLEN, \
+ "Increase IPPROTO_STRLEN to contain " #s); \
+ return s;
case IPPROTO_ICMP:
- return "ICMP";
+ CASE("ICMP");
case IPPROTO_TCP:
- return "TCP";
+ CASE("TCP");
case IPPROTO_UDP:
- return "UDP";
+ CASE("UDP");
case IPPROTO_ICMPV6:
- return "ICMPv6";
+ CASE("ICMPv6");
default:
- return "<unknown protocol>";
+ CASE("<unknown protocol>");
+#undef CASE
}
}
diff --git a/ip.h b/ip.h
index f6c29e00..aab9b86a 100644
--- a/ip.h
+++ b/ip.h
@@ -117,6 +117,7 @@ static inline uint32_t ip6_get_flow_lbl(const struct ipv6hdr *ip6h)
ip6h->flow_lbl[2];
}
+#define IPPROTO_STRLEN (sizeof("<unknown protocol>"))
const char *ipproto_name(uint8_t proto);
/* IPv6 link-local all-nodes multicast address, ff02::1 */
--
2.53.0
^ permalink raw reply [flat|nested] 16+ messages in thread
* [PATCH v2 14/15] fwd_rule: Move forwarding rule text formatting to common code
2026-03-19 6:11 [PATCH v2 00/15] RFC: Read-only dynamic update implementation David Gibson
` (12 preceding siblings ...)
2026-03-19 6:11 ` [PATCH v2 13/15] ip: Define a bound for the string returned by ipproto_name() David Gibson
@ 2026-03-19 6:11 ` David Gibson
2026-03-19 6:11 ` [PATCH v2 15/15] pesto: Read current ruleset from passt/pasta and display it David Gibson
14 siblings, 0 replies; 16+ messages in thread
From: David Gibson @ 2026-03-19 6:11 UTC (permalink / raw)
To: Stefano Brivio, passt-dev; +Cc: David Gibson
Move the logic for formatting forwarding rules into strings from
fwd_rules_print() into fwd_rule.c where it can be shared with pesto.
We also make the function explicitly construct a string, rather than
directly printing with info(), for greater flexibility.
Signed-off-by: David Gibson <david@gibson.dropbear.id.au>
---
Makefile | 11 ++++----
fwd.c | 40 +++---------------------------
fwd_rule.c | 73 ++++++++++++++++++++++++++++++++++++++++++++++++++++++
fwd_rule.h | 11 ++++++++
4 files changed, 94 insertions(+), 41 deletions(-)
create mode 100644 fwd_rule.c
diff --git a/Makefile b/Makefile
index bc325482..44c396e7 100644
--- a/Makefile
+++ b/Makefile
@@ -38,13 +38,14 @@ FLAGS += -DVERSION=\"$(VERSION)\"
FLAGS += -DDUAL_STACK_SOCKETS=$(DUAL_STACK_SOCKETS)
PASST_SRCS = arch.c arp.c checksum.c conf.c dhcp.c dhcpv6.c epoll_ctl.c \
- flow.c fwd.c icmp.c igmp.c inany.c iov.c ip.c isolation.c lineread.c \
- log.c mld.c ndp.c netlink.c migrate.c packet.c passt.c pasta.c pcap.c \
- pif.c repair.c serialise.c tap.c tcp.c tcp_buf.c tcp_splice.c tcp_vu.c \
- udp.c udp_flow.c udp_vu.c util.c vhost_user.c virtio.c vu_common.c
+ flow.c fwd.c fwd_rule.c icmp.c igmp.c inany.c iov.c ip.c isolation.c \
+ lineread.c log.c mld.c ndp.c netlink.c migrate.c packet.c passt.c \
+ pasta.c pcap.c pif.c repair.c serialise.c tap.c tcp.c tcp_buf.c \
+ tcp_splice.c tcp_vu.c udp.c udp_flow.c udp_vu.c util.c vhost_user.c \
+ virtio.c vu_common.c
QRAP_SRCS = qrap.c
PASST_REPAIR_SRCS = passt-repair.c
-PESTO_SRCS = pesto.c inany.c ip.c serialise.c
+PESTO_SRCS = pesto.c fwd_rule.c inany.c ip.c serialise.c
SRCS = $(PASST_SRCS) $(QRAP_SRCS) $(PASST_REPAIR_SRCS) $(PESTO_SRCS)
MANPAGES = passt.1 pasta.1 pesto.1 qrap.1 passt-repair.1
diff --git a/fwd.c b/fwd.c
index f157eeb6..72028e70 100644
--- a/fwd.c
+++ b/fwd.c
@@ -304,20 +304,6 @@ parse_err:
warn("Unable to parse %s", PORT_RANGE_SYSCTL);
}
-/**
- * fwd_rule_addr() - Return match address for a rule
- * @rule: Forwarding rule
- *
- * Return: matching address for rule, NULL if it matches all addresses
- */
-static const union inany_addr *fwd_rule_addr(const struct fwd_rule *rule)
-{
- if (rule->flags & FWD_DUAL_STACK_ANY)
- return NULL;
-
- return &rule->addr;
-}
-
/**
* fwd_port_is_ephemeral() - Is port number ephemeral?
* @port: Port number
@@ -495,28 +481,10 @@ void fwd_rules_print(const struct fwd_table *fwd)
unsigned i;
for (i = 0; i < fwd->count; i++) {
- const struct fwd_rule *rule = &fwd->rules[i].rule;
- const char *percent = *rule->ifname ? "%" : "";
- const char *weak = "", *scan = "";
- char addr[INANY_ADDRSTRLEN];
-
- inany_ntop(fwd_rule_addr(rule), addr, sizeof(addr));
- if (rule->flags & FWD_WEAK)
- weak = " (best effort)";
- if (rule->flags & FWD_SCAN)
- scan = " (auto-scan)";
-
- if (rule->first == rule->last) {
- info(" %s [%s]%s%s:%hu => %hu %s%s",
- ipproto_name(rule->proto), addr, percent,
- rule->ifname, rule->first, rule->to, weak, scan);
- } else {
- info(" %s [%s]%s%s:%hu-%hu => %hu-%hu %s%s",
- ipproto_name(rule->proto), addr, percent,
- rule->ifname, rule->first, rule->last,
- rule->to, rule->last - rule->first + rule->to,
- weak, scan);
- }
+ char rulestr[FWD_RULE_STRLEN];
+
+ info(" %s", fwd_rule_ntop(&fwd->rules[i].rule,
+ rulestr, sizeof(rulestr)));
}
}
diff --git a/fwd_rule.c b/fwd_rule.c
new file mode 100644
index 00000000..dfbdf683
--- /dev/null
+++ b/fwd_rule.c
@@ -0,0 +1,73 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+/* PASST - Plug A Simple Socket Transport
+ * for qemu/UNIX domain socket mode
+ *
+ * PASTA - Pack A Subtle Tap Abstraction
+ * for network namespace/tap device mode
+ *
+ * PESTO - Programmable Extensible Socket Translation Orchestrator
+ * front-end for passt(1) and pasta(1) forwarding configuration
+ *
+ * fwd_rule.c - Helpers for working with forwarding rule specifications
+ *
+ * Copyright Red Hat
+ * Author: David Gibson <david@gibson.dropbear.id.au>
+ */
+
+#include <stdio.h>
+
+#include "fwd_rule.h"
+
+/**
+ * fwd_rule_addr() - Return match address for a rule
+ * @rule: Forwarding rule
+ *
+ * Return: matching address for rule, NULL if it matches all addresses
+ */
+const union inany_addr *fwd_rule_addr(const struct fwd_rule *rule)
+{
+ if (rule->flags & FWD_DUAL_STACK_ANY)
+ return NULL;
+
+ return &rule->addr;
+}
+
+/**
+ * fwd_rule_ntop() - Format forwarding rule as a string
+ * @rule: Rule to format
+ * @dst: Buffer to store output (should have FWD_RULE_STRLEN bytes)
+ * @size: Size of @dst
+ */
+const char *fwd_rule_ntop(const struct fwd_rule *rule, char *dst, size_t size)
+{
+ const char *percent = *rule->ifname ? "%" : "";
+ const char *weak = "", *scan = "";
+ char addr[INANY_ADDRSTRLEN];
+ int len;
+
+ inany_ntop(fwd_rule_addr(rule), addr, sizeof(addr));
+ if (rule->flags & FWD_WEAK)
+ weak = " (best effort)";
+ if (rule->flags & FWD_SCAN)
+ scan = " (auto-scan)";
+
+ if (rule->first == rule->last) {
+ len = snprintf(dst, size,
+ "%s [%s]%s%s:%hu => %hu %s%s",
+ ipproto_name(rule->proto), addr, percent,
+ rule->ifname, rule->first, rule->to, weak, scan);
+ } else {
+ in_port_t tolast = rule->last - rule->first + rule->to;
+ len = snprintf(dst, size,
+ "%s [%s]%s%s:%hu-%hu => %hu-%hu %s%s",
+ ipproto_name(rule->proto), addr, percent,
+ rule->ifname, rule->first, rule->last,
+ rule->to, tolast, weak, scan);
+ }
+
+ if (len < 0 || (size_t)len >= size)
+ return NULL;
+
+ return dst;
+}
diff --git a/fwd_rule.h b/fwd_rule.h
index 84ec5cbe..59db0e95 100644
--- a/fwd_rule.h
+++ b/fwd_rule.h
@@ -42,4 +42,15 @@ struct fwd_rule {
uint8_t flags;
};
+const union inany_addr *fwd_rule_addr(const struct fwd_rule *rule);
+
+#define FWD_RULE_STRLEN \
+ (IPPROTO_STRLEN - 1 \
+ + INANY_ADDRSTRLEN - 1 \
+ + IFNAMSIZ - 1 \
+ + sizeof(" (best effort)") - 1 \
+ + sizeof(" (auto-scan)") - 1 \
+ + 15)
+const char *fwd_rule_ntop(const struct fwd_rule *rule, char *dst, size_t size);
+
#endif /* FWD_RULE_H */
--
2.53.0
^ permalink raw reply [flat|nested] 16+ messages in thread
* [PATCH v2 15/15] pesto: Read current ruleset from passt/pasta and display it
2026-03-19 6:11 [PATCH v2 00/15] RFC: Read-only dynamic update implementation David Gibson
` (13 preceding siblings ...)
2026-03-19 6:11 ` [PATCH v2 14/15] fwd_rule: Move forwarding rule text formatting to common code David Gibson
@ 2026-03-19 6:11 ` David Gibson
14 siblings, 0 replies; 16+ messages in thread
From: David Gibson @ 2026-03-19 6:11 UTC (permalink / raw)
To: Stefano Brivio, passt-dev; +Cc: David Gibson
Implement serialisation of our current forwarding rules in conf.c,
deserialising it to display in the pesto client.
Signed-off-by: David Gibson <david@gibson.dropbear.id.au>
---
conf.c | 44 +++++++++++++++++++++++++++++++
fwd_rule.c | 40 ++++++++++++++++++++++++++++
fwd_rule.h | 3 +++
pesto.c | 77 +++++++++++++++++++++++++++++++++++++++++++++++++++---
4 files changed, 160 insertions(+), 4 deletions(-)
diff --git a/conf.c b/conf.c
index 603ca2ac..b235221f 100644
--- a/conf.c
+++ b/conf.c
@@ -2339,6 +2339,47 @@ static int conf_send_pifs(const struct ctx *c, int fd)
return 0;
}
+/**
+ * conf_send_rules() - Send current forwarding rules to dynamic update client (pesto)
+ * @c: Execution context
+ * @fd: Socket to the client
+ *
+ * Return: 0 on success, -1 on failure
+ */
+static int conf_send_rules(const struct ctx *c, int fd)
+{
+ unsigned pif;
+
+ for (pif = 0; pif < PIF_NUM_TYPES; pif++) {
+ const struct fwd_table *fwd = c->fwd[pif];
+ unsigned i;
+
+ if (!fwd)
+ continue;
+
+ assert(pif);
+
+ /* PIF id */
+ if (sewrite_u8(fd, pif))
+ return -1;
+
+ /* Number of rules */
+ if (sewrite_u32(fd, fwd->count))
+ return -1;
+
+ for (i = 0; i < fwd->count; i++) {
+ if (fwd_rule_sewrite(fd, &fwd->rules[i].rule))
+ return -1;
+ }
+ }
+
+ /* Write 0 PIF id to finish */
+ if (sewrite_u8(fd, 0))
+ return -1;
+
+ return 0;
+}
+
/**
* conf_listen_handler() - Handle events on configuration listening socket
* @c: Execution context
@@ -2398,6 +2439,9 @@ void conf_listen_handler(struct ctx *c, uint32_t events)
if (conf_send_pifs(c, fd) < 0)
goto fail;
+ if (conf_send_rules(c, fd) < 0)
+ goto fail;
+
return;
fail:
diff --git a/fwd_rule.c b/fwd_rule.c
index dfbdf683..3e39b4f9 100644
--- a/fwd_rule.c
+++ b/fwd_rule.c
@@ -17,6 +17,8 @@
#include <stdio.h>
+#include "serialise.h"
+
#include "fwd_rule.h"
/**
@@ -71,3 +73,41 @@ const char *fwd_rule_ntop(const struct fwd_rule *rule, char *dst, size_t size)
return dst;
}
+
+/**
+ * fwd_rule_seread() - Read erialised rule from an fd
+ * @fd: fd to serialise to
+ * @rule: Buffer to store rule into
+ *
+ * Return: 0 on success, -1 on error (with errno set)
+ */
+int fwd_rule_seread(int fd, struct fwd_rule *rule)
+{
+ if (seread_var(fd, rule))
+ return -1;
+
+ /* Byteswap for host */
+ rule->first = ntohs(rule->first);
+ rule->last = ntohs(rule->last);
+ rule->to = htons(rule->to);
+ return 0;
+}
+
+/**
+ * fwd_rule_sewrite() - Serialise rule to an fd
+ * @fd: fd to serialise to
+ * @rule: Rule to send
+ *
+ * Return: 0 on success, -1 on error (with errno set)
+ */
+int fwd_rule_sewrite(int fd, const struct fwd_rule *rule)
+{
+ struct fwd_rule tmp = *rule;
+
+ /* Byteswap for transport */
+ tmp.first = htons(tmp.first);
+ tmp.last = htons(tmp.last);
+ tmp.to = htons(tmp.to);
+
+ return sewrite_var(fd, &tmp);
+}
diff --git a/fwd_rule.h b/fwd_rule.h
index 59db0e95..500b955d 100644
--- a/fwd_rule.h
+++ b/fwd_rule.h
@@ -53,4 +53,7 @@ const union inany_addr *fwd_rule_addr(const struct fwd_rule *rule);
+ 15)
const char *fwd_rule_ntop(const struct fwd_rule *rule, char *dst, size_t size);
+int fwd_rule_seread(int fd, struct fwd_rule *rule);
+int fwd_rule_sewrite(int fd, const struct fwd_rule *rule);
+
#endif /* FWD_RULE_H */
diff --git a/pesto.c b/pesto.c
index dbc27a58..f021cdb6 100644
--- a/pesto.c
+++ b/pesto.c
@@ -34,6 +34,7 @@
#include "common.h"
#include "seccomp_pesto.h"
#include "serialise.h"
+#include "fwd_rule.h"
#include "pesto.h"
static int verbosity = 1;
@@ -108,6 +109,8 @@ static const char *pesto_recv_str(int fd)
struct pif_state {
uint8_t pif;
const char *name;
+ uint32_t count;
+ struct fwd_rule *rule;
};
struct conf_state {
@@ -119,7 +122,7 @@ struct conf_state {
* pesto_read_pifs() - Read pif names and IDs from passt/pasta
* @fd: Control socket
*/
-static const struct conf_state *pesto_read_pifs(int fd)
+static struct conf_state *pesto_read_pifs(int fd)
{
uint32_t num;
struct conf_state *state;
@@ -146,18 +149,81 @@ static const struct conf_state *pesto_read_pifs(int fd)
return state;
}
+/**
+ * find_pif_state() - Find the pif state structure for a given pif id
+ * @state: Rule state information
+ * @pif: pif id
+ *
+ * Return: pointer to the pif_state for @pif, or NULL if not found
+ */
+static struct pif_state *find_pif_state(struct conf_state *state, uint8_t pif)
+{
+ unsigned i;
+
+ for (i = 0; i < state->npifs; i++) {
+ if (state->pif[i].pif == pif)
+ return &state->pif[i];
+ }
+
+ return NULL;
+}
+
+/**
+ * pesto_read_rules() - Read a set of rules for one pif
+ * @fd: Control socket
+ * @state: Rule state information to update
+ *
+ * Return: true if there may be more rules to read, false if finished
+ */
+static bool pesto_read_rules(int fd, struct conf_state *state)
+{
+ struct pif_state *ps;
+ uint8_t pif;
+ unsigned i;
+
+ if (seread_u8(fd, &pif) < 0)
+ die("Error reading from control socket");
+
+ if (!pif)
+ return false;
+
+ ps = find_pif_state(state, pif);
+ if (!ps)
+ die("Received rules for an unknown pif");
+
+ if (seread_u32(fd, &ps->count) < 0)
+ die("Error reading from control socket");
+
+ debug("Receiving rules %"PRIu32" rules for %s", ps->count, ps->name);
+
+ ps->rule = xmalloc(sizeof(*ps->rule) * ps->count);
+
+ for (i = 0; i < ps->count; i++) {
+ if (fwd_rule_seread(fd, &ps->rule[i]) < 0)
+ die("Error reading from control socket");
+ }
+
+ return true;
+}
+
/**
* show_state() - Show current rule state obtained from passt/pasta
* @pifs: PIF name information
*/
static void show_state(const struct conf_state *state)
{
- unsigned i;
+ unsigned i, j;
for (i = 0; i < state->npifs; i++) {
const struct pif_state *ps = &state->pif[i];
printf("Forwarding rules for %s interface\n", ps->name);
- printf("\tTBD\n");
+ for (j = 0; j < ps->count; j++) {
+ const struct fwd_rule *rule = &ps->rule[j];
+ char rulestr[FWD_RULE_STRLEN];
+
+ printf(" %s\n", fwd_rule_ntop(rule, rulestr,
+ sizeof(rulestr)));
+ }
}
}
@@ -183,9 +249,9 @@ int main(int argc, char **argv)
{ 0 },
};
struct sockaddr_un a = { AF_UNIX, "" };
- const struct conf_state *state;
const char *optstring = "vh";
struct pesto_hello hello;
+ struct conf_state *state;
struct sock_fprog prog;
int optname, ret, s;
uint32_t s_version;
@@ -266,6 +332,9 @@ int main(int argc, char **argv)
state = pesto_read_pifs(s);
+ while (pesto_read_rules(s, state))
+ ;
+
show_state(state);
exit(0);
--
2.53.0
^ permalink raw reply [flat|nested] 16+ messages in thread
end of thread, other threads:[~2026-03-19 6:12 UTC | newest]
Thread overview: 16+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2026-03-19 6:11 [PATCH v2 00/15] RFC: Read-only dynamic update implementation David Gibson
2026-03-19 6:11 ` [PATCH v2 01/15] treewide: Spell ASSERT() as assert() David Gibson
2026-03-19 6:11 ` [PATCH v2 02/15] serialise: Split functions user for serialisation from util.c David Gibson
2026-03-19 6:11 ` [PATCH v2 03/15] serialise: Add helpers for serialising unsigned integers David Gibson
2026-03-19 6:11 ` [PATCH v2 04/15] fwd: Move selecting correct scan bitmap into fwd_sync_one() David Gibson
2026-03-19 6:11 ` [PATCH v2 05/15] fwd: Look up rule index in fwd_sync_one() David Gibson
2026-03-19 6:11 ` [PATCH v2 06/15] fwd: Store forwarding tables indexed by (origin) pif David Gibson
2026-03-19 6:11 ` [PATCH v2 07/15] pesto: Introduce stub configuration interface and tool David Gibson
2026-03-19 6:11 ` [PATCH v2 08/15] pesto: Add command line option parsing and debug messages David Gibson
2026-03-19 6:11 ` [PATCH v2 09/15] pesto: Expose list of pifs to pesto David Gibson
2026-03-19 6:11 ` [PATCH v2 10/15] ip: Prepare ip.[ch] for sharing with pesto tool David Gibson
2026-03-19 6:11 ` [PATCH v2 11/15] inany: Prepare inany.[ch] " David Gibson
2026-03-19 6:11 ` [PATCH v2 12/15] fwd: Split forwading rule specification from its implementation state David Gibson
2026-03-19 6:11 ` [PATCH v2 13/15] ip: Define a bound for the string returned by ipproto_name() David Gibson
2026-03-19 6:11 ` [PATCH v2 14/15] fwd_rule: Move forwarding rule text formatting to common code David Gibson
2026-03-19 6:11 ` [PATCH v2 15/15] pesto: Read current ruleset from passt/pasta and display it David Gibson
Code repositories for project(s) associated with this public inbox
https://passt.top/passt
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for IMAP folder(s).