public inbox for passt-dev@passt.top
 help / color / mirror / code / Atom feed
From: Stefano Brivio <sbrivio@redhat.com>
To: Anshu Kumari <anskuma@redhat.com>
Cc: passt-dev@passt.top, david@gibson.dropbear.id.au,
	jmaloy@redhat.com, lvivier@redhat.com
Subject: Re: [PATCH v3 1/6] conf: Add --dhcp-opt command-line option
Date: Fri, 12 Jun 2026 01:04:28 +0200 (CEST)	[thread overview]
Message-ID: <20260612010426.319bc57d@elisabeth> (raw)
In-Reply-To: <20260601073758.1571317-2-anskuma@redhat.com>

Sorry for the late review.

I have a few remarks on top of the one from David (which is the only one
left that's really critical, I guess):

On Mon,  1 Jun 2026 13:07:51 +0530
Anshu Kumari <anskuma@redhat.com> wrote:

> Introduce the --dhcp-opt flag that allows setting arbitrary DHCP
> options from command-line in the form of [Option CODE,VALUE].
> This patch adds the option storage in struct ctx and CLI parsing;
> the type-aware value parser and DHCP reply injection follow
> in subsequent patches.

This split makes the patch smaller, but not necessarily easier to
review, or maybe actually harder:

- as David noted, it would be preferable to have man page changes
  together with functional changes they relate to, but not just for
  correctness: I personally use those during reviews to double check if
  the implementation corresponds to the intention.

  That is, in some sense, I use man pages as specification, which is
  particularly fitting when it comes to new command line options. So,
  from my side, while it's not really a blocker, my preference to have
  those man page changes together with the patch implementing the code
  changes is probably a bit stronger than the one expressed by David.

  It's not just for review right now, it also helps investigation later
  when somebody finds issues: having a more comprehensive description
  in this patch itself means not having to correlate multiple patches.

  Think of bisecting an issue and finding that this patch breaks
  something: git reaches this patch, and now you don't have the man
  page in the checked out tree at all...

- I've been asking myself: is dhcp_add_option() matter for conf.c,
  rather than for dhcp.c? That is, is it merely a matter of
  configuration parsing / handling, or a part of the DHCP
  implementation proper?

  I needed to reach 3/6 and actually grasp 3/6 to answer this
  question... which seems to be a good indication that this change
  belongs to the same patch as 3/6. And I had similar fundamental
  questions around this patch that I could only resolve by reading 3/6.

  For me, a more reasonable split would have been something like:

  - 1/4 dhcp: Refactor fill_one() to operate on a generic buffer

    it's a dependency for 3/4 but doesn't depend on others

  - 2/4 dhcp: Add option overload

    also a dependency of 3/4, it only depends on 1/4 anyway

  - 3/4 dhcp: Add --dhcp-opt with option table and value parser

    the main feature, with man page for --dhcp-opt, and a clear match
    between what you parse from conf() and how it's used

  - 4/4 conf: Add --dhcp-boot command-line option

    with its part of man page, as it depends on 3/4 but no other bits
    seem to depend on it

...I'm not sure how much effort that is at this point, but I think it
would be nice for the revision history (current review, doesn't matter
so much, as both David and myself are now familiar with it).

Some nits below:

> Link: https://bugs.passt.top/show_bug.cgi?id=192
> Signed-off-by: Anshu Kumari <anskuma@redhat.com>
> ---
> v3:
>   - Added dhcp_add_option() helper in dhcp.c for storing options with
>     duplicate-code detection.
>   - case 33 now calls dhcp_add_option() instead of inline storage.
> 
> v2:
>   - Added kerneldoc for @custom_opts, @custom_opts.code, @custom_opts.str, and @custom_opts_count in struct ctx
>   - Removed len and val[255] fields from struct (moved to patch 3)
>   - Removed braces from case 33, moved declarations (optcode, comma, end) to function scope
>   - Renamed code → optcode to follow function-scope convention.
> ---
>  conf.c  | 24 +++++++++++++++++++++++-
>  dhcp.c  | 38 ++++++++++++++++++++++++++++++++++++++
>  dhcp.h  |  1 +
>  passt.h | 12 ++++++++++++
>  4 files changed, 74 insertions(+), 1 deletion(-)
> 
> diff --git a/conf.c b/conf.c
> index 029b9c7..ce78af1 100644
> --- a/conf.c
> +++ b/conf.c
> @@ -47,6 +47,7 @@
>  #include "lineread.h"
>  #include "isolation.h"
>  #include "log.h"
> +#include "dhcp.h"
>  #include "vhost_user.h"
>  #include "epoll_ctl.h"
>  #include "conf.h"
> @@ -616,7 +617,8 @@ static void usage(const char *name, FILE *f, int status)
>  		"  -S, --search LIST	Space-separated list, search domains\n"
>  		"    a single, empty option disables the DNS search list\n"
>  		"  -H, --hostname NAME 	Hostname to configure client with\n"
> -		"  --fqdn NAME		FQDN to configure client with\n");
> +		"  --fqdn NAME		FQDN to configure client with\n"
> +		"  --dhcp-opt CODE,VAL	Set DHCP option by code\n");

