networkd/dhcpserver: Save and expose the client hostname sent when requesting a DHCP lease.

DHCP clients often send their own hostname in option 12. It can be useful
to store it with the lease so it can be shown to a human to easily identify
which lease belongs to which device.

RFC: https://www.rfc-editor.org/rfc/rfc2132#section-3.14
This commit is contained in:
Rene Hollander
2023-06-01 06:51:45 +09:00
committed by Yu Watanabe
parent 89e9df12b3
commit 930133d5df
7 changed files with 72 additions and 47 deletions
+2
View File
@@ -59,6 +59,8 @@ typedef int (*dhcp_option_callback_t)(uint8_t code, uint8_t len,
int dhcp_option_parse(DHCPMessage *message, size_t len, dhcp_option_callback_t cb, void *userdata, char **error_message);
int dhcp_option_parse_string(const uint8_t *option, size_t len, char **ret);
int dhcp_message_init(DHCPMessage *message, uint8_t op, uint32_t xid,
uint8_t type, uint16_t arp_type, uint8_t hlen, const uint8_t *chaddr,
size_t optlen, size_t *optoffset);
+25
View File
@@ -394,6 +394,31 @@ int dhcp_option_parse(DHCPMessage *message, size_t len, dhcp_option_callback_t c
return message_type;
}
int dhcp_option_parse_string(const uint8_t *option, size_t len, char **ret) {
int r;
assert(option);
assert(ret);
if (len <= 0)
*ret = mfree(*ret);
else {
char *string;
/*
* One trailing NUL byte is OK, we don't mind. See:
* https://github.com/systemd/systemd/issues/1337
*/
r = make_cstring((const char *) option, len, MAKE_CSTRING_ALLOW_TRAILING_NUL, &string);
if (r < 0)
return r;
free_and_replace(*ret, string);
}
return 0;
}
static sd_dhcp_option* dhcp_option_free(sd_dhcp_option *i) {
if (!i)
return NULL;
@@ -40,6 +40,7 @@ typedef struct DHCPLease {
be32_t gateway;
uint8_t chaddr[16];
usec_t expiration;
char *hostname;
} DHCPLease;
struct sd_dhcp_server {
@@ -102,6 +103,7 @@ typedef struct DHCPRequest {
be32_t requested_ip;
uint32_t lifetime;
const uint8_t *agent_info_option;
char *hostname;
} DHCPRequest;
extern const struct hash_ops dhcp_lease_hash_ops;
+4 -28
View File
@@ -13,6 +13,7 @@
#include "sd-dhcp-lease.h"
#include "alloc-util.h"
#include "dhcp-internal.h"
#include "dhcp-lease-internal.h"
#include "dhcp-protocol.h"
#include "dns-domain.h"
@@ -375,31 +376,6 @@ static int lease_parse_be32(const uint8_t *option, size_t len, be32_t *ret) {
return 0;
}
static int lease_parse_string(const uint8_t *option, size_t len, char **ret) {
int r;
assert(option);
assert(ret);
if (len <= 0)
*ret = mfree(*ret);
else {
char *string;
/*
* One trailing NUL byte is OK, we don't mind. See:
* https://github.com/systemd/systemd/issues/1337
*/
r = make_cstring((const char*) option, len, MAKE_CSTRING_ALLOW_TRAILING_NUL, &string);
if (r < 0)
return r;
free_and_replace(*ret, string);
}
return 0;
}
static int lease_parse_domain(const uint8_t *option, size_t len, char **ret) {
_cleanup_free_ char *name = NULL, *normalized = NULL;
int r;
@@ -407,7 +383,7 @@ static int lease_parse_domain(const uint8_t *option, size_t len, char **ret) {
assert(option);
assert(ret);
r = lease_parse_string(option, len, &name);
r = dhcp_option_parse_string(option, len, &name);
if (r < 0)
return r;
if (!name) {
@@ -741,7 +717,7 @@ int dhcp_lease_parse_options(uint8_t code, uint8_t len, const void *option, void
break;
case SD_DHCP_OPTION_ROOT_PATH:
r = lease_parse_string(option, len, &lease->root_path);
r = dhcp_option_parse_string(option, len, &lease->root_path);
if (r < 0)
log_debug_errno(r, "Failed to parse root path, ignoring: %m");
break;
@@ -767,7 +743,7 @@ int dhcp_lease_parse_options(uint8_t code, uint8_t len, const void *option, void
case SD_DHCP_OPTION_TZDB_TIMEZONE: {
_cleanup_free_ char *tz = NULL;
r = lease_parse_string(option, len, &tz);
r = dhcp_option_parse_string(option, len, &tz);
if (r < 0) {
log_debug_errno(r, "Failed to parse timezone option, ignoring: %m");
return 0;
+30 -19
View File
@@ -39,6 +39,7 @@ DHCPLease *dhcp_lease_free(DHCPLease *lease) {
}
free(lease->client_id.data);
free(lease->hostname);
return mfree(lease);
}
@@ -729,6 +730,7 @@ static int server_send_forcerenew(
static int parse_request(uint8_t code, uint8_t len, const void *option, void *userdata) {
DHCPRequest *req = ASSERT_PTR(userdata);
int r;
switch (code) {
case SD_DHCP_OPTION_IP_ADDRESS_LEASE_TIME:
@@ -768,6 +770,14 @@ static int parse_request(uint8_t code, uint8_t len, const void *option, void *us
case SD_DHCP_OPTION_RELAY_AGENT_INFORMATION:
req->agent_info_option = (uint8_t*)option - 2;
break;
case SD_DHCP_OPTION_HOST_NAME:
r = dhcp_option_parse_string(option, len, &req->hostname);
if (r < 0) {
log_debug_errno(r, "Failed to parse hostname, ignoring: %m");
return 0;
}
break;
}
@@ -779,6 +789,7 @@ static DHCPRequest* dhcp_request_free(DHCPRequest *req) {
return NULL;
free(req->client_id.data);
free(req->hostname);
return mfree(req);
}
@@ -937,35 +948,37 @@ static int dhcp_server_relay_message(sd_dhcp_server *server, DHCPMessage *messag
return -EBADMSG;
}
static int prepare_new_lease(
DHCPLease **ret_lease,
be32_t address,
const DHCPClientId *client_id,
uint8_t htype,
uint8_t hlen,
const uint8_t *chaddr,
be32_t gateway,
usec_t expiration) {
static int prepare_new_lease(DHCPLease **ret_lease, be32_t address, DHCPRequest *req, usec_t expiration) {
_cleanup_(dhcp_lease_freep) DHCPLease *lease = NULL;
assert(ret_lease);
assert(address != 0);
assert(req);
assert(expiration != 0);
lease = new(DHCPLease, 1);
if (!lease)
return -ENOMEM;
*lease = (DHCPLease) {
.address = address,
.client_id.length = client_id->length,
.htype = htype,
.hlen = hlen,
.gateway = gateway,
.client_id.length = req->client_id.length,
.htype = req->message->htype,
.hlen = req->message->hlen,
.gateway = req->message->giaddr,
.expiration = expiration,
};
lease->client_id.data = memdup(client_id->data, client_id->length);
lease->client_id.data = memdup(req->client_id.data, req->client_id.length);
if (!lease->client_id.data)
return -ENOMEM;
memcpy(lease->chaddr, chaddr, hlen);
memcpy(lease->chaddr, req->message->chaddr, req->message->hlen);
if (req->hostname) {
lease->hostname = strdup(req->hostname);
if (!lease->hostname)
return -ENOMEM;
}
*ret_lease = TAKE_PTR(lease);
@@ -994,9 +1007,7 @@ static int server_ack_request(sd_dhcp_server *server, DHCPRequest *req, DHCPLeas
} else {
_cleanup_(dhcp_lease_freep) DHCPLease *lease = NULL;
r = prepare_new_lease(&lease, address, &req->client_id,
req->message->htype, req->message->hlen,
req->message->chaddr, req->message->giaddr, expiration);
r = prepare_new_lease(&lease, address, req, expiration);
if (r < 0)
return log_dhcp_server_errno(server, r, "Failed to create new lease: %m");
@@ -98,6 +98,11 @@ static void test_message_handler(void) {
uint8_t length;
uint8_t id[7];
} _packed_ option_client_id;
struct {
uint8_t code;
uint8_t length;
uint8_t hostname[6];
} _packed_ option_hostname;
uint8_t end;
} _packed_ test = {
.message.op = BOOTREQUEST,
@@ -108,6 +113,9 @@ static void test_message_handler(void) {
.option_type.code = SD_DHCP_OPTION_MESSAGE_TYPE,
.option_type.length = 1,
.option_type.type = DHCP_DISCOVER,
.option_hostname.code = SD_DHCP_OPTION_HOST_NAME,
.option_hostname.length = 6,
.option_hostname.hostname = { 'T', 'E', 'S', 'T', 'H', 'N' },
.end = SD_DHCP_OPTION_END,
};
struct in_addr address_lo = {
+1
View File
@@ -1191,6 +1191,7 @@ static int dhcp_server_offered_leases_build_json(Link *link, JsonVariant **ret)
lease->client_id.data,
lease->client_id.length),
JSON_BUILD_PAIR_IN4_ADDR_NON_NULL("Address", &address),
JSON_BUILD_PAIR_STRING_NON_EMPTY("Hostname", lease->hostname),
JSON_BUILD_PAIR_FINITE_USEC(
"ExpirationUSec", lease->expiration)));
if (r < 0)