Bug 72 - bootp clients not supported
Summary: bootp clients not supported
Status: RESOLVED FIXED
Alias: None
Product: passt
Classification: Unclassified
Component: DHCP (show other bugs)
Version: unspecified
Hardware: All Linux
: Normal normal
Assignee: nobody
URL:
Depends on:
Blocks:
 
Reported: 2023-08-25 13:39 UTC by stsp
Modified: 2024-03-19 18:18 UTC (History)
2 users (show)

See Also:


Attachments

Description stsp 2023-08-25 13:39:39 UTC
Hi.

This patch adds the bootp clients support.
I looked into libslirp and it has that code.

diff --git a/dhcp.c b/dhcp.c
index d04648c..b385fcf 100644
--- a/dhcp.c
+++ b/dhcp.c
@@ -320,6 +320,8 @@ int dhcp(const struct ctx *c, const struct pool *p)
                opt_off += *olen + 2;
        }
 
+       if (opts[53].c[0] == 0)
+               opts[53].c[0] = DHCPREQUEST; /* Force reply for old BOOTP clients */
        if (opts[53].c[0] == DHCPDISCOVER) {
                info("DHCP: offer to discover");
                opts[53].s[0] = DHCPOFFER;
Comment 1 Stefano Brivio 2023-08-28 16:48:39 UTC
Hi stsp,

Thanks for the patch! I think with a couple of changes we could make this a bit safer for BOOTP (or in general for pre-RFC 1531) clients, instead of appending options as if we had a DHCP client.

That is, a bit after that part, we have:

	if (opts[53].c[0] == DHCPDISCOVER) {
		info("DHCP: offer to discover");
		opts[53].s[0] = DHCPOFFER;
	} else if (opts[53].c[0] == DHCPREQUEST) {
		info("DHCP: ack to request");
		opts[53].s[0] = DHCPACK;
	} else {
		return -1;
	}

and, instead of making that 'return -1' essentially dead code, we could more accurately detect a BOOTP client by doing something like:

	} else if (!opts[53].clen)
		info("BOOTP: BOOTREQUEST to BOOTREPLY");
		bootp = true;
	} else {
		return -1;
	}

and then, if 'bootp' is true, just do the yiaddr part without appending options (also a goto would be okay if it keeps things simple). My fear is that we would otherwise answer a BOOTP client with options and cause it to malfunction. What do you think?
Comment 2 stsp 2023-08-28 17:10:13 UTC
Yes, my patch was essentially a "slirp
does it this way and slirp works".
However I am not entirely sure if
something else is needed.

According to this:
http://www.tcpipguide.com/free/t_BOOTPMessageFormat.htm
bootp has not only yiaddr, but everything
else. More so, it has the vendor-specific
area, where I believe DHCP puts tags.
So I thought (which may be wrong) that
DHCP is fully derived from bootp, and
the difference is only in the tag fields
being unspecified for bootp.
You may argue that some "unknown" bootp
client could fail unless the vendor are
is zeroed out. But given that slirp does
not zero out the area, there is no such
fear.
Comment 3 Stefano Brivio 2023-08-29 07:37:39 UTC
(In reply to stsp from comment #2)
> According to this:
> http://www.tcpipguide.com/free/t_BOOTPMessageFormat.htm
> bootp has not only yiaddr, but everything
> else. More so, it has the vendor-specific
> area, where I believe DHCP puts tags.
Well, true, and RFC 1395 specifies almost all the options we add (except for option 121), *for BOOTP*, in a way that's compatible with DHCP.

> So I thought (which may be wrong) that
> DHCP is fully derived from bootp, and
> the difference is only in the tag fields
> being unspecified for bootp.
> You may argue that some "unknown" bootp
> client could fail unless the vendor are
> is zeroed out. But given that slirp does
> not zero out the area, there is no such
> fear.
Right -- let's keep the options then. But still, I think we should still merge that with the else if clauses as I mentioned in comment 1.
Comment 4 stsp 2023-08-29 08:03:47 UTC
OK, please use this patch then:

diff --git a/dhcp.c b/dhcp.c
index d04648c..c1ac95e 100644
--- a/dhcp.c
+++ b/dhcp.c
@@ -323,8 +323,8 @@ int dhcp(const struct ctx *c, const struct pool *p)
        if (opts[53].c[0] == DHCPDISCOVER) {
                info("DHCP: offer to discover");
                opts[53].s[0] = DHCPOFFER;
-       } else if (opts[53].c[0] == DHCPREQUEST) {
-               info("DHCP: ack to request");
+       } else if (opts[53].c[0] == DHCPREQUEST || !opts[53].clen) {
+               info("%s: ack to request", opts[53].clen ? "DHCP" : "BOOTP");
                opts[53].s[0] = DHCPACK;
        } else {
                return -1;
diff --git a/tap.c b/tap.c
Comment 5 Stefano Brivio 2023-08-29 08:56:24 UTC
Thanks, it looks good to me... can you post it for review to the development mailing list (passt-dev@passt.top) using git send-email with proper tags (see examples from the archive, https://archives.passt.top/passt-dev/)?

If it's too much of a hassle I can also post it there with your nickname as author, let me know.
Comment 6 stsp 2023-08-29 09:15:11 UTC
Done, patch sent.
Comment 7 Stefano Brivio 2023-08-29 16:16:09 UTC
Thanks!
Comment 8 stsp 2023-08-29 17:10:45 UTC
> It would be interesting (maybe as part of bug 72)
> to have more details of your DHCP issues, yes.

OK, so to reproduce the DHCP problem, you
need to install this:
https://copr.fedorainfracloud.org/coprs/stsp/dosemu2/
(I am pretty sure you use fedora, so not
pointing to the ubuntu PPA).
You need to install dosemu2 and the
install-freedos package.
After doing so, you can run dosemu and
type `insfdusr` to call the installer.
If that succeeds, you can CD into
`c:\net\gopherus` and run gopherus.exe -
the gopher client. It should work fine
because slirp is used at that point.
After every start of gopherus.exe you
need to check and rempve `C:\tmp\w32dhcp.tmp`
or it will not initiate the DHCP session,
taking the cached values instead.

Now if that all works, you can enable passt.
You need to create `~/.dosemu/dosemurc`
with this content:
`$_netsock = "/tmp/passt_1.socket"`
And you need to start passt.
Now when you start gopherus.exe, you
should see that passt receives the
DHCP packets and sends replies, but
unfortunately gopherus ignores them.
It works perfectly with slirp, so I
suspect the bug is on a passt side.
But I can't be sure because I know
slirp has kludges for ancient DHCP
clients. It has a long history working
through all of them. :)
Comment 9 stsp 2023-08-29 17:21:37 UTC
If you run dosemu with `-D9+Pn`
switch in cmdline, then in
~/.dosemu/boot.log you can see
the full dump of the network
packets. So you can even compare
the traffic with slirp and with
passt. One obvious thing is that
slirp replies to broadcast address,
whereas passt replies to unicast.
But I think I tried to change
that and it didn't help (unless
I changed it the wrong way somehow).
Comment 10 Stefano Brivio 2023-10-10 12:11:31 UTC
stsp, is this solved by the specific patch plus the solution for https://bugs.passt.top/show_bug.cgi?id=77 ?
Comment 11 stsp 2023-10-10 13:37:27 UTC
Indeed.
Comment 12 stsp 2024-03-19 18:18:26 UTC
I am pretty sure this was fixed.

Note You need to log in before you can comment on or make changes to this bug.