From c0fbc7ef2ae2919bf6162b4149d341f448289836 Mon Sep 17 00:00:00 2001
From: Stefano Brivio <sbrivio@redhat.com>
Date: Mon, 25 Nov 2024 00:52:57 +0100
Subject: dhcp: Honour broadcast flag (RFC 2131, 4.1)

It's widely considered a legacy option nowadays, and I've haven't seen
clients setting it since Windows 95, but it's convenient for a minimal
DHCP client not using raw IP sockets such as what I'm playing with for
muvm.

Signed-off-by: Stefano Brivio <sbrivio@redhat.com>
Reviewed-by: David Gibson <david@gibson.dropbear.id.au>
---
 dhcp.c | 12 ++++++++++--
 ip.h   |  3 +++
 2 files changed, 13 insertions(+), 2 deletions(-)

diff --git a/dhcp.c b/dhcp.c
index a16cde8..d8515aa 100644
--- a/dhcp.c
+++ b/dhcp.c
@@ -112,6 +112,8 @@ struct msg {
 	uint32_t xid;
 	uint16_t secs;
 	uint16_t flags;
+#define FLAG_BROADCAST	htons_constant(0x8000)
+
 	uint32_t ciaddr;
 	struct in_addr yiaddr;
 	uint32_t siaddr;
@@ -285,10 +287,10 @@ int dhcp(const struct ctx *c, const struct pool *p)
 {
 	size_t mlen, dlen, offset = 0, opt_len, opt_off = 0;
 	char macstr[ETH_ADDRSTRLEN];
+	struct in_addr mask, dst;
 	const struct ethhdr *eh;
 	const struct iphdr *iph;
 	const struct udphdr *uh;
-	struct in_addr mask;
 	unsigned int i;
 	struct msg *m;
 
@@ -400,7 +402,13 @@ int dhcp(const struct ctx *c, const struct pool *p)
 		opt_set_dns_search(c, sizeof(m->o));
 
 	dlen = offsetof(struct msg, o) + fill(m);
-	tap_udp4_send(c, c->ip4.our_tap_addr, 67, c->ip4.addr, 68, m, dlen);
+
+	if (m->flags & FLAG_BROADCAST)
+		dst = in4addr_broadcast;
+	else
+		dst = c->ip4.addr;
+
+	tap_udp4_send(c, c->ip4.our_tap_addr, 67, dst, 68, m, dlen);
 
 	return 1;
 }
diff --git a/ip.h b/ip.h
index 0742612..1544dbf 100644
--- a/ip.h
+++ b/ip.h
@@ -101,4 +101,7 @@ static const struct in6_addr in6addr_ll_all_nodes = {
 	},
 };
 
+/* IPv4 Limited Broadcast (RFC 919, Section 7), 255.255.255.255 */
+static const struct in_addr in4addr_broadcast = { 0xffffffff };
+
 #endif /* IP_H */
-- 
cgit v1.2.3