relay role implementation
This commit is contained in:
committed by
Zbigniew Jędrzejewski-Szmek
parent
d5bfddf037
commit
c95df5879e
@@ -39,6 +39,7 @@ typedef struct DHCPLease {
|
||||
} DHCPLease;
|
||||
|
||||
struct sd_dhcp_server {
|
||||
struct in_addr relay_target;
|
||||
unsigned n_ref;
|
||||
|
||||
sd_event *event;
|
||||
|
||||
@@ -114,6 +114,12 @@ int sd_dhcp_server_is_running(sd_dhcp_server *server) {
|
||||
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);
|
||||
@@ -343,10 +349,27 @@ static int dhcp_server_send_udp(sd_dhcp_server *server, be32_t destination,
|
||||
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,
|
||||
@@ -404,20 +427,8 @@ 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,
|
||||
@@ -701,6 +712,47 @@ static int get_pool_offset(sd_dhcp_server *server, be32_t requested_ip) {
|
||||
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,
|
||||
@@ -999,10 +1051,15 @@ static int server_receive_message(sd_event_source *s, int fd,
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
@@ -1238,3 +1295,14 @@ int sd_dhcp_server_set_callback(sd_dhcp_server *server, sd_dhcp_server_callback_
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user