return !!server->receive_message;
}
+int sd_dhcp_server_is_in_relay_mode(sd_dhcp_server *server) {
+ assert_return(server, -EINVAL);
+
+ return in4_addr_is_set(&server->relay_target);
+}
+
void client_id_hash_func(const DHCPClientId *id, struct siphash *state) {
assert(id);
assert(id->length);
return 0;
}
-static bool requested_broadcast(DHCPRequest *req) {
- assert(req);
+static bool requested_broadcast(DHCPMessage *message) {
+ assert(message);
+ return message->flags & htobe16(0x8000);
+}
- return req->message->flags & htobe16(0x8000);
+static int dhcp_server_send(sd_dhcp_server *server, be32_t destination, uint16_t destination_port,
+ 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, packet,
+ sizeof(DHCPPacket) + optoffset);
}
int dhcp_server_send_packet(sd_dhcp_server *server,
} else if (req->message->ciaddr && type != DHCP_NAK)
destination = req->message->ciaddr;
- if (destination != INADDR_ANY)
- return dhcp_server_send_udp(server, destination,
- destination_port, &packet->dhcp,
- sizeof(DHCPMessage) + optoffset);
- else if (requested_broadcast(req) || type == DHCP_NAK)
- 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, packet,
- sizeof(DHCPPacket) + optoffset);
+ bool l2_broadcast = requested_broadcast(req->message) || type == DHCP_NAK;
+ return dhcp_server_send(server, destination, destination_port, packet, optoffset, l2_broadcast);
}
static int server_message_init(sd_dhcp_server *server, DHCPPacket **ret,
return be32toh(requested_ip & ~server->netmask) - server->pool_offset;
}
+static int dhcp_server_relay_message(sd_dhcp_server *server, DHCPMessage *message, size_t opt_length) {
+ _cleanup_free_ DHCPPacket *packet = NULL;
+
+ assert(server);
+ assert(message);
+ assert(sd_dhcp_server_is_in_relay_mode(server));
+
+ if (message->op == BOOTREQUEST) {
+ log_dhcp_server(server, "(relay agent) BOOTREQUEST (0x%x)", be32toh(message->xid));
+ if (message->hops >= 16)
+ return -ETIME;
+ message->hops++;
+
+ /* https://tools.ietf.org/html/rfc1542#section-4.1.1 */
+ if (message->giaddr == 0)
+ message->giaddr = server->address;
+
+ return dhcp_server_send_udp(server, server->relay_target.s_addr, DHCP_PORT_SERVER, message, sizeof(DHCPMessage) + opt_length);
+ } else if (message->op == BOOTREPLY) {
+ log_dhcp_server(server, "(relay agent) BOOTREPLY (0x%x)", be32toh(message->xid));
+ if (message->giaddr != server->address) {
+ return log_dhcp_server_errno(server, SYNTHETIC_ERRNO(EBADMSG),
+ "(relay agent) BOOTREPLY giaddr mismatch, discarding");
+ }
+
+ int message_type = dhcp_option_parse(message, sizeof(DHCPMessage) + opt_length, NULL, NULL, NULL);
+ if (message_type < 0)
+ return message_type;
+
+ packet = malloc0(sizeof(DHCPPacket) + opt_length);
+ if (!packet)
+ return -ENOMEM;
+ memcpy(&packet->dhcp, message, sizeof(DHCPMessage) + opt_length);
+
+ bool l2_broadcast = requested_broadcast(message) || message_type == DHCP_NAK;
+ const be32_t destination = message_type == DHCP_NAK ? INADDR_ANY : message->ciaddr;
+ return dhcp_server_send(server, destination, DHCP_PORT_CLIENT, packet, opt_length, l2_broadcast);
+ }
+ return -EBADMSG;
+}
+
#define HASH_KEY SD_ID128_MAKE(0d,1d,fe,bd,f1,24,bd,b3,47,f1,dd,6e,73,21,93,30)
int dhcp_server_handle_message(sd_dhcp_server *server, DHCPMessage *message,
}
}
- r = dhcp_server_handle_message(server, message, (size_t) len);
- if (r < 0)
- log_dhcp_server_errno(server, r, "Couldn't process incoming message: %m");
-
+ if (sd_dhcp_server_is_in_relay_mode(server)) {
+ r = dhcp_server_relay_message(server, message, len - sizeof(DHCPMessage));
+ if (r < 0)
+ log_dhcp_server_errno(server, r, "Couldn't relay message: %m");
+ } else {
+ r = dhcp_server_handle_message(server, message, (size_t) len);
+ if (r < 0)
+ log_dhcp_server_errno(server, r, "Couldn't process incoming message: %m");
+ }
return 0;
}
return 0;
}
+
+int sd_dhcp_server_set_relay_target(sd_dhcp_server *server, const struct in_addr* address) {
+ assert_return(server, -EINVAL);
+ assert_return(!sd_dhcp_server_is_running(server), -EBUSY);
+
+ if (memcmp(address, &server->relay_target, sizeof(struct in_addr)) == 0)
+ return 0;
+
+ server->relay_target = *address;
+ return 1;
+}
self.assertRegex(output, '192.168.5.*')
self.assertRegex(output, 'Europe/Berlin')
+class NetworkdDHCPServerRelayAgentTests(unittest.TestCase, Utilities):
+ links = [
+ 'client',
+ 'server',
+ 'client-peer',
+ 'server-peer',
+ ]
+
+ units = [
+ 'agent-veth-client.netdev',
+ 'agent-veth-server.netdev',
+ 'agent-client.network',
+ 'agent-server.network',
+ 'agent-client-peer.network',
+ 'agent-server-peer.network',
+ ]
+
+ def setUp(self):
+ remove_links(self.links)
+ stop_networkd(show_logs=False)
+
+ def tearDown(self):
+ remove_links(self.links)
+ remove_unit_from_networkd_path(self.units)
+ stop_networkd(show_logs=True)
+
+ def test_relay_agent(self):
+ copy_unit_to_networkd_unit_path(*self.units)
+ start_networkd()
+
+ #Test is disabled until BindToInterface DHCP server configuration option is supported
+ self.wait_online(['client:routable'])
+
+ output = check_output(*networkctl_cmd, '-n', '0', 'status', 'client', env=env)
+ print(output)
+ self.assertRegex(output, 'Address: 192.168.5.150 \(DHCP4 via 192.168.5.1\)')
+
class NetworkdDHCPClientTests(unittest.TestCase, Utilities):
links = [
'veth99',