dhcp-server-send: rework sending DHCP reply message

This should mostly not change anything, except for some corner cases.
Just refactoring.
This commit is contained in:
Yu Watanabe
2026-05-22 04:30:34 +09:00
parent 29b662184b
commit dae51cc08e
+66 -62
View File
@@ -141,42 +141,79 @@ static int dhcp_server_send_udp(sd_dhcp_server *server, be32_t destination,
return 0;
}
static bool requested_broadcast(DHCPMessage *message) {
assert(message);
return message->flags & htobe16(0x8000);
}
static int dhcp_server_send(
static int dhcp_server_send_message(
sd_dhcp_server *server,
uint8_t hlen,
const uint8_t *chaddr,
be32_t destination,
uint16_t destination_port,
DHCPRequest *req,
uint8_t type,
DHCPPacket *packet,
size_t optoffset,
bool l2_broadcast) {
size_t optoffset) {
if (destination != INADDR_ANY)
return dhcp_server_send_udp(server, destination,
destination_port, &packet->dhcp,
sizeof(DHCPMessage) + optoffset);
else if (l2_broadcast)
return dhcp_server_send_udp(server, INADDR_BROADCAST,
destination_port, &packet->dhcp,
sizeof(DHCPMessage) + optoffset);
else
/* we cannot send UDP packet to specific MAC address when the
address is not yet configured, so must fall back to raw
packets */
return dhcp_server_send_unicast_raw(server, hlen, chaddr, packet,
sizeof(DHCPPacket) + optoffset);
assert(server);
assert(req);
assert(req->message);
assert(packet);
/* RFC 2131 Section 4.1 */
/* If the ’giaddr’ field in a DHCP message from a client is non-zero, the server sends any
* return messages to the ’DHCP server’ port on the BOOTP relay agent whose address appears
* in ’giaddr’. */
if (req->message->giaddr != INADDR_ANY)
return dhcp_server_send_udp(
server,
req->message->giaddr,
DHCP_PORT_SERVER,
&packet->dhcp,
sizeof(DHCPMessage) + optoffset);
/* when ’giaddr’ is zero, the server broadcasts any DHCPNAK messages to 0xffffffff. */
if (type == DHCP_NAK)
return dhcp_server_send_udp(
server,
INADDR_BROADCAST,
DHCP_PORT_CLIENT,
&packet->dhcp,
sizeof(DHCPMessage) + optoffset);
/* If the ’giaddr’ field is zero and the ’ciaddr’ field is nonzero, then the server unicasts
* DHCPOFFER and DHCPACK messages to the address in ’ciaddr’. */
if (req->message->ciaddr != INADDR_ANY)
return dhcp_server_send_udp(
server,
req->message->ciaddr,
DHCP_PORT_CLIENT,
&packet->dhcp,
sizeof(DHCPMessage) + optoffset);
/* If ’giaddr’ is zero and ’ciaddr’ is zero, and the broadcast bit is set, then the server
* broadcasts DHCPOFFER and DHCPACK messages to 0xffffffff.
*
* (Note, even the broadcast flag is unset, we may not know the client hardware address, e.g.
* InfiniBand. In that case, we cannot unicast in the below, so need to broadcast. Also, broadcast
* the message if 'yiaddr' is zero.) */
if (FLAGS_SET(be16toh(req->message->flags), 0x8000) ||
req->message->hlen == 0 || memeqzero(req->message->chaddr, req->message->hlen) ||
packet->dhcp.yiaddr == INADDR_ANY)
return dhcp_server_send_udp(
server,
INADDR_BROADCAST,
DHCP_PORT_CLIENT,
&packet->dhcp,
sizeof(DHCPMessage) + optoffset);
/* If the broadcast bit is not set and ’giaddr’ is zero and ’ciaddr’ is zero, then the server
* unicasts DHCPOFFER and DHCPACK messages to the client’s hardware address and ’yiaddr’ address. */
return dhcp_server_send_unicast_raw(
server,
req->message->hlen,
req->message->chaddr,
packet,
sizeof(DHCPPacket) + optoffset);
}
static int dhcp_server_send_packet(sd_dhcp_server *server,
DHCPRequest *req, DHCPPacket *packet,
int type, size_t optoffset) {
be32_t destination = INADDR_ANY;
uint16_t destination_port = DHCP_PORT_CLIENT;
int r;
assert(server);
@@ -206,40 +243,7 @@ static int dhcp_server_send_packet(sd_dhcp_server *server,
if (r < 0)
return r;
/* RFC 2131 Section 4.1
If the ’giaddr’ field in a DHCP message from a client is non-zero,
the server sends any return messages to the ’DHCP server’ port on the
BOOTP relay agent whose address appears in ’giaddr’. If the ’giaddr’
field is zero and the ’ciaddr’ field is nonzero, then the server
unicasts DHCPOFFER and DHCPACK messages to the address in ’ciaddr’.
If ’giaddr’ is zero and ’ciaddr’ is zero, and the broadcast bit is
set, then the server broadcasts DHCPOFFER and DHCPACK messages to
0xffffffff. If the broadcast bit is not set and ’giaddr’ is zero and
’ciaddr’ is zero, then the server unicasts DHCPOFFER and DHCPACK
messages to the client’s hardware address and ’yiaddr’ address. In
all cases, when ’giaddr’ is zero, the server broadcasts any DHCPNAK
messages to 0xffffffff.
Section 4.3.2
If ’giaddr’ is set in the DHCPREQUEST message, the client is on a
different subnet. The server MUST set the broadcast bit in the
DHCPNAK, so that the relay agent will broadcast the DHCPNAK to the
client, because the client may not have a correct network address
or subnet mask, and the client may not be answering ARP requests.
*/
if (req->message->giaddr != 0) {
destination = req->message->giaddr;
destination_port = DHCP_PORT_SERVER;
if (type == DHCP_NAK)
packet->dhcp.flags = htobe16(0x8000);
} else if (req->message->ciaddr != 0 && type != DHCP_NAK)
destination = req->message->ciaddr;
bool l2_broadcast = requested_broadcast(req->message) || type == DHCP_NAK;
return dhcp_server_send(server, req->message->hlen, req->message->chaddr,
destination, destination_port, packet, optoffset, l2_broadcast);
return dhcp_server_send_message(server, req, type, packet, optoffset);
}
static int server_message_init(