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-04 22:08:40 +09:00
parent 29b662184b
commit dae51cc08e

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 clients 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 clients 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(