From: Yu Watanabe Date: Mon, 4 May 2026 13:08:40 +0000 (+0900) Subject: dhcp-server-send: rework sending DHCP reply message X-Git-Tag: v261-rc1~22^2~3 X-Git-Url: http://git.ipfire.org/gitweb/index.cgi?a=commitdiff_plain;h=dae51cc08e2a273481f8563ea56a52083cee53ae;p=thirdparty%2Fsystemd.git dhcp-server-send: rework sending DHCP reply message This should mostly not change anything, except for some corner cases. Just refactoring. --- diff --git a/src/libsystemd-network/dhcp-server-send.c b/src/libsystemd-network/dhcp-server-send.c index 11d553404c5..a7b827f56e6 100644 --- a/src/libsystemd-network/dhcp-server-send.c +++ b/src/libsystemd-network/dhcp-server-send.c @@ -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) { - - 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); + size_t 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(