On Wed, Mar 25, 2026 at 01:56:22AM +0100, Stefano Brivio wrote:
On Mon, 23 Mar 2026 18:37:22 +1100 David Gibson
wrote: 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
--- 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 7b960fe9..b878db94 100644 --- a/conf.c +++ b/conf.c @@ -2303,6 +2303,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 (write_u32(fd, num)) + return -1; + + for (pif = 0; pif < PIF_NUM_TYPES; pif++) { + if (!c->fwd[pif]) + continue; + + if (write_u8(fd, pif)) + return -1; + + if (write_str(fd, pif_name(pif)) < 0) + return -1; + } + + return 0; +} + /** * conf_listen_handler() - Handle events on configuration listening socket * @c: Execution context @@ -2359,6 +2394,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 f8c9e01d..ed4f2aab 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);
I would really really prefer to skip this unless it's absolutely necessary, for a number of reasons:
1. this thing is talking to passt which talks to untrusted workloads, and there's a risk of arbitrary code execution, which implies the possibility of specially crafted messages that might make us allocate:
a. in specific regions and build a heap spraying attack based on that
b. a lot, and cause a denial of service
Eh, I suppose.
...and, while this process should be short lived, we can't assume that, if somebody achieves arbitrary code execution in passt and malicious messages, because we risk looping on rules, or having passt send us information very slowly (minutes / hours), etc.
...ah, those are good points.
2. besides, we might want to add a "daemon" mode one day, and then it's not short lived anymore, which opens the door to leaks and double-frees (adding to a. and b. above)
I really don't see any reason we'd want that, but I guess that's irrelevant given that previous paragraph.
3. we might need to reuse functions from passt and not notice right away that they call this xmalloc(), or have to eventually adapt them anyway
I mean.. when we test the path in question, we should hit the seccomp filter, which should make it pretty obvious.
4. consistency, in messaging ("passt doesn't allocate dynamic memory", without caveats) and for auditing reasons
Yeah, fair.
Strings can be 32 bytes, or 1024 if we want to splurge. Unless we need to pile a lot of them on the stack (but it's not the case in pesto) a malloc() doesn't really bring any advantage compared to static buffers here and there. It's not megabytes.
So, you've convinced me in principle, but I'm not putting this at the top of my priorities. Using malloc() makes things a bit easier while we're playing around with the protocol a bunch. Once we look reasonably close to a v1 protocol, I'll do the malloc() removal.
+ + 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 (read_u32(fd, &len) < 0) + die("Error reading from control socket"); + + buf = xmalloc(len); + if (read_all_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 {
More as a to-do list item rather than as a review comment: we need documentation for those structs.
Ah, yes.
+ 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 (read_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 (read_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 e3ea86e3..94c34d3c 100644 --- a/serialise.c +++ b/serialise.c @@ -19,6 +19,7 @@ #include
#include #include +#include #include #include "serialise.h" @@ -121,6 +122,26 @@ int write_all_buf(int fd, const void *buf, size_t len) return write_all_buf(fd, &beval, sizeof(beval)); \ }
+#define be8toh(x) (x) +#define htobe8(x) (x) + +SERIALISE_UINT(8) SERIALISE_UINT(32)
#undef SERIALISE_UNIT + +/** + * write_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 write_str(int fd, const char *s) +{ + uint32_t len = strlen(s) + 1; /* Include \0 */ + + if (write_u32(fd, len) < 0) + return -1;
Even if we don't need to allocate here, I would feel more comfortable if we passed fixed-size strings only to passt to have a slightly lower risk of buffer overflows, in general.
That's fair. I'd basically already decided to move to fixed length pif names, just haven't implemented it yet.
+ return write_all_buf(fd, s, len); +} diff --git a/serialise.h b/serialise.h index 0a4ed086..7ef35786 100644 --- a/serialise.h +++ b/serialise.h @@ -16,6 +16,9 @@ int write_all_buf(int fd, const void *buf, size_t len); int read_u##bits(int fd, uint##bits##_t *val); \ int write_u##bits(int fd, uint##bits##_t val);
+SERIALISE_UINT_DECL(8) SERIALISE_UINT_DECL(32)
+int write_str(int fd, const char *s); + #endif /* _SERIALISE_H */
-- Stefano
-- David Gibson (he or they) | I'll have my music baroque, and my code david AT gibson.dropbear.id.au | minimalist, thank you, not the other way | around. http://www.ozlabs.org/~dgibson