udhcpd: support per-client hostnames in static leases
authorDenys Vlasenko <vda.linux@googlemail.com>
Thu, 16 May 2019 09:18:49 +0000 (11:18 +0200)
committerDenys Vlasenko <vda.linux@googlemail.com>
Thu, 16 May 2019 09:18:49 +0000 (11:18 +0200)
function                                             old     new   delta
read_staticlease                                     222     299     +77
add_server_options                                    92     154     +62
------------------------------------------------------------------------------
(add/remove: 0/0 grow/shrink: 2/0 up/down: 139/0)             Total: 139 bytes

Signed-off-by: Denys Vlasenko <vda.linux@googlemail.com>
examples/udhcp/udhcpd.conf
networking/udhcp/dhcpd.c
networking/udhcp/dhcpd.h

index bb8774e08b6a6c8facb5fff58e9f2b9646518208..df1258aafa44bea56247c3c4917ed0c99c6149bc 100644 (file)
@@ -44,7 +44,7 @@ interface     eth0
 #notify_file                   # default: no script
 #notify_file   dumpleases      # useful for debugging
 
-# The following are bootp specific options
+# The following are BOOTP specific options
 # next server to use in bootstrap
 #siaddr                192.168.0.22    # default: 0.0.0.0 (none)
 # tftp server name
@@ -52,9 +52,14 @@ interface    eth0
 # tftp file to download (e.g. kernel image)
 #boot_file     /var/nfs_root   # default: none
 
+# NOTE: "boot_file FILE" and "opt bootfile FILE" are conceptually the same,
+# but "boot_file" goes into BOOTP-defined fixed-size field in the packet,
+# whereas "opt bootfile" goes into DHCP option 0x43.
+# Same for "sname HOST" and "opt tftp HOST".
+
 # Static leases map
 #static_lease 00:60:08:11:CE:4E 192.168.0.54
-#static_lease 00:60:08:11:CE:3E 192.168.0.44
+#static_lease 00:60:08:11:CE:3E 192.168.0.44 optional_hostname
 
 # The remainder of options are DHCP options and can be specified with the
 # keyword 'opt' or 'option'. If an option can take multiple items, such
index bf44320a1975d6485227d6f425ccb9982918c2a6..f231e4001be34706da167c6dad32ed5401ced3fe 100644 (file)
 #define g_leases ((struct dyn_lease*)ptr_to_globals)
 /* struct server_config_t server_config is in bb_common_bufsiz1 */
 
