]> git.ipfire.org Git - thirdparty/systemd.git/commitdiff
networkd/dhcpserver: Save and expose the client hostname sent when requesting a DHCP...
authorRene Hollander <mail@renehollander.at>
Sat, 27 May 2023 11:33:37 +0000 (13:33 +0200)
committerYu Watanabe <watanabe.yu+github@gmail.com>
Wed, 31 May 2023 21:51:45 +0000 (06:51 +0900)
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

src/libsystemd-network/dhcp-internal.h
src/libsystemd-network/dhcp-option.c
src/libsystemd-network/dhcp-server-internal.h
src/libsystemd-network/sd-dhcp-lease.c
src/libsystemd-network/sd-dhcp-server.c
src/libsystemd-network/test-dhcp-server.c
src/network/networkd-json.c

index 5a7308c5ac3968ccbd4ae955fe4dcdf26951fce2..2cf9c756174bd1046d35a6ef241c0e48b4f5db35 100644 (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);
index a52259c238a480c64f1b3d348675d62460937e36..95b88e9840cfc0057c29249d173449c122495eae 100644 (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;
index 466d4541c368a07a1574cff6b4b9ca707aedd9c8..16b92545685a3fd55c82c048bff49df9270e0565 100644 (file)
@@ -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;
index b0a36324121835569471dd6cc35518d3ef92702a..fcc3f551549ea2ac9c0e29609796959fa4b086f7 100644 (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;
index 14ac1cf66f18e918a58594129e0be49ceaa2f2f8..df6812bb8a255a6f532012784c08271de6e204c8 100644 (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");
 
index a19735b420b22b9b812947fc27da8860af265e8d..c31a44e37709eb553c912b7779790dad953f0593 100644 (file)
@@ -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 = {
index 0aeab985aa9af03096e4a119b11cc8590fac9d16..15cd1a93f68c82720d5851f12dc6cd9286cf1e4d 100644 (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)