From: Rene Hollander Date: Sat, 27 May 2023 11:33:37 +0000 (+0200) Subject: networkd/dhcpserver: Save and expose the client hostname sent when requesting a DHCP... X-Git-Tag: v254-rc1~318 X-Git-Url: http://git.ipfire.org/gitweb.cgi?a=commitdiff_plain;h=930133d5df0674ba7cbf1bf5b838ba99cb27dc73;p=thirdparty%2Fsystemd.git 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 --- diff --git a/src/libsystemd-network/dhcp-internal.h b/src/libsystemd-network/dhcp-internal.h index 5a7308c5ac3..2cf9c756174 100644 --- a/src/libsystemd-network/dhcp-internal.h +++ b/src/libsystemd-network/dhcp-internal.h @@ -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); diff --git a/src/libsystemd-network/dhcp-option.c b/src/libsystemd-network/dhcp-option.c index a52259c238a..95b88e9840c 100644 --- a/src/libsystemd-network/dhcp-option.c +++ b/src/libsystemd-network/dhcp-option.c @@ -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; diff --git a/src/libsystemd-network/dhcp-server-internal.h b/src/libsystemd-network/dhcp-server-internal.h index 466d4541c36..16b92545685 100644 --- a/src/libsystemd-network/dhcp-server-internal.h +++ b/src/libsystemd-network/dhcp-server-internal.h @@ -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; diff --git a/src/libsystemd-network/sd-dhcp-lease.c b/src/libsystemd-network/sd-dhcp-lease.c index b0a36324121..fcc3f551549 100644 --- a/src/libsystemd-network/sd-dhcp-lease.c +++ b/src/libsystemd-network/sd-dhcp-lease.c @@ -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; diff --git a/src/libsystemd-network/sd-dhcp-server.c b/src/libsystemd-network/sd-dhcp-server.c index 14ac1cf66f1..df6812bb8a2 100644 --- a/src/libsystemd-network/sd-dhcp-server.c +++ b/src/libsystemd-network/sd-dhcp-server.c @@ -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"); diff --git a/src/libsystemd-network/test-dhcp-server.c b/src/libsystemd-network/test-dhcp-server.c index a19735b420b..c31a44e3770 100644 --- a/src/libsystemd-network/test-dhcp-server.c +++ b/src/libsystemd-network/test-dhcp-server.c @@ -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 = { diff --git a/src/network/networkd-json.c b/src/network/networkd-json.c index 0aeab985aa9..15cd1a93f68 100644 --- a/src/network/networkd-json.c +++ b/src/network/networkd-json.c @@ -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)