+struct static_lease {
+       struct static_lease *next;
+       uint32_t nip;
+       uint8_t mac[6];
+       uint8_t opt[1];
+};
+
 /* Takes the address of the pointer to the static_leases linked list,
  * address to a 6 byte mac address,
  * 4 byte IP address */
 static void add_static_lease(struct static_lease **st_lease_pp,
                uint8_t *mac,
-               uint32_t nip)
+               uint32_t nip,
+               const char *opts)
 {
        struct static_lease *st_lease;
+       unsigned optlen;
 
        /* Find the tail of the list */
        while ((st_lease = *st_lease_pp) != NULL) {
@@ -63,10 +72,17 @@ static void add_static_lease(struct static_lease **st_lease_pp,
        }
 
        /* Add new node */
-       *st_lease_pp = st_lease = xzalloc(sizeof(*st_lease));
+       optlen = (opts ? 1+1+strnlen(opts, 120) : 0);
+       *st_lease_pp = st_lease = xzalloc(sizeof(*st_lease) + optlen);
        memcpy(st_lease->mac, mac, 6);
        st_lease->nip = nip;
        /*st_lease->next = NULL;*/
+       if (optlen) {
+               st_lease->opt[OPT_CODE] = DHCP_HOST_NAME;
+               optlen -= 2;
+               st_lease->opt[OPT_LEN] = optlen;
+               memcpy(&st_lease->opt[OPT_DATA], opts, optlen);
+       }
 }
 
 /* Find static lease IP by mac */
@@ -344,6 +360,7 @@ static int FAST_FUNC read_staticlease(const char *const_line, void *arg)
        char *line;
        char *mac_string;
        char *ip_string;
+       char *opts;
        struct ether_addr mac_bytes; /* it's "struct { uint8_t mac[6]; }" */
        uint32_t nip;
 
@@ -358,7 +375,10 @@ static int FAST_FUNC read_staticlease(const char *const_line, void *arg)
        if (!ip_string || !udhcp_str2nip(ip_string, &nip))
                return 0;
 
-       add_static_lease(arg, (uint8_t*) &mac_bytes, nip);
+       opts = strtok_r(NULL, " \t", &line);
+       /* opts might be NULL, that's not an error */
+
+       add_static_lease(arg, (uint8_t*) &mac_bytes, nip, opts);
 
        log_static_leases(arg);
 
@@ -626,14 +646,49 @@ static void init_packet(struct dhcp_packet *packet, struct dhcp_packet *oldpacke
  */
 static void add_server_options(struct dhcp_packet *packet)
 {
-       struct option_set *curr = server_config.options;
+       struct option_set *config_opts;
+       uint8_t *client_hostname_opt;
+
+       client_hostname_opt = NULL;
+       if (packet->yiaddr) { /* if we aren't from send_inform()... */
+               struct static_lease *st_lease = server_config.static_leases;
+               while (st_lease) {
+                       if (st_lease->nip == packet->yiaddr) {
+                               if (st_lease->opt[0] != 0)
+                                       client_hostname_opt = st_lease->opt;
+                               break;
+                       }
+                       st_lease = st_lease->next;
+               }
+       }
 
-       while (curr) {
-               if (curr->data[OPT_CODE] != DHCP_LEASE_TIME)
-                       udhcp_add_binary_option(packet, curr->data);
-               curr = curr->next;
+       config_opts = server_config.options;
+       while (config_opts) {
+               if (config_opts->data[OPT_CODE] != DHCP_LEASE_TIME) {
+                       /* ^^^^
+                        * DHCP_LEASE_TIME is already filled, or in case of
+                        * send_inform(), should not be filled at all.
+                        */
+                       if (config_opts->data[OPT_CODE] != DHCP_HOST_NAME
+                        || !client_hostname_opt
+                       ) {
+                               /* Why "!client_hostname_opt":
+                                * add hostname only if client has no hostname
+                                * on its static lease line.
+                                * (Not that "opt hostname HOST"
+                                * makes much sense in udhcpd.conf,
+                                * that'd give all clients the same hostname,
+                                * but it's a valid configuration).
+                                */
+                               udhcp_add_binary_option(packet, config_opts->data);
+                       }
+               }
+               config_opts = config_opts->next;
        }
 
+       if (client_hostname_opt)
+               udhcp_add_binary_option(packet, client_hostname_opt);
+
        packet->siaddr_nip = server_config.siaddr_nip;
 
        if (server_config.sname)
@@ -753,7 +808,6 @@ static NOINLINE void send_ACK(struct dhcp_packet *oldpacket, uint32_t yiaddr)
 
        lease_time_sec = select_lease_time(oldpacket);
        udhcp_add_simple_option(&packet, DHCP_LEASE_TIME, htonl(lease_time_sec));
-
        add_server_options(&packet);
 
        addr.s_addr = yiaddr;
index b8f96b02988f6d8d545aa1a302953c434e44d2a6..5c3bf514732e48c735896f7b90d0ca7912ce389e 100644 (file)
@@ -15,11 +15,7 @@ PUSH_AND_SET_FUNCTION_VISIBILITY_TO_HIDDEN
 #define DHCPD_CONF_FILE         "/etc/udhcpd.conf"
 
 
-struct static_lease {
-       struct static_lease *next;
-       uint32_t nip;
-       uint8_t mac[6];
-};
+struct static_lease;
 
 struct server_config_t {
        char *interface;                /* interface to use */