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:
committed by
Yu Watanabe
parent
89e9df12b3
commit
930133d5df
@@ -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);
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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 = {
|
||||
|
||||
@@ -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)
|
||||
|
||||
Reference in New Issue
Block a user