[PATCH 13/18] conf: Rework stepping through chunks of port specifiers

David Gibson
2026-04-07T03:16:43Z

Port specifier strings are made up of ',' separated chunks. Rework the
logic we use to step through the chunks.

Specifically, maintain a pointer to the end of each chunk as well as the
start. This is not really used yet, but will be useful in future.

This also has side effect on semantics. Previously an empty specifier (0
chunks) was not accepted. Now it is, and will be treated as an "exclude
only" spec which excludes only ephemeral ports. This seems a bit odd, and
I don't expect it to be (directly) used in practice. However, it falls
naturally out of the existing semantics, and will combine well with some
upcoming changes.

Signed-off-by: David Gibson <david@gibson.dropbear.id.au>
---
conf.c | 39 +++++++++++++++++----------------------
1 file changed, 17 insertions(+), 22 deletions(-)

diff --git a/conf.c b/conf.c
index 1e456361..1ab1b071 100644
--- a/conf.c
+++ b/conf.c
@@ -65,21 +65,6 @@

const char *pasta_default_ifn = "tap0";

-/**
- * next_chunk() - Return the next piece of a string delimited by a character
- * @s: String to search
- * @c: Delimiter character
- *
- * Return: if another @c is found in @s, returns a pointer to the
- * character *after* the delimiter, if no further @c is in @s,
- * return NULL
- */
-static const char *next_chunk(const char *s, char c)
-{
- char *sep = strchr(s, c);
- return sep ? sep + 1 : NULL;
-}
-
/**
* port_range() - Represents a non-empty range of ports
* @first: First port number in the range
@@ -232,6 +217,18 @@ fail:
fwd_rule_fmt(&rule, rulestr, sizeof(rulestr)));
}

+/*
+ * for_each_chunk - Step through delimited chunks of a string
+ * @p_: Pointer to start of each chunk (updated)
+ * @ep_: Pointer to end of each chunk (updated)
+ * @s_: String to step through
+ * @sep_: String of all allowed delimiters
+ */
+#define for_each_chunk(p_, ep_, s_, sep_) \
+ for ((p_) = (s_); \
+ (ep_) = (p_) + strcspn((p_), (sep_)), *(p_); \
+ (p_) = *(ep_) ? (ep_) + 1 : (ep_))
+
/**
* conf_ports_spec() - Parse port range(s) specifier
* @c: Execution context
@@ -251,12 +248,11 @@ static void conf_ports_spec(const struct ctx *c,
{
uint8_t exclude[PORT_BITMAP_SIZE] = { 0 };
bool exclude_only = true;
- const char *p;
+ const char *p, *ep;
unsigned i;

/* Mark all exclusions first, they might be given after base ranges */
- p = spec;
- do {
+ for_each_chunk(p, ep, spec, ",") {
struct port_range xrange;

if (*p != '~') {
@@ -273,7 +269,7 @@ static void conf_ports_spec(const struct ctx *c,

for (i = xrange.first; i <= xrange.last; i++)
bitmap_set(exclude, i);
- } while ((p = next_chunk(p, ',')));
+ }

if (exclude_only) {
/* Exclude ephemeral ports */
@@ -287,8 +283,7 @@ static void conf_ports_spec(const struct ctx *c,
}

/* Now process base ranges, skipping exclusions */
- p = spec;
- do {
+ for_each_chunk(p, ep, spec, ",") {
struct port_range orig_range, mapped_range;

if (*p == '~')
@@ -321,7 +316,7 @@ static void conf_ports_spec(const struct ctx *c,
orig_range.first, orig_range.last,
exclude,
mapped_range.first, 0);
- } while ((p = next_chunk(p, ',')));
+ }

return;
bad:
--
2.53.0