What's VAL? It becomes obvious after some thinking but perhaps this
would be clearer:

		"  --dhcp-opt CODE,VAL	Set DHCP option CODE to VAL\n");

>  	if (strstr(name, "pasta"))
>  		FPRINTF(f, "    default: don't use any search list\n");
>  	else
> @@ -844,6 +846,10 @@ static void conf_print(const struct ctx *c)
>  			info("    router: %s",
>  			     inet_ntop(AF_INET, &c->ip4.guest_gw,
>  				       buf, sizeof(buf)));
> +			for (i = 0; i < c->custom_opts_count; i++)
> +				info("    option %u: %s",
> +				     c->custom_opts[i].code,
> +				     c->custom_opts[i].str);
>  		}
>  
>  		for (i = 0; i < ARRAY_SIZE(c->ip4.dns); i++) {
> @@ -1233,6 +1239,7 @@ void conf(struct ctx *c, int argc, char **argv)
>  		{"migrate-no-linger", no_argument,	NULL,		30 },
>  		{"stats", required_argument,		NULL,		31 },
>  		{"conf-path",	required_argument,	NULL,		'c' },
> +		{"dhcp-opt", required_argument,		NULL,		33 },
>  		{ 0 },
>  	};
>  	const char *optstring = "+dqfel:hs:c:F:I:p:P:m:a:n:M:g:i:o:D:S:H:461t:u:T:U:";
> @@ -1248,10 +1255,13 @@ void conf(struct ctx *c, int argc, char **argv)
>  	uint8_t prefix_len_from_opt = 0;
>  	unsigned int ifi4 = 0, ifi6 = 0;
>  	const char *logfile = NULL;
> +	unsigned long optcode;
>  	char *runas = NULL;
>  	size_t logsize = 0;
> +	const char *comma;
>  	long fd_tap_opt;
>  	int name, ret;
> +	char *end;
>  	uid_t uid;
>  	gid_t gid;
>  	
> @@ -1465,6 +1475,18 @@ void conf(struct ctx *c, int argc, char **argv)
>  				die("Can't display statistics if not running in foreground");
>  			c->stats = strtol(optarg, NULL, 0);
>  			break;
> +		case 33:
> +			comma = strchr(optarg, ',');
> +			if (!comma)
> +				die("--dhcp-opt requires CODE,VALUE format");
> +
> +			optcode = strtoul(optarg, &end, 0);
> +			if (end != comma || optcode < 1 || optcode > 254)
> +				die("DHCP option code must be 1-254: %s",
> +				    optarg);
> +
> +			dhcp_add_option(c, optcode, comma + 1);
> +			break;
>  		case 'd':
>  			c->debug = 1;
>  			c->quiet = 0;
> diff --git a/dhcp.c b/dhcp.c
> index 1ff8cba..c5fbf37 100644
> --- a/dhcp.c
> +++ b/dhcp.c
> @@ -33,6 +33,44 @@
>  #include "log.h"
>  #include "dhcp.h"
>  
> +
> +/**
> + * dhcp_add_option() - Add or update a custom DHCP option

It's not clear where it's added, which is rather fundamental (to a
reply message or to the configuration?)

"Set" can replace "Add or update", and "custom" is not really important
or well defined I think (what makes an option custom? The fact that
it's not assigned by IANA or the fact that it's specified by the user?
But then what's not custom...?).

So, maybe... "Set value for a DHCP option in configuration"?

And looking at this, I guess conf_dhcp_option() in conf.c would be a
better fit.

> + * @c:		Execution context
> + * @code:	DHCP option code
> + * @val_str:	Value string from command line

@str would be equally expressive I think, but I'm fine with both
(slightly less typing for 'str' though).

> + *
> + * If @code was already added, the previous value is overwritten.
> + * Calls die() on any error.
> + *
> + * Return: 0 on success
> + */
> +int dhcp_add_option(struct ctx *c, uint8_t code, const char *val_str)
> +{
> +	int idx;
> +
> +	for (idx = 0; idx < c->custom_opts_count; idx++) {
> +		if (c->custom_opts[idx].code == code)
> +			break;
> +	}
> +
> +	if (idx == c->custom_opts_count) {
> +		if (c->custom_opts_count >= MAX_CUSTOM_DHCP_OPTS)
> +			die("Too many --dhcp-opt entries (max %d)",
> +			    MAX_CUSTOM_DHCP_OPTS);

What's the rationale of this, though? You now have option overload and
a ("working") table with 256 slots, one might actually try to set more
than 32 options I think. If not, showing the calculation you made for
MAX_CUSTOM_DHCP_OPTS in its #define directive would make that clear.

If you're trying to save some memory in struct ctx by storing only
32 entries in the configuration, note that, as it's now moving to the
data segment (see Jon's "[PATCH v3] util, passt: Close daemon-lifetime
fds on exit to avoid Coverity warning") as long as you have a block of
zeroed bytes that's large enough to matter, that's going to take no
memory at all.

> +		c->custom_opts_count++;
> +	}
> +
> +	c->custom_opts[idx].code = code;
> +
> +	if (snprintf_check(c->custom_opts[idx].str,
> +			   sizeof(c->custom_opts[0].str),
> +			   "%s", val_str))
> +		die("DHCP option value too long: %s", val_str);
> +
> +	return 0;
> +}
> +
>  /**
>   * struct opt - DHCP option
>   * @sent:	Convenience flag, set while filling replies
> diff --git a/dhcp.h b/dhcp.h
> index cd50c99..9c8f1e3 100644
> --- a/dhcp.h
> +++ b/dhcp.h
> @@ -8,5 +8,6 @@
>  
>  int dhcp(const struct ctx *c, struct iov_tail *data);
>  void dhcp_init(void);
> +int dhcp_add_option(struct ctx *c, uint8_t code, const char *val_str);
>  
>  #endif /* DHCP_H */
> diff --git a/passt.h b/passt.h
> index 1726965..3a0816f 100644
> --- a/passt.h
> +++ b/passt.h
> @@ -182,6 +182,10 @@ struct ip6_ctx {
>   * @dns_search:		DNS search list
>   * @hostname:		Guest hostname
>   * @fqdn:		Guest FQDN
> + * @custom_opts:	User-specified DHCP options from --dhcp-opt

I think this should be called @dhcp_opts, because @custom_opts in
the... context of ctx isn't really clear. They're all custom anyway in
some sense.

> + * @custom_opts.code:	DHCP option code
> + * @custom_opts.str:	Original string value from command line

It's the only one, there isn't one that's original and one that isn't
(right?).

> + * @custom_opts_count:	Number of entries in @custom_opts

And if you allow for 256 items here, you would skip this and the whole
complexity.

>   * @ifi6:		Template interface for IPv6, -1: none, 0: IPv6 disabled
>   * @ip6:		IPv6 configuration
>   * @pasta_ifn:		Name of namespace interface for pasta
> @@ -263,6 +267,14 @@ struct ctx {
>  	char hostname[PASST_MAXDNAME];
>  	char fqdn[PASST_MAXDNAME];
>  
> +#define MAX_CUSTOM_DHCP_OPTS	32
> +
> +	struct {
> +		uint8_t code;
> +		char str[256];

This should be 255 characters long, otherwise it might suggest to
others that it's NULL-terminated, which is a rather dangerous
assumption.

> +	} custom_opts[MAX_CUSTOM_DHCP_OPTS];
> +	int custom_opts_count;
> +
>  	int ifi6;
>  	struct ip6_ctx ip6;
>  

-- 
Stefano


  parent reply	other threads:[~2026-06-11 23:04 UTC|newest]

Thread overview: 19+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-06-01  7:37 [PATCH v3 0/6] Add --dhcp-boot and --dhcp-opt options Anshu Kumari
2026-06-01  7:37 ` [PATCH v3 1/6] conf: Add --dhcp-opt command-line option Anshu Kumari
2026-06-02  2:00   ` David Gibson
2026-06-11 23:04   ` Stefano Brivio [this message]
2026-06-01  7:37 ` [PATCH v3 2/6] conf: Add --dhcp-boot " Anshu Kumari
2026-06-02  2:02   ` David Gibson
2026-06-11 23:05   ` Stefano Brivio
2026-06-01  7:37 ` [PATCH v3 3/6] dhcp: Add option type table and value parser Anshu Kumari
2026-06-02  2:23   ` David Gibson
2026-06-11 23:04     ` Stefano Brivio
2026-06-11 23:04   ` Stefano Brivio
2026-06-01  7:37 ` [PATCH v3 4/6] dhcp: Refactor fill_one() to operate on a generic buffer Anshu Kumari
2026-06-02  2:25   ` David Gibson
2026-06-01  7:37 ` [PATCH v3 5/6] dhcp: Add option overload Anshu Kumari
2026-06-02  2:50   ` David Gibson
2026-06-11 23:04   ` Stefano Brivio
2026-06-01  7:37 ` [PATCH v3 6/6] doc: Add --dhcp-boot and --dhcp-opt to man page Anshu Kumari
2026-06-02  2:54   ` David Gibson
2026-06-11 23:05   ` Stefano Brivio

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20260612010426.319bc57d@elisabeth \
    --to=sbrivio@redhat.com \
    --cc=anskuma@redhat.com \
    --cc=david@gibson.dropbear.id.au \
    --cc=jmaloy@redhat.com \
    --cc=lvivier@redhat.com \
    --cc=passt-dev@passt.top \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
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).