From mboxrd@z Thu Jan 1 00:00:00 1970 Authentication-Results: passt.top; dmarc=none (p=none dis=none) header.from=gibson.dropbear.id.au Authentication-Results: passt.top; dkim=pass (2048-bit key; secure) header.d=gibson.dropbear.id.au header.i=@gibson.dropbear.id.au header.a=rsa-sha256 header.s=202606 header.b=EROjcFLp; dkim-atps=neutral Received: from mail.ozlabs.org (gandalf.ozlabs.org [150.107.74.76]) by passt.top (Postfix) with ESMTPS id 28D675A026E for ; Fri, 26 Jun 2026 09:10:12 +0200 (CEST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gibson.dropbear.id.au; s=202606; t=1782457808; bh=v6r10jV2s/tqYSn9e5gsenv1hPoeFaWSv8gWLttuP94=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=EROjcFLpiOrn9vZHzh+9zuOVP0i6Q4zt+yD9oX9PdA4rQ1ju70l28GVfRoYwbkM68 qz8mhjFyYPUEzLh7AxN4GiC8rjUJiOzh+0e9lhw1NGsa3XhRWYzU4oLNzuE8NWqyVA uQ3UT8bGPWd1pUbtKa2MfrKXT4t4UodiOGaptoB3dVjCWFd2zjU8NSehYDCIGa83Ha R0B0yGEwSLjiDoPM+ivPCCfJlFWdrTKBYxgxxOSJrU6r1tZcd4r4RHLEvWyXhU2uEL +2lPCYRqS6fTiWSMZ/nE7PBLvskhffvmZPPSVgGC0u7ZRildxdq+TENodXDL2RK3w4 CyO4l7O9cKWtA== Received: by gandalf.ozlabs.org (Postfix, from userid 1007) id 4gmmym4GKHz4whx; Fri, 26 Jun 2026 17:10:08 +1000 (AEST) From: David Gibson To: passt-dev@passt.top, Stefano Brivio Subject: [PATCH 03/12] parse: Start splitting out parsing helpers Date: Fri, 26 Jun 2026 17:09:54 +1000 Message-ID: <20260626071003.3472194-4-david@gibson.dropbear.id.au> X-Mailer: git-send-email 2.54.0 In-Reply-To: <20260626071003.3472194-1-david@gibson.dropbear.id.au> References: <20260626071003.3472194-1-david@gibson.dropbear.id.au> MIME-Version: 1.0 Content-Transfer-Encoding: 8bit Message-ID-Hash: OMCFL4VETHMD66GEGIR4JRWZSFSUPEWC X-Message-ID-Hash: OMCFL4VETHMD66GEGIR4JRWZSFSUPEWC X-MailFrom: dgibson@gandalf.ozlabs.org X-Mailman-Rule-Misses: dmarc-mitigation; no-senders; approved; emergency; loop; banned-address; member-moderation; nonmember-moderation; administrivia; implicit-dest; max-recipients; max-size; news-moderation; no-subject; digests; suspicious-header CC: David Gibson X-Mailman-Version: 3.3.8 Precedence: list List-Id: Development discussion and patches for passt Archived-At: Archived-At: List-Archive: List-Archive: List-Help: List-Owner: List-Post: List-Subscribe: List-Unsubscribe: As we add more complexity to what forwarding rules are allowed, our existing ad-hoc C parsing is starting to become quite awkward. We already have some parts that resemble a very simple recursive[0] descent parser, with composable subfunctions that consume as much input as they need, rather than pre-splitting based on known delimiters. Start extending this approach, by creating parse.[ch] with parsing helpers with a uniform interface. Initially we start with very simple cases: the parse_keyword() function from fwd_rule.c and another helper to check that we've reached the end of input. Rename parse_keyword() to parse_literal(), because it's not just useful for "keywords" as such. We can use it in a bunch of additional places for parsing delimiters and other symbols. This doesn't gain us a lot now but will make things clearer as we use more such parser helpers. [0] Except that the grammars we have aren't actually recursive, so neither is the code. Signed-off-by: David Gibson --- Makefile | 17 ++++++++------ conf.c | 5 +++- fwd_rule.c | 33 +++++---------------------- parse.c | 67 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ parse.h | 14 ++++++++++++ 5 files changed, 101 insertions(+), 35 deletions(-) create mode 100644 parse.c create mode 100644 parse.h diff --git a/Makefile b/Makefile index 5ed0f702..bd729c75 100644 --- a/Makefile +++ b/Makefile @@ -36,12 +36,13 @@ BASE_CFLAGS += -pedantic -Wall -Wextra -Wno-format-zero-length -Wformat-security PASST_SRCS = arch.c arp.c bitmap.c checksum.c conf.c dhcp.c dhcpv6.c \ epoll_ctl.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 \ + parse.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 bitmap.c fwd_rule.c inany.c ip.c lineread.c serialise.c +PESTO_SRCS = pesto.c bitmap.c fwd_rule.c inany.c ip.c lineread.c parse.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 @@ -49,13 +50,14 @@ MANPAGES = passt.1 pasta.1 pesto.1 qrap.1 passt-repair.1 PASST_HEADERS = arch.h arp.h bitmap.h checksum.h conf.h dhcp.h dhcpv6.h \ epoll_ctl.h flow.h fwd.h fwd_rule.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 + netlink.h packet.h parse.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 QRAP_HEADERS = arp.h ip.h passt.h util.h PASST_REPAIR_HEADERS = linux_dep.h -PESTO_HEADERS = bitmap.h common.h fwd_rule.h inany.h ip.h log.h pesto.h serialise.h +PESTO_HEADERS = bitmap.h common.h fwd_rule.h inany.h ip.h log.h parse.h \ + pesto.h serialise.h C := \#include \nint main(){int a=getrandom(0, 0, 0);} ifeq ($(shell printf "$(C)" | $(CC) -S -xc - -o - >/dev/null 2>&1; echo $$?),0) @@ -223,6 +225,7 @@ pesto.cppcheck: CPPCHECK_FLAGS += --suppress=unusedFunction:bitmap.c pesto.cppcheck: CPPCHECK_FLAGS += --suppress=unusedFunction:inany.h pesto.cppcheck: CPPCHECK_FLAGS += --suppress=unusedFunction:inany.c pesto.cppcheck: CPPCHECK_FLAGS += --suppress=unusedFunction:ip.h +pesto.cppcheck: CPPCHECK_FLAGS += --suppress=unusedFunction:parse.c pesto.cppcheck: CPPCHECK_FLAGS += --suppress=unusedFunction:serialise.c pesto.cppcheck: CPPCHECK_FLAGS += --suppress=staticFunction:fwd_rule.c pesto.cppcheck: $(PESTO_SRCS) $(PESTO_HEADERS) seccomp_pesto.h diff --git a/conf.c b/conf.c index 6ab8efec..ca6c859c 100644 --- a/conf.c +++ b/conf.c @@ -52,6 +52,7 @@ #include "conf.h" #include "pesto.h" #include "serialise.h" +#include "parse.h" #define NETNS_RUN_DIR "/run/netns" @@ -1028,7 +1029,9 @@ static void conf_ugid(char *runas, uid_t *uid, gid_t *gid) static void conf_nat(const char *arg, struct in_addr *addr4, struct in6_addr *addr6, int *no_map_gw) { - if (strcmp(arg, "none") == 0) { + const char *p = arg; + + if (parse_literal(&p, "none") && parse_eoi(p)) { *addr4 = in4addr_any; *addr6 = in6addr_any; if (no_map_gw) diff --git a/fwd_rule.c b/fwd_rule.c index 04a0101d..d0e90402 100644 --- a/fwd_rule.c +++ b/fwd_rule.c @@ -24,6 +24,7 @@ #include "fwd_rule.h" #include "lineread.h" #include "log.h" +#include "parse.h" #include "serialise.h" /* Ephemeral port range: values from RFC 6335 */ @@ -427,28 +428,6 @@ static int parse_port_range(const char *s, const char **endptr, return 0; } -/** - * parse_keyword() - Parse a literal keyword - * @s: String to parse - * @endptr: Update to the character after the keyword - * @kw: Keyword to accept - * - * Return: 0, if @s starts with @kw, -EINVAL if it does not - */ -static int parse_keyword(const char *s, const char **endptr, const char *kw) -{ - size_t len = strlen(kw); - - if (strlen(s) < len) - return -EINVAL; - - if (memcmp(s, kw, len)) - return -EINVAL; - - *endptr = s + len; - return 0; -} - /** * fwd_rule_range_except() - Set up forwarding for a range of ports minus a * bitmap of exclusions @@ -568,7 +547,7 @@ static void fwd_rule_parse_ports(struct fwd_table *fwd, bool del, uint8_t proto, continue; } - if (parse_keyword(p, &p, "auto") == 0) { + if (parse_literal(&p, "auto")) { if (p != ep) /* Garbage after the keyword */ goto bad; @@ -582,9 +561,8 @@ static void fwd_rule_parse_ports(struct fwd_table *fwd, bool del, uint8_t proto, } /* Should be an exclude range */ - if (*p != '~') + if (!parse_literal(&p, "~")) goto bad; - p++; if (parse_port_range(p, &p, &xrange)) goto bad; @@ -616,8 +594,9 @@ static void fwd_rule_parse_ports(struct fwd_table *fwd, bool del, uint8_t proto, if (parse_port_range(p, &p, &orig_range)) goto bad; - if (*p == ':') { /* There's a range to map to as well */ - if (parse_port_range(p + 1, &p, &mapped_range)) + if (parse_literal(&p, ":")) { + /* There's a range to map to as well */ + if (parse_port_range(p, &p, &mapped_range)) goto bad; if ((mapped_range.last - mapped_range.first) != (orig_range.last - orig_range.first)) diff --git a/parse.c b/parse.c new file mode 100644 index 00000000..c2a92422 --- /dev/null +++ b/parse.c @@ -0,0 +1,67 @@ +// 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 + * + * parse.c - Composable parsing helpers + * + * Copyright Red Hat + * Author: David Gibson + */ + +#include +#include +#include +#include +#include +#include + +#include "common.h" + +/** + * DOC: Theory of Operation + * + * These are a number of primitives which can be combined into parsers for + * moderately complex input. For simpler composability, they have common + * conventions. + * + * - Functions return a bool indicating whether they successfully parsed. + * - Any specific output from the parse is as output parameters + * - First argument is always @cursor, a const char **. + * - On entry, *@cursor has the point to start parsing. + * - On successful exit, *@cursor is updated to the next character after the + parsed portion of the input + * - On failure, *@cursor and any output arguments are not modified + * + * For brevity the common parameters and return information are omitted from the + * individual function documentation comments. + */ + +/** + * parse_literal() - Parse a specified literal string + * @lit: Keyword to accept + */ +bool parse_literal(const char **cursor, const char *lit) +{ + size_t len = strlen(lit); + + if (strlen(*cursor) < len || memcmp(*cursor, lit, len)) + return false; + + *cursor += len; + return true; +} + +/** + * parse_eoi() - Parse end of input + * @cursor: Current parse pointer + * + * Return: true if @p is at End of Input (\0), false otherwise + */ +bool parse_eoi(const char *cursor) +{ + return !(*cursor); +} diff --git a/parse.h b/parse.h new file mode 100644 index 00000000..eb51a469 --- /dev/null +++ b/parse.h @@ -0,0 +1,14 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later + * Copyright Red Hat + * Author: David Gibson + */ + +#ifndef PARSE_H +#define PARSE_H + +#include + +bool parse_literal(const char **cursor, const char *lit); +bool parse_eoi(const char *cursor); + +#endif /* _PARSE_H */ -- 2.54.0